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