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
5package fcgi
6
7// This file implements FastCGI from the perspective of a child process.
8
9import (
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// request holds the state for an in-progress request. As soon as it's complete,
24// it's converted to an http.Request.
25type 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// envVarsContextKey uniquely identifies a mapping of CGI
35// environment variables to their values in a request context
36type envVarsContextKey struct{}
37
38func 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// parseParams reads an encoded []byte into Params.
49func (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// response implements http.ResponseWriter.
75type response struct {
76	req            *request
77	header         http.Header
78	code           int
79	wroteHeader    bool
80	wroteCGIHeader bool
81	w              *bufWriter
82}
83
84func 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
92func (r *response) Header() http.Header {
93	return r.header
94}
95
96func (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
106func (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		// Must not have body.
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// writeCGIHeader finalizes the header sent to the client and writes it to the output.
124// p is not written by writeHeader, but is the first chunk of the body
125// that will be written. It is sniffed for a Content-Type if none is
126// set explicitly.
127func (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
141func (r *response) Flush() {
142	if !r.wroteHeader {
143		r.WriteHeader(http.StatusOK)
144	}
145	r.w.Flush()
146}
147
148func (r *response) Close() error {
149	r.Flush()
150	return r.w.Close()
151}
152
153type child struct {
154	conn    *conn
155	handler http.Handler
156
157	mu       sync.Mutex          // protects requests:
158	requests map[uint16]*request // keyed by request ID
159}
160
161func 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
169func (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
183var errCloseConn = errors.New("fcgi: connection should be closed")
184
185var emptyBody = io.NopCloser(strings.NewReader(""))
186
187// ErrRequestAborted is returned by Read when a handler attempts to read the
188// body of a request that has been aborted by the web server.
189var ErrRequestAborted = errors.New("fcgi: request aborted by web server")
190
191// ErrConnClosed is returned by Read when a handler attempts to read the body of
192// a request after the connection to the web server has been closed.
193var ErrConnClosed = errors.New("fcgi: connection to web server closed")
194
195func (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		// The spec says to ignore unknown request IDs.
201		return nil
202	}
203
204	switch rec.h.Type {
205	case typeBeginRequest:
206		if req != nil {
207			// The server is trying to begin a request with the same ID
208			// as an in-progress request. This is an error.
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		// NOTE(eds): Technically a key-value pair can straddle the boundary
227		// between two packets. We buffer until we've received all parameters.
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				// body could be an io.LimitReader, but it shouldn't matter
240				// as long as both sides are behaving.
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			// TODO(eds): This blocks until the handler reads from the pipe.
249			// If the handler takes a long time, it might be a problem.
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		// If the filter role is implemented, read the data stream here.
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			// connection will close upon return
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// filterOutUsedEnvVars returns a new map of env vars without the
284// variables in the given envVars map that are read for creating each http.Request
285func 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
295func (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		// there was an error reading the request
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	// Make sure we serve something even if nothing was written to r
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	// Consume the entire body, so the host isn't still writing to
318	// us when we close the socket below in the !keepConn case,
319	// otherwise we'd send a RST. (golang.org/issue/4183)
320	// TODO(bradfitz): also bound this copy in time. Or send
321	// some sort of abort request to the host, so the host
322	// can properly cut off the client sending all the data.
323	// For now just bound it a little and
324	io.CopyN(io.Discard, body, 100<<20)
325	body.Close()
326
327	if !req.keepConn {
328		c.conn.Close()
329	}
330}
331
332func (c *child) cleanUp() {
333	c.mu.Lock()
334	defer c.mu.Unlock()
335	for _, req := range c.requests {
336		if req.pw != nil {
337			// race with call to Close in c.serveRequest doesn't matter because
338			// Pipe(Reader|Writer).Close are idempotent
339			req.pw.CloseWithError(ErrConnClosed)
340		}
341	}
342}
343
344// Serve accepts incoming FastCGI connections on the listener l, creating a new
345// goroutine for each. The goroutine reads requests and then calls handler
346// to reply to them.
347// If l is nil, Serve accepts connections from os.Stdin.
348// If handler is nil, http.DefaultServeMux is used.
349func 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// ProcessEnv returns FastCGI environment variables associated with the request r
372// for which no effort was made to be included in the request itself - the data
373// is hidden in the request's context. As an example, if REMOTE_USER is set for a
374// request, it will not be found anywhere in r, but it will be included in
375// ProcessEnv's response (via r's context).
376func ProcessEnv(r *http.Request) map[string]string {
377	env, _ := r.Context().Value(envVarsContextKey{}).(map[string]string)
378	return env
379}
380
381// addFastCGIEnvToContext reports whether to include the FastCGI environment variable s
382// in the http.Request.Context, accessible via ProcessEnv.
383func addFastCGIEnvToContext(s string) bool {
384	// Exclude things supported by net/http natively:
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	// Explicitly include FastCGI-specific things.
396	// This list is redundant with the default "return true" below.
397	// Consider this documentation of the sorts of things we expect
398	// to maybe see.
399	switch s {
400	case "REMOTE_USER":
401		return true
402	}
403	// Unknown, so include it to be safe.
404	return true
405}
406