Factor out header parsing code for CGI and SCGI into one function, so the checks from last commit can be applied to both.

This commit is contained in:
Solderpunk 2025-02-09 18:58:24 +01:00
parent 0f833ba57b
commit 565f54bff8

View file

@ -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])
}