github.com/alejandroesc/spdy@v0.0.0-20200317064415-01a02f0eb389/client_test.go (about) 1 // Copyright 2014 Jamie Hall. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package spdy_test 6 7 import ( 8 "crypto/tls" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "log" 13 "net/http" 14 "net/http/httptest" 15 "strings" 16 "sync" 17 "testing" 18 19 "github.com/SlyMarbo/spdy" 20 ) 21 22 func init() { 23 // spdy.EnableDebugOutput() 24 } 25 26 func TestClient(t *testing.T) { 27 ts := newServer(robotsTxtHandler) 28 defer ts.Close() 29 30 client := newClient() 31 32 r, err := client.Get(ts.URL) 33 var b []byte 34 if err == nil { 35 b, err = pedanticReadAll(r.Body) 36 r.Body.Close() 37 } 38 if err != nil { 39 t.Error(err) 40 } else if s := string(b); !strings.HasPrefix(s, "User-agent:") { 41 t.Errorf("Incorrect page body (did not begin with User-agent): %q", s) 42 } 43 } 44 45 func TestClientHead(t *testing.T) { 46 ts := newServer(robotsTxtHandler) 47 defer ts.Close() 48 49 client := newClient() 50 r, err := client.Head(ts.URL) 51 if err != nil { 52 t.Fatal(err) 53 } 54 if _, ok := r.Header["Last-Modified"]; !ok { 55 t.Error("Last-Modified header not found.") 56 } 57 } 58 59 func TestClientInGoroutines(t *testing.T) { 60 ts := newServer(robotsTxtHandler) 61 ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0) // ignore messages 62 defer ts.Close() 63 64 client := spdy.NewClient(false) 65 66 var wg sync.WaitGroup 67 68 for i := 0; i < 10; i++ { 69 wg.Add(1) 70 go func() { 71 defer wg.Done() 72 var b []byte 73 r, err := client.Get(ts.URL) 74 if err == nil { 75 b, err = pedanticReadAll(r.Body) 76 r.Body.Close() 77 } 78 if err != nil { 79 // We've turned off InsecureSkipVerify, so 80 // ignore bad cert warnings. 81 if !strings.Contains(err.Error(), "certificate signed by unknown authority") { 82 t.Error(err) 83 } 84 } else if s := string(b); !strings.HasPrefix(s, "User-agent:") { 85 t.Errorf("Incorrect page body (did not begin with User-agent): %q", s) 86 } 87 }() 88 } 89 90 wg.Wait() 91 } 92 93 // FIXME: Fails 94 // func TestClientRedirects(t *testing.T) { 95 // defer afterTest(t) 96 // var ts *httptest.Server 97 // ts = newServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 98 // n, _ := strconv.Atoi(r.FormValue("n")) 99 // // Test Referer header. (7 is arbitrary position to test at) 100 // if n == 7 { 101 // if g, e := r.Referer(), ts.URL+"/?n=6"; e != g { 102 // t.Errorf("on request ?n=7, expected referer of %q; got %q", e, g) 103 // } 104 // } 105 // if n < 15 { 106 // http.Redirect(w, r, fmt.Sprintf("/?n=%d", n+1), http.StatusFound) 107 // return 108 // } 109 // fmt.Fprintf(w, "n=%d", n) 110 // })) 111 // defer ts.Close() 112 113 // c := newClient() 114 // _, err := c.Get(ts.URL) 115 // if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g { 116 // t.Errorf("with default client Get, expected error %q, got %q", e, g) 117 // } 118 119 // // HEAD request should also have the ability to follow redirects. 120 // _, err = c.Head(ts.URL) 121 // if e, g := "Head /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g { 122 // t.Errorf("with default client Head, expected error %q, got %q", e, g) 123 // } 124 125 // // Do should also follow redirects. 126 // greq, _ := http.NewRequest("GET", ts.URL, nil) 127 // _, err = c.Do(greq) 128 // if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g { 129 // t.Errorf("with default client Do, expected error %q, got %q", e, g) 130 // } 131 132 // var checkErr error 133 // var lastVia []*http.Request 134 // c = newClient() 135 // c.CheckRedirect = func(_ *http.Request, via []*http.Request) error { 136 // lastVia = via 137 // return checkErr 138 // } 139 // res, err := c.Get(ts.URL) 140 // if err != nil { 141 // t.Fatalf("Get error: %v", err) 142 // } 143 // res.Body.Close() 144 // finalUrl := res.Request.URL.String() 145 // if e, g := "<nil>", fmt.Sprintf("%v", err); e != g { 146 // t.Errorf("with custom client, expected error %q, got %q", e, g) 147 // } 148 // if !strings.HasSuffix(finalUrl, "/?n=15") { 149 // t.Errorf("expected final url to end in /?n=15; got url %q", finalUrl) 150 // } 151 // if e, g := 15, len(lastVia); e != g { 152 // t.Errorf("expected lastVia to have contained %d elements; got %d", e, g) 153 // } 154 155 // checkErr = errors.New("no redirects allowed") 156 // res, err = c.Get(ts.URL) 157 // if urlError, ok := err.(*url.Error); !ok || urlError.Err != checkErr { 158 // t.Errorf("with redirects forbidden, expected a *url.Error with our 'no redirects allowed' error inside; got %#v (%q)", err, err) 159 // } 160 // if res == nil { 161 // t.Fatalf("Expected a non-nil Response on CheckRedirect failure (http://golang.org/issue/3795)") 162 // } 163 // res.Body.Close() 164 // if res.Header.Get("Location") == "" { 165 // t.Errorf("no Location header in Response") 166 // } 167 // } 168 169 // FIXME: Deadlocks 170 // func TestPostRedirects(t *testing.T) { 171 // defer afterTest(t) 172 // var log struct { 173 // sync.Mutex 174 // bytes.Buffer 175 // } 176 // var ts *httptest.Server 177 // ts = newServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 178 // log.Lock() 179 // fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI) 180 // log.Unlock() 181 // if v := r.URL.Query().Get("code"); v != "" { 182 // code, _ := strconv.Atoi(v) 183 // if code/100 == 3 { 184 // w.Header().Set("Location", ts.URL) 185 // } 186 // w.WriteHeader(code) 187 // } 188 // })) 189 // defer ts.Close() 190 // tests := []struct { 191 // suffix string 192 // want int // response code 193 // }{ 194 // {"/", 200}, 195 // {"/?code=301", 301}, 196 // {"/?code=302", 200}, 197 // {"/?code=303", 200}, 198 // {"/?code=404", 404}, 199 // } 200 // client := newClient() 201 // for _, tt := range tests { 202 // res, err := client.Post(ts.URL+tt.suffix, "text/plain", strings.NewReader("Some content")) 203 // if err != nil { 204 // t.Fatal(err) 205 // } 206 // if res.StatusCode != tt.want { 207 // t.Errorf("POST %s: status code = %d; want %d", tt.suffix, res.StatusCode, tt.want) 208 // } 209 // } 210 // log.Lock() 211 // got := log.String() 212 // log.Unlock() 213 // want := "POST / POST /?code=301 POST /?code=302 GET / POST /?code=303 GET / POST /?code=404 " 214 // if got != want { 215 // t.Errorf("Log differs.\n Got: %q\nWant: %q", got, want) 216 // } 217 // } 218 219 // FIXME: Fails (not a Flusher) 220 // func TestStreamingGet(t *testing.T) { 221 // defer afterTest(t) 222 // say := make(chan string) 223 // ts := newServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 224 // w.(http.Flusher).Flush() 225 // for str := range say { 226 // w.Write([]byte(str)) 227 // w.(http.Flusher).Flush() 228 // } 229 // })) 230 // defer ts.Close() 231 232 // c := newClient() 233 // res, err := c.Get(ts.URL + "/") 234 // if err != nil { 235 // t.Fatal(err) 236 // } 237 // var buf [10]byte 238 // for _, str := range []string{"i", "am", "also", "known", "as", "comet"} { 239 // say <- str 240 // n, err := io.ReadFull(res.Body, buf[0:len(str)]) 241 // if err != nil { 242 // t.Fatalf("ReadFull on %q: %v", str, err) 243 // } 244 // if n != len(str) { 245 // t.Fatalf("Receiving %q, only read %d bytes", str, n) 246 // } 247 // got := string(buf[0:n]) 248 // if got != str { 249 // t.Fatalf("Expected %q, got %q", str, got) 250 // } 251 // } 252 // close(say) 253 // _, err = io.ReadFull(res.Body, buf[0:1]) 254 // if err != io.EOF { 255 // t.Fatalf("at end expected EOF, got %v", err) 256 // } 257 // } 258 259 // 260 // HELPERS 261 // 262 263 var robotsTxtHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 264 w.Header().Set("Last-Modified", "sometime") 265 fmt.Fprintf(w, "User-agent: go\nDisallow: /something/") 266 }) 267 268 // pedanticReadAll works like ioutil.ReadAll but additionally 269 // verifies that r obeys the documented io.Reader contract. 270 func pedanticReadAll(r io.Reader) (b []byte, err error) { 271 var bufa [64]byte 272 buf := bufa[:] 273 for { 274 n, err := r.Read(buf) 275 if n == 0 && err == nil { 276 return nil, fmt.Errorf("Read: n=0 with err=nil") 277 } 278 b = append(b, buf[:n]...) 279 if err == io.EOF { 280 n, err := r.Read(buf) 281 if n != 0 || err != io.EOF { 282 return nil, fmt.Errorf("Read: n=%d err=%#v after EOF", n, err) 283 } 284 return b, nil 285 } 286 if err != nil { 287 return b, err 288 } 289 } 290 } 291 292 func newServer(handler http.Handler) *httptest.Server { 293 ts := httptest.NewUnstartedServer(handler) 294 spdy.AddSPDY(ts.Config) 295 ts.TLS = ts.Config.TLSConfig 296 ts.StartTLS() 297 return ts 298 } 299 300 func newClient() *http.Client { 301 tr := spdy.Transport{ 302 TLSClientConfig: &tls.Config{ 303 InsecureSkipVerify: true, 304 NextProtos: []string{"spdy/3.1"}, 305 }, 306 } 307 return &http.Client{Transport: &tr} 308 }