OLD | NEW |
(Empty) | |
| 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 re
sponse 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 } |
OLD | NEW |