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