Commit 98945a2b authored by Evan Shaw's avatar Evan Shaw Committed by Brad Fitzpatrick

cgi: export RequestFromMap

R=rsc, bradfitz
CC=golang-dev
https://golang.org/cl/4452056
parent 1801972b
...@@ -22,7 +22,14 @@ import ( ...@@ -22,7 +22,14 @@ import (
// environment. This assumes the current program is being run // environment. This assumes the current program is being run
// by a web server in a CGI environment. // by a web server in a CGI environment.
func Request() (*http.Request, os.Error) { func Request() (*http.Request, os.Error) {
return requestFromEnvironment(envMap(os.Environ())) r, err := RequestFromMap(envMap(os.Environ()))
if err != nil {
return nil, err
}
if r.ContentLength > 0 {
r.Body = ioutil.NopCloser(io.LimitReader(os.Stdin, r.ContentLength))
}
return r, nil
} }
func envMap(env []string) map[string]string { func envMap(env []string) map[string]string {
...@@ -42,37 +49,39 @@ var skipHeader = map[string]bool{ ...@@ -42,37 +49,39 @@ var skipHeader = map[string]bool{
"HTTP_USER_AGENT": true, "HTTP_USER_AGENT": true,
} }
func requestFromEnvironment(env map[string]string) (*http.Request, os.Error) { // RequestFromMap creates an http.Request from CGI variables.
func RequestFromMap(params map[string]string) (*http.Request, os.Error) {
r := new(http.Request) r := new(http.Request)
r.Method = env["REQUEST_METHOD"] r.Method = params["REQUEST_METHOD"]
if r.Method == "" { if r.Method == "" {
return nil, os.NewError("cgi: no REQUEST_METHOD in environment") return nil, os.NewError("cgi: no REQUEST_METHOD in environment")
} }
r.Proto = params["SERVER_PROTOCOL"]
var ok bool
r.ProtoMajor, r.ProtoMinor, ok = http.ParseHTTPVersion(r.Proto)
if !ok {
return nil, os.NewError("cgi: invalid SERVER_PROTOCOL version")
}
r.Close = true r.Close = true
r.Trailer = http.Header{} r.Trailer = http.Header{}
r.Header = http.Header{} r.Header = http.Header{}
r.Host = env["HTTP_HOST"] r.Host = params["HTTP_HOST"]
r.Referer = env["HTTP_REFERER"] r.Referer = params["HTTP_REFERER"]
r.UserAgent = env["HTTP_USER_AGENT"] r.UserAgent = params["HTTP_USER_AGENT"]
// CGI doesn't allow chunked requests, so these should all be accurate:
r.Proto = "HTTP/1.0"
r.ProtoMajor = 1
r.ProtoMinor = 0
r.TransferEncoding = nil
if lenstr := env["CONTENT_LENGTH"]; lenstr != "" { if lenstr := params["CONTENT_LENGTH"]; lenstr != "" {
clen, err := strconv.Atoi64(lenstr) clen, err := strconv.Atoi64(lenstr)
if err != nil { if err != nil {
return nil, os.NewError("cgi: bad CONTENT_LENGTH in environment: " + lenstr) return nil, os.NewError("cgi: bad CONTENT_LENGTH in environment: " + lenstr)
} }
r.ContentLength = clen r.ContentLength = clen
r.Body = ioutil.NopCloser(io.LimitReader(os.Stdin, clen))
} }
// Copy "HTTP_FOO_BAR" variables to "Foo-Bar" Headers // Copy "HTTP_FOO_BAR" variables to "Foo-Bar" Headers
for k, v := range env { for k, v := range params {
if !strings.HasPrefix(k, "HTTP_") || skipHeader[k] { if !strings.HasPrefix(k, "HTTP_") || skipHeader[k] {
continue continue
} }
...@@ -84,7 +93,7 @@ func requestFromEnvironment(env map[string]string) (*http.Request, os.Error) { ...@@ -84,7 +93,7 @@ func requestFromEnvironment(env map[string]string) (*http.Request, os.Error) {
if r.Host != "" { if r.Host != "" {
// Hostname is provided, so we can reasonably construct a URL, // Hostname is provided, so we can reasonably construct a URL,
// even if we have to assume 'http' for the scheme. // even if we have to assume 'http' for the scheme.
r.RawURL = "http://" + r.Host + env["REQUEST_URI"] r.RawURL = "http://" + r.Host + params["REQUEST_URI"]
url, err := http.ParseURL(r.RawURL) url, err := http.ParseURL(r.RawURL)
if err != nil { if err != nil {
return nil, os.NewError("cgi: failed to parse host and REQUEST_URI into a URL: " + r.RawURL) return nil, os.NewError("cgi: failed to parse host and REQUEST_URI into a URL: " + r.RawURL)
...@@ -94,7 +103,7 @@ func requestFromEnvironment(env map[string]string) (*http.Request, os.Error) { ...@@ -94,7 +103,7 @@ func requestFromEnvironment(env map[string]string) (*http.Request, os.Error) {
// Fallback logic if we don't have a Host header or the URL // Fallback logic if we don't have a Host header or the URL
// failed to parse // failed to parse
if r.URL == nil { if r.URL == nil {
r.RawURL = env["REQUEST_URI"] r.RawURL = params["REQUEST_URI"]
url, err := http.ParseURL(r.RawURL) url, err := http.ParseURL(r.RawURL)
if err != nil { if err != nil {
return nil, os.NewError("cgi: failed to parse REQUEST_URI into a URL: " + r.RawURL) return nil, os.NewError("cgi: failed to parse REQUEST_URI into a URL: " + r.RawURL)
...@@ -172,12 +181,3 @@ func (r *response) WriteHeader(code int) { ...@@ -172,12 +181,3 @@ func (r *response) WriteHeader(code int) {
r.bufw.WriteString("\r\n") r.bufw.WriteString("\r\n")
r.bufw.Flush() r.bufw.Flush()
} }
func (r *response) UsingTLS() bool {
// There's apparently a de-facto standard for this.
// http://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636
if s := os.Getenv("HTTPS"); s == "on" || s == "ON" || s == "1" {
return true
}
return false
}
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
func TestRequest(t *testing.T) { func TestRequest(t *testing.T) {
env := map[string]string{ env := map[string]string{
"SERVER_PROTOCOL": "HTTP/1.1",
"REQUEST_METHOD": "GET", "REQUEST_METHOD": "GET",
"HTTP_HOST": "example.com", "HTTP_HOST": "example.com",
"HTTP_REFERER": "elsewhere", "HTTP_REFERER": "elsewhere",
...@@ -20,9 +21,9 @@ func TestRequest(t *testing.T) { ...@@ -20,9 +21,9 @@ func TestRequest(t *testing.T) {
"REQUEST_URI": "/path?a=b", "REQUEST_URI": "/path?a=b",
"CONTENT_LENGTH": "123", "CONTENT_LENGTH": "123",
} }
req, err := requestFromEnvironment(env) req, err := RequestFromMap(env)
if err != nil { if err != nil {
t.Fatalf("requestFromEnvironment: %v", err) t.Fatalf("RequestFromMap: %v", err)
} }
if g, e := req.UserAgent, "goclient"; e != g { if g, e := req.UserAgent, "goclient"; e != g {
t.Errorf("expected UserAgent %q; got %q", e, g) t.Errorf("expected UserAgent %q; got %q", e, g)
...@@ -62,14 +63,15 @@ func TestRequest(t *testing.T) { ...@@ -62,14 +63,15 @@ func TestRequest(t *testing.T) {
func TestRequestWithoutHost(t *testing.T) { func TestRequestWithoutHost(t *testing.T) {
env := map[string]string{ env := map[string]string{
"HTTP_HOST": "", "SERVER_PROTOCOL": "HTTP/1.1",
"REQUEST_METHOD": "GET", "HTTP_HOST": "",
"REQUEST_URI": "/path?a=b", "REQUEST_METHOD": "GET",
"CONTENT_LENGTH": "123", "REQUEST_URI": "/path?a=b",
"CONTENT_LENGTH": "123",
} }
req, err := requestFromEnvironment(env) req, err := RequestFromMap(env)
if err != nil { if err != nil {
t.Fatalf("requestFromEnvironment: %v", err) t.Fatalf("RequestFromMap: %v", err)
} }
if g, e := req.RawURL, "/path?a=b"; e != g { if g, e := req.RawURL, "/path?a=b"; e != g {
t.Errorf("expected RawURL %q; got %q", e, g) t.Errorf("expected RawURL %q; got %q", e, g)
......
...@@ -86,6 +86,7 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { ...@@ -86,6 +86,7 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
env := []string{ env := []string{
"SERVER_SOFTWARE=go", "SERVER_SOFTWARE=go",
"SERVER_NAME=" + req.Host, "SERVER_NAME=" + req.Host,
"SERVER_PROTOCOL=HTTP/1.1",
"HTTP_HOST=" + req.Host, "HTTP_HOST=" + req.Host,
"GATEWAY_INTERFACE=CGI/1.1", "GATEWAY_INTERFACE=CGI/1.1",
"REQUEST_METHOD=" + req.Method, "REQUEST_METHOD=" + req.Method,
......
...@@ -9,11 +9,10 @@ package fcgi ...@@ -9,11 +9,10 @@ package fcgi
import ( import (
"fmt" "fmt"
"http" "http"
"http/cgi"
"io" "io"
"net" "net"
"os" "os"
"strconv"
"strings"
"time" "time"
) )
...@@ -38,68 +37,6 @@ func newRequest(reqId uint16, flags uint8) *request { ...@@ -38,68 +37,6 @@ func newRequest(reqId uint16, flags uint8) *request {
return r return r
} }
// TODO(eds): copied from http/cgi
var skipHeader = map[string]bool{
"HTTP_HOST": true,
"HTTP_REFERER": true,
"HTTP_USER_AGENT": true,
}
// httpRequest converts r to an http.Request.
// TODO(eds): this is very similar to http/cgi's requestFromEnvironment
func (r *request) httpRequest(body io.ReadCloser) (*http.Request, os.Error) {
req := &http.Request{
Method: r.params["REQUEST_METHOD"],
RawURL: r.params["REQUEST_URI"],
Body: body,
Header: http.Header{},
Trailer: http.Header{},
Proto: r.params["SERVER_PROTOCOL"],
}
var ok bool
req.ProtoMajor, req.ProtoMinor, ok = http.ParseHTTPVersion(req.Proto)
if !ok {
return nil, os.NewError("fcgi: invalid HTTP version")
}
req.Host = r.params["HTTP_HOST"]
req.Referer = r.params["HTTP_REFERER"]
req.UserAgent = r.params["HTTP_USER_AGENT"]
if lenstr := r.params["CONTENT_LENGTH"]; lenstr != "" {
clen, err := strconv.Atoi64(r.params["CONTENT_LENGTH"])
if err != nil {
return nil, os.NewError("fcgi: bad CONTENT_LENGTH parameter: " + lenstr)
}
req.ContentLength = clen
}
if req.Host != "" {
req.RawURL = "http://" + req.Host + r.params["REQUEST_URI"]
url, err := http.ParseURL(req.RawURL)
if err != nil {
return nil, os.NewError("fcgi: failed to parse host and REQUEST_URI into a URL: " + req.RawURL)
}
req.URL = url
}
if req.URL == nil {
req.RawURL = r.params["REQUEST_URI"]
url, err := http.ParseURL(req.RawURL)
if err != nil {
return nil, os.NewError("fcgi: failed to parse REQUEST_URI into a URL: " + req.RawURL)
}
req.URL = url
}
for key, val := range r.params {
if strings.HasPrefix(key, "HTTP_") && !skipHeader[key] {
req.Header.Add(strings.Replace(key[5:], "_", "-", -1), val)
}
}
return req, nil
}
// parseParams reads an encoded []byte into Params. // parseParams reads an encoded []byte into Params.
func (r *request) parseParams() { func (r *request) parseParams() {
text := r.rawParams text := r.rawParams
...@@ -273,12 +210,13 @@ func (c *child) serve() { ...@@ -273,12 +210,13 @@ func (c *child) serve() {
func (c *child) serveRequest(req *request, body io.ReadCloser) { func (c *child) serveRequest(req *request, body io.ReadCloser) {
r := newResponse(c, req) r := newResponse(c, req)
httpReq, err := req.httpRequest(body) httpReq, err := cgi.RequestFromMap(req.params)
if err != nil { if err != nil {
// there was an error reading the request // there was an error reading the request
r.WriteHeader(http.StatusInternalServerError) r.WriteHeader(http.StatusInternalServerError)
c.conn.writeRecord(typeStderr, req.reqId, []byte(err.String())) c.conn.writeRecord(typeStderr, req.reqId, []byte(err.String()))
} else { } else {
httpReq.Body = body
c.handler.ServeHTTP(r, httpReq) c.handler.ServeHTTP(r, httpReq)
} }
if body != nil { if body != nil {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment