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.
19 // request holds the state for an in-progress request. As soon as it's complete,
20 // it's converted to an http.Request.
24 params map[string]string
30 func newRequest(reqId uint16, flags uint8) *request {
33 params: map[string]string{},
34 keepConn: flags&flagKeepConn != 0,
36 r.rawParams = r.buf[:0]
40 // parseParams reads an encoded []byte into Params.
41 func (r *request) parseParams() {
45 keyLen, n := readSize(text)
50 valLen, n := readSize(text)
55 key := readString(text, keyLen)
57 val := readString(text, valLen)
63 // response implements http.ResponseWriter.
64 type response struct {
71 func newResponse(c *child, req *request) *response {
74 header: http.Header{},
75 w: newWriter(c.conn, typeStdout, req.reqId),
79 func (r *response) Header() http.Header {
83 func (r *response) Write(data []byte) (int, error) {
85 r.WriteHeader(http.StatusOK)
87 return r.w.Write(data)
90 func (r *response) WriteHeader(code int) {
95 if code == http.StatusNotModified {
96 // Must not have body.
97 r.header.Del("Content-Type")
98 r.header.Del("Content-Length")
99 r.header.Del("Transfer-Encoding")
100 } else if r.header.Get("Content-Type") == "" {
101 r.header.Set("Content-Type", "text/html; charset=utf-8")
104 if r.header.Get("Date") == "" {
105 r.header.Set("Date", time.UTC().Format(http.TimeFormat))
108 fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code))
110 r.w.WriteString("\r\n")
113 func (r *response) Flush() {
115 r.WriteHeader(http.StatusOK)
120 func (r *response) Close() error {
130 func newChild(rwc net.Conn, handler http.Handler) *child {
131 return &child{newConn(rwc), handler}
134 func (c *child) serve() {
135 requests := map[uint16]*request{}
140 if err := rec.read(c.conn.rwc); err != nil {
144 req, ok := requests[rec.h.Id]
145 if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues {
146 // The spec says to ignore unknown request IDs.
149 if ok && rec.h.Type == typeBeginRequest {
150 // The server is trying to begin a request with the same ID
151 // as an in-progress request. This is an error.
156 case typeBeginRequest:
157 if err := br.read(rec.content()); err != nil {
160 if br.role != roleResponder {
161 c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole)
164 requests[rec.h.Id] = newRequest(rec.h.Id, br.flags)
166 // NOTE(eds): Technically a key-value pair can straddle the boundary
167 // between two packets. We buffer until we've received all parameters.
168 if len(rec.content()) > 0 {
169 req.rawParams = append(req.rawParams, rec.content()...)
174 content := rec.content()
176 var body io.ReadCloser
177 if len(content) > 0 {
178 // body could be an io.LimitReader, but it shouldn't matter
179 // as long as both sides are behaving.
180 body, req.pw = io.Pipe()
182 go c.serveRequest(req, body)
184 if len(content) > 0 {
185 // TODO(eds): This blocks until the handler reads from the pipe.
186 // If the handler takes a long time, it might be a problem.
187 req.pw.Write(content)
188 } else if req.pw != nil {
192 values := map[string]string{"FCGI_MPXS_CONNS": "1"}
193 c.conn.writePairs(0, typeGetValuesResult, values)
195 // If the filter role is implemented, read the data stream here.
196 case typeAbortRequest:
197 delete(requests, rec.h.Id)
198 c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete)
200 // connection will close upon return
206 c.conn.writeRecord(typeUnknownType, 0, b)
211 func (c *child) serveRequest(req *request, body io.ReadCloser) {
212 r := newResponse(c, req)
213 httpReq, err := cgi.RequestFromMap(req.params)
215 // there was an error reading the request
216 r.WriteHeader(http.StatusInternalServerError)
217 c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error()))
220 c.handler.ServeHTTP(r, httpReq)
226 c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete)
232 // Serve accepts incoming FastCGI connections on the listener l, creating a new
233 // service thread for each. The service threads read requests and then call handler
235 // If l is nil, Serve accepts connections on stdin.
236 // If handler is nil, http.DefaultServeMux is used.
237 func Serve(l net.Listener, handler http.Handler) error {
240 l, err = net.FileListener(os.Stdin)
247 handler = http.DefaultServeMux
250 rw, err := l.Accept()
254 c := newChild(rw, handler)