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