Home | History | Annotate | Download | only in testserver
      1 // Test server to facilitate the data reduction proxy Telemetry tests.
      2 //
      3 // The server runs at http://chromeproxy-test.appspot.com/. Please contact
      4 // people in OWNERS for server issues.
      5 //
      6 // For running an AppEngine Go server, see:
      7 // https://developers.google.com/appengine/docs/go/gettingstarted/introduction.
      8 //
      9 // The goal is to keep the test logic on the client side (Telemetry)
     10 // as much as possible. This server will only return a resource
     11 // and/or override the response as specified by the data encoded
     12 // in the request URL queries.
     13 //
     14 // For example, on receiving the query
     15 // /default?respBody=bmV3IGJvZHk=&respHeader=eyJWaWEiOlsiVmlhMSIsIlZpYTIiXX0%3D&respStatus=204
     16 // the server sends back a response with
     17 //	Status code: 204
     18 //	Additional response headers: "Via: Via1" and "Via: Via2"
     19 //	Response body: "new body"
     20 // where the overriding headers and body are base64 encoded in the request query.
     21 
     22 package server
     23 
     24 import (
     25 	"bytes"
     26 	"encoding/base64"
     27 	"encoding/json"
     28 	"errors"
     29 	"fmt"
     30 	"io"
     31 	"net/http"
     32 	"os"
     33 	"strconv"
     34 )
     35 
     36 func init() {
     37 	http.HandleFunc("/requestHeader", requestHeader)
     38 	http.HandleFunc("/resource", resource)
     39 	http.HandleFunc("/default", defaultResponse)
     40 }
     41 
     42 // requestHander returns request headers in response body as text.
     43 func requestHeader(w http.ResponseWriter, r *http.Request) {
     44 	r.Header.Write(w)
     45 }
     46 
     47 // resource returns the content of a data file specified by "r=" query as the response body.
     48 // The response could be overridden by request queries.
     49 // See parseOverrideQuery.
     50 func resource(w http.ResponseWriter, r *http.Request) {
     51 	wroteBody, err := applyOverride(w, r)
     52 	if err != nil || wroteBody {
     53 		return
     54 	}
     55 	path, ok := r.URL.Query()["r"]
     56 	if !ok || len(path) != 1 {
     57 		w.WriteHeader(http.StatusBadRequest)
     58 		w.Write([]byte("no resource in query"))
     59 		return
     60 	}
     61 	if _, err := writeFromFile(w, path[0]); err != nil {
     62 		w.WriteHeader(http.StatusBadRequest)
     63 		w.Write([]byte(fmt.Sprintf("Failed to get %s: %v", path[0], err)))
     64 		return
     65 	}
     66 }
     67 
     68 // defaultResponse returns "ok" as response body, if the body is not overridden.
     69 // The response could be overridden by request queries.
     70 // See parseOverrideQuery.
     71 func defaultResponse(w http.ResponseWriter, r *http.Request) {
     72 	wroteBody, err := applyOverride(w, r)
     73 	if err != nil {
     74 		return
     75 	}
     76 	if !wroteBody {
     77 		w.Write([]byte("ok"))
     78 	}
     79 }
     80 
     81 type override struct {
     82 	status int
     83 	header http.Header
     84 	body   io.Reader
     85 }
     86 
     87 // parseOverrideQuery parses the queries in r and returns an override.
     88 // It supports the following queries:
     89 //   "respStatus": an integer to override response status code;
     90 //   "respHeader": base64 encoded JSON data to override the response headers;
     91 //   "respBody": base64 encoded JSON data to override the response body.
     92 func parseOverrideQuery(r *http.Request) (*override, error) {
     93 	q := r.URL.Query()
     94 	resp := &override{0, nil, nil}
     95 	if v, ok := q["respStatus"]; ok && len(v) == 1 && len(v[0]) > 0 {
     96 		status, err := strconv.ParseInt(v[0], 10, 0)
     97 		if err != nil {
     98 			return nil, errors.New(fmt.Sprintf("respStatus: %v", err))
     99 		}
    100 		resp.status = int(status)
    101 	}
    102 	if v, ok := q["respHeader"]; ok && len(v) == 1 && len(v[0]) > 0 {
    103 		// Example header after base64 decoding:
    104 		//  {"Via": ["Telemetry Test", "Test2"], "Name": ["XYZ"], "Cache-Control": ["public"]}
    105 		headerValue, err := base64.URLEncoding.DecodeString(v[0])
    106 		if err != nil {
    107 			return nil, errors.New(fmt.Sprintf("Decoding respHeader: %v", err))
    108 		}
    109 		var header http.Header
    110 		err = json.Unmarshal(headerValue, &header)
    111 		if err != nil {
    112 			return nil, errors.New(
    113 				fmt.Sprintf("Unmarlshal (%s) error: %v", string(headerValue), err))
    114 		}
    115 		resp.header = header
    116 	}
    117 	if v, ok := q["respBody"]; ok && len(v) == 1 && len(v[0]) > 0 {
    118 		body, err := base64.URLEncoding.DecodeString(v[0])
    119 		if err != nil {
    120 			return nil, errors.New(
    121 				fmt.Sprintf("Decoding respBody error: %v", err))
    122 		}
    123 		resp.body = bytes.NewBuffer(body)
    124 	}
    125 	return resp, nil
    126 }
    127 
    128 // applyOverride applies the override queries in r to w and returns whether the response
    129 // body is overridden.
    130 func applyOverride(w http.ResponseWriter, r *http.Request) (wroteBody bool, err error) {
    131 	resp, err := parseOverrideQuery(r)
    132 	if err != nil {
    133 		w.WriteHeader(http.StatusBadRequest)
    134 		w.Write([]byte(err.Error()))
    135 		return false, err
    136 	}
    137 	headers := w.Header()
    138 	if resp.header != nil {
    139 		for k, v := range resp.header {
    140 			headers[k] = v
    141 		}
    142 	}
    143 	if resp.status > 0 {
    144 		w.WriteHeader(resp.status)
    145 	}
    146 	if resp.body != nil {
    147 		_, err := io.Copy(w, resp.body)
    148 		return true, err
    149 	}
    150 	return false, nil
    151 }
    152 
    153 func writeFromFile(w io.Writer, filename string) (int64, error) {
    154 	f, err := os.Open(filename)
    155 	if err != nil {
    156 		return 0, err
    157 	}
    158 	return io.Copy(w, f)
    159 }
    160