read input data properly and pipe data as stdin to cgi

This commit is contained in:
Hedy Li 2021-08-02 17:38:12 +08:00
parent 45fa97a266
commit 87a57d48fb
Signed by: hedy
GPG Key ID: B51B5A8D1B176372
2 changed files with 68 additions and 11 deletions

View File

@ -3,6 +3,7 @@ package main
import (
"bufio"
"context"
"io"
"log"
"net"
"os"
@ -42,11 +43,23 @@ func handleCGI(conf *Config, req *Request, cgiPath string) (ok bool) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, scriptPath)
// Put input data into stdin pipe
stdin, err := cmd.StdinPipe()
if err != nil {
log.Println("Error creating a stdin pipe:", err.Error())
ok = false
return
}
io.WriteString(stdin, req.data)
stdin.Close()
// Set environment variables
cmd.Env = []string{}
for key, value := range vars {
cmd.Env = append(cmd.Env, key+"="+value)
}
// Manually change the uid/gid for the command, rather than the calling goroutine
// Manually change the uid/gid for the command
// Fetch user info
// user, err := user.Lookup(req.user)
// if err == nil {

View File

@ -2,6 +2,7 @@ package main
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
@ -17,12 +18,15 @@ import (
flag "github.com/spf13/pflag"
)
var doneScanningRequest = false
type Request struct {
conn io.ReadWriteCloser
netConn *net.Conn
user string
path string // Requested path
filePath string // Actual file path that does not include the content dir name
data string
}
const (
@ -86,14 +90,15 @@ func serveSpartan(listener net.Listener, conf *Config) {
// handleConnection handles a request and does the response
func handleConnection(netConn net.Conn, conf *Config) {
conn := io.ReadWriteCloser(netConn)
defer conn.Close()
// defer conn.Close()
defer func() {
conn.Close()
log.Println("Closed connection")
}()
// Check the size of the request buffer.
s := bufio.NewScanner(conn)
if len(s.Bytes()) > 1024 {
sendResponseHeader(conn, statusClientError, "Request exceeds maximum permitted length")
return
}
s.Split(ScanRequest)
// Sanity check incoming request URL content.
if ok := s.Scan(); !ok {
@ -101,10 +106,11 @@ func handleConnection(netConn net.Conn, conf *Config) {
return
}
// Parse incoming request URL.
// Parse request
request := s.Text()
doneScanningRequest = true
log.Println("--> Incoming request: \"" + request + "\"")
host, reqPath, _, err := parseRequest(request)
host, reqPath, dataLen, err := parseRequest(request)
if err != nil {
log.Println("Bad request")
sendResponseHeader(conn, statusClientError, "Bad request")
@ -123,7 +129,24 @@ func handleConnection(netConn net.Conn, conf *Config) {
return
}
req := &Request{path: reqPath, netConn: &netConn, conn: conn}
var data string
if dataLen != 0 {
log.Println("Reading data, length", dataLen)
// Read the dataLen amount of data from the data block
var newData string
for s.Scan() {
newData = s.Text()
if len(data) + len(newData) == dataLen {
data += newData
break
}
if len(data) + len(newData) > dataLen {
data += newData[:dataLen-len(data)-1]
}
data += newData
}
}
req := &Request{path: reqPath, netConn: &netConn, conn: conn, data: data}
// Time to fetch the files!
path := resolvePath(reqPath, conf, req)
@ -135,7 +158,6 @@ func handleConnection(netConn net.Conn, conf *Config) {
ok := handleCGI(conf, req, cgiPath)
if ok {
log.Println("Closed connection")
return
}
@ -143,8 +165,14 @@ func handleConnection(netConn net.Conn, conf *Config) {
}
}
// Reaching here means it is a static file
if dataLen != 0 {
log.Printf("Got data block of length %v, returning client error.", dataLen)
sendResponseHeader(conn, statusClientError, "Unwanted input data block received")
return
}
serveFile(conn, reqPath, path, conf)
log.Println("Closed connection")
}
// resolvePath takes in teh request path and returns the cleaned filepath that needs to be fetched.
@ -290,3 +318,19 @@ func parseRequest(r string) (host, path string, contentLength int, err error) {
}
return
}
// ScanRequest is like bufio.ScanBytes but returns a byte if no more \n is found
func ScanRequest(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if doneScanningRequest {
// Return a byte
return 1, data[:1], nil
}
// Read request
if i := bytes.IndexByte(data, '\n'); i >= 0 {
return i + 1, bytes.TrimRight(data[0:i], "\r"), nil
}
return 0, nil, nil
}