Documentation
¶
Overview ¶
A lot of this code is copied straight from the Go standard library: https://golang.org/src/net/http/cgi/host.go
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Handler ¶
type Handler struct {
Path string
Root string
Name string // value to use for SERVER_SOFTWARE env var
Port string
Dir string
InheritEnv []string
Logger *log.Logger
Args []string
Stderr io.Writer
// Header contains header values that should be used by default.
// If the client CGI process writes a header to its stdout thats already in Header, it will be replaced.
Header http.Header
// OutputHandler takes care of responding the HTTP client based on the CGI client processes output.
OutputHandler OutputHandler
}
Handler runs an executable in a subprocess with an almost CGI environment. Currently ignored headers: "Location"
type OutputHandler ¶ added in v0.6.0
OutputHandler should handle reading in the client CGI process' output from stdoutRead and write out the response to the HTTP client. By the time OutputHandler is called, the client CGI process will have already been started and will be killed right after OutputHandler returns.
The client CGI process does not need to provide any headers, Handler will provide default Header values. If the executable does provide header values, they will overwrite the default values in Header. Currently ignored headers: "Location"
var DefaultOutputHandler OutputHandler = func(w http.ResponseWriter, r *http.Request, h *Handler, stdoutRead io.Reader) { linebody := bufio.NewReaderSize(stdoutRead, 1024) headers := make(http.Header) statusCode := 0 headerLines := 0 sawBlankLine := false for { line, isPrefix, err := linebody.ReadLine() if isPrefix { w.WriteHeader(http.StatusInternalServerError) h.logErr("cgi: long header line from subprocess.") return } if err == io.EOF { break } if err != nil { w.WriteHeader(http.StatusInternalServerError) h.logErr("cgi: error reading headers: %v", err) return } if len(line) == 0 { sawBlankLine = true break } headerLines++ parts := strings.SplitN(string(line), ":", 2) if len(parts) < 2 { h.logErr("cgi: bogus header line: %s", string(line)) continue } header, val := parts[0], parts[1] header = strings.TrimSpace(header) val = strings.TrimSpace(val) switch { case header == "Status": if len(val) < 3 { h.logErr("cgi: bogus status (short): %q", val) return } code, err := strconv.Atoi(val[0:3]) if err != nil { h.logErr("cgi: bogus status: %q", val) h.logErr("cgi: line was %q", line) return } statusCode = code default: headers.Add(header, val) } } if headerLines == 0 || !sawBlankLine { w.WriteHeader(http.StatusInternalServerError) h.logErr("cgi: no headers") return } if loc := headers.Get("Location"); loc != "" { if statusCode == 0 { statusCode = http.StatusFound } } if statusCode == 0 && headers.Get("Content-Type") == "" { w.WriteHeader(http.StatusInternalServerError) h.logErr("cgi: missing required Content-Type in headers") return } if statusCode == 0 { statusCode = http.StatusOK } for k, vv := range headers { for _, v := range vv { w.Header().Add(k, v) } } w.WriteHeader(statusCode) _, err := io.Copy(w, linebody) if err != nil { h.logErr("cgi: copy error: %v", err) } }
DefaultOutputHandler *mostly* mimics the behavior of the net/http/cgi package in the Go standard library. The only difference is DefaultOutputHandler does not call on the PathLocationHandler function found in the standard library. Currently ignored headers: "Location"
var EZOutputHandler OutputHandler = func(w http.ResponseWriter, r *http.Request, h *Handler, stdoutRead io.Reader) { internalError := func(err error) { w.WriteHeader(http.StatusInternalServerError) h.logErr("CGI error: %v", err) } // readBytes holds the bytes read during header scan but that aren't part of the header. // This data will be added to the front of the responses body var readBytes []byte linebody := bufio.NewReaderSize(stdoutRead, 1024) statusCode := 0 for { line, tooBig, err := linebody.ReadLine() if tooBig || err == io.EOF { break } if err != nil { internalError(err) return } if len(line) == 0 { break } parts := strings.SplitN(string(line), ":", 2) if len(parts) < 2 { readBytes = line readBytes = append(line, '\n', '\r') break } k := strings.TrimSpace(parts[0]) v := strings.TrimSpace(parts[1]) switch { case k == "Status": if len(v) < 3 { h.logErr("cgi: bogus status (short): %q", v) return } code, err := strconv.Atoi(v[0:3]) if err != nil { h.logErr("cgi: bogus status: %q", v) h.logErr("cgi: line was %q", line) return } statusCode = code default: h.Header.Set(k, v) } } if statusCode == 0 { statusCode = http.StatusOK } for k, vv := range h.Header { for _, v := range vv { w.Header().Add(k, v) } } w.WriteHeader(statusCode) if readBytes != nil { _, err := w.Write(readBytes) if err != nil { h.logErr("cgi: copy error: %v", err) return } } _, err := io.Copy(w, linebody) if err != nil { h.logErr("cgi: copy error: %v", err) return } }