diff --git a/internal/http/request/parser/reqline/private.go b/internal/http/request/parser/reqline/private.go new file mode 100644 index 0000000..43b52a0 --- /dev/null +++ b/internal/http/request/parser/reqline/private.go @@ -0,0 +1,73 @@ +package reqline + +import ( + "regexp" + "fmt" +) + + +// extractHttpMethod extracts the HTTP method from a []byte +// and checks for errors +// allowed format: OPTIONS|GET|HEAD|POST|PUT|DELETE +func (r *T) 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 + + default: + return fmt.Errorf("Unknown HTTP method '%s'", b) + } + + return nil +} + + +// extractURI extracts the URI from a []byte and checks for errors +// allowed format: /([^/]/)*/? +func (r *T) extractURI(b []byte) error { + + /* (1) Check format */ + checker := regexp.MustCompile("^(?:/[^/]+)*/?$") + if !checker.Match(b) { + return fmt.Errorf("Invalid URI format, expected an absolute path (starts with /), got '%s'", b) + } + + /* (2) Store */ + r.uri = string(b) + + return nil + +} + + +// extractHttpVersion extracts the version and checks for errors +// allowed format: [1-9] or [1.9].[0-9] +func (r *T) extractHttpVersion(b []byte) error { + + /* (1) Extract version parts */ + extractor := regexp.MustCompile(`^HTTP/([1-9])(?:\.([0-9]))?$`); + + if !extractor.Match(b) { + return fmt.Errorf("Cannot parse HTTP version, expected INT or INT.INT, got '%s'", b) + } + + /* (2) Extract version number */ + matches := extractor.FindSubmatch(b) + var version byte = matches[1][0] - '0' + + /* (3) Extract subversion (if exists) */ + var subVersion byte = 0 + if len(matches[2]) > 0 { + subVersion = matches[2][0] - '0' + } + + /* (4) Store version (x 10 to fit uint8) */ + r.version = version * 10 + subVersion + + return nil +} \ No newline at end of file diff --git a/internal/http/request/parser/reqline/public.go b/internal/http/request/parser/reqline/public.go new file mode 100644 index 0000000..dad9931 --- /dev/null +++ b/internal/http/request/parser/reqline/public.go @@ -0,0 +1,41 @@ +package reqline + +import ( + "fmt" + "bytes" +) + +// parseRequestLine parses the first HTTP request line +func (r *T) Parse(b []byte) error { + + /* (1) Split by ' ' */ + parts := bytes.Split(b, []byte(" ")) + + /* (2) Fail when missing parts */ + if len(parts) != 3 { + return fmt.Errorf("Malformed Request-Line must have 3 space-separated elements, got %d", len(parts)) + } + + /* (3) Extract HTTP method */ + err := r.extractHttpMethod(parts[0]) + if err != nil { + return err + } + + /* (4) Extract URI */ + err = r.extractURI(parts[1]) + if err != nil { + return err + } + + /* (5) Extract version */ + err = r.extractHttpVersion(parts[2]) + if err != nil { + return err + } + + fmt.Printf("REQUEST-LINE\n------------\n - Method: '%d'\n - URI: '%s'\n - Version: %f\n", r.method, r.uri, float32(r.version)/10); + + return nil + +} \ No newline at end of file diff --git a/internal/http/request/parser/reqline/types.go b/internal/http/request/parser/reqline/types.go new file mode 100644 index 0000000..7ee809b --- /dev/null +++ b/internal/http/request/parser/reqline/types.go @@ -0,0 +1,21 @@ +package reqline + +// httpMethod represents available http methods +type httpMethod byte +const ( + OPTIONS httpMethod = iota + GET + HEAD + POST + PUT + DELETE +) + + +// httpRequestLine represents the HTTP Request line +// defined in rfc-2616 : https://tools.ietf.org/html/rfc2616#section-5.1 +type T struct { + method httpMethod + uri string + version byte +} \ No newline at end of file