~rbn/scgi

03b7042f8ffa2df98b46c9abc4f326458ec3ad91 — Ruben Schuller 1 year, 5 months ago 7f9a55c
cleanup, use net/http/cgi
1 files changed, 42 insertions(+), 79 deletions(-)

M scgi.go
M scgi.go => scgi.go +42 -79
@@ 2,20 2,21 @@ package scgi

import (
	"net"
//	"time"
	"io"
	"io/ioutil"
	"fmt"
	"strconv"
	"bytes"
	"net/http"
//	"log"
	"net/http/cgi"
)

// Listener implements net.Listener, converting incoming SCGI requests into
// a connection which presents a http.Request if read.
type Listener struct {
	net.Listener
}

// NewListener wraps a net.Listener.
func NewListener(l net.Listener) *Listener {
	return &Listener{Listener: l}
}


@@ 29,19 30,13 @@ func (l *Listener) Accept() (net.Conn, error) {
	return NewConn(c)
}

//func (l *Listener) Close() error {
//	return l.Listener.Close()
//}
//
//func (l *Listener) Addr() net.Addr {
//	return l.Listener.Addr()
//}

// Conn converts an incoming SCGI-connection into a connection presenting a http.Request.
type Conn struct {
	buf io.Reader
	net.Conn
}

// netstring reads a netstring from an io.Reader.
func netstring(r io.Reader) ([]byte, error) {
	lbuf := []byte{}
	x := make([]byte, 1)


@@ 81,110 76,78 @@ func netstring(r io.Reader) ([]byte, error) {
	return buf, nil
}

func headers(c net.Conn) (http.Header, string, string, int64, error) {
	buf, err := netstring(c)
// headerMap reads netstring formatted headers from a reader and converts them
// to a map usable with cgi.RequestFromMap. Basic sanity checks for the presence
// of the SCGI and CONTENT_LENGTH headers are performed.
func headerMap(r io.Reader) (map[string]string, error) {
	buf, err := netstring(r)
	if err != nil {
		return nil, "", "", 0, fmt.Errorf("error reading netstring: %w", err)
		return nil, fmt.Errorf("error reading netstring: %w", err)
	}

	fs := bytes.Split(buf, []byte{0x0})
	fs = fs[0:len(fs)-1] // remove final field due to zero terminated strings
	if len(fs) % 2 != 0 {
		return nil, "", "", 0, fmt.Errorf("header fields count not even: %v", len(fs))
		return nil, fmt.Errorf("header fields count not even: %v", len(fs))

	}

	hs := make(http.Header)
	var url string
	var method string
	contentLength := int64(-1)
	scgi := false
	headers := make(map[string]string)
	for i := 0; i < len(fs); i+=2 {
		key := string(fs[i])
		value := string(fs[i+1])

		switch key {
		case "REQUEST_URI":
			url = value
		case "REQUEST_METHOD":
			method = value
		case "SCGI":
			scgi = true
		case "CONTENT_LENGTH":
			contentLength, err = strconv.ParseInt(value, 10, 64)
			if err != nil {
				return nil, "", "", 0, fmt.Errorf("error reading content length header: %w", err)
			}
		default:
			hs.Add(key, value)	
		}
		headers[key] = value
	}

	if scgi == false {
		return nil, "", "", 0, fmt.Errorf("SCGI header must be set to 1")
	if s, ok := headers["SCGI"]; !ok || s != "1" {
		return nil, fmt.Errorf("SCGI header has to be set and 1")
	}

	if contentLength == -1 {
		return nil, "", "", 0, fmt.Errorf("CONTENT_LENGTH header must be set")
	if _, ok := headers["CONTENT_LENGTH"]; !ok {
		return nil, fmt.Errorf("CONTENT_LENGTH not set")
	}

	return hs, method, url, contentLength, nil
	return headers, nil
}

func NewConn(c net.Conn) (*Conn, error) {
	hs, method, url, contentLength, err := headers(c)
// Request reads an SCGI request from an io.Reader, returning a http.Request.
func Request(r io.Reader) (*http.Request, error) {
	headers, err := headerMap(r)
	if err != nil {
		return nil, err
	}

	var body bytes.Buffer
	body.ReadFrom(io.LimitReader(c, int64(contentLength)))
	req, err := cgi.RequestFromMap(headers)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest(method, url, nil)
	if req.ContentLength > 0 {
                req.Body = io.NopCloser(io.LimitReader(r, req.ContentLength))
        }

	return req, nil
}

// NewConn wraps a net.Conn, converting SCGI requests to http.Requests.
// Note that the full request is read into a buffer which is then served
// from.
func NewConn(c net.Conn) (*Conn, error) {
	req, err := Request(c)
	if err != nil {
		return nil, err
	}
	req.Header = hs
	req.Body = ioutil.NopCloser(&body)
	req.TransferEncoding = append(req.TransferEncoding, "identity")

	var reqBuf bytes.Buffer
	err = req.Write(&reqBuf)
	var buf bytes.Buffer
	err = req.Write(&buf)
	if err != nil {
		return nil, err
	}

	return &Conn{Conn: c, buf: &reqBuf}, nil
	return &Conn{Conn: c, buf: &buf}, nil
}

func (c *Conn) Read(b []byte) (n int, err error) {
	return c.buf.Read(b)
}

//func (c *Conn) Write(b []byte) (n int, err error) {
//	return c.Conn.Write(b)
//}
//
//func (c *Conn) Close() error {
//	return c.Conn.Close()
//}
//
//func (c *Conn) LocalAddr() net.Addr {
//	return c.Conn.LocalAddr()
//}
//
//func (c *Conn) RemoteAddr() net.Addr {
//	return c.Conn.RemoteAddr()
//}
//
//func (c *Conn) SetDeadline(t time.Time) error {
//	return c.Conn.SetDeadline(t)
//}
//
//func (c *Conn) SetReadDeadline(t time.Time) error {
//	return c.Conn.SetReadDeadline(t)
//}
//
//func (c *Conn) SetWriteDeadline(t time.Time) error {
//	return c.Conn.SetWriteDeadline(t)
//}