OLD | NEW |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // This file implements CGI from the perspective of a child | 5 // This file implements CGI from the perspective of a child |
6 // process. | 6 // process. |
7 | 7 |
8 package cgi | 8 package cgi |
9 | 9 |
10 import ( | 10 import ( |
11 "bufio" | 11 "bufio" |
| 12 "crypto/tls" |
12 "fmt" | 13 "fmt" |
13 "http" | 14 "http" |
14 "io" | 15 "io" |
15 "io/ioutil" | 16 "io/ioutil" |
| 17 "net" |
16 "os" | 18 "os" |
17 "strconv" | 19 "strconv" |
18 "strings" | 20 "strings" |
19 ) | 21 ) |
20 | 22 |
21 // Request returns the HTTP request as represented in the current | 23 // Request returns the HTTP request as represented in the current |
22 // environment. This assumes the current program is being run | 24 // environment. This assumes the current program is being run |
23 // by a web server in a CGI environment. | 25 // by a web server in a CGI environment. |
| 26 // The returned Request's Body is populated, if applicable. |
24 func Request() (*http.Request, os.Error) { | 27 func Request() (*http.Request, os.Error) { |
25 r, err := RequestFromMap(envMap(os.Environ())) | 28 r, err := RequestFromMap(envMap(os.Environ())) |
26 if err != nil { | 29 if err != nil { |
27 return nil, err | 30 return nil, err |
28 } | 31 } |
29 if r.ContentLength > 0 { | 32 if r.ContentLength > 0 { |
30 r.Body = ioutil.NopCloser(io.LimitReader(os.Stdin, r.ContentLeng
th)) | 33 r.Body = ioutil.NopCloser(io.LimitReader(os.Stdin, r.ContentLeng
th)) |
31 } | 34 } |
32 return r, nil | 35 return r, nil |
33 } | 36 } |
34 | 37 |
35 func envMap(env []string) map[string]string { | 38 func envMap(env []string) map[string]string { |
36 m := make(map[string]string) | 39 m := make(map[string]string) |
37 for _, kv := range env { | 40 for _, kv := range env { |
38 if idx := strings.Index(kv, "="); idx != -1 { | 41 if idx := strings.Index(kv, "="); idx != -1 { |
39 m[kv[:idx]] = kv[idx+1:] | 42 m[kv[:idx]] = kv[idx+1:] |
40 } | 43 } |
41 } | 44 } |
42 return m | 45 return m |
43 } | 46 } |
44 | 47 |
45 // These environment variables are manually copied into Request | 48 // These environment variables are manually copied into Request |
46 var skipHeader = map[string]bool{ | 49 var skipHeader = map[string]bool{ |
47 "HTTP_HOST": true, | 50 "HTTP_HOST": true, |
48 "HTTP_REFERER": true, | 51 "HTTP_REFERER": true, |
49 "HTTP_USER_AGENT": true, | 52 "HTTP_USER_AGENT": true, |
50 } | 53 } |
51 | 54 |
52 // RequestFromMap creates an http.Request from CGI variables. | 55 // RequestFromMap creates an http.Request from CGI variables. |
| 56 // The returned Request's Body field is not populated. |
53 func RequestFromMap(params map[string]string) (*http.Request, os.Error) { | 57 func RequestFromMap(params map[string]string) (*http.Request, os.Error) { |
54 r := new(http.Request) | 58 r := new(http.Request) |
55 r.Method = params["REQUEST_METHOD"] | 59 r.Method = params["REQUEST_METHOD"] |
56 if r.Method == "" { | 60 if r.Method == "" { |
57 return nil, os.NewError("cgi: no REQUEST_METHOD in environment") | 61 return nil, os.NewError("cgi: no REQUEST_METHOD in environment") |
58 } | 62 } |
59 | 63 |
60 r.Proto = params["SERVER_PROTOCOL"] | 64 r.Proto = params["SERVER_PROTOCOL"] |
61 var ok bool | 65 var ok bool |
62 r.ProtoMajor, r.ProtoMinor, ok = http.ParseHTTPVersion(r.Proto) | 66 r.ProtoMajor, r.ProtoMinor, ok = http.ParseHTTPVersion(r.Proto) |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
103 // Fallback logic if we don't have a Host header or the URL | 107 // Fallback logic if we don't have a Host header or the URL |
104 // failed to parse | 108 // failed to parse |
105 if r.URL == nil { | 109 if r.URL == nil { |
106 r.RawURL = params["REQUEST_URI"] | 110 r.RawURL = params["REQUEST_URI"] |
107 url, err := http.ParseURL(r.RawURL) | 111 url, err := http.ParseURL(r.RawURL) |
108 if err != nil { | 112 if err != nil { |
109 return nil, os.NewError("cgi: failed to parse REQUEST_UR
I into a URL: " + r.RawURL) | 113 return nil, os.NewError("cgi: failed to parse REQUEST_UR
I into a URL: " + r.RawURL) |
110 } | 114 } |
111 r.URL = url | 115 r.URL = url |
112 } | 116 } |
| 117 |
| 118 // There's apparently a de-facto standard for this. |
| 119 // http://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636 |
| 120 if s := params["HTTPS"]; s == "on" || s == "ON" || s == "1" { |
| 121 r.TLS = &tls.ConnectionState{HandshakeComplete: true} |
| 122 } |
| 123 |
| 124 // Request.RemoteAddr has its port set by Go's standard http |
| 125 // server, so we do here too. We don't have one, though, so we |
| 126 // use a dummy one. |
| 127 r.RemoteAddr = net.JoinHostPort(params["REMOTE_ADDR"], "0") |
| 128 |
113 return r, nil | 129 return r, nil |
114 } | 130 } |
115 | 131 |
116 // Serve executes the provided Handler on the currently active CGI | 132 // Serve executes the provided Handler on the currently active CGI |
117 // request, if any. If there's no current CGI environment | 133 // request, if any. If there's no current CGI environment |
118 // an error is returned. The provided handler may be nil to use | 134 // an error is returned. The provided handler may be nil to use |
119 // http.DefaultServeMux. | 135 // http.DefaultServeMux. |
120 func Serve(handler http.Handler) os.Error { | 136 func Serve(handler http.Handler) os.Error { |
121 req, err := Request() | 137 req, err := Request() |
122 if err != nil { | 138 if err != nil { |
(...skipping 18 matching lines...) Expand all Loading... |
141 req *http.Request | 157 req *http.Request |
142 header http.Header | 158 header http.Header |
143 bufw *bufio.Writer | 159 bufw *bufio.Writer |
144 headerSent bool | 160 headerSent bool |
145 } | 161 } |
146 | 162 |
147 func (r *response) Flush() { | 163 func (r *response) Flush() { |
148 r.bufw.Flush() | 164 r.bufw.Flush() |
149 } | 165 } |
150 | 166 |
151 func (r *response) RemoteAddr() string { | |
152 return os.Getenv("REMOTE_ADDR") | |
153 } | |
154 | |
155 func (r *response) Header() http.Header { | 167 func (r *response) Header() http.Header { |
156 return r.header | 168 return r.header |
157 } | 169 } |
158 | 170 |
159 func (r *response) Write(p []byte) (n int, err os.Error) { | 171 func (r *response) Write(p []byte) (n int, err os.Error) { |
160 if !r.headerSent { | 172 if !r.headerSent { |
161 r.WriteHeader(http.StatusOK) | 173 r.WriteHeader(http.StatusOK) |
162 } | 174 } |
163 return r.bufw.Write(p) | 175 return r.bufw.Write(p) |
164 } | 176 } |
165 | 177 |
166 func (r *response) WriteHeader(code int) { | 178 func (r *response) WriteHeader(code int) { |
167 if r.headerSent { | 179 if r.headerSent { |
168 // Note: explicitly using Stderr, as Stdout is our HTTP output. | 180 // Note: explicitly using Stderr, as Stdout is our HTTP output. |
169 fmt.Fprintf(os.Stderr, "CGI attempted to write header twice on r
equest for %s", r.req.URL) | 181 fmt.Fprintf(os.Stderr, "CGI attempted to write header twice on r
equest for %s", r.req.URL) |
170 return | 182 return |
171 } | 183 } |
172 r.headerSent = true | 184 r.headerSent = true |
173 fmt.Fprintf(r.bufw, "Status: %d %s\r\n", code, http.StatusText(code)) | 185 fmt.Fprintf(r.bufw, "Status: %d %s\r\n", code, http.StatusText(code)) |
174 | 186 |
175 // Set a default Content-Type | 187 // Set a default Content-Type |
176 if _, hasType := r.header["Content-Type"]; !hasType { | 188 if _, hasType := r.header["Content-Type"]; !hasType { |
177 r.header.Add("Content-Type", "text/html; charset=utf-8") | 189 r.header.Add("Content-Type", "text/html; charset=utf-8") |
178 } | 190 } |
179 | 191 |
180 r.header.Write(r.bufw) | 192 r.header.Write(r.bufw) |
181 r.bufw.WriteString("\r\n") | 193 r.bufw.WriteString("\r\n") |
182 r.bufw.Flush() | 194 r.bufw.Flush() |
183 } | 195 } |
OLD | NEW |