diff --git a/requestline/bytes_tolerant.go b/requestline/bytes_tolerant.go new file mode 100644 index 0000000..f60a569 --- /dev/null +++ b/requestline/bytes_tolerant.go @@ -0,0 +1,122 @@ +package requestline + +import ( + "sourcecode.social/reiver/go-rfc2616/eol" + rfc2616method "sourcecode.social/reiver/go-rfc2616/method" + rfc2616requesturi "sourcecode.social/reiver/go-rfc2616/requesturi" + rfc2616version "sourcecode.social/reiver/go-rfc2616/version" + "sourcecode.social/reiver/go-rfc2616/sp" +) + +func BytesTolerant(p []byte) (method []byte, requesturi []byte, version []byte, rest []byte, ok bool) { + + if len(p) < 3 { + return nil, nil, nil, p, false + } + + var pp []byte = p + + { + result, rest, ok := rfc2616method.BytesTolerant(pp) + if !ok { + return nil, nil, nil, p, false + } + + method = result + pp = rest + + if len(pp) <= 0 { + return nil, nil, nil, p, false + } + } + + { + pp0 := pp[0] + if !sp.ByteIs(pp0) { + return nil, nil, nil, p, false + } + + pp = pp[1:] + } + + { + pp = sp.SkipTolerant(pp) + + if len(pp) <= 0 { + return nil, nil, nil, p, false + } + } + + { + result, rest, ok := rfc2616requesturi.BytesTolerant(pp) + if !ok { + return nil, nil, nil, p, false + } + + requesturi = result + pp = rest + + if len(pp) <= 0 { + return method, requesturi, nil, pp, true + } + } + + { + pp0 := pp[0] + + switch { + case sp.ByteIs(pp0): + pp = pp[1:] + + // Nothing (else) here. + default: + _, rest, ok := eol.BytesTolerant(pp) + if !ok { + return nil, nil, nil, p, false + } + + return method, requesturi, nil, rest, true + } + } + + { + pp = sp.SkipTolerant(pp) + + if len(pp) <= 0 { + return method, requesturi, nil, pp, true + } + } + + { + result, rest, ok := rfc2616version.BytesTolerant(pp) + if !ok { + return method, requesturi, nil, pp, true + } + + version = result + pp = rest + + if len(pp) <= 0 { + return method, requesturi, version, pp, true + } + } + + { + pp = sp.SkipTolerant(pp) + + if len(pp) <= 0 { + return method, requesturi, version, pp, true + } + } + + { + _, rest, ok := eol.BytesTolerant(pp) + if !ok { + return nil, nil, nil, p, false + } + + pp = rest + } + + return method, requesturi, version, pp, true +} diff --git a/requestline/bytes_tolerant_test.go b/requestline/bytes_tolerant_test.go new file mode 100644 index 0000000..5acaca1 --- /dev/null +++ b/requestline/bytes_tolerant_test.go @@ -0,0 +1,281 @@ +package requestline_test + +import ( + "testing" + + "bytes" + + "sourcecode.social/reiver/go-rfc2616/requestline" +) + +func TestBytesTolerant(t *testing.T) { + + tests := []struct{ + Value []byte + ExpectedMethod []byte + ExpectedRequestURI []byte + ExpectedVersion []byte + ExpectedRest []byte + ExpectedOK bool + }{ + { + Value: nil, + ExpectedMethod: nil, + ExpectedRequestURI: nil, + ExpectedVersion: nil, + ExpectedRest: nil, + ExpectedOK: false, + }, + { + Value: []byte(nil), + ExpectedMethod: nil, + ExpectedRequestURI: nil, + ExpectedVersion: nil, + ExpectedRest: nil, + ExpectedOK: false, + }, + + + + { + Value: []byte{}, + ExpectedMethod: nil, + ExpectedRequestURI: nil, + ExpectedVersion: nil, + ExpectedRest: nil, + ExpectedOK: false, + }, + { + Value: []byte(""), + ExpectedMethod: nil, + ExpectedRequestURI: nil, + ExpectedVersion: nil, + ExpectedRest: nil, + ExpectedOK: false, + }, + + + + { + Value: []byte("GET"), + ExpectedMethod: nil, + ExpectedRequestURI: nil, + ExpectedVersion: nil, + ExpectedRest: []byte("GET"), + ExpectedOK: false, + }, + { + Value: []byte("GET "), + ExpectedMethod: nil, + ExpectedRequestURI: nil, + ExpectedVersion: nil, + ExpectedRest: []byte("GET "), + ExpectedOK: false, + }, + { + Value: []byte("GET /"), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: nil, + ExpectedRest: nil, + ExpectedOK: true, + }, + + { + Value: []byte("GET /\n"), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: nil, + ExpectedRest: nil, + ExpectedOK: true, + }, + { + Value: []byte("GET /\r\n"), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: nil, + ExpectedRest: nil, + ExpectedOK: true, + }, + { + Value: []byte("GET /\nHost: example.com\n\n"), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: nil, + ExpectedRest: []byte("Host: example.com\n\n"), + ExpectedOK: true, + }, + { + Value: []byte("GET /\r\nHost: example.com\r\n\r\n"), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: nil, + ExpectedRest: []byte("Host: example.com\r\n\r\n"), + ExpectedOK: true, + }, + + { + Value: []byte("GET / "), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: nil, + ExpectedRest: nil, + ExpectedOK: true, + }, + + { + Value: []byte("GET / \n"), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: nil, + ExpectedRest: nil, + ExpectedOK: true, + }, + { + Value: []byte("GET / \r\n"), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: nil, + ExpectedRest: nil, + ExpectedOK: true, + }, + { + Value: []byte("GET / \nHost: example.com\n"), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: nil, + ExpectedRest: []byte("Host: example.com\n"), + ExpectedOK: true, + }, + { + Value: []byte("GET / \r\nHost: example.com\r\n"), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: nil, + ExpectedRest: []byte("Host: example.com\r\n"), + ExpectedOK: true, + }, + + { + Value: []byte("GET / HTTP/1.1"), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: []byte("HTTP/1.1"), + ExpectedRest: nil, + ExpectedOK: true, + }, + { + Value: []byte("GET / HTTP/1.1\n"), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: []byte("HTTP/1.1"), + ExpectedRest: nil, + ExpectedOK: true, + }, + { + Value: []byte("GET / HTTP/1.1\r\n"), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: []byte("HTTP/1.1"), + ExpectedRest: nil, + ExpectedOK: true, + }, + { + Value: []byte("GET / HTTP/1.1\nHost: example.com\n\n"), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: []byte("HTTP/1.1"), + ExpectedRest: []byte("Host: example.com\n\n"), + ExpectedOK: true, + }, + { + Value: []byte("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"), + ExpectedMethod: []byte("GET"), + ExpectedRequestURI: []byte("/"), + ExpectedVersion: []byte("HTTP/1.1"), + ExpectedRest: []byte("Host: example.com\r\n\r\n"), + ExpectedOK: true, + }, + + + + { + Value: []byte("PUNCH /apple/banana/cherry.php HTTP/1.1\r\nHost: example.com\r\n\r\n"), + ExpectedMethod: []byte("PUNCH"), + ExpectedRequestURI: []byte("/apple/banana/cherry.php"), + ExpectedVersion: []byte("HTTP/1.1"), + ExpectedRest: []byte("Host: example.com\r\n\r\n"), + ExpectedOK: true, + }, + } + + for testNumber, test := range tests { + + actualMethod, actualRequestURI, actualVersion, actualRest, actualOK := requestline.BytesTolerant(test.Value) + + { + expected := test.ExpectedOK + actual := actualOK + + if expected != actual { + t.Errorf("For test #%d, the actual ok-result is not what was expected.", testNumber) + t.Logf("EXPECTED: %t", expected) + t.Logf("ACTUAL: %t", actual) + t.Logf("VALUE; %q (%#v)", test.Value, test.Value) + continue + } + } + + { + expected := test.ExpectedMethod + actual := actualMethod + + if !bytes.Equal(expected, actual) { + t.Errorf("For test #%d, the actual method is not what was expected.", testNumber) + t.Logf("EXPECTED: %q (%#v)", expected, expected) + t.Logf("ACTUAL: %q (%#v)", actual, actual) + t.Logf("VALUE; %q (%#v)", test.Value, test.Value) + continue + } + } + + { + expected := test.ExpectedRequestURI + actual := actualRequestURI + + if !bytes.Equal(expected, actual) { + t.Errorf("For test #%d, the actual request-uri is not what was expected.", testNumber) + t.Logf("EXPECTED: %q (%#v)", expected, expected) + t.Logf("ACTUAL: %q (%#v)", actual, actual) + t.Logf("VALUE; %q (%#v)", test.Value, test.Value) + continue + } + } + + { + expected := test.ExpectedVersion + actual := actualVersion + + if !bytes.Equal(expected, actual) { + t.Errorf("For test #%d, the actual version is not what was expected.", testNumber) + t.Logf("EXPECTED: %q (%#v)", expected, expected) + t.Logf("ACTUAL: %q (%#v)", actual, actual) + t.Logf("VALUE; %q (%#v)", test.Value, test.Value) + continue + } + } + + { + expected := test.ExpectedRest + actual := actualRest + + if !bytes.Equal(expected, actual) { + t.Errorf("For test #%d, the actual rest is not what was expected.", testNumber) + t.Logf("EXPECTED: %q (%#v)", expected, expected) + t.Logf("ACTUAL: %q (%#v)", actual, actual) + t.Logf("VALUE; %q (%#v)", test.Value, test.Value) + continue + } + } + } +}