package spartan import ( "bufio" "errors" "io" "net/url" "strconv" "strings" "tildegit.org/tjp/sliderule/internal/types" ) var ( // InvalidRequestLine indicates a malformed first-line of a spartan request. InvalidRequestLine = errors.New("invalid request line") // InvalidRequestLineEnding says that a spartan request's first line wasn't terminated with CRLF. InvalidRequestLineEnding = errors.New("invalid request line ending") ) // ParseRequest parses a single spartan request and the indicated content-length from a reader. // // If ther reader artument is a *bufio.Reader, it will only read a single line from it. func ParseRequest(rdr io.Reader) (*types.Request, int, error) { bufrdr, ok := rdr.(*bufio.Reader) if !ok { bufrdr = bufio.NewReader(rdr) } line, err := bufrdr.ReadString('\n') if err != io.EOF && err != nil { return nil, 0, err } line = strings.TrimSuffix(line, "\n") line = strings.TrimSuffix(line, "\r") host, rest, ok := strings.Cut(line, " ") if !ok { return nil, 0, InvalidRequestLine } path, rest, ok := strings.Cut(rest, " ") if !ok || len(rest) == 0 { return nil, 0, InvalidRequestLine } u, err := url.Parse(path) if err != nil { return nil, 0, err } u.Scheme = "spartan" u.Host = host contentlen, err := strconv.Atoi(rest) if err != nil { return nil, 0, err } return &types.Request{ URL: u, Meta: &io.LimitedReader{R: bufrdr, N: int64(contentlen)}, }, contentlen, nil } // GetRequestContentLength reads the remaining un-read number of bytes in a request body. // // It will immediately return 0 if there is no request body. func GetRequestContentLength(request *types.Request) int { if lr, ok := request.Meta.(*io.LimitedReader); ok { return int(lr.N) } return 0 } // GetRequestBody returns a reader of the spartan request body. // // It will return nil if the request has no body. func GetRequestBody(request *types.Request) io.Reader { if rdr, ok := request.Meta.(io.Reader); ok { return rdr } return nil }