-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #18 from kkrull/parameter-decode-prep
Parameter decode prep
- Loading branch information
Showing
17 changed files
with
556 additions
and
335 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package http | ||
|
||
import ( | ||
"bufio" | ||
"strings" | ||
|
||
"github.com/kkrull/gohttp/msg/clienterror" | ||
) | ||
|
||
// Parses an HTTP request message one line at a time. | ||
type LineRequestParser struct{} | ||
|
||
func (parser *LineRequestParser) Parse(reader *bufio.Reader) (ok *RequestLine, err Response) { | ||
methodObject := &parseMethodObject{reader: reader} | ||
return methodObject.Parse() | ||
} | ||
|
||
type parseMethodObject struct { | ||
reader *bufio.Reader | ||
} | ||
|
||
func (parser *parseMethodObject) Parse() (ok *RequestLine, err Response) { | ||
requestLine, err := parser.readCRLFLine() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return parser.doParseRequestLine(requestLine) | ||
} | ||
|
||
func (parser *parseMethodObject) doParseRequestLine(requestLine string) (ok *RequestLine, err Response) { | ||
requested, err := parser.parseRequestLine(requestLine) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return parser.doParseHeaders(requested) | ||
} | ||
|
||
func (parser *parseMethodObject) doParseHeaders(requested *RequestLine) (ok *RequestLine, err Response) { | ||
err = parser.parseHeaders() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return requested, nil | ||
} | ||
|
||
func (parser *parseMethodObject) parseRequestLine(text string) (ok *RequestLine, badRequest Response) { | ||
fields := strings.Split(text, " ") | ||
if len(fields) != 3 { | ||
return nil, &clienterror.BadRequest{DisplayText: "incorrectly formatted or missing request-line"} | ||
} | ||
|
||
return &RequestLine{ | ||
Method: fields[0], | ||
Target: fields[1], | ||
}, nil | ||
} | ||
|
||
func (parser *parseMethodObject) parseHeaders() (badRequest Response) { | ||
isBlankLineBetweenHeadersAndBody := func(line string) bool { return line == "" } | ||
|
||
for { | ||
line, err := parser.readCRLFLine() | ||
if err != nil { | ||
return err | ||
} else if isBlankLineBetweenHeadersAndBody(line) { | ||
return nil | ||
} | ||
} | ||
} | ||
|
||
func (parser *parseMethodObject) readCRLFLine() (line string, badRequest Response) { | ||
maybeEndsInCR, _ := parser.reader.ReadString('\r') | ||
if len(maybeEndsInCR) == 0 { | ||
return "", &clienterror.BadRequest{DisplayText: "end of input before terminating CRLF"} | ||
} else if !strings.HasSuffix(maybeEndsInCR, "\r") { | ||
return "", &clienterror.BadRequest{DisplayText: "line in request header not ending in CRLF"} | ||
} | ||
|
||
nextCharacter, _ := parser.reader.ReadByte() | ||
if nextCharacter != '\n' { | ||
return "", &clienterror.BadRequest{DisplayText: "message header line does not end in LF"} | ||
} | ||
|
||
trimmed := strings.TrimSuffix(maybeEndsInCR, "\r") | ||
return trimmed, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package http_test | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
|
||
"github.com/kkrull/gohttp/http" | ||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
var _ = Describe("LineRequestParser", func() { | ||
Describe("#Parse", func() { | ||
var ( | ||
parser http.RequestParser | ||
request *http.RequestLine | ||
err http.Response | ||
) | ||
|
||
BeforeEach(func() { | ||
parser = &http.LineRequestParser{} | ||
}) | ||
|
||
Describe("it returns 400 Bad Request", func() { | ||
It("for a completely blank request", func() { | ||
request, err = parser.Parse(makeReader("")) | ||
Expect(err).To(beABadRequestResponse("line in request header not ending in CRLF")) | ||
}) | ||
|
||
It("for any line missing CR", func() { | ||
request, err = parser.Parse(makeReader("GET / HTTP/1.1\r\n\n")) | ||
Expect(err).To(beABadRequestResponse("line in request header not ending in CRLF")) | ||
}) | ||
|
||
It("for any line missing LF", func() { | ||
request, err = parser.Parse(makeReader("GET / HTTP/1.1\r")) | ||
Expect(err).To(beABadRequestResponse("message header line does not end in LF")) | ||
}) | ||
|
||
It("for a request missing a request-line", func() { | ||
request, err = parser.Parse(makeReader("\r\n")) | ||
Expect(err).To(beABadRequestResponse("incorrectly formatted or missing request-line")) | ||
}) | ||
|
||
It("for a request missing an ending CRLF", func() { | ||
request, err = parser.Parse(makeReader("GET / HTTP/1.1\r\n")) | ||
Expect(err).To(beABadRequestResponse("line in request header not ending in CRLF")) | ||
}) | ||
|
||
It("when multiple spaces are separating fields in request-line", func() { | ||
request, err = parser.Parse(makeReader("GET / HTTP/1.1\r\n\r\n")) | ||
Expect(err).To(beABadRequestResponse("incorrectly formatted or missing request-line")) | ||
}) | ||
|
||
It("when fields in request-line contain spaces", func() { | ||
request, err = parser.Parse(makeReader("GET /a\\ b HTTP/1.1\r\n\r\n")) | ||
Expect(err).To(beABadRequestResponse("incorrectly formatted or missing request-line")) | ||
}) | ||
|
||
It("when the request starts with whitespace", func() { | ||
request, err = parser.Parse(makeReader(" GET / HTTP/1.1\r\n\r\n")) | ||
Expect(err).To(beABadRequestResponse("incorrectly formatted or missing request-line")) | ||
}) | ||
}) | ||
|
||
Context("given a well-formed request", func() { | ||
var ( | ||
reader *bufio.Reader | ||
) | ||
|
||
BeforeEach(func() { | ||
buffer := bytes.NewBufferString("GET /foo HTTP/1.1\r\nAccept: */*\r\n\r\n") | ||
reader = bufio.NewReader(buffer) | ||
request, err = parser.Parse(reader) | ||
}) | ||
|
||
It("returns no error", func() { | ||
Expect(err).To(BeNil()) | ||
}) | ||
|
||
It("reads the entire request until reaching a line with only CRLF", func() { | ||
Expect(reader.Buffered()).To(Equal(0)) | ||
}) | ||
}) | ||
|
||
Context("given a well-formed request with query parameters", func() { | ||
BeforeEach(func() { | ||
request, err = parser.Parse(makeReader("GET /foo?one=1 HTTP/1.1\r\n\r\n")) | ||
Expect(err).NotTo(HaveOccurred()) | ||
}) | ||
|
||
XIt("removes the query string from the target") | ||
XIt("passes decoded parameters to the routes") | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.