• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
22package server
23
24import (
25	"bytes"
26	"encoding/base64"
27	"encoding/json"
28	"errors"
29	"fmt"
30	"io"
31	"net/http"
32	"os"
33	"strconv"
34)
35
36func 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.
43func 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.
50func 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.
71func 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
81type 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.
92func 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.
130func 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
153func 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