1
2
3
4
5 package fcgi
6
7
8
9 import (
10 "context"
11 "errors"
12 "fmt"
13 "io"
14 "net"
15 "net/http"
16 "net/http/cgi"
17 "os"
18 "strings"
19 "sync"
20 "time"
21 )
22
23
24
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
32 }
33
34
35
36 type envVarsContextKey struct{}
37
38 func newRequest(reqId uint16, flags uint8) *request {
39 r := &request{
40 reqId: reqId,
41 params: map[string]string{},
42 keepConn: flags&flagKeepConn != 0,
43 }
44 r.rawParams = r.buf[:0]
45 return r
46 }
47
48
49 func (r *request) parseParams() {
50 text := r.rawParams
51 r.rawParams = nil
52 for len(text) > 0 {
53 keyLen, n := readSize(text)
54 if n == 0 {
55 return
56 }
57 text = text[n:]
58 valLen, n := readSize(text)
59 if n == 0 {
60 return
61 }
62 text = text[n:]
63 if int(keyLen)+int(valLen) > len(text) {
64 return
65 }
66 key := readString(text, keyLen)
67 text = text[keyLen:]
68 val := readString(text, valLen)
69 text = text[valLen:]
70 r.params[key] = val
71 }
72 }
73
74
75 type response struct {
76 req *request
77 header http.Header
78 code int
79 wroteHeader bool
80 wroteCGIHeader bool
81 w *bufWriter
82 }
83
84 func newResponse(c *child, req *request) *response {
85 return &response{
86 req: req,
87 header: http.Header{},
88 w: newWriter(c.conn, typeStdout, req.reqId),
89 }
90 }
91
92 func (r *response) Header() http.Header {
93 return r.header
94 }
95
96 func (r *response) Write(p []byte) (n int, err error) {
97 if !r.wroteHeader {
98 r.WriteHeader(http.StatusOK)
99 }
100 if !r.wroteCGIHeader {
101 r.writeCGIHeader(p)
102 }
103 return r.w.Write(p)
104 }
105
106 func (r *response) WriteHeader(code int) {
107 if r.wroteHeader {
108 return
109 }
110 r.wroteHeader = true
111 r.code = code
112 if code == http.StatusNotModified {
113
114 r.header.Del("Content-Type")
115 r.header.Del("Content-Length")
116 r.header.Del("Transfer-Encoding")
117 }
118 if r.header.Get("Date") == "" {
119 r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
120 }
121 }
122
123
124
125
126
127 func (r *response) writeCGIHeader(p []byte) {
128 if r.wroteCGIHeader {
129 return
130 }
131 r.wroteCGIHeader = true
132 fmt.Fprintf(r.w, "Status: %d %s\r\n", r.code, http.StatusText(r.code))
133 if _, hasType := r.header["Content-Type"]; r.code != http.StatusNotModified && !hasType {
134 r.header.Set("Content-Type", http.DetectContentType(p))
135 }
136 r.header.Write(r.w)
137 r.w.WriteString("\r\n")
138 r.w.Flush()
139 }
140
141 func (r *response) Flush() {
142 if !r.wroteHeader {
143 r.WriteHeader(http.StatusOK)
144 }
145 r.w.Flush()
146 }
147
148 func (r *response) Close() error {
149 r.Flush()
150 return r.w.Close()
151 }
152
153 type child struct {
154 conn *conn
155 handler http.Handler
156
157 mu sync.Mutex
158 requests map[uint16]*request
159 }
160
161 func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child {
162 return &child{
163 conn: newConn(rwc),
164 handler: handler,
165 requests: make(map[uint16]*request),
166 }
167 }
168
169 func (c *child) serve() {
170 defer c.conn.Close()
171 defer c.cleanUp()
172 var rec record
173 for {
174 if err := rec.read(c.conn.rwc); err != nil {
175 return
176 }
177 if err := c.handleRecord(&rec); err != nil {
178 return
179 }
180 }
181 }
182
183 var errCloseConn = errors.New("fcgi: connection should be closed")
184
185 var emptyBody = io.NopCloser(strings.NewReader(""))
186
187
188
189 var ErrRequestAborted = errors.New("fcgi: request aborted by web server")
190
191
192
193 var ErrConnClosed = errors.New("fcgi: connection to web server closed")
194
195 func (c *child) handleRecord(rec *record) error {
196 c.mu.Lock()
197 req, ok := c.requests[rec.h.Id]
198 c.mu.Unlock()
199 if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues {
200
201 return nil
202 }
203
204 switch rec.h.Type {
205 case typeBeginRequest:
206 if req != nil {
207
208
209 return errors.New("fcgi: received ID that is already in-flight")
210 }
211
212 var br beginRequest
213 if err := br.read(rec.content()); err != nil {
214 return err
215 }
216 if br.role != roleResponder {
217 c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole)
218 return nil
219 }
220 req = newRequest(rec.h.Id, br.flags)
221 c.mu.Lock()
222 c.requests[rec.h.Id] = req
223 c.mu.Unlock()
224 return nil
225 case typeParams:
226
227
228 if len(rec.content()) > 0 {
229 req.rawParams = append(req.rawParams, rec.content()...)
230 return nil
231 }
232 req.parseParams()
233 return nil
234 case typeStdin:
235 content := rec.content()
236 if req.pw == nil {
237 var body io.ReadCloser
238 if len(content) > 0 {
239
240
241 body, req.pw = io.Pipe()
242 } else {
243 body = emptyBody
244 }
245 go c.serveRequest(req, body)
246 }
247 if len(content) > 0 {
248
249
250 req.pw.Write(content)
251 } else if req.pw != nil {
252 req.pw.Close()
253 }
254 return nil
255 case typeGetValues:
256 values := map[string]string{"FCGI_MPXS_CONNS": "1"}
257 c.conn.writePairs(typeGetValuesResult, 0, values)
258 return nil
259 case typeData:
260
261 return nil
262 case typeAbortRequest:
263 c.mu.Lock()
264 delete(c.requests, rec.h.Id)
265 c.mu.Unlock()
266 c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete)
267 if req.pw != nil {
268 req.pw.CloseWithError(ErrRequestAborted)
269 }
270 if !req.keepConn {
271
272 return errCloseConn
273 }
274 return nil
275 default:
276 b := make([]byte, 8)
277 b[0] = byte(rec.h.Type)
278 c.conn.writeRecord(typeUnknownType, 0, b)
279 return nil
280 }
281 }
282
283
284
285 func filterOutUsedEnvVars(envVars map[string]string) map[string]string {
286 withoutUsedEnvVars := make(map[string]string)
287 for k, v := range envVars {
288 if addFastCGIEnvToContext(k) {
289 withoutUsedEnvVars[k] = v
290 }
291 }
292 return withoutUsedEnvVars
293 }
294
295 func (c *child) serveRequest(req *request, body io.ReadCloser) {
296 r := newResponse(c, req)
297 httpReq, err := cgi.RequestFromMap(req.params)
298 if err != nil {
299
300 r.WriteHeader(http.StatusInternalServerError)
301 c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error()))
302 } else {
303 httpReq.Body = body
304 withoutUsedEnvVars := filterOutUsedEnvVars(req.params)
305 envVarCtx := context.WithValue(httpReq.Context(), envVarsContextKey{}, withoutUsedEnvVars)
306 httpReq = httpReq.WithContext(envVarCtx)
307 c.handler.ServeHTTP(r, httpReq)
308 }
309
310 r.Write(nil)
311 r.Close()
312 c.mu.Lock()
313 delete(c.requests, req.reqId)
314 c.mu.Unlock()
315 c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete)
316
317
318
319
320
321
322
323
324 io.CopyN(io.Discard, body, 100<<20)
325 body.Close()
326
327 if !req.keepConn {
328 c.conn.Close()
329 }
330 }
331
332 func (c *child) cleanUp() {
333 c.mu.Lock()
334 defer c.mu.Unlock()
335 for _, req := range c.requests {
336 if req.pw != nil {
337
338
339 req.pw.CloseWithError(ErrConnClosed)
340 }
341 }
342 }
343
344
345
346
347
348
349 func Serve(l net.Listener, handler http.Handler) error {
350 if l == nil {
351 var err error
352 l, err = net.FileListener(os.Stdin)
353 if err != nil {
354 return err
355 }
356 defer l.Close()
357 }
358 if handler == nil {
359 handler = http.DefaultServeMux
360 }
361 for {
362 rw, err := l.Accept()
363 if err != nil {
364 return err
365 }
366 c := newChild(rw, handler)
367 go c.serve()
368 }
369 }
370
371
372
373
374
375
376 func ProcessEnv(r *http.Request) map[string]string {
377 env, _ := r.Context().Value(envVarsContextKey{}).(map[string]string)
378 return env
379 }
380
381
382
383 func addFastCGIEnvToContext(s string) bool {
384
385 switch s {
386 case "CONTENT_LENGTH", "CONTENT_TYPE", "HTTPS",
387 "PATH_INFO", "QUERY_STRING", "REMOTE_ADDR",
388 "REMOTE_HOST", "REMOTE_PORT", "REQUEST_METHOD",
389 "REQUEST_URI", "SCRIPT_NAME", "SERVER_PROTOCOL":
390 return false
391 }
392 if strings.HasPrefix(s, "HTTP_") {
393 return false
394 }
395
396
397
398
399 switch s {
400 case "REMOTE_USER":
401 return true
402 }
403
404 return true
405 }
406
View as plain text