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.
5 // This file implements the host side of CGI (being the webserver
8 // Package cgi implements CGI (Common Gateway Interface) as specified
11 // Note that using CGI means starting a new process to handle each
12 // request, which is typically less efficient than using a
13 // long-running server. This package is intended primarily for
14 // compatibility with existing systems.
32 var trailingPort
= regexp
.MustCompile(`:([0-9]+)$`)
34 var osDefaultInheritEnv
= map[string][]string{
35 "darwin": {"DYLD_LIBRARY_PATH"},
36 "freebsd": {"LD_LIBRARY_PATH"},
37 "hpux": {"LD_LIBRARY_PATH", "SHLIB_PATH"},
38 "irix": {"LD_LIBRARY_PATH", "LD_LIBRARYN32_PATH", "LD_LIBRARY64_PATH"},
39 "linux": {"LD_LIBRARY_PATH"},
40 "openbsd": {"LD_LIBRARY_PATH"},
41 "solaris": {"LD_LIBRARY_PATH", "LD_LIBRARY_PATH_32", "LD_LIBRARY_PATH_64"},
42 "windows": {"SystemRoot", "COMSPEC", "PATHEXT", "WINDIR"},
45 // Handler runs an executable in a subprocess with a CGI environment.
47 Path
string // path to the CGI executable
48 Root
string // root URI prefix of handler or empty for "/"
50 // Dir specifies the CGI executable's working directory.
51 // If Dir is empty, the base directory of Path is used.
52 // If Path has no base directory, the current working
56 Env
[]string // extra environment variables to set, if any, as "key=value"
57 InheritEnv
[]string // environment variables to inherit from host, as "key"
58 Logger
*log
.Logger
// optional log for errors or nil to use log.Print
59 Args
[]string // optional arguments to pass to child process
61 // PathLocationHandler specifies the root http Handler that
62 // should handle internal redirects when the CGI process
63 // returns a Location header value starting with a "/", as
64 // specified in RFC 3875 ยง 6.3.2. This will likely be
65 // http.DefaultServeMux.
67 // If nil, a CGI response with a local URI path is instead sent
68 // back to the client and not redirected internally.
69 PathLocationHandler http
.Handler
72 // removeLeadingDuplicates remove leading duplicate in environments.
73 // It's possible to override environment like following.
76 // Env: []string{"SCRIPT_FILENAME=foo.php"},
78 func removeLeadingDuplicates(env
[]string) (ret
[]string) {
80 for i
:= 0; i
< n
; i
++ {
82 s
:= strings
.SplitN(e
, "=", 2)[0]
84 for j
:= i
+ 1; j
< n
; j
++ {
85 if s
== strings
.SplitN(env
[j
], "=", 2)[0] {
97 func (h
*Handler
) ServeHTTP(rw http
.ResponseWriter
, req
*http
.Request
) {
103 if len(req
.TransferEncoding
) > 0 && req
.TransferEncoding
[0] == "chunked" {
104 rw
.WriteHeader(http
.StatusBadRequest
)
105 rw
.Write([]byte("Chunked request bodies are not supported by CGI."))
109 pathInfo
:= req
.URL
.Path
110 if root
!= "/" && strings
.HasPrefix(pathInfo
, root
) {
111 pathInfo
= pathInfo
[len(root
):]
115 if matches
:= trailingPort
.FindStringSubmatch(req
.Host
); len(matches
) != 0 {
120 "SERVER_SOFTWARE=go",
121 "SERVER_NAME=" + req
.Host
,
122 "SERVER_PROTOCOL=HTTP/1.1",
123 "HTTP_HOST=" + req
.Host
,
124 "GATEWAY_INTERFACE=CGI/1.1",
125 "REQUEST_METHOD=" + req
.Method
,
126 "QUERY_STRING=" + req
.URL
.RawQuery
,
127 "REQUEST_URI=" + req
.URL
.RequestURI(),
128 "PATH_INFO=" + pathInfo
,
129 "SCRIPT_NAME=" + root
,
130 "SCRIPT_FILENAME=" + h
.Path
,
131 "REMOTE_ADDR=" + req
.RemoteAddr
,
132 "REMOTE_HOST=" + req
.RemoteAddr
,
133 "SERVER_PORT=" + port
,
137 env
= append(env
, "HTTPS=on")
140 for k
, v
:= range req
.Header
{
141 k
= strings
.Map(upperCaseAndUnderscore
, k
)
146 env
= append(env
, "HTTP_"+k
+"="+strings
.Join(v
, joinStr
))
149 if req
.ContentLength
> 0 {
150 env
= append(env
, fmt
.Sprintf("CONTENT_LENGTH=%d", req
.ContentLength
))
152 if ctype
:= req
.Header
.Get("Content-Type"); ctype
!= "" {
153 env
= append(env
, "CONTENT_TYPE="+ctype
)
157 env
= append(env
, h
.Env
...)
160 envPath
:= os
.Getenv("PATH")
162 envPath
= "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin"
164 env
= append(env
, "PATH="+envPath
)
166 for _
, e
:= range h
.InheritEnv
{
167 if v
:= os
.Getenv(e
); v
!= "" {
168 env
= append(env
, e
+"="+v
)
172 for _
, e
:= range osDefaultInheritEnv
[runtime
.GOOS
] {
173 if v
:= os
.Getenv(e
); v
!= "" {
174 env
= append(env
, e
+"="+v
)
178 env
= removeLeadingDuplicates(env
)
185 cwd
, path
= filepath
.Split(h
.Path
)
191 internalError
:= func(err error
) {
192 rw
.WriteHeader(http
.StatusInternalServerError
)
193 h
.printf("CGI error: %v", err
)
198 Args
: append([]string{h
.Path
}, h
.Args
...),
201 Stderr
: os
.Stderr
, // for now
203 if req
.ContentLength
!= 0 {
206 stdoutRead
, err
:= cmd
.StdoutPipe()
217 if hook
:= testHookStartProcess
; hook
!= nil {
221 defer stdoutRead
.Close()
223 linebody
:= bufio
.NewReaderSize(stdoutRead
, 1024)
224 headers
:= make(http
.Header
)
227 sawBlankLine
:= false
229 line
, isPrefix
, err
:= linebody
.ReadLine()
231 rw
.WriteHeader(http
.StatusInternalServerError
)
232 h
.printf("cgi: long header line from subprocess.")
239 rw
.WriteHeader(http
.StatusInternalServerError
)
240 h
.printf("cgi: error reading headers: %v", err
)
248 parts
:= strings
.SplitN(string(line
), ":", 2)
250 h
.printf("cgi: bogus header line: %s", string(line
))
253 header
, val
:= parts
[0], parts
[1]
254 header
= strings
.TrimSpace(header
)
255 val
= strings
.TrimSpace(val
)
257 case header
== "Status":
259 h
.printf("cgi: bogus status (short): %q", val
)
262 code
, err
:= strconv
.Atoi(val
[0:3])
264 h
.printf("cgi: bogus status: %q", val
)
265 h
.printf("cgi: line was %q", line
)
270 headers
.Add(header
, val
)
273 if headerLines
== 0 ||
!sawBlankLine
{
274 rw
.WriteHeader(http
.StatusInternalServerError
)
275 h
.printf("cgi: no headers")
279 if loc
:= headers
.Get("Location"); loc
!= "" {
280 if strings
.HasPrefix(loc
, "/") && h
.PathLocationHandler
!= nil {
281 h
.handleInternalRedirect(rw
, req
, loc
)
285 statusCode
= http
.StatusFound
289 if statusCode
== 0 && headers
.Get("Content-Type") == "" {
290 rw
.WriteHeader(http
.StatusInternalServerError
)
291 h
.printf("cgi: missing required Content-Type in headers")
296 statusCode
= http
.StatusOK
299 // Copy headers to rw's headers, after we've decided not to
300 // go into handleInternalRedirect, which won't want its rw
301 // headers to have been touched.
302 for k
, vv
:= range headers
{
303 for _
, v
:= range vv
{
304 rw
.Header().Add(k
, v
)
308 rw
.WriteHeader(statusCode
)
310 _
, err
= io
.Copy(rw
, linebody
)
312 h
.printf("cgi: copy error: %v", err
)
313 // And kill the child CGI process so we don't hang on
314 // the deferred cmd.Wait above if the error was just
315 // the client (rw) going away. If it was a read error
316 // (because the child died itself), then the extra
317 // kill of an already-dead process is harmless (the PID
318 // won't be reused until the Wait above).
323 func (h
*Handler
) printf(format
string, v
...interface{}) {
325 h
.Logger
.Printf(format
, v
...)
327 log
.Printf(format
, v
...)
331 func (h
*Handler
) handleInternalRedirect(rw http
.ResponseWriter
, req
*http
.Request
, path
string) {
332 url
, err
:= req
.URL
.Parse(path
)
334 rw
.WriteHeader(http
.StatusInternalServerError
)
335 h
.printf("cgi: error resolving local URI path %q: %v", path
, err
)
338 // TODO: RFC 3875 isn't clear if only GET is supported, but it
339 // suggests so: "Note that any message-body attached to the
340 // request (such as for a POST request) may not be available
341 // to the resource that is the target of the redirect." We
342 // should do some tests against Apache to see how it handles
343 // POST, HEAD, etc. Does the internal redirect get the same
344 // method or just GET? What about incoming headers?
345 // (e.g. Cookies) Which headers, if any, are copied into the
347 newReq
:= &http
.Request
{
353 Header
: make(http
.Header
),
355 RemoteAddr
: req
.RemoteAddr
,
358 h
.PathLocationHandler
.ServeHTTP(rw
, newReq
)
361 func upperCaseAndUnderscore(r rune
) rune
{
363 case r
>= 'a' && r
<= 'z':
364 return r
- ('a' - 'A')
368 // Maybe not part of the CGI 'spec' but would mess up
369 // the environment in any case, as Go represents the
370 // environment as a slice of "key=value" strings.
373 // TODO: other transformations in spec or practice?
377 var testHookStartProcess
func(*os
.Process
) // nil except for some tests