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