Source file src/pkg/net/http/fcgi/child.go
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 "errors"
11 "fmt"
12 "io"
13 "net"
14 "net/http"
15 "net/http/cgi"
16 "os"
17 "time"
18 )
19
20 // request holds the state for an in-progress request. As soon as it's complete,
21 // it's converted to an http.Request.
22 type request struct {
23 pw *io.PipeWriter
24 reqId uint16
25 params map[string]string
26 buf [1024]byte
27 rawParams []byte
28 keepConn bool
29 }
30
31 func newRequest(reqId uint16, flags uint8) *request {
32 r := &request{
33 reqId: reqId,
34 params: map[string]string{},
35 keepConn: flags&flagKeepConn != 0,
36 }
37 r.rawParams = r.buf[:0]
38 return r
39 }
40
41 // parseParams reads an encoded []byte into Params.
42 func (r *request) parseParams() {
43 text := r.rawParams
44 r.rawParams = nil
45 for len(text) > 0 {
46 keyLen, n := readSize(text)
47 if n == 0 {
48 return
49 }
50 text = text[n:]
51 valLen, n := readSize(text)
52 if n == 0 {
53 return
54 }
55 text = text[n:]
56 key := readString(text, keyLen)
57 text = text[keyLen:]
58 val := readString(text, valLen)
59 text = text[valLen:]
60 r.params[key] = val
61 }
62 }
63
64 // response implements http.ResponseWriter.
65 type response struct {
66 req *request
67 header http.Header
68 w *bufWriter
69 wroteHeader bool
70 }
71
72 func newResponse(c *child, req *request) *response {
73 return &response{
74 req: req,
75 header: http.Header{},
76 w: newWriter(c.conn, typeStdout, req.reqId),
77 }
78 }
79
80 func (r *response) Header() http.Header {
81 return r.header
82 }
83
84 func (r *response) Write(data []byte) (int, error) {
85 if !r.wroteHeader {
86 r.WriteHeader(http.StatusOK)
87 }
88 return r.w.Write(data)
89 }
90
91 func (r *response) WriteHeader(code int) {
92 if r.wroteHeader {
93 return
94 }
95 r.wroteHeader = true
96 if code == http.StatusNotModified {
97 // Must not have body.
98 r.header.Del("Content-Type")
99 r.header.Del("Content-Length")
100 r.header.Del("Transfer-Encoding")
101 } else if r.header.Get("Content-Type") == "" {
102 r.header.Set("Content-Type", "text/html; charset=utf-8")
103 }
104
105 if r.header.Get("Date") == "" {
106 r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
107 }
108
109 fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code))
110 r.header.Write(r.w)
111 r.w.WriteString("\r\n")
112 }
113
114 func (r *response) Flush() {
115 if !r.wroteHeader {
116 r.WriteHeader(http.StatusOK)
117 }
118 r.w.Flush()
119 }
120
121 func (r *response) Close() error {
122 r.Flush()
123 return r.w.Close()
124 }
125
126 type child struct {
127 conn *conn
128 handler http.Handler
129 requests map[uint16]*request // keyed by request ID
130 }
131
132 func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child {
133 return &child{
134 conn: newConn(rwc),
135 handler: handler,
136 requests: make(map[uint16]*request),
137 }
138 }
139
140 func (c *child) serve() {
141 defer c.conn.Close()
142 var rec record
143 for {
144 if err := rec.read(c.conn.rwc); err != nil {
145 return
146 }
147 if err := c.handleRecord(&rec); err != nil {
148 return
149 }
150 }
151 }
152
153 var errCloseConn = errors.New("fcgi: connection should be closed")
154
155 func (c *child) handleRecord(rec *record) error {
156 req, ok := c.requests[rec.h.Id]
157 if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues {
158 // The spec says to ignore unknown request IDs.
159 return nil
160 }
161 if ok && rec.h.Type == typeBeginRequest {
162 // The server is trying to begin a request with the same ID
163 // as an in-progress request. This is an error.
164 return errors.New("fcgi: received ID that is already in-flight")
165 }
166
167 switch rec.h.Type {
168 case typeBeginRequest:
169 var br beginRequest
170 if err := br.read(rec.content()); err != nil {
171 return err
172 }
173 if br.role != roleResponder {
174 c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole)
175 return nil
176 }
177 c.requests[rec.h.Id] = newRequest(rec.h.Id, br.flags)
178 case typeParams:
179 // NOTE(eds): Technically a key-value pair can straddle the boundary
180 // between two packets. We buffer until we've received all parameters.
181 if len(rec.content()) > 0 {
182 req.rawParams = append(req.rawParams, rec.content()...)
183 return nil
184 }
185 req.parseParams()
186 case typeStdin:
187 content := rec.content()
188 if req.pw == nil {
189 var body io.ReadCloser
190 if len(content) > 0 {
191 // body could be an io.LimitReader, but it shouldn't matter
192 // as long as both sides are behaving.
193 body, req.pw = io.Pipe()
194 }
195 go c.serveRequest(req, body)
196 }
197 if len(content) > 0 {
198 // TODO(eds): This blocks until the handler reads from the pipe.
199 // If the handler takes a long time, it might be a problem.
200 req.pw.Write(content)
201 } else if req.pw != nil {
202 req.pw.Close()
203 }
204 case typeGetValues:
205 values := map[string]string{"FCGI_MPXS_CONNS": "1"}
206 c.conn.writePairs(typeGetValuesResult, 0, values)
207 case typeData:
208 // If the filter role is implemented, read the data stream here.
209 case typeAbortRequest:
210 delete(c.requests, rec.h.Id)
211 c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete)
212 if !req.keepConn {
213 // connection will close upon return
214 return errCloseConn
215 }
216 default:
217 b := make([]byte, 8)
218 b[0] = byte(rec.h.Type)
219 c.conn.writeRecord(typeUnknownType, 0, b)
220 }
221 return nil
222 }
223
224 func (c *child) serveRequest(req *request, body io.ReadCloser) {
225 r := newResponse(c, req)
226 httpReq, err := cgi.RequestFromMap(req.params)
227 if err != nil {
228 // there was an error reading the request
229 r.WriteHeader(http.StatusInternalServerError)
230 c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error()))
231 } else {
232 httpReq.Body = body
233 c.handler.ServeHTTP(r, httpReq)
234 }
235 if body != nil {
236 body.Close()
237 }
238 r.Close()
239 c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete)
240 if !req.keepConn {
241 c.conn.Close()
242 }
243 }
244
245 // Serve accepts incoming FastCGI connections on the listener l, creating a new
246 // goroutine for each. The goroutine reads requests and then calls handler
247 // to reply to them.
248 // If l is nil, Serve accepts connections from os.Stdin.
249 // If handler is nil, http.DefaultServeMux is used.
250 func Serve(l net.Listener, handler http.Handler) error {
251 if l == nil {
252 var err error
253 l, err = net.FileListener(os.Stdin)
254 if err != nil {
255 return err
256 }
257 defer l.Close()
258 }
259 if handler == nil {
260 handler = http.DefaultServeMux
261 }
262 for {
263 rw, err := l.Accept()
264 if err != nil {
265 return err
266 }
267 c := newChild(rw, handler)
268 go c.serve()
269 }
270 panic("unreachable")
271 }