diff --git a/internal/http/upgrade/request/errors.go b/internal/http/upgrade/request/errors.go index 7133505..7a31351 100644 --- a/internal/http/upgrade/request/errors.go +++ b/internal/http/upgrade/request/errors.go @@ -3,6 +3,7 @@ package request import ( "fmt" ) + // invalid request // - multiple-value if only 1 expected type InvalidRequest struct { @@ -23,13 +24,13 @@ func (err IncompleteRequest) Error() string { return fmt.Sprintf("imcomplete request, '%s' is invalid or missing", err.MissingField) } - // Request has a violated origin policy type InvalidOriginPolicy struct { Host string Origin string err error } + func (err InvalidOriginPolicy) Error() string { return fmt.Sprintf("invalid origin policy; (host: '%s' origin: '%s' error: '%s')", err.Host, err.Origin, err.err) -} \ No newline at end of file +} diff --git a/internal/http/upgrade/request/request-line.go b/internal/http/upgrade/request/request-line.go index fddd8ea..839d763 100644 --- a/internal/http/upgrade/request/request-line.go +++ b/internal/http/upgrade/request/request-line.go @@ -1,13 +1,14 @@ package request import ( - "fmt" "bytes" + "fmt" "regexp" ) // httpMethod represents available http methods type httpMethod byte + const ( OPTIONS httpMethod = iota GET @@ -17,7 +18,6 @@ const ( DELETE ) - // RequestLine represents the HTTP Request line // defined in rfc-2616 : https://tools.ietf.org/html/rfc2616#section-5.1 type RequestLine struct { @@ -26,7 +26,6 @@ type RequestLine struct { version byte } - // parseRequestLine parses the first HTTP request line func (r *RequestLine) Parse(b []byte) error { @@ -60,35 +59,32 @@ func (r *RequestLine) Parse(b []byte) error { } - // GetURI returns the actual URI func (r RequestLine) GetURI() string { return r.uri } - - // extractHttpMethod extracts the HTTP method from a []byte // and checks for errors // allowed format: OPTIONS|GET|HEAD|POST|PUT|DELETE func (r *RequestLine) extractHttpMethod(b []byte) error { switch string(b) { - // case "OPTIONS": r.method = OPTIONS - case "GET": r.method = GET - // case "HEAD": r.method = HEAD - // case "POST": r.method = POST - // case "PUT": r.method = PUT - // case "DELETE": r.method = DELETE + // case "OPTIONS": r.method = OPTIONS + case "GET": + r.method = GET + // case "HEAD": r.method = HEAD + // case "POST": r.method = POST + // case "PUT": r.method = PUT + // case "DELETE": r.method = DELETE - default: - return fmt.Errorf("Invalid HTTP method '%s', expected 'GET'", b) + default: + return fmt.Errorf("Invalid HTTP method '%s', expected 'GET'", b) } return nil } - // extractURI extracts the URI from a []byte and checks for errors // allowed format: /([^/]/)*/? func (r *RequestLine) extractURI(b []byte) error { @@ -106,13 +102,12 @@ func (r *RequestLine) extractURI(b []byte) error { } - // extractHttpVersion extracts the version and checks for errors // allowed format: [1-9] or [1.9].[0-9] func (r *RequestLine) extractHttpVersion(b []byte) error { /* (1) Extract version parts */ - extractor := regexp.MustCompile(`^HTTP/([1-9])(?:\.([0-9]))?$`); + extractor := regexp.MustCompile(`^HTTP/([1-9])(?:\.([0-9]))?$`) if !extractor.Match(b) { return fmt.Errorf("HTTP version, expected INT or INT.INT, got '%s'", b) @@ -120,7 +115,7 @@ func (r *RequestLine) extractHttpVersion(b []byte) error { /* (2) Extract version number */ matches := extractor.FindSubmatch(b) - var version byte = matches[1][0] - '0' + var version byte = matches[1][0] - '0' /* (3) Extract subversion (if exists) */ var subVersion byte = 0 @@ -129,7 +124,7 @@ func (r *RequestLine) extractHttpVersion(b []byte) error { } /* (4) Store version (x 10 to fit uint8) */ - r.version = version * 10 + subVersion + r.version = version*10 + subVersion return nil -} \ No newline at end of file +} diff --git a/internal/http/upgrade/request/request_test.go b/internal/http/upgrade/request/request_test.go index e7efa3c..ec78c5d 100644 --- a/internal/http/upgrade/request/request_test.go +++ b/internal/http/upgrade/request/request_test.go @@ -1,10 +1,11 @@ package request import ( - "io" "bytes" + "io" "testing" ) + // /* (1) Parse request */ // req, _ := request.Parse(s) @@ -22,7 +23,7 @@ import ( // return nil, fmt.Errorf("Upgrade error (HTTP %d)\n", res.GetStatusCode()) // } -func TestEOFSocket(t *testing.T){ +func TestEOFSocket(t *testing.T) { socket := new(bytes.Buffer) @@ -36,44 +37,44 @@ func TestEOFSocket(t *testing.T){ } -func TestInvalidRequestLine(t *testing.T){ +func TestInvalidRequestLine(t *testing.T) { socket := new(bytes.Buffer) - cases := []struct{ + cases := []struct { Reqline string HasError bool }{ - { "abc", true }, - { "a c", true }, - { "a c", true }, - { "a c", true }, - { "a b c", true }, + {"abc", true}, + {"a c", true}, + {"a c", true}, + {"a c", true}, + {"a b c", true}, - { "GET invaliduri HTTP/1.1", true }, - { "GET /validuri HTTP/1.1", false }, + {"GET invaliduri HTTP/1.1", true}, + {"GET /validuri HTTP/1.1", false}, - { "POST /validuri HTTP/1.1", true }, - { "PUT /validuri HTTP/1.1", true }, - { "DELETE /validuri HTTP/1.1", true }, - { "OPTIONS /validuri HTTP/1.1", true }, - { "UNKNOWN /validuri HTTP/1.1", true }, + {"POST /validuri HTTP/1.1", true}, + {"PUT /validuri HTTP/1.1", true}, + {"DELETE /validuri HTTP/1.1", true}, + {"OPTIONS /validuri HTTP/1.1", true}, + {"UNKNOWN /validuri HTTP/1.1", true}, - { "GET / HTTP", true }, - { "GET / HTTP/", true }, - { "GET / 1.1", true }, - { "GET / 1", true }, - { "GET / HTTP/52", true }, - { "GET / HTTP/1.", true }, - { "GET / HTTP/.1", true }, - { "GET / HTTP/1.1", false }, - { "GET / HTTP/2", false }, + {"GET / HTTP", true}, + {"GET / HTTP/", true}, + {"GET / 1.1", true}, + {"GET / 1", true}, + {"GET / HTTP/52", true}, + {"GET / HTTP/1.", true}, + {"GET / HTTP/.1", true}, + {"GET / HTTP/1.1", false}, + {"GET / HTTP/2", false}, } for ti, tc := range cases { socket.Reset() - socket.Write( []byte(tc.Reqline) ) - socket.Write( []byte("\r\n\r\n") ) + socket.Write([]byte(tc.Reqline)) + socket.Write([]byte("\r\n\r\n")) _, err := Parse(socket) @@ -82,7 +83,7 @@ func TestInvalidRequestLine(t *testing.T){ // no error -> ok if err == nil { continue - // error for the end of the request -> ok + // error for the end of the request -> ok } else if _, ok := err.(*IncompleteRequest); ok { continue } @@ -96,7 +97,7 @@ func TestInvalidRequestLine(t *testing.T){ continue } - ir, ok := err.(*InvalidRequest); + ir, ok := err.(*InvalidRequest) // not InvalidRequest err -> error if !ok || ir.Field != "Request-Line" { @@ -108,54 +109,53 @@ func TestInvalidRequestLine(t *testing.T){ } -func TestInvalidHost(t *testing.T){ +func TestInvalidHost(t *testing.T) { - requestLine := []byte( "GET / HTTP/1.1\r\n" ) + requestLine := []byte("GET / HTTP/1.1\r\n") socket := new(bytes.Buffer) - cases := []struct{ + cases := []struct { Host string HasError bool }{ - { "1", true }, - { "12", true }, - { "123", true }, - { "1234", false }, + {"1", true}, + {"12", true}, + {"123", true}, + {"1234", false}, - { "singlevalue", false }, - { "multi value", true }, + {"singlevalue", false}, + {"multi value", true}, - { "singlevalue:1", false }, - { "singlevalue:", true }, - { "singlevalue:x", true }, - { "xx:x", true }, - { ":xxx", true }, - { "xxx:", true }, - { "a:12", false }, + {"singlevalue:1", false}, + {"singlevalue:", true}, + {"singlevalue:x", true}, + {"xx:x", true}, + {":xxx", true}, + {"xxx:", true}, + {"a:12", false}, - { "google.com", false }, - { "8.8.8.8", false }, - { "google.com:8080", false }, - { "8.8.8.8:8080", false }, + {"google.com", false}, + {"8.8.8.8", false}, + {"google.com:8080", false}, + {"8.8.8.8:8080", false}, } for ti, tc := range cases { socket.Reset() socket.Write(requestLine) - socket.Write( []byte("Host: ") ) - socket.Write( []byte(tc.Host) ) - socket.Write( []byte("\r\n\r\n") ) + socket.Write([]byte("Host: ")) + socket.Write([]byte(tc.Host)) + socket.Write([]byte("\r\n\r\n")) _, err := Parse(socket) if !tc.HasError { - // no error -> ok if err == nil { continue - // error for the end of the request -> ok + // error for the end of the request -> ok } else if _, ok := err.(*IncompleteRequest); ok { continue } @@ -170,7 +170,7 @@ func TestInvalidHost(t *testing.T){ } // check if InvalidRequest - ir, ok := err.(*InvalidRequest); + ir, ok := err.(*InvalidRequest) // not InvalidRequest err -> error if ok && ir.Field != "Host" { @@ -180,4 +180,4 @@ func TestInvalidHost(t *testing.T){ } -} \ No newline at end of file +} diff --git a/internal/http/upgrade/response/public.go b/internal/http/upgrade/response/public.go index 462a17f..5f1f848 100644 --- a/internal/http/upgrade/response/public.go +++ b/internal/http/upgrade/response/public.go @@ -1,14 +1,12 @@ package response import ( + "crypto/sha1" + "encoding/base64" "fmt" "io" - "encoding/base64" - "crypto/sha1" ) - - // SetStatusCode sets the status code func (r *T) SetStatusCode(sc StatusCode) { r.code = sc @@ -36,11 +34,10 @@ func (r *T) ProcessKey(k []byte) { digest := sha1.Sum(mix) /* (3) Base64 encode it */ - r.accept = []byte( base64.StdEncoding.EncodeToString( digest[:sha1.Size] ) ) + r.accept = []byte(base64.StdEncoding.EncodeToString(digest[:sha1.Size])) } - // Send sends the response through an io.Writer // typically a socket func (r T) Send(w io.Writer) (int, error) { @@ -75,8 +72,7 @@ func (r T) GetProtocol() []byte { return r.protocol } - // GetStatusCode returns the response status code func (r T) GetStatusCode() StatusCode { return r.code -} \ No newline at end of file +} diff --git a/internal/http/upgrade/response/status_code.go b/internal/http/upgrade/response/status_code.go index 56ac52f..78dac02 100644 --- a/internal/http/upgrade/response/status_code.go +++ b/internal/http/upgrade/response/status_code.go @@ -4,22 +4,28 @@ package response type StatusCode uint16 var SWITCHING_PROTOCOLS StatusCode = 101 // handshake success -var BAD_REQUEST StatusCode = 400 // missing/malformed headers -var FORBIDDEN StatusCode = 403 // invalid origin policy, TLS required -var UPGRADE_REQUIRED StatusCode = 426 // invalid WS version -var NOT_FOUND StatusCode = 404 // unserved or invalid URI -var INTERNAL StatusCode = 500 // custom error +var BAD_REQUEST StatusCode = 400 // missing/malformed headers +var FORBIDDEN StatusCode = 403 // invalid origin policy, TLS required +var UPGRADE_REQUIRED StatusCode = 426 // invalid WS version +var NOT_FOUND StatusCode = 404 // unserved or invalid URI +var INTERNAL StatusCode = 500 // custom error func (sc StatusCode) Message() string { switch sc { - case SWITCHING_PROTOCOLS: return "Switching Protocols" - case BAD_REQUEST: return "Bad Request" - case FORBIDDEN: return "Forbidden" - case UPGRADE_REQUIRED: return "Upgrade Required" - case NOT_FOUND: return "Not Found" - case INTERNAL: return "Internal Server Error" - default: - return "Unknown Status Code" + case SWITCHING_PROTOCOLS: + return "Switching Protocols" + case BAD_REQUEST: + return "Bad Request" + case FORBIDDEN: + return "Forbidden" + case UPGRADE_REQUIRED: + return "Upgrade Required" + case NOT_FOUND: + return "Not Found" + case INTERNAL: + return "Internal Server Error" + default: + return "Unknown Status Code" } -} \ No newline at end of file +} diff --git a/internal/http/upgrade/response/types.go b/internal/http/upgrade/response/types.go index 735b549..6784ea8 100644 --- a/internal/http/upgrade/response/types.go +++ b/internal/http/upgrade/response/types.go @@ -1,15 +1,15 @@ package response // Constant -const HttpVersion = "1.1" -const WSVersion = 13 -var WSSalt []byte = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") +const HttpVersion = "1.1" +const WSVersion = 13 + +var WSSalt []byte = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") // T represents an HTTP Upgrade Response type T struct { - code StatusCode // status code accept []byte // processed from Sec-WebSocket-Key protocol []byte // set from Sec-WebSocket-Protocol or none if not received -} \ No newline at end of file +} diff --git a/internal/uri/parser/private.go b/internal/uri/parser/private.go index aa06646..e2a22c1 100644 --- a/internal/uri/parser/private.go +++ b/internal/uri/parser/private.go @@ -1,11 +1,10 @@ package parser import ( - "strings" "fmt" + "strings" ) - // buildScheme builds a 'basic' scheme // from a pattern string func buildScheme(ss []string) (Scheme, error) { @@ -16,42 +15,44 @@ func buildScheme(ss []string) (Scheme, error) { for _, s := range ss { /* (2) ignore empty */ - if len(s) == 0 { continue } + if len(s) == 0 { + continue + } m := new(matcher) switch s { - /* (3) Card: 0, N */ - case "**": - m.req = false - m.mul = true - sch = append(sch, m) + /* (3) Card: 0, N */ + case "**": + m.req = false + m.mul = true + sch = append(sch, m) - /* (4) Card: 1, N */ - case "..": - m.req = true - m.mul = true - sch = append(sch, m) + /* (4) Card: 1, N */ + case "..": + m.req = true + m.mul = true + sch = append(sch, m) - /* (5) Card: 0, 1 */ - case "*": - m.req = false - m.mul = false - sch = append(sch, m) + /* (5) Card: 0, 1 */ + case "*": + m.req = false + m.mul = false + sch = append(sch, m) - /* (6) Card: 1 */ - case ".": - m.req = true - m.mul = false - sch = append(sch, m) + /* (6) Card: 1 */ + case ".": + m.req = true + m.mul = false + sch = append(sch, m) - /* (7) Card: 1, literal string */ - default: - m.req = true - m.mul = false - m.pat = fmt.Sprintf("/%s", s) - sch = append(sch, m) + /* (7) Card: 1, literal string */ + default: + m.req = true + m.mul = false + m.pat = fmt.Sprintf("/%s", s) + sch = append(sch, m) } @@ -60,8 +61,6 @@ func buildScheme(ss []string) (Scheme, error) { return sch, nil } - - // optimise optimised the scheme for further parsing func (s Scheme) optimise() (Scheme, error) { @@ -74,9 +73,8 @@ func (s Scheme) optimise() (Scheme, error) { rshift := make(Scheme, 0, maxMatch) rshift = append(rshift, s[0]) - /* (2) Iterate over matchers */ - for p, i, l := 0, 1, len(s) ; i < l ; i++ { + for p, i, l := 0, 1, len(s); i < l; i++ { pre, cur := s[p], s[i] @@ -103,7 +101,6 @@ func (s Scheme) optimise() (Scheme, error) { } - // matchString checks the STRING matchers from an URI // it returns a boolean : false when not matching, true eitherway // it returns a cleared uri, without STRING data @@ -116,18 +113,23 @@ func (s Scheme) matchString(uri string) (string, bool) { /* (2) Iterate over strings */ for _, m := range s { - ls := len(m.pat) // {1} If not STRING matcher -> ignore // - if ls == 0 { continue } + if ls == 0 { + continue + } // {2} Get offset in URI (else -1) // off := strings.Index(clr, m.pat) - if off < 0 { return "", false } + if off < 0 { + return "", false + } // {3} Fail on invalid offset range // - if off < minOff { return "", false } + if off < minOff { + return "", false + } // {4} Check for trailing '/' // hasSlash := 0 @@ -141,7 +143,7 @@ func (s Scheme) matchString(uri string) (string, bool) { // {6} Update offset range // minOff = len(beg) + 2 - 1 // +2 slash separators - // -1 because strings begin with 1 slash already + // -1 because strings begin with 1 slash already } @@ -159,7 +161,6 @@ func (s Scheme) matchString(uri string) (string, bool) { } - // matchWildcards check the WILCARDS (non-string) matchers from // a cleared URI. it returns if the string matches // + it sets the matchers buffers for later extraction @@ -212,4 +213,4 @@ func (s Scheme) matchWildcards(clear string) bool { /* (5) Match */ return true -} \ No newline at end of file +} diff --git a/internal/uri/parser/public.go b/internal/uri/parser/public.go index cfc5ad7..9726ed6 100644 --- a/internal/uri/parser/public.go +++ b/internal/uri/parser/public.go @@ -6,7 +6,7 @@ import ( ) // Build builds an URI scheme from a pattern string -func Build(s string) (*Scheme, error){ +func Build(s string) (*Scheme, error) { /* (1) Manage '/' at the start */ if len(s) < 1 || s[0] != '/' { @@ -19,7 +19,7 @@ func Build(s string) (*Scheme, error){ /* (3) Max exceeded */ if len(parts)-2 > maxMatch { for i, p := range parts { - fmt.Printf("%d: '%s'\n", i, p); + fmt.Printf("%d: '%s'\n", i, p) } return nil, fmt.Errorf("URI must not exceed %d slash-separated components, got %d", maxMatch, len(parts)) } @@ -40,12 +40,13 @@ func Build(s string) (*Scheme, error){ } - // Match returns if the given URI is matched by the scheme func (s Scheme) Match(str string) bool { /* (1) Nothing -> match all */ - if len(s) == 0 { return true } + if len(s) == 0 { + return true + } /* (2) Check for string match */ clearURI, match := s.matchString(str) @@ -62,7 +63,6 @@ func (s Scheme) Match(str string) bool { return true } - // GetMatch returns the indexed match (excluding string matchers) func (s Scheme) GetMatch(n uint8) ([]string, error) { @@ -76,7 +76,9 @@ func (s Scheme) GetMatch(n uint8) ([]string, error) { for _, m := range s { // ignore strings - if len(m.pat) > 0 { continue } + if len(m.pat) > 0 { + continue + } // increment match counter : ni ni++ @@ -93,9 +95,6 @@ func (s Scheme) GetMatch(n uint8) ([]string, error) { } - - - // GetAllMatch returns all the indexed match (excluding string matchers) func (s Scheme) GetAllMatch() [][]string { @@ -104,7 +103,9 @@ func (s Scheme) GetAllMatch() [][]string { for _, m := range s { // ignore strings - if len(m.pat) > 0 { continue } + if len(m.pat) > 0 { + continue + } match = append(match, m.buf) @@ -112,4 +113,4 @@ func (s Scheme) GetAllMatch() [][]string { return match -} \ No newline at end of file +} diff --git a/internal/uri/parser/types.go b/internal/uri/parser/types.go index 987ba48..f68826c 100644 --- a/internal/uri/parser/types.go +++ b/internal/uri/parser/types.go @@ -24,13 +24,12 @@ const maxMatch = 16 // Represents an URI matcher type matcher struct { - pat string // pattern to match (empty if wildcard) - req bool // whether it is required - mul bool // whether multiple matches are allowed + pat string // pattern to match (empty if wildcard) + req bool // whether it is required + mul bool // whether multiple matches are allowed buf []string // matched content (when matching) } - // Represents an URI scheme -type Scheme []*matcher \ No newline at end of file +type Scheme []*matcher