mirror of
https://git.sr.ht/~adnano/go-gemini
synced 2024-09-18 13:31:36 +02:00
Tweak request and response parsing
This commit is contained in:
parent
b3c0a5040d
commit
ca2680a958
14
gemini.go
14
gemini.go
@ -11,8 +11,6 @@ func init() {
|
|||||||
mime.AddExtensionType(".gemini", "text/gemini")
|
mime.AddExtensionType(".gemini", "text/gemini")
|
||||||
}
|
}
|
||||||
|
|
||||||
var crlf = []byte("\r\n")
|
|
||||||
|
|
||||||
// Errors.
|
// Errors.
|
||||||
var (
|
var (
|
||||||
ErrInvalidRequest = errors.New("gemini: invalid request")
|
ErrInvalidRequest = errors.New("gemini: invalid request")
|
||||||
@ -22,3 +20,15 @@ var (
|
|||||||
// when the response status code does not permit a body.
|
// when the response status code does not permit a body.
|
||||||
ErrBodyNotAllowed = errors.New("gemini: response status code does not allow body")
|
ErrBodyNotAllowed = errors.New("gemini: response status code does not allow body")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var crlf = []byte("\r\n")
|
||||||
|
|
||||||
|
func trimCRLF(b []byte) ([]byte, bool) {
|
||||||
|
// Check for CR
|
||||||
|
if len(b) < 2 || b[len(b)-2] != '\r' {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
// Trim CRLF
|
||||||
|
b = b[:len(b)-2]
|
||||||
|
return b, true
|
||||||
|
}
|
||||||
|
21
request.go
21
request.go
@ -51,26 +51,25 @@ func NewRequest(rawurl string) (*Request, error) {
|
|||||||
// for specialized applications; most code should use the Server
|
// for specialized applications; most code should use the Server
|
||||||
// to read requests and handle them via the Handler interface.
|
// to read requests and handle them via the Handler interface.
|
||||||
func ReadRequest(r io.Reader) (*Request, error) {
|
func ReadRequest(r io.Reader) (*Request, error) {
|
||||||
// Read URL
|
// Limit request size
|
||||||
r = io.LimitReader(r, 1026)
|
r = io.LimitReader(r, 1026)
|
||||||
br := bufio.NewReaderSize(r, 1026)
|
br := bufio.NewReaderSize(r, 1026)
|
||||||
rawurl, err := br.ReadString('\r')
|
b, err := br.ReadBytes('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil, ErrInvalidRequest
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Read terminating line feed
|
// Read URL
|
||||||
if b, err := br.ReadByte(); err != nil {
|
rawurl, ok := trimCRLF(b)
|
||||||
return nil, err
|
if !ok {
|
||||||
} else if b != '\n' {
|
|
||||||
return nil, ErrInvalidRequest
|
return nil, ErrInvalidRequest
|
||||||
}
|
}
|
||||||
// Trim carriage return
|
if len(rawurl) == 0 {
|
||||||
rawurl = rawurl[:len(rawurl)-1]
|
|
||||||
// Validate URL
|
|
||||||
if len(rawurl) > 1024 {
|
|
||||||
return nil, ErrInvalidRequest
|
return nil, ErrInvalidRequest
|
||||||
}
|
}
|
||||||
u, err := url.Parse(rawurl)
|
u, err := url.Parse(string(rawurl))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package gemini
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"io"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -36,25 +35,25 @@ func TestReadRequest(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Raw: "\r\n",
|
Raw: "\r\n",
|
||||||
URL: &url.URL{},
|
Err: ErrInvalidRequest,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Raw: "gemini://example.com\n",
|
Raw: "gemini://example.com\n",
|
||||||
Err: io.EOF,
|
Err: ErrInvalidRequest,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Raw: "gemini://example.com",
|
Raw: "gemini://example.com",
|
||||||
Err: io.EOF,
|
Err: ErrInvalidRequest,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 1030 bytes
|
// 1030 bytes
|
||||||
Raw: maxURL + "xxxxxx",
|
Raw: maxURL + "xxxxxx",
|
||||||
Err: io.EOF,
|
Err: ErrInvalidRequest,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 1027 bytes
|
// 1027 bytes
|
||||||
Raw: maxURL + "x" + "\r\n",
|
Raw: maxURL + "x" + "\r\n",
|
||||||
Err: io.EOF,
|
Err: ErrInvalidRequest,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 1024 bytes
|
// 1024 bytes
|
||||||
|
38
response.go
38
response.go
@ -46,43 +46,39 @@ func ReadResponse(r io.ReadCloser) (*Response, error) {
|
|||||||
resp := &Response{}
|
resp := &Response{}
|
||||||
br := bufio.NewReader(r)
|
br := bufio.NewReader(r)
|
||||||
|
|
||||||
// Read the status
|
// Read response header
|
||||||
statusB := make([]byte, 2)
|
b, err := br.ReadBytes('\n')
|
||||||
if _, err := br.Read(statusB); err != nil {
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil, ErrInvalidResponse
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
status, err := strconv.Atoi(string(statusB))
|
if len(b) < 3 {
|
||||||
|
return nil, ErrInvalidResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the status
|
||||||
|
status, err := strconv.Atoi(string(b[:2]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrInvalidResponse
|
return nil, ErrInvalidResponse
|
||||||
}
|
}
|
||||||
resp.Status = Status(status)
|
resp.Status = Status(status)
|
||||||
|
|
||||||
// Read one space
|
// Read one space
|
||||||
if b, err := br.ReadByte(); err != nil {
|
if b[2] != ' ' {
|
||||||
return nil, err
|
|
||||||
} else if b != ' ' {
|
|
||||||
return nil, ErrInvalidResponse
|
return nil, ErrInvalidResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the meta
|
// Read the meta
|
||||||
meta, err := br.ReadString('\r')
|
meta, ok := trimCRLF(b[3:])
|
||||||
if err != nil {
|
if !ok {
|
||||||
return nil, err
|
return nil, ErrInvalidResponse
|
||||||
}
|
}
|
||||||
// Trim carriage return
|
|
||||||
meta = meta[:len(meta)-1]
|
|
||||||
// Ensure meta is less than or equal to 1024 bytes
|
|
||||||
if len(meta) == 0 || len(meta) > 1024 {
|
if len(meta) == 0 || len(meta) > 1024 {
|
||||||
return nil, ErrInvalidResponse
|
return nil, ErrInvalidResponse
|
||||||
}
|
}
|
||||||
resp.Meta = meta
|
resp.Meta = string(meta)
|
||||||
|
|
||||||
// Read terminating newline
|
|
||||||
if b, err := br.ReadByte(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if b != '\n' {
|
|
||||||
return nil, ErrInvalidResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.Status.Class() == StatusSuccess {
|
if resp.Status.Class() == StatusSuccess {
|
||||||
resp.Body = newBufReadCloser(br, r)
|
resp.Body = newBufReadCloser(br, r)
|
||||||
|
@ -65,15 +65,15 @@ func TestReadWriteResponse(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Raw: "",
|
Raw: "",
|
||||||
Err: io.EOF,
|
Err: ErrInvalidResponse,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Raw: "10 Search query",
|
Raw: "10 Search query",
|
||||||
Err: io.EOF,
|
Err: ErrInvalidResponse,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Raw: "20 text/gemini\nHello, world!",
|
Raw: "20 text/gemini\nHello, world!",
|
||||||
Err: io.EOF,
|
Err: ErrInvalidResponse,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Raw: "20 text/gemini\rHello, world!",
|
Raw: "20 text/gemini\rHello, world!",
|
||||||
@ -81,7 +81,7 @@ func TestReadWriteResponse(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Raw: "20 text/gemini\r",
|
Raw: "20 text/gemini\r",
|
||||||
Err: io.EOF,
|
Err: ErrInvalidResponse,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Raw: "abcdefghijklmnopqrstuvwxyz",
|
Raw: "abcdefghijklmnopqrstuvwxyz",
|
||||||
|
Loading…
Reference in New Issue
Block a user