package upgrade import ( "bytes" "fmt" "regexp" ) // RequestLine represents the HTTP Request line // defined in rfc-2616 : https://tools.ietf.org/html/rfc2616#section-5.1 type RequestLine struct { uri string version byte } // Read an HTTP request line from a byte array // // implements io.Reader func (rl *RequestLine) Read(b []byte) (int, error) { var read = len(b) // split by spaces parts := bytes.Split(b, []byte(" ")) if len(parts) != 3 { return read, fmt.Errorf("expected 3 space-separated elements, got %d elements", len(parts)) } err := rl.extractHttpMethod(parts[0]) if err != nil { return read, err } err = rl.extractURI(parts[1]) if err != nil { return read, err } err = rl.extractHttpVersion(parts[2]) if err != nil { return read, err } return read, nil } // URI of the request line func (rl RequestLine) URI() string { return rl.uri } // extractHttpMethod extracts the HTTP method from a []byte // and checks for errors // allowed format: OPTIONS|GET|HEAD|POST|PUT|DELETE func (rl *RequestLine) extractHttpMethod(b []byte) error { if string(b) != "GET" { 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 (rl *RequestLine) extractURI(b []byte) error { checker := regexp.MustCompile("^(?:/[^/]+)*/?$") if !checker.Match(b) { return fmt.Errorf("invalid URI, expected an absolute path, got '%s'", b) } rl.uri = string(b) return nil } // extractHttpVersion extracts the version and checks for errors // allowed format: [1-9] or [1.9].[0-9] func (rl *RequestLine) extractHttpVersion(b []byte) error { extractor := regexp.MustCompile(`^HTTP/([1-9])(?:\.([0-9]))?$`) if !extractor.Match(b) { return fmt.Errorf("invalid HTTP version, expected INT or INT.INT, got '%s'", b) } matches := extractor.FindSubmatch(b) var version byte = matches[1][0] - '0' var subversion byte = 0 if len(matches[2]) > 0 { subversion = matches[2][0] - '0' } rl.version = version*10 + subversion return nil }