github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/net/http/fcgi/fcgi_test.go (about) 1 // Copyright 2011 The Go Authors. 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 fcgi 6 7 import ( 8 "bytes" 9 "errors" 10 "io" 11 "net/http" 12 "strings" 13 "testing" 14 "time" 15 ) 16 17 var sizeTests = []struct { 18 size uint32 19 bytes []byte 20 }{ 21 {0, []byte{0x00}}, 22 {127, []byte{0x7F}}, 23 {128, []byte{0x80, 0x00, 0x00, 0x80}}, 24 {1000, []byte{0x80, 0x00, 0x03, 0xE8}}, 25 {33554431, []byte{0x81, 0xFF, 0xFF, 0xFF}}, 26 } 27 28 func TestSize(t *testing.T) { 29 b := make([]byte, 4) 30 for i, test := range sizeTests { 31 n := encodeSize(b, test.size) 32 if !bytes.Equal(b[:n], test.bytes) { 33 t.Errorf("%d expected %x, encoded %x", i, test.bytes, b) 34 } 35 size, n := readSize(test.bytes) 36 if size != test.size { 37 t.Errorf("%d expected %d, read %d", i, test.size, size) 38 } 39 if len(test.bytes) != n { 40 t.Errorf("%d did not consume all the bytes", i) 41 } 42 } 43 } 44 45 var streamTests = []struct { 46 desc string 47 recType recType 48 reqId uint16 49 content []byte 50 raw []byte 51 }{ 52 {"single record", typeStdout, 1, nil, 53 []byte{1, byte(typeStdout), 0, 1, 0, 0, 0, 0}, 54 }, 55 // this data will have to be split into two records 56 {"two records", typeStdin, 300, make([]byte, 66000), 57 bytes.Join([][]byte{ 58 // header for the first record 59 {1, byte(typeStdin), 0x01, 0x2C, 0xFF, 0xFF, 1, 0}, 60 make([]byte, 65536), 61 // header for the second 62 {1, byte(typeStdin), 0x01, 0x2C, 0x01, 0xD1, 7, 0}, 63 make([]byte, 472), 64 // header for the empty record 65 {1, byte(typeStdin), 0x01, 0x2C, 0, 0, 0, 0}, 66 }, 67 nil), 68 }, 69 } 70 71 type nilCloser struct { 72 io.ReadWriter 73 } 74 75 func (c *nilCloser) Close() error { return nil } 76 77 func TestStreams(t *testing.T) { 78 var rec record 79 outer: 80 for _, test := range streamTests { 81 buf := bytes.NewBuffer(test.raw) 82 var content []byte 83 for buf.Len() > 0 { 84 if err := rec.read(buf); err != nil { 85 t.Errorf("%s: error reading record: %v", test.desc, err) 86 continue outer 87 } 88 content = append(content, rec.content()...) 89 } 90 if rec.h.Type != test.recType { 91 t.Errorf("%s: got type %d expected %d", test.desc, rec.h.Type, test.recType) 92 continue 93 } 94 if rec.h.Id != test.reqId { 95 t.Errorf("%s: got request ID %d expected %d", test.desc, rec.h.Id, test.reqId) 96 continue 97 } 98 if !bytes.Equal(content, test.content) { 99 t.Errorf("%s: read wrong content", test.desc) 100 continue 101 } 102 buf.Reset() 103 c := newConn(&nilCloser{buf}) 104 w := newWriter(c, test.recType, test.reqId) 105 if _, err := w.Write(test.content); err != nil { 106 t.Errorf("%s: error writing record: %v", test.desc, err) 107 continue 108 } 109 if err := w.Close(); err != nil { 110 t.Errorf("%s: error closing stream: %v", test.desc, err) 111 continue 112 } 113 if !bytes.Equal(buf.Bytes(), test.raw) { 114 t.Errorf("%s: wrote wrong content", test.desc) 115 } 116 } 117 } 118 119 type writeOnlyConn struct { 120 buf []byte 121 } 122 123 func (c *writeOnlyConn) Write(p []byte) (int, error) { 124 c.buf = append(c.buf, p...) 125 return len(p), nil 126 } 127 128 func (c *writeOnlyConn) Read(p []byte) (int, error) { 129 return 0, errors.New("conn is write-only") 130 } 131 132 func (c *writeOnlyConn) Close() error { 133 return nil 134 } 135 136 func TestGetValues(t *testing.T) { 137 var rec record 138 rec.h.Type = typeGetValues 139 140 wc := new(writeOnlyConn) 141 c := newChild(wc, nil) 142 err := c.handleRecord(&rec) 143 if err != nil { 144 t.Fatalf("handleRecord: %v", err) 145 } 146 147 const want = "\x01\n\x00\x00\x00\x12\x06\x00" + 148 "\x0f\x01FCGI_MPXS_CONNS1" + 149 "\x00\x00\x00\x00\x00\x00\x01\n\x00\x00\x00\x00\x00\x00" 150 if got := string(wc.buf); got != want { 151 t.Errorf(" got: %q\nwant: %q\n", got, want) 152 } 153 } 154 155 func nameValuePair11(nameData, valueData string) []byte { 156 return bytes.Join( 157 [][]byte{ 158 {byte(len(nameData)), byte(len(valueData))}, 159 []byte(nameData), 160 []byte(valueData), 161 }, 162 nil, 163 ) 164 } 165 166 func makeRecord( 167 recordType recType, 168 requestId uint16, 169 contentData []byte, 170 ) []byte { 171 requestIdB1 := byte(requestId >> 8) 172 requestIdB0 := byte(requestId) 173 174 contentLength := len(contentData) 175 contentLengthB1 := byte(contentLength >> 8) 176 contentLengthB0 := byte(contentLength) 177 return bytes.Join([][]byte{ 178 {1, byte(recordType), requestIdB1, requestIdB0, contentLengthB1, 179 contentLengthB0, 0, 0}, 180 contentData, 181 }, 182 nil) 183 } 184 185 // a series of FastCGI records that start a request and begin sending the 186 // request body 187 var streamBeginTypeStdin = bytes.Join([][]byte{ 188 // set up request 1 189 makeRecord(typeBeginRequest, 1, 190 []byte{0, byte(roleResponder), 0, 0, 0, 0, 0, 0}), 191 // add required parameters to request 1 192 makeRecord(typeParams, 1, nameValuePair11("REQUEST_METHOD", "GET")), 193 makeRecord(typeParams, 1, nameValuePair11("SERVER_PROTOCOL", "HTTP/1.1")), 194 makeRecord(typeParams, 1, nil), 195 // begin sending body of request 1 196 makeRecord(typeStdin, 1, []byte("0123456789abcdef")), 197 }, 198 nil) 199 200 var cleanUpTests = []struct { 201 input []byte 202 err error 203 }{ 204 // confirm that child.handleRecord closes req.pw after aborting req 205 { 206 bytes.Join([][]byte{ 207 streamBeginTypeStdin, 208 makeRecord(typeAbortRequest, 1, nil), 209 }, 210 nil), 211 ErrRequestAborted, 212 }, 213 // confirm that child.serve closes all pipes after error reading record 214 { 215 bytes.Join([][]byte{ 216 streamBeginTypeStdin, 217 nil, 218 }, 219 nil), 220 ErrConnClosed, 221 }, 222 } 223 224 type nopWriteCloser struct { 225 io.Reader 226 } 227 228 func (nopWriteCloser) Write(buf []byte) (int, error) { 229 return len(buf), nil 230 } 231 232 func (nopWriteCloser) Close() error { 233 return nil 234 } 235 236 // Test that child.serve closes the bodies of aborted requests and closes the 237 // bodies of all requests before returning. Causes deadlock if either condition 238 // isn't met. See issue 6934. 239 func TestChildServeCleansUp(t *testing.T) { 240 for _, tt := range cleanUpTests { 241 input := make([]byte, len(tt.input)) 242 copy(input, tt.input) 243 rc := nopWriteCloser{bytes.NewReader(input)} 244 done := make(chan struct{}) 245 c := newChild(rc, http.HandlerFunc(func( 246 w http.ResponseWriter, 247 r *http.Request, 248 ) { 249 // block on reading body of request 250 _, err := io.Copy(io.Discard, r.Body) 251 if err != tt.err { 252 t.Errorf("Expected %#v, got %#v", tt.err, err) 253 } 254 // not reached if body of request isn't closed 255 close(done) 256 })) 257 c.serve() 258 // wait for body of request to be closed or all goroutines to block 259 <-done 260 } 261 } 262 263 type rwNopCloser struct { 264 io.Reader 265 io.Writer 266 } 267 268 func (rwNopCloser) Close() error { 269 return nil 270 } 271 272 // Verifies it doesn't crash. Issue 11824. 273 func TestMalformedParams(t *testing.T) { 274 input := []byte{ 275 // beginRequest, requestId=1, contentLength=8, role=1, keepConn=1 276 1, 1, 0, 1, 0, 8, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 277 // params, requestId=1, contentLength=10, k1Len=50, v1Len=50 (malformed, wrong length) 278 1, 4, 0, 1, 0, 10, 0, 0, 50, 50, 3, 4, 5, 6, 7, 8, 9, 10, 279 // end of params 280 1, 4, 0, 1, 0, 0, 0, 0, 281 } 282 rw := rwNopCloser{bytes.NewReader(input), io.Discard} 283 c := newChild(rw, http.DefaultServeMux) 284 c.serve() 285 } 286 287 // a series of FastCGI records that start and end a request 288 var streamFullRequestStdin = bytes.Join([][]byte{ 289 // set up request 290 makeRecord(typeBeginRequest, 1, 291 []byte{0, byte(roleResponder), 0, 0, 0, 0, 0, 0}), 292 // add required parameters 293 makeRecord(typeParams, 1, nameValuePair11("REQUEST_METHOD", "GET")), 294 makeRecord(typeParams, 1, nameValuePair11("SERVER_PROTOCOL", "HTTP/1.1")), 295 // set optional parameters 296 makeRecord(typeParams, 1, nameValuePair11("REMOTE_USER", "jane.doe")), 297 makeRecord(typeParams, 1, nameValuePair11("QUERY_STRING", "/foo/bar")), 298 makeRecord(typeParams, 1, nil), 299 // begin sending body of request 300 makeRecord(typeStdin, 1, []byte("0123456789abcdef")), 301 // end request 302 makeRecord(typeEndRequest, 1, nil), 303 }, 304 nil) 305 306 var envVarTests = []struct { 307 input []byte 308 envVar string 309 expectedVal string 310 expectedFilteredOut bool 311 }{ 312 { 313 streamFullRequestStdin, 314 "REMOTE_USER", 315 "jane.doe", 316 false, 317 }, 318 { 319 streamFullRequestStdin, 320 "QUERY_STRING", 321 "", 322 true, 323 }, 324 } 325 326 // Test that environment variables set for a request can be 327 // read by a handler. Ensures that variables not set will not 328 // be exposed to a handler. 329 func TestChildServeReadsEnvVars(t *testing.T) { 330 for _, tt := range envVarTests { 331 input := make([]byte, len(tt.input)) 332 copy(input, tt.input) 333 rc := nopWriteCloser{bytes.NewReader(input)} 334 done := make(chan struct{}) 335 c := newChild(rc, http.HandlerFunc(func( 336 w http.ResponseWriter, 337 r *http.Request, 338 ) { 339 env := ProcessEnv(r) 340 if _, ok := env[tt.envVar]; ok && tt.expectedFilteredOut { 341 t.Errorf("Expected environment variable %s to not be set, but set to %s", 342 tt.envVar, env[tt.envVar]) 343 } else if env[tt.envVar] != tt.expectedVal { 344 t.Errorf("Expected %s, got %s", tt.expectedVal, env[tt.envVar]) 345 } 346 close(done) 347 })) 348 c.serve() 349 <-done 350 } 351 } 352 353 func TestResponseWriterSniffsContentType(t *testing.T) { 354 var tests = []struct { 355 name string 356 body string 357 wantCT string 358 }{ 359 { 360 name: "no body", 361 wantCT: "text/plain; charset=utf-8", 362 }, 363 { 364 name: "html", 365 body: "<html><head><title>test page</title></head><body>This is a body</body></html>", 366 wantCT: "text/html; charset=utf-8", 367 }, 368 { 369 name: "text", 370 body: strings.Repeat("gopher", 86), 371 wantCT: "text/plain; charset=utf-8", 372 }, 373 { 374 name: "jpg", 375 body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024), 376 wantCT: "image/jpeg", 377 }, 378 } 379 for _, tt := range tests { 380 t.Run(tt.name, func(t *testing.T) { 381 input := make([]byte, len(streamFullRequestStdin)) 382 copy(input, streamFullRequestStdin) 383 rc := nopWriteCloser{bytes.NewReader(input)} 384 done := make(chan struct{}) 385 var resp *response 386 c := newChild(rc, http.HandlerFunc(func( 387 w http.ResponseWriter, 388 r *http.Request, 389 ) { 390 io.WriteString(w, tt.body) 391 resp = w.(*response) 392 close(done) 393 })) 394 c.serve() 395 <-done 396 if got := resp.Header().Get("Content-Type"); got != tt.wantCT { 397 t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT) 398 } 399 }) 400 } 401 } 402 403 type signalingNopWriteCloser struct { 404 io.ReadCloser 405 closed chan bool 406 } 407 408 func (*signalingNopWriteCloser) Write(buf []byte) (int, error) { 409 return len(buf), nil 410 } 411 412 func (rc *signalingNopWriteCloser) Close() error { 413 close(rc.closed) 414 return rc.ReadCloser.Close() 415 } 416 417 // Test whether server properly closes connection when processing slow 418 // requests 419 func TestSlowRequest(t *testing.T) { 420 pr, pw := io.Pipe() 421 422 writerDone := make(chan struct{}) 423 go func() { 424 for _, buf := range [][]byte{ 425 streamBeginTypeStdin, 426 makeRecord(typeStdin, 1, nil), 427 } { 428 pw.Write(buf) 429 time.Sleep(100 * time.Millisecond) 430 } 431 close(writerDone) 432 }() 433 defer func() { 434 <-writerDone 435 pw.Close() 436 }() 437 438 rc := &signalingNopWriteCloser{pr, make(chan bool)} 439 handlerDone := make(chan bool) 440 441 c := newChild(rc, http.HandlerFunc(func( 442 w http.ResponseWriter, 443 r *http.Request, 444 ) { 445 w.WriteHeader(200) 446 close(handlerDone) 447 })) 448 c.serve() 449 450 <-handlerDone 451 <-rc.closed 452 t.Log("FastCGI child closed connection") 453 }