read input data properly and pipe data as stdin to cgi
This commit is contained in:
parent
45fa97a266
commit
87a57d48fb
15
dynamic.go
15
dynamic.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
@ -42,11 +43,23 @@ func handleCGI(conf *Config, req *Request, cgiPath string) (ok bool) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
cmd := exec.CommandContext(ctx, scriptPath)
|
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{}
|
cmd.Env = []string{}
|
||||||
for key, value := range vars {
|
for key, value := range vars {
|
||||||
cmd.Env = append(cmd.Env, key+"="+value)
|
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
|
// Fetch user info
|
||||||
// user, err := user.Lookup(req.user)
|
// user, err := user.Lookup(req.user)
|
||||||
// if err == nil {
|
// if err == nil {
|
||||||
|
|
64
spsrv.go
64
spsrv.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -17,12 +18,15 @@ import (
|
||||||
flag "github.com/spf13/pflag"
|
flag "github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var doneScanningRequest = false
|
||||||
|
|
||||||
type Request struct {
|
type Request struct {
|
||||||
conn io.ReadWriteCloser
|
conn io.ReadWriteCloser
|
||||||
netConn *net.Conn
|
netConn *net.Conn
|
||||||
user string
|
user string
|
||||||
path string // Requested path
|
path string // Requested path
|
||||||
filePath string // Actual file path that does not include the content dir name
|
filePath string // Actual file path that does not include the content dir name
|
||||||
|
data string
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -86,14 +90,15 @@ func serveSpartan(listener net.Listener, conf *Config) {
|
||||||
// handleConnection handles a request and does the response
|
// handleConnection handles a request and does the response
|
||||||
func handleConnection(netConn net.Conn, conf *Config) {
|
func handleConnection(netConn net.Conn, conf *Config) {
|
||||||
conn := io.ReadWriteCloser(netConn)
|
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.
|
// Check the size of the request buffer.
|
||||||
s := bufio.NewScanner(conn)
|
s := bufio.NewScanner(conn)
|
||||||
if len(s.Bytes()) > 1024 {
|
s.Split(ScanRequest)
|
||||||
sendResponseHeader(conn, statusClientError, "Request exceeds maximum permitted length")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check incoming request URL content.
|
// Sanity check incoming request URL content.
|
||||||
if ok := s.Scan(); !ok {
|
if ok := s.Scan(); !ok {
|
||||||
|
@ -101,10 +106,11 @@ func handleConnection(netConn net.Conn, conf *Config) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse incoming request URL.
|
// Parse request
|
||||||
request := s.Text()
|
request := s.Text()
|
||||||
|
doneScanningRequest = true
|
||||||
log.Println("--> Incoming request: \"" + request + "\"")
|
log.Println("--> Incoming request: \"" + request + "\"")
|
||||||
host, reqPath, _, err := parseRequest(request)
|
host, reqPath, dataLen, err := parseRequest(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Bad request")
|
log.Println("Bad request")
|
||||||
sendResponseHeader(conn, statusClientError, "Bad request")
|
sendResponseHeader(conn, statusClientError, "Bad request")
|
||||||
|
@ -123,7 +129,24 @@ func handleConnection(netConn net.Conn, conf *Config) {
|
||||||
return
|
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!
|
// Time to fetch the files!
|
||||||
path := resolvePath(reqPath, conf, req)
|
path := resolvePath(reqPath, conf, req)
|
||||||
|
@ -135,7 +158,6 @@ func handleConnection(netConn net.Conn, conf *Config) {
|
||||||
|
|
||||||
ok := handleCGI(conf, req, cgiPath)
|
ok := handleCGI(conf, req, cgiPath)
|
||||||
if ok {
|
if ok {
|
||||||
log.Println("Closed connection")
|
|
||||||
return
|
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)
|
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.
|
// 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
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue