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.
7 // This file implements FastCGI from the perspective of a child process.
24 // request holds the state for an in-progress request. As soon as it's complete,
25 // it's converted to an http.Request.
29 params
map[string]string
35 // envVarsContextKey uniquely identifies a mapping of CGI
36 // environment variables to their values in a request context
37 type envVarsContextKey
struct{}
39 func newRequest(reqId
uint16, flags
uint8) *request
{
42 params
: map[string]string{},
43 keepConn
: flags
&flagKeepConn
!= 0,
45 r
.rawParams
= r
.buf
[:0]
49 // parseParams reads an encoded []byte into Params.
50 func (r
*request
) parseParams() {
54 keyLen
, n
:= readSize(text
)
59 valLen
, n
:= readSize(text
)
64 if int(keyLen
)+int(valLen
) > len(text
) {
67 key
:= readString(text
, keyLen
)
69 val
:= readString(text
, valLen
)
75 // response implements http.ResponseWriter.
76 type response
struct {
83 func newResponse(c
*child
, req
*request
) *response
{
86 header
: http
.Header
{},
87 w
: newWriter(c
.conn
, typeStdout
, req
.reqId
),
91 func (r
*response
) Header() http
.Header
{
95 func (r
*response
) Write(data
[]byte) (int, error
) {
97 r
.WriteHeader(http
.StatusOK
)
99 return r
.w
.Write(data
)
102 func (r
*response
) WriteHeader(code
int) {
107 if code
== http
.StatusNotModified
{
108 // Must not have body.
109 r
.header
.Del("Content-Type")
110 r
.header
.Del("Content-Length")
111 r
.header
.Del("Transfer-Encoding")
112 } else if r
.header
.Get("Content-Type") == "" {
113 r
.header
.Set("Content-Type", "text/html; charset=utf-8")
116 if r
.header
.Get("Date") == "" {
117 r
.header
.Set("Date", time
.Now().UTC().Format(http
.TimeFormat
))
120 fmt
.Fprintf(r
.w
, "Status: %d %s\r\n", code
, http
.StatusText(code
))
122 r
.w
.WriteString("\r\n")
125 func (r
*response
) Flush() {
127 r
.WriteHeader(http
.StatusOK
)
132 func (r
*response
) Close() error
{
141 mu sync
.Mutex
// protects requests:
142 requests
map[uint16]*request
// keyed by request ID
145 func newChild(rwc io
.ReadWriteCloser
, handler http
.Handler
) *child
{
149 requests
: make(map[uint16]*request
),
153 func (c
*child
) serve() {
158 if err
:= rec
.read(c
.conn
.rwc
); err
!= nil {
161 if err
:= c
.handleRecord(&rec
); err
!= nil {
167 var errCloseConn
= errors
.New("fcgi: connection should be closed")
169 var emptyBody
= ioutil
.NopCloser(strings
.NewReader(""))
171 // ErrRequestAborted is returned by Read when a handler attempts to read the
172 // body of a request that has been aborted by the web server.
173 var ErrRequestAborted
= errors
.New("fcgi: request aborted by web server")
175 // ErrConnClosed is returned by Read when a handler attempts to read the body of
176 // a request after the connection to the web server has been closed.
177 var ErrConnClosed
= errors
.New("fcgi: connection to web server closed")
179 func (c
*child
) handleRecord(rec
*record
) error
{
181 req
, ok
:= c
.requests
[rec
.h
.Id
]
183 if !ok
&& rec
.h
.Type
!= typeBeginRequest
&& rec
.h
.Type
!= typeGetValues
{
184 // The spec says to ignore unknown request IDs.
189 case typeBeginRequest
:
191 // The server is trying to begin a request with the same ID
192 // as an in-progress request. This is an error.
193 return errors
.New("fcgi: received ID that is already in-flight")
197 if err
:= br
.read(rec
.content()); err
!= nil {
200 if br
.role
!= roleResponder
{
201 c
.conn
.writeEndRequest(rec
.h
.Id
, 0, statusUnknownRole
)
204 req
= newRequest(rec
.h
.Id
, br
.flags
)
206 c
.requests
[rec
.h
.Id
] = req
210 // NOTE(eds): Technically a key-value pair can straddle the boundary
211 // between two packets. We buffer until we've received all parameters.
212 if len(rec
.content()) > 0 {
213 req
.rawParams
= append(req
.rawParams
, rec
.content()...)
219 content
:= rec
.content()
221 var body io
.ReadCloser
222 if len(content
) > 0 {
223 // body could be an io.LimitReader, but it shouldn't matter
224 // as long as both sides are behaving.
225 body
, req
.pw
= io
.Pipe()
229 go c
.serveRequest(req
, body
)
231 if len(content
) > 0 {
232 // TODO(eds): This blocks until the handler reads from the pipe.
233 // If the handler takes a long time, it might be a problem.
234 req
.pw
.Write(content
)
235 } else if req
.pw
!= nil {
240 values
:= map[string]string{"FCGI_MPXS_CONNS": "1"}
241 c
.conn
.writePairs(typeGetValuesResult
, 0, values
)
244 // If the filter role is implemented, read the data stream here.
246 case typeAbortRequest
:
248 delete(c
.requests
, rec
.h
.Id
)
250 c
.conn
.writeEndRequest(rec
.h
.Id
, 0, statusRequestComplete
)
252 req
.pw
.CloseWithError(ErrRequestAborted
)
255 // connection will close upon return
261 b
[0] = byte(rec
.h
.Type
)
262 c
.conn
.writeRecord(typeUnknownType
, 0, b
)
267 // filterOutUsedEnvVars returns a new map of env vars without the
268 // variables in the given envVars map that are read for creating each http.Request
269 func filterOutUsedEnvVars(envVars
map[string]string) map[string]string {
270 withoutUsedEnvVars
:= make(map[string]string)
271 for k
, v
:= range envVars
{
272 if addFastCGIEnvToContext(k
) {
273 withoutUsedEnvVars
[k
] = v
276 return withoutUsedEnvVars
279 func (c
*child
) serveRequest(req
*request
, body io
.ReadCloser
) {
280 r
:= newResponse(c
, req
)
281 httpReq
, err
:= cgi
.RequestFromMap(req
.params
)
283 // there was an error reading the request
284 r
.WriteHeader(http
.StatusInternalServerError
)
285 c
.conn
.writeRecord(typeStderr
, req
.reqId
, []byte(err
.Error()))
288 withoutUsedEnvVars
:= filterOutUsedEnvVars(req
.params
)
289 envVarCtx
:= context
.WithValue(httpReq
.Context(), envVarsContextKey
{}, withoutUsedEnvVars
)
290 httpReq
= httpReq
.WithContext(envVarCtx
)
291 c
.handler
.ServeHTTP(r
, httpReq
)
295 delete(c
.requests
, req
.reqId
)
297 c
.conn
.writeEndRequest(req
.reqId
, 0, statusRequestComplete
)
299 // Consume the entire body, so the host isn't still writing to
300 // us when we close the socket below in the !keepConn case,
301 // otherwise we'd send a RST. (golang.org/issue/4183)
302 // TODO(bradfitz): also bound this copy in time. Or send
303 // some sort of abort request to the host, so the host
304 // can properly cut off the client sending all the data.
305 // For now just bound it a little and
306 io
.CopyN(ioutil
.Discard
, body
, 100<<20)
314 func (c
*child
) cleanUp() {
317 for _
, req
:= range c
.requests
{
319 // race with call to Close in c.serveRequest doesn't matter because
320 // Pipe(Reader|Writer).Close are idempotent
321 req
.pw
.CloseWithError(ErrConnClosed
)
326 // Serve accepts incoming FastCGI connections on the listener l, creating a new
327 // goroutine for each. The goroutine reads requests and then calls handler
329 // If l is nil, Serve accepts connections from os.Stdin.
330 // If handler is nil, http.DefaultServeMux is used.
331 func Serve(l net
.Listener
, handler http
.Handler
) error
{
334 l
, err
= net
.FileListener(os
.Stdin
)
341 handler
= http
.DefaultServeMux
344 rw
, err
:= l
.Accept()
348 c
:= newChild(rw
, handler
)
353 // ProcessEnv returns FastCGI environment variables associated with the request r
354 // for which no effort was made to be included in the request itself - the data
355 // is hidden in the request's context. As an example, if REMOTE_USER is set for a
356 // request, it will not be found anywhere in r, but it will be included in
357 // ProcessEnv's response (via r's context).
358 func ProcessEnv(r
*http
.Request
) map[string]string {
359 env
, _
:= r
.Context().Value(envVarsContextKey
{}).(map[string]string)
363 // addFastCGIEnvToContext reports whether to include the FastCGI environment variable s
364 // in the http.Request.Context, accessible via ProcessEnv.
365 func addFastCGIEnvToContext(s
string) bool {
366 // Exclude things supported by net/http natively:
368 case "CONTENT_LENGTH", "CONTENT_TYPE", "HTTPS",
369 "PATH_INFO", "QUERY_STRING", "REMOTE_ADDR",
370 "REMOTE_HOST", "REMOTE_PORT", "REQUEST_METHOD",
371 "REQUEST_URI", "SCRIPT_NAME", "SERVER_PROTOCOL":
374 if strings
.HasPrefix(s
, "HTTP_") {
377 // Explicitly include FastCGI-specific things.
378 // This list is redundant with the default "return true" below.
379 // Consider this documentation of the sorts of things we expect
385 // Unknown, so include it to be safe.