PageRenderTime 41ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/libgo/go/net/http/cgi/host.go

https://gitlab.com/4144/gcc
Go | 395 lines | 300 code | 40 blank | 55 comment | 99 complexity | 2f07c6f437396b1cd4f097a1efdddd8e MD5 | raw file
  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. // This file implements the host side of CGI (being the webserver
  5. // parent process).
  6. // Package cgi implements CGI (Common Gateway Interface) as specified
  7. // in RFC 3875.
  8. //
  9. // Note that using CGI means starting a new process to handle each
  10. // request, which is typically less efficient than using a
  11. // long-running server. This package is intended primarily for
  12. // compatibility with existing systems.
  13. package cgi
  14. import (
  15. "bufio"
  16. "fmt"
  17. "io"
  18. "log"
  19. "net"
  20. "net/http"
  21. "os"
  22. "os/exec"
  23. "path/filepath"
  24. "regexp"
  25. "runtime"
  26. "strconv"
  27. "strings"
  28. )
  29. var trailingPort = regexp.MustCompile(`:([0-9]+)$`)
  30. var osDefaultInheritEnv = map[string][]string{
  31. "darwin": {"DYLD_LIBRARY_PATH"},
  32. "freebsd": {"LD_LIBRARY_PATH"},
  33. "hpux": {"LD_LIBRARY_PATH", "SHLIB_PATH"},
  34. "irix": {"LD_LIBRARY_PATH", "LD_LIBRARYN32_PATH", "LD_LIBRARY64_PATH"},
  35. "linux": {"LD_LIBRARY_PATH"},
  36. "openbsd": {"LD_LIBRARY_PATH"},
  37. "solaris": {"LD_LIBRARY_PATH", "LD_LIBRARY_PATH_32", "LD_LIBRARY_PATH_64"},
  38. "windows": {"SystemRoot", "COMSPEC", "PATHEXT", "WINDIR"},
  39. }
  40. // Handler runs an executable in a subprocess with a CGI environment.
  41. type Handler struct {
  42. Path string // path to the CGI executable
  43. Root string // root URI prefix of handler or empty for "/"
  44. // Dir specifies the CGI executable's working directory.
  45. // If Dir is empty, the base directory of Path is used.
  46. // If Path has no base directory, the current working
  47. // directory is used.
  48. Dir string
  49. Env []string // extra environment variables to set, if any, as "key=value"
  50. InheritEnv []string // environment variables to inherit from host, as "key"
  51. Logger *log.Logger // optional log for errors or nil to use log.Print
  52. Args []string // optional arguments to pass to child process
  53. Stderr io.Writer // optional stderr for the child process; nil means os.Stderr
  54. // PathLocationHandler specifies the root http Handler that
  55. // should handle internal redirects when the CGI process
  56. // returns a Location header value starting with a "/", as
  57. // specified in RFC 3875 ยง 6.3.2. This will likely be
  58. // http.DefaultServeMux.
  59. //
  60. // If nil, a CGI response with a local URI path is instead sent
  61. // back to the client and not redirected internally.
  62. PathLocationHandler http.Handler
  63. }
  64. func (h *Handler) stderr() io.Writer {
  65. if h.Stderr != nil {
  66. return h.Stderr
  67. }
  68. return os.Stderr
  69. }
  70. // removeLeadingDuplicates remove leading duplicate in environments.
  71. // It's possible to override environment like following.
  72. // cgi.Handler{
  73. // ...
  74. // Env: []string{"SCRIPT_FILENAME=foo.php"},
  75. // }
  76. func removeLeadingDuplicates(env []string) (ret []string) {
  77. for i, e := range env {
  78. found := false
  79. if eq := strings.IndexByte(e, '='); eq != -1 {
  80. keq := e[:eq+1] // "key="
  81. for _, e2 := range env[i+1:] {
  82. if strings.HasPrefix(e2, keq) {
  83. found = true
  84. break
  85. }
  86. }
  87. }
  88. if !found {
  89. ret = append(ret, e)
  90. }
  91. }
  92. return
  93. }
  94. func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
  95. root := h.Root
  96. if root == "" {
  97. root = "/"
  98. }
  99. if len(req.TransferEncoding) > 0 && req.TransferEncoding[0] == "chunked" {
  100. rw.WriteHeader(http.StatusBadRequest)
  101. rw.Write([]byte("Chunked request bodies are not supported by CGI."))
  102. return
  103. }
  104. pathInfo := req.URL.Path
  105. if root != "/" && strings.HasPrefix(pathInfo, root) {
  106. pathInfo = pathInfo[len(root):]
  107. }
  108. port := "80"
  109. if matches := trailingPort.FindStringSubmatch(req.Host); len(matches) != 0 {
  110. port = matches[1]
  111. }
  112. env := []string{
  113. "SERVER_SOFTWARE=go",
  114. "SERVER_NAME=" + req.Host,
  115. "SERVER_PROTOCOL=HTTP/1.1",
  116. "HTTP_HOST=" + req.Host,
  117. "GATEWAY_INTERFACE=CGI/1.1",
  118. "REQUEST_METHOD=" + req.Method,
  119. "QUERY_STRING=" + req.URL.RawQuery,
  120. "REQUEST_URI=" + req.URL.RequestURI(),
  121. "PATH_INFO=" + pathInfo,
  122. "SCRIPT_NAME=" + root,
  123. "SCRIPT_FILENAME=" + h.Path,
  124. "SERVER_PORT=" + port,
  125. }
  126. if remoteIP, remotePort, err := net.SplitHostPort(req.RemoteAddr); err == nil {
  127. env = append(env, "REMOTE_ADDR="+remoteIP, "REMOTE_HOST="+remoteIP, "REMOTE_PORT="+remotePort)
  128. } else {
  129. // could not parse ip:port, let's use whole RemoteAddr and leave REMOTE_PORT undefined
  130. env = append(env, "REMOTE_ADDR="+req.RemoteAddr, "REMOTE_HOST="+req.RemoteAddr)
  131. }
  132. if req.TLS != nil {
  133. env = append(env, "HTTPS=on")
  134. }
  135. for k, v := range req.Header {
  136. k = strings.Map(upperCaseAndUnderscore, k)
  137. if k == "PROXY" {
  138. // See Issue 16405
  139. continue
  140. }
  141. joinStr := ", "
  142. if k == "COOKIE" {
  143. joinStr = "; "
  144. }
  145. env = append(env, "HTTP_"+k+"="+strings.Join(v, joinStr))
  146. }
  147. if req.ContentLength > 0 {
  148. env = append(env, fmt.Sprintf("CONTENT_LENGTH=%d", req.ContentLength))
  149. }
  150. if ctype := req.Header.Get("Content-Type"); ctype != "" {
  151. env = append(env, "CONTENT_TYPE="+ctype)
  152. }
  153. envPath := os.Getenv("PATH")
  154. if envPath == "" {
  155. envPath = "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin"
  156. }
  157. env = append(env, "PATH="+envPath)
  158. for _, e := range h.InheritEnv {
  159. if v := os.Getenv(e); v != "" {
  160. env = append(env, e+"="+v)
  161. }
  162. }
  163. for _, e := range osDefaultInheritEnv[runtime.GOOS] {
  164. if v := os.Getenv(e); v != "" {
  165. env = append(env, e+"="+v)
  166. }
  167. }
  168. if h.Env != nil {
  169. env = append(env, h.Env...)
  170. }
  171. env = removeLeadingDuplicates(env)
  172. var cwd, path string
  173. if h.Dir != "" {
  174. path = h.Path
  175. cwd = h.Dir
  176. } else {
  177. cwd, path = filepath.Split(h.Path)
  178. }
  179. if cwd == "" {
  180. cwd = "."
  181. }
  182. internalError := func(err error) {
  183. rw.WriteHeader(http.StatusInternalServerError)
  184. h.printf("CGI error: %v", err)
  185. }
  186. cmd := &exec.Cmd{
  187. Path: path,
  188. Args: append([]string{h.Path}, h.Args...),
  189. Dir: cwd,
  190. Env: env,
  191. Stderr: h.stderr(),
  192. }
  193. if req.ContentLength != 0 {
  194. cmd.Stdin = req.Body
  195. }
  196. stdoutRead, err := cmd.StdoutPipe()
  197. if err != nil {
  198. internalError(err)
  199. return
  200. }
  201. err = cmd.Start()
  202. if err != nil {
  203. internalError(err)
  204. return
  205. }
  206. if hook := testHookStartProcess; hook != nil {
  207. hook(cmd.Process)
  208. }
  209. defer cmd.Wait()
  210. defer stdoutRead.Close()
  211. linebody := bufio.NewReaderSize(stdoutRead, 1024)
  212. headers := make(http.Header)
  213. statusCode := 0
  214. headerLines := 0
  215. sawBlankLine := false
  216. for {
  217. line, isPrefix, err := linebody.ReadLine()
  218. if isPrefix {
  219. rw.WriteHeader(http.StatusInternalServerError)
  220. h.printf("cgi: long header line from subprocess.")
  221. return
  222. }
  223. if err == io.EOF {
  224. break
  225. }
  226. if err != nil {
  227. rw.WriteHeader(http.StatusInternalServerError)
  228. h.printf("cgi: error reading headers: %v", err)
  229. return
  230. }
  231. if len(line) == 0 {
  232. sawBlankLine = true
  233. break
  234. }
  235. headerLines++
  236. parts := strings.SplitN(string(line), ":", 2)
  237. if len(parts) < 2 {
  238. h.printf("cgi: bogus header line: %s", string(line))
  239. continue
  240. }
  241. header, val := parts[0], parts[1]
  242. header = strings.TrimSpace(header)
  243. val = strings.TrimSpace(val)
  244. switch {
  245. case header == "Status":
  246. if len(val) < 3 {
  247. h.printf("cgi: bogus status (short): %q", val)
  248. return
  249. }
  250. code, err := strconv.Atoi(val[0:3])
  251. if err != nil {
  252. h.printf("cgi: bogus status: %q", val)
  253. h.printf("cgi: line was %q", line)
  254. return
  255. }
  256. statusCode = code
  257. default:
  258. headers.Add(header, val)
  259. }
  260. }
  261. if headerLines == 0 || !sawBlankLine {
  262. rw.WriteHeader(http.StatusInternalServerError)
  263. h.printf("cgi: no headers")
  264. return
  265. }
  266. if loc := headers.Get("Location"); loc != "" {
  267. if strings.HasPrefix(loc, "/") && h.PathLocationHandler != nil {
  268. h.handleInternalRedirect(rw, req, loc)
  269. return
  270. }
  271. if statusCode == 0 {
  272. statusCode = http.StatusFound
  273. }
  274. }
  275. if statusCode == 0 && headers.Get("Content-Type") == "" {
  276. rw.WriteHeader(http.StatusInternalServerError)
  277. h.printf("cgi: missing required Content-Type in headers")
  278. return
  279. }
  280. if statusCode == 0 {
  281. statusCode = http.StatusOK
  282. }
  283. // Copy headers to rw's headers, after we've decided not to
  284. // go into handleInternalRedirect, which won't want its rw
  285. // headers to have been touched.
  286. for k, vv := range headers {
  287. for _, v := range vv {
  288. rw.Header().Add(k, v)
  289. }
  290. }
  291. rw.WriteHeader(statusCode)
  292. _, err = io.Copy(rw, linebody)
  293. if err != nil {
  294. h.printf("cgi: copy error: %v", err)
  295. // And kill the child CGI process so we don't hang on
  296. // the deferred cmd.Wait above if the error was just
  297. // the client (rw) going away. If it was a read error
  298. // (because the child died itself), then the extra
  299. // kill of an already-dead process is harmless (the PID
  300. // won't be reused until the Wait above).
  301. cmd.Process.Kill()
  302. }
  303. }
  304. func (h *Handler) printf(format string, v ...interface{}) {
  305. if h.Logger != nil {
  306. h.Logger.Printf(format, v...)
  307. } else {
  308. log.Printf(format, v...)
  309. }
  310. }
  311. func (h *Handler) handleInternalRedirect(rw http.ResponseWriter, req *http.Request, path string) {
  312. url, err := req.URL.Parse(path)
  313. if err != nil {
  314. rw.WriteHeader(http.StatusInternalServerError)
  315. h.printf("cgi: error resolving local URI path %q: %v", path, err)
  316. return
  317. }
  318. // TODO: RFC 3875 isn't clear if only GET is supported, but it
  319. // suggests so: "Note that any message-body attached to the
  320. // request (such as for a POST request) may not be available
  321. // to the resource that is the target of the redirect." We
  322. // should do some tests against Apache to see how it handles
  323. // POST, HEAD, etc. Does the internal redirect get the same
  324. // method or just GET? What about incoming headers?
  325. // (e.g. Cookies) Which headers, if any, are copied into the
  326. // second request?
  327. newReq := &http.Request{
  328. Method: "GET",
  329. URL: url,
  330. Proto: "HTTP/1.1",
  331. ProtoMajor: 1,
  332. ProtoMinor: 1,
  333. Header: make(http.Header),
  334. Host: url.Host,
  335. RemoteAddr: req.RemoteAddr,
  336. TLS: req.TLS,
  337. }
  338. h.PathLocationHandler.ServeHTTP(rw, newReq)
  339. }
  340. func upperCaseAndUnderscore(r rune) rune {
  341. switch {
  342. case r >= 'a' && r <= 'z':
  343. return r - ('a' - 'A')
  344. case r == '-':
  345. return '_'
  346. case r == '=':
  347. // Maybe not part of the CGI 'spec' but would mess up
  348. // the environment in any case, as Go represents the
  349. // environment as a slice of "key=value" strings.
  350. return '_'
  351. }
  352. // TODO: other transformations in spec or practice?
  353. return r
  354. }
  355. var testHookStartProcess func(*os.Process) // nil except for some tests