...

Source file src/net/http/cgi/child.go

Documentation: net/http/cgi

		 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  // This file implements CGI from the perspective of a child
		 6  // process.
		 7  
		 8  package cgi
		 9  
		10  import (
		11  	"bufio"
		12  	"crypto/tls"
		13  	"errors"
		14  	"fmt"
		15  	"io"
		16  	"net"
		17  	"net/http"
		18  	"net/url"
		19  	"os"
		20  	"strconv"
		21  	"strings"
		22  )
		23  
		24  // Request returns the HTTP request as represented in the current
		25  // environment. This assumes the current program is being run
		26  // by a web server in a CGI environment.
		27  // The returned Request's Body is populated, if applicable.
		28  func Request() (*http.Request, error) {
		29  	r, err := RequestFromMap(envMap(os.Environ()))
		30  	if err != nil {
		31  		return nil, err
		32  	}
		33  	if r.ContentLength > 0 {
		34  		r.Body = io.NopCloser(io.LimitReader(os.Stdin, r.ContentLength))
		35  	}
		36  	return r, nil
		37  }
		38  
		39  func envMap(env []string) map[string]string {
		40  	m := make(map[string]string)
		41  	for _, kv := range env {
		42  		if idx := strings.Index(kv, "="); idx != -1 {
		43  			m[kv[:idx]] = kv[idx+1:]
		44  		}
		45  	}
		46  	return m
		47  }
		48  
		49  // RequestFromMap creates an http.Request from CGI variables.
		50  // The returned Request's Body field is not populated.
		51  func RequestFromMap(params map[string]string) (*http.Request, error) {
		52  	r := new(http.Request)
		53  	r.Method = params["REQUEST_METHOD"]
		54  	if r.Method == "" {
		55  		return nil, errors.New("cgi: no REQUEST_METHOD in environment")
		56  	}
		57  
		58  	r.Proto = params["SERVER_PROTOCOL"]
		59  	var ok bool
		60  	r.ProtoMajor, r.ProtoMinor, ok = http.ParseHTTPVersion(r.Proto)
		61  	if !ok {
		62  		return nil, errors.New("cgi: invalid SERVER_PROTOCOL version")
		63  	}
		64  
		65  	r.Close = true
		66  	r.Trailer = http.Header{}
		67  	r.Header = http.Header{}
		68  
		69  	r.Host = params["HTTP_HOST"]
		70  
		71  	if lenstr := params["CONTENT_LENGTH"]; lenstr != "" {
		72  		clen, err := strconv.ParseInt(lenstr, 10, 64)
		73  		if err != nil {
		74  			return nil, errors.New("cgi: bad CONTENT_LENGTH in environment: " + lenstr)
		75  		}
		76  		r.ContentLength = clen
		77  	}
		78  
		79  	if ct := params["CONTENT_TYPE"]; ct != "" {
		80  		r.Header.Set("Content-Type", ct)
		81  	}
		82  
		83  	// Copy "HTTP_FOO_BAR" variables to "Foo-Bar" Headers
		84  	for k, v := range params {
		85  		if !strings.HasPrefix(k, "HTTP_") || k == "HTTP_HOST" {
		86  			continue
		87  		}
		88  		r.Header.Add(strings.ReplaceAll(k[5:], "_", "-"), v)
		89  	}
		90  
		91  	uriStr := params["REQUEST_URI"]
		92  	if uriStr == "" {
		93  		// Fallback to SCRIPT_NAME, PATH_INFO and QUERY_STRING.
		94  		uriStr = params["SCRIPT_NAME"] + params["PATH_INFO"]
		95  		s := params["QUERY_STRING"]
		96  		if s != "" {
		97  			uriStr += "?" + s
		98  		}
		99  	}
	 100  
	 101  	// There's apparently a de-facto standard for this.
	 102  	// https://web.archive.org/web/20170105004655/http://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636
	 103  	if s := params["HTTPS"]; s == "on" || s == "ON" || s == "1" {
	 104  		r.TLS = &tls.ConnectionState{HandshakeComplete: true}
	 105  	}
	 106  
	 107  	if r.Host != "" {
	 108  		// Hostname is provided, so we can reasonably construct a URL.
	 109  		rawurl := r.Host + uriStr
	 110  		if r.TLS == nil {
	 111  			rawurl = "http://" + rawurl
	 112  		} else {
	 113  			rawurl = "https://" + rawurl
	 114  		}
	 115  		url, err := url.Parse(rawurl)
	 116  		if err != nil {
	 117  			return nil, errors.New("cgi: failed to parse host and REQUEST_URI into a URL: " + rawurl)
	 118  		}
	 119  		r.URL = url
	 120  	}
	 121  	// Fallback logic if we don't have a Host header or the URL
	 122  	// failed to parse
	 123  	if r.URL == nil {
	 124  		url, err := url.Parse(uriStr)
	 125  		if err != nil {
	 126  			return nil, errors.New("cgi: failed to parse REQUEST_URI into a URL: " + uriStr)
	 127  		}
	 128  		r.URL = url
	 129  	}
	 130  
	 131  	// Request.RemoteAddr has its port set by Go's standard http
	 132  	// server, so we do here too.
	 133  	remotePort, _ := strconv.Atoi(params["REMOTE_PORT"]) // zero if unset or invalid
	 134  	r.RemoteAddr = net.JoinHostPort(params["REMOTE_ADDR"], strconv.Itoa(remotePort))
	 135  
	 136  	return r, nil
	 137  }
	 138  
	 139  // Serve executes the provided Handler on the currently active CGI
	 140  // request, if any. If there's no current CGI environment
	 141  // an error is returned. The provided handler may be nil to use
	 142  // http.DefaultServeMux.
	 143  func Serve(handler http.Handler) error {
	 144  	req, err := Request()
	 145  	if err != nil {
	 146  		return err
	 147  	}
	 148  	if req.Body == nil {
	 149  		req.Body = http.NoBody
	 150  	}
	 151  	if handler == nil {
	 152  		handler = http.DefaultServeMux
	 153  	}
	 154  	rw := &response{
	 155  		req:		req,
	 156  		header: make(http.Header),
	 157  		bufw:	 bufio.NewWriter(os.Stdout),
	 158  	}
	 159  	handler.ServeHTTP(rw, req)
	 160  	rw.Write(nil) // make sure a response is sent
	 161  	if err = rw.bufw.Flush(); err != nil {
	 162  		return err
	 163  	}
	 164  	return nil
	 165  }
	 166  
	 167  type response struct {
	 168  	req						*http.Request
	 169  	header				 http.Header
	 170  	code					 int
	 171  	wroteHeader		bool
	 172  	wroteCGIHeader bool
	 173  	bufw					 *bufio.Writer
	 174  }
	 175  
	 176  func (r *response) Flush() {
	 177  	r.bufw.Flush()
	 178  }
	 179  
	 180  func (r *response) Header() http.Header {
	 181  	return r.header
	 182  }
	 183  
	 184  func (r *response) Write(p []byte) (n int, err error) {
	 185  	if !r.wroteHeader {
	 186  		r.WriteHeader(http.StatusOK)
	 187  	}
	 188  	if !r.wroteCGIHeader {
	 189  		r.writeCGIHeader(p)
	 190  	}
	 191  	return r.bufw.Write(p)
	 192  }
	 193  
	 194  func (r *response) WriteHeader(code int) {
	 195  	if r.wroteHeader {
	 196  		// Note: explicitly using Stderr, as Stdout is our HTTP output.
	 197  		fmt.Fprintf(os.Stderr, "CGI attempted to write header twice on request for %s", r.req.URL)
	 198  		return
	 199  	}
	 200  	r.wroteHeader = true
	 201  	r.code = code
	 202  }
	 203  
	 204  // writeCGIHeader finalizes the header sent to the client and writes it to the output.
	 205  // p is not written by writeHeader, but is the first chunk of the body
	 206  // that will be written. It is sniffed for a Content-Type if none is
	 207  // set explicitly.
	 208  func (r *response) writeCGIHeader(p []byte) {
	 209  	if r.wroteCGIHeader {
	 210  		return
	 211  	}
	 212  	r.wroteCGIHeader = true
	 213  	fmt.Fprintf(r.bufw, "Status: %d %s\r\n", r.code, http.StatusText(r.code))
	 214  	if _, hasType := r.header["Content-Type"]; !hasType {
	 215  		r.header.Set("Content-Type", http.DetectContentType(p))
	 216  	}
	 217  	r.header.Write(r.bufw)
	 218  	r.bufw.WriteString("\r\n")
	 219  	r.bufw.Flush()
	 220  }
	 221  

View as plain text