Home | History | Annotate | Download | only in 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