diff --git a/cmd/gwstester/tester.go b/cmd/gwstester/tester.go index 343d5c1..49aa1dc 100644 --- a/cmd/gwstester/tester.go +++ b/cmd/gwstester/tester.go @@ -45,6 +45,7 @@ func manageClient(sock net.Conn) { req, err := request.Build(sock) if err != nil { + fmt.Printf("STATUS CODE: %d %s\n", req.StatusCode(), req.StatusCode().Explicit()) panic(err) } diff --git a/http/upgrade/request/header_check.go b/http/upgrade/request/header_check.go index 3701885..73f5212 100644 --- a/http/upgrade/request/header_check.go +++ b/http/upgrade/request/header_check.go @@ -1,6 +1,7 @@ package request import ( + "git.xdrm.io/gws/http/upgrade/response" "git.xdrm.io/gws/internal/http/upgrade/request/parser/header" "fmt" "strconv" @@ -28,6 +29,7 @@ func (r *T) extractHostPort(bb header.HeaderValue) error { // extract port readPort, err := strconv.ParseUint(split[1], 10, 16) if err != nil { + r.code = response.BAD_REQUEST return fmt.Errorf("Cannot read port number '%s'", split[1]) } @@ -35,8 +37,9 @@ func (r *T) extractHostPort(bb header.HeaderValue) error { // if 'Origin' header is already read, check it if len(r.origin) > 0 { - err = r.checkOriginPolicy() if err != nil { + err = r.checkOriginPolicy() + r.code = response.FORBIDDEN return err } } @@ -50,6 +53,7 @@ func (r *T) extractHostPort(bb header.HeaderValue) error { func (r *T) extractOrigin(bb header.HeaderValue) error { if len(bb) != 1 { + r.code = response.FORBIDDEN return fmt.Errorf("Origin header must have a unique value") } @@ -59,6 +63,7 @@ func (r *T) extractOrigin(bb header.HeaderValue) error { if len(r.host) > 0 { err := r.checkOriginPolicy() if err != nil { + r.code = response.FORBIDDEN return err } } @@ -87,6 +92,7 @@ func (r *T) checkConnection(bb header.HeaderValue) error { } + r.code = response.BAD_REQUEST return fmt.Errorf("Connection header must be 'Upgrade'") } @@ -96,6 +102,7 @@ func (r *T) checkConnection(bb header.HeaderValue) error { func (r *T) checkUpgrade(bb header.HeaderValue) error { if len(bb) != 1 { + r.code = response.BAD_REQUEST return fmt.Errorf("Upgrade header must have only 1 element") } @@ -104,6 +111,7 @@ func (r *T) checkUpgrade(bb header.HeaderValue) error { return nil } + r.code = response.BAD_REQUEST return fmt.Errorf("Upgrade header must be 'websocket', got '%s'", bb[0]) } @@ -113,6 +121,7 @@ func (r *T) checkUpgrade(bb header.HeaderValue) error { func (r *T) checkVersion(bb header.HeaderValue) error { if len(bb) != 1 || string(bb[0]) != "13" { + r.code = response.UPGRADE_REQUIRED return fmt.Errorf("Sec-WebSocket-Version header must be '13'") } @@ -126,6 +135,7 @@ func (r *T) checkVersion(bb header.HeaderValue) error { func (r *T) extractKey(bb header.HeaderValue) error { if len(bb) != 1 || len(bb[0]) != 24 { + r.code = response.BAD_REQUEST return fmt.Errorf("Sec-WebSocket-Key header must be a unique 24 bytes base64 value, got %d bytes", len(bb[0])) } diff --git a/http/upgrade/request/private.go b/http/upgrade/request/private.go index 045be21..83c721b 100644 --- a/http/upgrade/request/private.go +++ b/http/upgrade/request/private.go @@ -1,6 +1,7 @@ package request import ( + "git.xdrm.io/gws/http/upgrade/response" "fmt" "git.xdrm.io/gws/internal/http/upgrade/request/parser/header" ) @@ -17,6 +18,7 @@ func (r *T) parseHeader(b []byte) error { err := r.request.Parse(b) if err != nil { + r.code = response.BAD_REQUEST return fmt.Errorf("Error while parsing first line: %s", err) } @@ -32,6 +34,7 @@ func (r *T) parseHeader(b []byte) error { /* (1) Try to parse header */ head, err := header.Parse(b) if err != nil { + r.code = response.BAD_REQUEST return fmt.Errorf("Error parsing header: %s", err) } diff --git a/http/upgrade/request/public.go b/http/upgrade/request/public.go index 77ec7bc..1883243 100644 --- a/http/upgrade/request/public.go +++ b/http/upgrade/request/public.go @@ -1,6 +1,7 @@ package request import ( + "git.xdrm.io/gws/http/upgrade/response" "git.xdrm.io/gws/internal/http/reader" "fmt" "io" @@ -10,18 +11,19 @@ import ( // from a reader (typically bufio.NewRead of the socket) func Build(r io.Reader) (request *T, err error) { + req := new(T) + req.code = 500 + + /* (1) Parse request ---------------------------------------------------------*/ /* (1) Get chunk reader */ cr := reader.NewReader(r) if err != nil { - return nil, fmt.Errorf("Error while creating chunk reader: %s", err) + return req, fmt.Errorf("Error while creating chunk reader: %s", err) } - /* (2) Init request */ - req := new(T) - - /* (3) Parse header line by line */ + /* (2) Parse header line by line */ for { line, err := cr.Read() @@ -30,21 +32,22 @@ func Build(r io.Reader) (request *T, err error) { } if err != nil { - return nil, fmt.Errorf("Cannot read from reader: %s", err) + return req, fmt.Errorf("Cannot read from reader: %s", err) } err = req.parseHeader(line) if err != nil { - return nil, fmt.Errorf("Parsing error: %s\n", err); + return req, fmt.Errorf("Parsing error: %s\n", err); } } - /* (4) Check completion */ + /* (3) Check completion */ err = req.isComplete() if err != nil { - return nil, err + req.code = response.BAD_REQUEST + return req, err } @@ -60,7 +63,14 @@ func (r T) String() string{ l1 := fmt.Sprintf("Upgrade Request\n - host: %s\n - port: %d\n - origin: %s\n", r.host, r.port, r.origin) l2 := fmt.Sprintf(" - origin policy: %t\n - connection header: %t\n - upgrade header: %t\n - valid ws version: %t\n", r.validPolicy, r.hasConnection, r.hasUpgrade, r.hasVersion) l3 := fmt.Sprintf(" - key: %s\n - protocols: %s\n", r.key, r.protocols) + l4 := fmt.Sprintf(" - current status: %d %s\n", r.code, r.code.Explicit()) - return fmt.Sprintf("%s%s%s", l1, l2, l3) -} \ No newline at end of file + return fmt.Sprintf("%s%s%s%s", l1, l2, l3, l4) +} + + +// StatusCode returns the status current +func (r T) StatusCode() response.StatusCode { + return r.code +} diff --git a/http/upgrade/request/types.go b/http/upgrade/request/types.go index 370572e..f149332 100644 --- a/http/upgrade/request/types.go +++ b/http/upgrade/request/types.go @@ -1,11 +1,15 @@ package request import "git.xdrm.io/gws/internal/http/upgrade/request/parser/reqline" +import "git.xdrm.io/gws/http/upgrade/response" // T represents an HTTP Upgrade request type T struct { first bool // whether the first line has been read (GET uri HTTP/version) + // status code + code response.StatusCode + // request line request reqline.T diff --git a/http/upgrade/response/status_code.go b/http/upgrade/response/status_code.go new file mode 100644 index 0000000..a033e03 --- /dev/null +++ b/http/upgrade/response/status_code.go @@ -0,0 +1,25 @@ +package response + +// StatusCode maps the status codes (and description) +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 + +func (sc StatusCode) Explicit() 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" + } +} \ No newline at end of file diff --git a/http/upgrade/response/types.go b/http/upgrade/response/types.go index e69de29..9349bb0 100644 --- a/http/upgrade/response/types.go +++ b/http/upgrade/response/types.go @@ -0,0 +1,9 @@ +package response + +// 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