From 565f54bff8bcec39da3a74eed91ee832fff7f932 Mon Sep 17 00:00:00 2001 From: Solderpunk Date: Sun, 9 Feb 2025 18:58:24 +0100 Subject: [PATCH] Factor out header parsing code for CGI and SCGI into one function, so the checks from last commit can be applied to both. --- dynamic.go | 87 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/dynamic.go b/dynamic.go index f1ec402..938b7f0 100644 --- a/dynamic.go +++ b/dynamic.go @@ -4,6 +4,7 @@ import ( "bufio" "context" "crypto/tls" + "errors" "io" "log" "net" @@ -15,6 +16,35 @@ import ( "time" ) +func extractStatusFromDynamicResponse(reader *bufio.Reader, source string) (int, error) { + rawHeader, _, err := reader.ReadLine() + if err != nil { + log.Println("Unable to read header in dynamic output from " + source + ".") + return 0, err + } + header := string(rawHeader) + logErrorMsg := "Unable to parse first line of dynamic output from " + + source + + " as valid Gemini response header. Line was: " + + header + header_fields := strings.Fields(header) + if len(header_fields) == 0 { + log.Println(logErrorMsg) + return 0, errors.New("") + } + status, err := strconv.Atoi(header_fields[0]) + if err != nil { + log.Println(logErrorMsg) + return 0, err + } + if status < 10 || status > 70 { + log.Println(logErrorMsg) + return 0, errors.New("") + } + + return status, nil +} + func handleCGI(config SysConfig, path string, cgiPath string, URL *url.URL, logEntry *LogEntry, conn net.Conn) { // Find the shortest leading part of path which maps to an executable file. // Call this part scriptPath, and everything after it pathInfo. @@ -72,6 +102,7 @@ func handleCGI(config SysConfig, path string, cgiPath string, URL *url.URL, logE logEntry.Status = 42 return } + // Extract response header responseString := string(response) if len(responseString) == 0 { @@ -80,28 +111,16 @@ func handleCGI(config SysConfig, path string, cgiPath string, URL *url.URL, logE logEntry.Status = 42 return } - header, _, err := bufio.NewReader(strings.NewReader(string(response))).ReadLine() - if err != nil || len(header) == 0 { - log.Println("Unable to parse first line of output from CGI process " + path + " as valid Gemini response header. Line was: " + string(header)) + + responseReader := bufio.NewReader(strings.NewReader(string(response))) + status, err := extractStatusFromDynamicResponse(responseReader, path) + if err != nil { conn.Write([]byte("42 CGI error!\r\n")) logEntry.Status = 42 return + } else { + logEntry.Status = status } - header_fields := strings.Fields(string(header)) - if len(header_fields) == 0 { - log.Println("Unable to parse first line of output from CGI process " + path + " as valid Gemini response header. Line was: " + string(header)) - conn.Write([]byte("42 CGI error!\r\n")) - logEntry.Status = 42 - return - } - status, err := strconv.Atoi(header_fields[0]) - if err != nil || status < 10 || status > 70 { - log.Println("Unable to parse first line of output from CGI process " + path + " as valid Gemini response header. Line was: " + string(header)) - conn.Write([]byte("42 CGI error!\r\n")) - logEntry.Status = 42 - return - } - logEntry.Status = status // Write response conn.Write(response) @@ -135,36 +154,28 @@ func handleSCGI(URL *url.URL, scgiPath string, scgiSocket string, config SysConf socket.Write([]byte(",")) // Read and relay response + responseReader := bufio.NewReader(socket) + status, err := extractStatusFromDynamicResponse(responseReader, scgiSocket) + if err != nil { + conn.Write([]byte("42 SCGI error!\r\n")) + logEntry.Status = 42 + return + } else { + logEntry.Status = status + } + buffer := make([]byte, 1027) - first := true for { - n, err := socket.Read(buffer) + n, err := responseReader.Read(buffer) if err != nil { if err == io.EOF { break - } else if !first { + } else { // Err log.Println("Error reading from SCGI socket " + scgiSocket + ": " + err.Error()) - conn.Write([]byte("42 Error reading from SCGI service!\r\n")) - logEntry.Status = 42 - return - } else { break } } - // Extract status code from first line - if first { - first = false - lines := strings.SplitN(string(buffer), "\r\n", 2) - status, err := strconv.Atoi(strings.Fields(lines[0])[0]) - if err != nil || status < 10 || status > 70 { - conn.Write([]byte("42 CGI error!\r\n")) - log.Println("Unable to parse first line of output from SCGI socket " + scgiSocket + " as valid Gemini response header. Line was: " + lines[0]) - logEntry.Status = 42 - return - } - logEntry.Status = status - } // Send to client conn.Write(buffer[:n]) }