119 lines
2.4 KiB
Go
119 lines
2.4 KiB
Go
package upgrade
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"regexp"
|
|
)
|
|
|
|
// Line represents the HTTP Request line
|
|
// defined in rfc-2616 : https://tools.ietf.org/html/rfc2616#section-5.1
|
|
type Line struct {
|
|
method Method
|
|
uri string
|
|
version byte
|
|
}
|
|
|
|
// Parse parses the first HTTP request line
|
|
func (r *Line) Parse(b []byte) error {
|
|
|
|
// 1. Split by ' '
|
|
parts := bytes.Split(b, []byte(" "))
|
|
|
|
// 2. Fail when missing parts
|
|
if len(parts) != 3 {
|
|
return fmt.Errorf("expected 3 space-separated elements, got %d elements", 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
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
// GetURI returns the actual URI
|
|
func (r Line) 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 *Line) 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("Invalid HTTP method '%s', expected 'GET'", b)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// extractURI extracts the URI from a []byte and checks for errors
|
|
// allowed format: /([^/]/)*/?
|
|
func (r *Line) extractURI(b []byte) error {
|
|
|
|
// 1. Check format
|
|
checker := regexp.MustCompile("^(?:/[^/]+)*/?$")
|
|
if !checker.Match(b) {
|
|
return fmt.Errorf("invalid URI, expected an absolute path, 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 *Line) extractHttpVersion(b []byte) error {
|
|
|
|
// 1. Extract version parts
|
|
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)
|
|
}
|
|
|
|
// 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
|
|
}
|