libgo: update to go1.9
[official-gcc.git] / libgo / go / net / http / fcgi / child.go
blob30a6b2ce2dfe8335efce24b38c274bc0476fd8ed
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.
5 package fcgi
7 // This file implements FastCGI from the perspective of a child process.
9 import (
10 "context"
11 "errors"
12 "fmt"
13 "io"
14 "io/ioutil"
15 "net"
16 "net/http"
17 "net/http/cgi"
18 "os"
19 "strings"
20 "sync"
21 "time"
24 // request holds the state for an in-progress request. As soon as it's complete,
25 // it's converted to an http.Request.
26 type request struct {
27 pw *io.PipeWriter
28 reqId uint16
29 params map[string]string
30 buf [1024]byte
31 rawParams []byte
32 keepConn bool
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 {
40 r := &request{
41 reqId: reqId,
42 params: map[string]string{},
43 keepConn: flags&flagKeepConn != 0,
45 r.rawParams = r.buf[:0]
46 return r
49 // parseParams reads an encoded []byte into Params.
50 func (r *request) parseParams() {
51 text := r.rawParams
52 r.rawParams = nil
53 for len(text) > 0 {
54 keyLen, n := readSize(text)
55 if n == 0 {
56 return
58 text = text[n:]
59 valLen, n := readSize(text)
60 if n == 0 {
61 return
63 text = text[n:]
64 if int(keyLen)+int(valLen) > len(text) {
65 return
67 key := readString(text, keyLen)
68 text = text[keyLen:]
69 val := readString(text, valLen)
70 text = text[valLen:]
71 r.params[key] = val
75 // response implements http.ResponseWriter.
76 type response struct {
77 req *request
78 header http.Header
79 w *bufWriter
80 wroteHeader bool
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),
91 func (r *response) Header() http.Header {
92 return r.header
95 func (r *response) Write(data []byte) (int, error) {
96 if !r.wroteHeader {
97 r.WriteHeader(http.StatusOK)
99 return r.w.Write(data)
102 func (r *response) WriteHeader(code int) {
103 if r.wroteHeader {
104 return
106 r.wroteHeader = true
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))
121 r.header.Write(r.w)
122 r.w.WriteString("\r\n")
125 func (r *response) Flush() {
126 if !r.wroteHeader {
127 r.WriteHeader(http.StatusOK)
129 r.w.Flush()
132 func (r *response) Close() error {
133 r.Flush()
134 return r.w.Close()
137 type child struct {
138 conn *conn
139 handler http.Handler
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 {
146 return &child{
147 conn: newConn(rwc),
148 handler: handler,
149 requests: make(map[uint16]*request),
153 func (c *child) serve() {
154 defer c.conn.Close()
155 defer c.cleanUp()
156 var rec record
157 for {
158 if err := rec.read(c.conn.rwc); err != nil {
159 return
161 if err := c.handleRecord(&rec); err != nil {
162 return
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 {
180 c.mu.Lock()
181 req, ok := c.requests[rec.h.Id]
182 c.mu.Unlock()
183 if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues {
184 // The spec says to ignore unknown request IDs.
185 return nil
188 switch rec.h.Type {
189 case typeBeginRequest:
190 if req != nil {
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")
196 var br beginRequest
197 if err := br.read(rec.content()); err != nil {
198 return err
200 if br.role != roleResponder {
201 c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole)
202 return nil
204 req = newRequest(rec.h.Id, br.flags)
205 c.mu.Lock()
206 c.requests[rec.h.Id] = req
207 c.mu.Unlock()
208 return nil
209 case typeParams:
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()...)
214 return nil
216 req.parseParams()
217 return nil
218 case typeStdin:
219 content := rec.content()
220 if req.pw == nil {
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()
226 } else {
227 body = emptyBody
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 {
236 req.pw.Close()
238 return nil
239 case typeGetValues:
240 values := map[string]string{"FCGI_MPXS_CONNS": "1"}
241 c.conn.writePairs(typeGetValuesResult, 0, values)
242 return nil
243 case typeData:
244 // If the filter role is implemented, read the data stream here.
245 return nil
246 case typeAbortRequest:
247 c.mu.Lock()
248 delete(c.requests, rec.h.Id)
249 c.mu.Unlock()
250 c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete)
251 if req.pw != nil {
252 req.pw.CloseWithError(ErrRequestAborted)
254 if !req.keepConn {
255 // connection will close upon return
256 return errCloseConn
258 return nil
259 default:
260 b := make([]byte, 8)
261 b[0] = byte(rec.h.Type)
262 c.conn.writeRecord(typeUnknownType, 0, b)
263 return nil
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)
282 if err != nil {
283 // there was an error reading the request
284 r.WriteHeader(http.StatusInternalServerError)
285 c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error()))
286 } else {
287 httpReq.Body = body
288 withoutUsedEnvVars := filterOutUsedEnvVars(req.params)
289 envVarCtx := context.WithValue(httpReq.Context(), envVarsContextKey{}, withoutUsedEnvVars)
290 httpReq = httpReq.WithContext(envVarCtx)
291 c.handler.ServeHTTP(r, httpReq)
293 r.Close()
294 c.mu.Lock()
295 delete(c.requests, req.reqId)
296 c.mu.Unlock()
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)
307 body.Close()
309 if !req.keepConn {
310 c.conn.Close()
314 func (c *child) cleanUp() {
315 c.mu.Lock()
316 defer c.mu.Unlock()
317 for _, req := range c.requests {
318 if req.pw != nil {
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
328 // to reply to them.
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 {
332 if l == nil {
333 var err error
334 l, err = net.FileListener(os.Stdin)
335 if err != nil {
336 return err
338 defer l.Close()
340 if handler == nil {
341 handler = http.DefaultServeMux
343 for {
344 rw, err := l.Accept()
345 if err != nil {
346 return err
348 c := newChild(rw, handler)
349 go c.serve()
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)
360 return env
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:
367 switch s {
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":
372 return false
374 if strings.HasPrefix(s, "HTTP_") {
375 return false
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
380 // to maybe see.
381 switch s {
382 case "REMOTE_USER":
383 return true
385 // Unknown, so include it to be safe.
386 return true