libgo: Update to Go 1.3 release.
[official-gcc.git] / libgo / go / net / http / fcgi / child.go
bloba3beaa33a8604cb35a7258078ef546021438cec6
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 "errors"
11 "fmt"
12 "io"
13 "io/ioutil"
14 "net"
15 "net/http"
16 "net/http/cgi"
17 "os"
18 "strings"
19 "sync"
20 "time"
23 // request holds the state for an in-progress request. As soon as it's complete,
24 // it's converted to an http.Request.
25 type request struct {
26 pw *io.PipeWriter
27 reqId uint16
28 params map[string]string
29 buf [1024]byte
30 rawParams []byte
31 keepConn bool
34 func newRequest(reqId uint16, flags uint8) *request {
35 r := &request{
36 reqId: reqId,
37 params: map[string]string{},
38 keepConn: flags&flagKeepConn != 0,
40 r.rawParams = r.buf[:0]
41 return r
44 // parseParams reads an encoded []byte into Params.
45 func (r *request) parseParams() {
46 text := r.rawParams
47 r.rawParams = nil
48 for len(text) > 0 {
49 keyLen, n := readSize(text)
50 if n == 0 {
51 return
53 text = text[n:]
54 valLen, n := readSize(text)
55 if n == 0 {
56 return
58 text = text[n:]
59 key := readString(text, keyLen)
60 text = text[keyLen:]
61 val := readString(text, valLen)
62 text = text[valLen:]
63 r.params[key] = val
67 // response implements http.ResponseWriter.
68 type response struct {
69 req *request
70 header http.Header
71 w *bufWriter
72 wroteHeader bool
75 func newResponse(c *child, req *request) *response {
76 return &response{
77 req: req,
78 header: http.Header{},
79 w: newWriter(c.conn, typeStdout, req.reqId),
83 func (r *response) Header() http.Header {
84 return r.header
87 func (r *response) Write(data []byte) (int, error) {
88 if !r.wroteHeader {
89 r.WriteHeader(http.StatusOK)
91 return r.w.Write(data)
94 func (r *response) WriteHeader(code int) {
95 if r.wroteHeader {
96 return
98 r.wroteHeader = true
99 if code == http.StatusNotModified {
100 // Must not have body.
101 r.header.Del("Content-Type")
102 r.header.Del("Content-Length")
103 r.header.Del("Transfer-Encoding")
104 } else if r.header.Get("Content-Type") == "" {
105 r.header.Set("Content-Type", "text/html; charset=utf-8")
108 if r.header.Get("Date") == "" {
109 r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
112 fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code))
113 r.header.Write(r.w)
114 r.w.WriteString("\r\n")
117 func (r *response) Flush() {
118 if !r.wroteHeader {
119 r.WriteHeader(http.StatusOK)
121 r.w.Flush()
124 func (r *response) Close() error {
125 r.Flush()
126 return r.w.Close()
129 type child struct {
130 conn *conn
131 handler http.Handler
133 mu sync.Mutex // protects requests:
134 requests map[uint16]*request // keyed by request ID
137 func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child {
138 return &child{
139 conn: newConn(rwc),
140 handler: handler,
141 requests: make(map[uint16]*request),
145 func (c *child) serve() {
146 defer c.conn.Close()
147 var rec record
148 for {
149 if err := rec.read(c.conn.rwc); err != nil {
150 return
152 if err := c.handleRecord(&rec); err != nil {
153 return
158 var errCloseConn = errors.New("fcgi: connection should be closed")
160 var emptyBody = ioutil.NopCloser(strings.NewReader(""))
162 func (c *child) handleRecord(rec *record) error {
163 c.mu.Lock()
164 req, ok := c.requests[rec.h.Id]
165 c.mu.Unlock()
166 if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues {
167 // The spec says to ignore unknown request IDs.
168 return nil
171 switch rec.h.Type {
172 case typeBeginRequest:
173 if req != nil {
174 // The server is trying to begin a request with the same ID
175 // as an in-progress request. This is an error.
176 return errors.New("fcgi: received ID that is already in-flight")
179 var br beginRequest
180 if err := br.read(rec.content()); err != nil {
181 return err
183 if br.role != roleResponder {
184 c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole)
185 return nil
187 req = newRequest(rec.h.Id, br.flags)
188 c.mu.Lock()
189 c.requests[rec.h.Id] = req
190 c.mu.Unlock()
191 return nil
192 case typeParams:
193 // NOTE(eds): Technically a key-value pair can straddle the boundary
194 // between two packets. We buffer until we've received all parameters.
195 if len(rec.content()) > 0 {
196 req.rawParams = append(req.rawParams, rec.content()...)
197 return nil
199 req.parseParams()
200 return nil
201 case typeStdin:
202 content := rec.content()
203 if req.pw == nil {
204 var body io.ReadCloser
205 if len(content) > 0 {
206 // body could be an io.LimitReader, but it shouldn't matter
207 // as long as both sides are behaving.
208 body, req.pw = io.Pipe()
209 } else {
210 body = emptyBody
212 go c.serveRequest(req, body)
214 if len(content) > 0 {
215 // TODO(eds): This blocks until the handler reads from the pipe.
216 // If the handler takes a long time, it might be a problem.
217 req.pw.Write(content)
218 } else if req.pw != nil {
219 req.pw.Close()
221 return nil
222 case typeGetValues:
223 values := map[string]string{"FCGI_MPXS_CONNS": "1"}
224 c.conn.writePairs(typeGetValuesResult, 0, values)
225 return nil
226 case typeData:
227 // If the filter role is implemented, read the data stream here.
228 return nil
229 case typeAbortRequest:
230 println("abort")
231 c.mu.Lock()
232 delete(c.requests, rec.h.Id)
233 c.mu.Unlock()
234 c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete)
235 if !req.keepConn {
236 // connection will close upon return
237 return errCloseConn
239 return nil
240 default:
241 b := make([]byte, 8)
242 b[0] = byte(rec.h.Type)
243 c.conn.writeRecord(typeUnknownType, 0, b)
244 return nil
248 func (c *child) serveRequest(req *request, body io.ReadCloser) {
249 r := newResponse(c, req)
250 httpReq, err := cgi.RequestFromMap(req.params)
251 if err != nil {
252 // there was an error reading the request
253 r.WriteHeader(http.StatusInternalServerError)
254 c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error()))
255 } else {
256 httpReq.Body = body
257 c.handler.ServeHTTP(r, httpReq)
259 r.Close()
260 c.mu.Lock()
261 delete(c.requests, req.reqId)
262 c.mu.Unlock()
263 c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete)
265 // Consume the entire body, so the host isn't still writing to
266 // us when we close the socket below in the !keepConn case,
267 // otherwise we'd send a RST. (golang.org/issue/4183)
268 // TODO(bradfitz): also bound this copy in time. Or send
269 // some sort of abort request to the host, so the host
270 // can properly cut off the client sending all the data.
271 // For now just bound it a little and
272 io.CopyN(ioutil.Discard, body, 100<<20)
273 body.Close()
275 if !req.keepConn {
276 c.conn.Close()
280 // Serve accepts incoming FastCGI connections on the listener l, creating a new
281 // goroutine for each. The goroutine reads requests and then calls handler
282 // to reply to them.
283 // If l is nil, Serve accepts connections from os.Stdin.
284 // If handler is nil, http.DefaultServeMux is used.
285 func Serve(l net.Listener, handler http.Handler) error {
286 if l == nil {
287 var err error
288 l, err = net.FileListener(os.Stdin)
289 if err != nil {
290 return err
292 defer l.Close()
294 if handler == nil {
295 handler = http.DefaultServeMux
297 for {
298 rw, err := l.Accept()
299 if err != nil {
300 return err
302 c := newChild(rw, handler)
303 go c.serve()