...

Source file src/net/http/fcgi/child.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  // This file implements FastCGI from the perspective of a child process.
		 8  
		 9  import (
		10  	"context"
		11  	"errors"
		12  	"fmt"
		13  	"io"
		14  	"net"
		15  	"net/http"
		16  	"net/http/cgi"
		17  	"os"
		18  	"strings"
		19  	"time"
		20  )
		21  
		22  // request holds the state for an in-progress request. As soon as it's complete,
		23  // it's converted to an http.Request.
		24  type request struct {
		25  	pw				*io.PipeWriter
		26  	reqId		 uint16
		27  	params		map[string]string
		28  	buf			 [1024]byte
		29  	rawParams []byte
		30  	keepConn	bool
		31  }
		32  
		33  // envVarsContextKey uniquely identifies a mapping of CGI
		34  // environment variables to their values in a request context
		35  type envVarsContextKey struct{}
		36  
		37  func newRequest(reqId uint16, flags uint8) *request {
		38  	r := &request{
		39  		reqId:		reqId,
		40  		params:	 map[string]string{},
		41  		keepConn: flags&flagKeepConn != 0,
		42  	}
		43  	r.rawParams = r.buf[:0]
		44  	return r
		45  }
		46  
		47  // parseParams reads an encoded []byte into Params.
		48  func (r *request) parseParams() {
		49  	text := r.rawParams
		50  	r.rawParams = nil
		51  	for len(text) > 0 {
		52  		keyLen, n := readSize(text)
		53  		if n == 0 {
		54  			return
		55  		}
		56  		text = text[n:]
		57  		valLen, n := readSize(text)
		58  		if n == 0 {
		59  			return
		60  		}
		61  		text = text[n:]
		62  		if int(keyLen)+int(valLen) > len(text) {
		63  			return
		64  		}
		65  		key := readString(text, keyLen)
		66  		text = text[keyLen:]
		67  		val := readString(text, valLen)
		68  		text = text[valLen:]
		69  		r.params[key] = val
		70  	}
		71  }
		72  
		73  // response implements http.ResponseWriter.
		74  type response struct {
		75  	req						*request
		76  	header				 http.Header
		77  	code					 int
		78  	wroteHeader		bool
		79  	wroteCGIHeader bool
		80  	w							*bufWriter
		81  }
		82  
		83  func newResponse(c *child, req *request) *response {
		84  	return &response{
		85  		req:		req,
		86  		header: http.Header{},
		87  		w:			newWriter(c.conn, typeStdout, req.reqId),
		88  	}
		89  }
		90  
		91  func (r *response) Header() http.Header {
		92  	return r.header
		93  }
		94  
		95  func (r *response) Write(p []byte) (n int, err error) {
		96  	if !r.wroteHeader {
		97  		r.WriteHeader(http.StatusOK)
		98  	}
		99  	if !r.wroteCGIHeader {
	 100  		r.writeCGIHeader(p)
	 101  	}
	 102  	return r.w.Write(p)
	 103  }
	 104  
	 105  func (r *response) WriteHeader(code int) {
	 106  	if r.wroteHeader {
	 107  		return
	 108  	}
	 109  	r.wroteHeader = true
	 110  	r.code = code
	 111  	if code == http.StatusNotModified {
	 112  		// Must not have body.
	 113  		r.header.Del("Content-Type")
	 114  		r.header.Del("Content-Length")
	 115  		r.header.Del("Transfer-Encoding")
	 116  	}
	 117  	if r.header.Get("Date") == "" {
	 118  		r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
	 119  	}
	 120  }
	 121  
	 122  // writeCGIHeader finalizes the header sent to the client and writes it to the output.
	 123  // p is not written by writeHeader, but is the first chunk of the body
	 124  // that will be written. It is sniffed for a Content-Type if none is
	 125  // set explicitly.
	 126  func (r *response) writeCGIHeader(p []byte) {
	 127  	if r.wroteCGIHeader {
	 128  		return
	 129  	}
	 130  	r.wroteCGIHeader = true
	 131  	fmt.Fprintf(r.w, "Status: %d %s\r\n", r.code, http.StatusText(r.code))
	 132  	if _, hasType := r.header["Content-Type"]; r.code != http.StatusNotModified && !hasType {
	 133  		r.header.Set("Content-Type", http.DetectContentType(p))
	 134  	}
	 135  	r.header.Write(r.w)
	 136  	r.w.WriteString("\r\n")
	 137  	r.w.Flush()
	 138  }
	 139  
	 140  func (r *response) Flush() {
	 141  	if !r.wroteHeader {
	 142  		r.WriteHeader(http.StatusOK)
	 143  	}
	 144  	r.w.Flush()
	 145  }
	 146  
	 147  func (r *response) Close() error {
	 148  	r.Flush()
	 149  	return r.w.Close()
	 150  }
	 151  
	 152  type child struct {
	 153  	conn		*conn
	 154  	handler http.Handler
	 155  
	 156  	requests map[uint16]*request // keyed by request ID
	 157  }
	 158  
	 159  func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child {
	 160  	return &child{
	 161  		conn:		 newConn(rwc),
	 162  		handler:	handler,
	 163  		requests: make(map[uint16]*request),
	 164  	}
	 165  }
	 166  
	 167  func (c *child) serve() {
	 168  	defer c.conn.Close()
	 169  	defer c.cleanUp()
	 170  	var rec record
	 171  	for {
	 172  		if err := rec.read(c.conn.rwc); err != nil {
	 173  			return
	 174  		}
	 175  		if err := c.handleRecord(&rec); err != nil {
	 176  			return
	 177  		}
	 178  	}
	 179  }
	 180  
	 181  var errCloseConn = errors.New("fcgi: connection should be closed")
	 182  
	 183  var emptyBody = io.NopCloser(strings.NewReader(""))
	 184  
	 185  // ErrRequestAborted is returned by Read when a handler attempts to read the
	 186  // body of a request that has been aborted by the web server.
	 187  var ErrRequestAborted = errors.New("fcgi: request aborted by web server")
	 188  
	 189  // ErrConnClosed is returned by Read when a handler attempts to read the body of
	 190  // a request after the connection to the web server has been closed.
	 191  var ErrConnClosed = errors.New("fcgi: connection to web server closed")
	 192  
	 193  func (c *child) handleRecord(rec *record) error {
	 194  	req, ok := c.requests[rec.h.Id]
	 195  	if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues {
	 196  		// The spec says to ignore unknown request IDs.
	 197  		return nil
	 198  	}
	 199  
	 200  	switch rec.h.Type {
	 201  	case typeBeginRequest:
	 202  		if req != nil {
	 203  			// The server is trying to begin a request with the same ID
	 204  			// as an in-progress request. This is an error.
	 205  			return errors.New("fcgi: received ID that is already in-flight")
	 206  		}
	 207  
	 208  		var br beginRequest
	 209  		if err := br.read(rec.content()); err != nil {
	 210  			return err
	 211  		}
	 212  		if br.role != roleResponder {
	 213  			c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole)
	 214  			return nil
	 215  		}
	 216  		req = newRequest(rec.h.Id, br.flags)
	 217  		c.requests[rec.h.Id] = req
	 218  		return nil
	 219  	case typeParams:
	 220  		// NOTE(eds): Technically a key-value pair can straddle the boundary
	 221  		// between two packets. We buffer until we've received all parameters.
	 222  		if len(rec.content()) > 0 {
	 223  			req.rawParams = append(req.rawParams, rec.content()...)
	 224  			return nil
	 225  		}
	 226  		req.parseParams()
	 227  		return nil
	 228  	case typeStdin:
	 229  		content := rec.content()
	 230  		if req.pw == nil {
	 231  			var body io.ReadCloser
	 232  			if len(content) > 0 {
	 233  				// body could be an io.LimitReader, but it shouldn't matter
	 234  				// as long as both sides are behaving.
	 235  				body, req.pw = io.Pipe()
	 236  			} else {
	 237  				body = emptyBody
	 238  			}
	 239  			go c.serveRequest(req, body)
	 240  		}
	 241  		if len(content) > 0 {
	 242  			// TODO(eds): This blocks until the handler reads from the pipe.
	 243  			// If the handler takes a long time, it might be a problem.
	 244  			req.pw.Write(content)
	 245  		} else {
	 246  			delete(c.requests, req.reqId)
	 247  			if req.pw != nil {
	 248  				req.pw.Close()
	 249  			}
	 250  		}
	 251  		return nil
	 252  	case typeGetValues:
	 253  		values := map[string]string{"FCGI_MPXS_CONNS": "1"}
	 254  		c.conn.writePairs(typeGetValuesResult, 0, values)
	 255  		return nil
	 256  	case typeData:
	 257  		// If the filter role is implemented, read the data stream here.
	 258  		return nil
	 259  	case typeAbortRequest:
	 260  		delete(c.requests, rec.h.Id)
	 261  		c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete)
	 262  		if req.pw != nil {
	 263  			req.pw.CloseWithError(ErrRequestAborted)
	 264  		}
	 265  		if !req.keepConn {
	 266  			// connection will close upon return
	 267  			return errCloseConn
	 268  		}
	 269  		return nil
	 270  	default:
	 271  		b := make([]byte, 8)
	 272  		b[0] = byte(rec.h.Type)
	 273  		c.conn.writeRecord(typeUnknownType, 0, b)
	 274  		return nil
	 275  	}
	 276  }
	 277  
	 278  // filterOutUsedEnvVars returns a new map of env vars without the
	 279  // variables in the given envVars map that are read for creating each http.Request
	 280  func filterOutUsedEnvVars(envVars map[string]string) map[string]string {
	 281  	withoutUsedEnvVars := make(map[string]string)
	 282  	for k, v := range envVars {
	 283  		if addFastCGIEnvToContext(k) {
	 284  			withoutUsedEnvVars[k] = v
	 285  		}
	 286  	}
	 287  	return withoutUsedEnvVars
	 288  }
	 289  
	 290  func (c *child) serveRequest(req *request, body io.ReadCloser) {
	 291  	r := newResponse(c, req)
	 292  	httpReq, err := cgi.RequestFromMap(req.params)
	 293  	if err != nil {
	 294  		// there was an error reading the request
	 295  		r.WriteHeader(http.StatusInternalServerError)
	 296  		c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error()))
	 297  	} else {
	 298  		httpReq.Body = body
	 299  		withoutUsedEnvVars := filterOutUsedEnvVars(req.params)
	 300  		envVarCtx := context.WithValue(httpReq.Context(), envVarsContextKey{}, withoutUsedEnvVars)
	 301  		httpReq = httpReq.WithContext(envVarCtx)
	 302  		c.handler.ServeHTTP(r, httpReq)
	 303  	}
	 304  	// Make sure we serve something even if nothing was written to r
	 305  	r.Write(nil)
	 306  	r.Close()
	 307  	c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete)
	 308  
	 309  	// Consume the entire body, so the host isn't still writing to
	 310  	// us when we close the socket below in the !keepConn case,
	 311  	// otherwise we'd send a RST. (golang.org/issue/4183)
	 312  	// TODO(bradfitz): also bound this copy in time. Or send
	 313  	// some sort of abort request to the host, so the host
	 314  	// can properly cut off the client sending all the data.
	 315  	// For now just bound it a little and
	 316  	io.CopyN(io.Discard, body, 100<<20)
	 317  	body.Close()
	 318  
	 319  	if !req.keepConn {
	 320  		c.conn.Close()
	 321  	}
	 322  }
	 323  
	 324  func (c *child) cleanUp() {
	 325  	for _, req := range c.requests {
	 326  		if req.pw != nil {
	 327  			// race with call to Close in c.serveRequest doesn't matter because
	 328  			// Pipe(Reader|Writer).Close are idempotent
	 329  			req.pw.CloseWithError(ErrConnClosed)
	 330  		}
	 331  	}
	 332  }
	 333  
	 334  // Serve accepts incoming FastCGI connections on the listener l, creating a new
	 335  // goroutine for each. The goroutine reads requests and then calls handler
	 336  // to reply to them.
	 337  // If l is nil, Serve accepts connections from os.Stdin.
	 338  // If handler is nil, http.DefaultServeMux is used.
	 339  func Serve(l net.Listener, handler http.Handler) error {
	 340  	if l == nil {
	 341  		var err error
	 342  		l, err = net.FileListener(os.Stdin)
	 343  		if err != nil {
	 344  			return err
	 345  		}
	 346  		defer l.Close()
	 347  	}
	 348  	if handler == nil {
	 349  		handler = http.DefaultServeMux
	 350  	}
	 351  	for {
	 352  		rw, err := l.Accept()
	 353  		if err != nil {
	 354  			return err
	 355  		}
	 356  		c := newChild(rw, handler)
	 357  		go c.serve()
	 358  	}
	 359  }
	 360  
	 361  // ProcessEnv returns FastCGI environment variables associated with the request r
	 362  // for which no effort was made to be included in the request itself - the data
	 363  // is hidden in the request's context. As an example, if REMOTE_USER is set for a
	 364  // request, it will not be found anywhere in r, but it will be included in
	 365  // ProcessEnv's response (via r's context).
	 366  func ProcessEnv(r *http.Request) map[string]string {
	 367  	env, _ := r.Context().Value(envVarsContextKey{}).(map[string]string)
	 368  	return env
	 369  }
	 370  
	 371  // addFastCGIEnvToContext reports whether to include the FastCGI environment variable s
	 372  // in the http.Request.Context, accessible via ProcessEnv.
	 373  func addFastCGIEnvToContext(s string) bool {
	 374  	// Exclude things supported by net/http natively:
	 375  	switch s {
	 376  	case "CONTENT_LENGTH", "CONTENT_TYPE", "HTTPS",
	 377  		"PATH_INFO", "QUERY_STRING", "REMOTE_ADDR",
	 378  		"REMOTE_HOST", "REMOTE_PORT", "REQUEST_METHOD",
	 379  		"REQUEST_URI", "SCRIPT_NAME", "SERVER_PROTOCOL":
	 380  		return false
	 381  	}
	 382  	if strings.HasPrefix(s, "HTTP_") {
	 383  		return false
	 384  	}
	 385  	// Explicitly include FastCGI-specific things.
	 386  	// This list is redundant with the default "return true" below.
	 387  	// Consider this documentation of the sorts of things we expect
	 388  	// to maybe see.
	 389  	switch s {
	 390  	case "REMOTE_USER":
	 391  		return true
	 392  	}
	 393  	// Unknown, so include it to be safe.
	 394  	return true
	 395  }
	 396  

View as plain text