...
Run Format

Source file src/net/http/fcgi/fcgi_test.go

Documentation: net/http/fcgi

     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  	"io/ioutil"
    12  	"net/http"
    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.ReadWriter
   225  }
   226  
   227  func (nopWriteCloser) Close() error {
   228  	return nil
   229  }
   230  
   231  // Test that child.serve closes the bodies of aborted requests and closes the
   232  // bodies of all requests before returning. Causes deadlock if either condition
   233  // isn't met. See issue 6934.
   234  func TestChildServeCleansUp(t *testing.T) {
   235  	for _, tt := range cleanUpTests {
   236  		input := make([]byte, len(tt.input))
   237  		copy(input, tt.input)
   238  		rc := nopWriteCloser{bytes.NewBuffer(input)}
   239  		done := make(chan bool)
   240  		c := newChild(rc, http.HandlerFunc(func(
   241  			w http.ResponseWriter,
   242  			r *http.Request,
   243  		) {
   244  			// block on reading body of request
   245  			_, err := io.Copy(ioutil.Discard, r.Body)
   246  			if err != tt.err {
   247  				t.Errorf("Expected %#v, got %#v", tt.err, err)
   248  			}
   249  			// not reached if body of request isn't closed
   250  			done <- true
   251  		}))
   252  		go c.serve()
   253  		// wait for body of request to be closed or all goroutines to block
   254  		<-done
   255  	}
   256  }
   257  
   258  type rwNopCloser struct {
   259  	io.Reader
   260  	io.Writer
   261  }
   262  
   263  func (rwNopCloser) Close() error {
   264  	return nil
   265  }
   266  
   267  // Verifies it doesn't crash. 	Issue 11824.
   268  func TestMalformedParams(t *testing.T) {
   269  	input := []byte{
   270  		// beginRequest, requestId=1, contentLength=8, role=1, keepConn=1
   271  		1, 1, 0, 1, 0, 8, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0,
   272  		// params, requestId=1, contentLength=10, k1Len=50, v1Len=50 (malformed, wrong length)
   273  		1, 4, 0, 1, 0, 10, 0, 0, 50, 50, 3, 4, 5, 6, 7, 8, 9, 10,
   274  		// end of params
   275  		1, 4, 0, 1, 0, 0, 0, 0,
   276  	}
   277  	rw := rwNopCloser{bytes.NewReader(input), ioutil.Discard}
   278  	c := newChild(rw, http.DefaultServeMux)
   279  	c.serve()
   280  }
   281  
   282  // a series of FastCGI records that start and end a request
   283  var streamFullRequestStdin = bytes.Join([][]byte{
   284  	// set up request
   285  	makeRecord(typeBeginRequest, 1,
   286  		[]byte{0, byte(roleResponder), 0, 0, 0, 0, 0, 0}),
   287  	// add required parameters
   288  	makeRecord(typeParams, 1, nameValuePair11("REQUEST_METHOD", "GET")),
   289  	makeRecord(typeParams, 1, nameValuePair11("SERVER_PROTOCOL", "HTTP/1.1")),
   290  	// set optional parameters
   291  	makeRecord(typeParams, 1, nameValuePair11("REMOTE_USER", "jane.doe")),
   292  	makeRecord(typeParams, 1, nameValuePair11("QUERY_STRING", "/foo/bar")),
   293  	makeRecord(typeParams, 1, nil),
   294  	// begin sending body of request
   295  	makeRecord(typeStdin, 1, []byte("0123456789abcdef")),
   296  	// end request
   297  	makeRecord(typeEndRequest, 1, nil),
   298  },
   299  	nil)
   300  
   301  var envVarTests = []struct {
   302  	input               []byte
   303  	envVar              string
   304  	expectedVal         string
   305  	expectedFilteredOut bool
   306  }{
   307  	{
   308  		streamFullRequestStdin,
   309  		"REMOTE_USER",
   310  		"jane.doe",
   311  		false,
   312  	},
   313  	{
   314  		streamFullRequestStdin,
   315  		"QUERY_STRING",
   316  		"",
   317  		true,
   318  	},
   319  }
   320  
   321  // Test that environment variables set for a request can be
   322  // read by a handler. Ensures that variables not set will not
   323  // be exposed to a handler.
   324  func TestChildServeReadsEnvVars(t *testing.T) {
   325  	for _, tt := range envVarTests {
   326  		input := make([]byte, len(tt.input))
   327  		copy(input, tt.input)
   328  		rc := nopWriteCloser{bytes.NewBuffer(input)}
   329  		done := make(chan bool)
   330  		c := newChild(rc, http.HandlerFunc(func(
   331  			w http.ResponseWriter,
   332  			r *http.Request,
   333  		) {
   334  			env := ProcessEnv(r)
   335  			if _, ok := env[tt.envVar]; ok && tt.expectedFilteredOut {
   336  				t.Errorf("Expected environment variable %s to not be set, but set to %s",
   337  					tt.envVar, env[tt.envVar])
   338  			} else if env[tt.envVar] != tt.expectedVal {
   339  				t.Errorf("Expected %s, got %s", tt.expectedVal, env[tt.envVar])
   340  			}
   341  			done <- true
   342  		}))
   343  		go c.serve()
   344  		<-done
   345  	}
   346  }
   347  

View as plain text