From da77deba78c8a7447b4a38324d2422a5df293b26 Mon Sep 17 00:00:00 2001 From: clsr Date: Fri, 18 Aug 2017 13:46:10 +0200 Subject: Initial commit --- request_test.go | 341 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 341 insertions(+) create mode 100644 request_test.go (limited to 'request_test.go') diff --git a/request_test.go b/request_test.go new file mode 100644 index 0000000..bb4aa9b --- /dev/null +++ b/request_test.go @@ -0,0 +1,341 @@ +package cnp + +import ( + "bytes" + "strconv" + "strings" + "testing" + "time" +) + +type requestTest struct { + h, s string + p Parameters + e, v error +} + +var requestTests = []requestTest{ + // invalid intent + {"", "", nil, ErrorInvalid{}, nil}, + {"", "foo/bar", nil, ErrorInvalid{}, nil}, + {"foo/", "bar", nil, ErrorInvalid{}, nil}, + {"foo/", "/bar", nil, ErrorInvalid{}, nil}, + + // invalid request params + {"", "/", Parameters{"length": "w"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"length": "-1"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"length": "03"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"name": "foo/bar"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"name": "foo\x00bar"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"type": "foo"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"type": "\x00"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"type": "text/plain\x00"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"type": "foo/bar "}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"type": " foo/bar"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"type": "foo /bar"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"type": "foo/ bar"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"type": "foo/bar\n"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"type": "foo/b(r"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "0"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "now"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "today"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "Thu Jan 1 00:00:00 UTC 1970"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "1970-01-01 00:00:00+00:00"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "1970-01-01 00:00:00 UTC"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "1970-01-01 00:00:00+0000"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "1970-01-01 00:00:00+00"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "1970-01-01T00:00:00+00:00"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "1970-01-01T00:00:00 UTC"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "1970-01-01T00:00:00+0000"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "1970-01-01T00:00:00+00"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "0000-00-01T00:00:00Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "0000-01-00T00:00:00Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "0000-01-01T24:00:00Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "0000-01-01T00:60:00Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "0000-01-01T00:00:60Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "0000-11-31T00:00:00Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "0001-02-29T00:00:00Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "0002-02-29T00:00:00Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "0003-02-29T00:00:00Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "0005-02-29T00:00:00Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "0100-02-29T00:00:00Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "1000-02-29T00:00:00Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "123-01-01T00:00:00Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "12345-01-01T00:00:00Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "-5-01-01T00:00:00Z"}, nil, ErrorInvalid{}}, + {"", "/", Parameters{"if_modified": "-2005-01-01T00:00:00Z"}, nil, ErrorInvalid{}}, + + // valid simple requests + {"", "/", nil, nil, nil}, + {"", "/foo/bar", nil, nil, nil}, + {"cnp.example.com", "/", nil, nil, nil}, + {"foo", "/bar", nil, nil, nil}, + {"example.com", "/ f=#\\oo///.././.../~/\x01/\xff/ba\nr", nil, nil, nil}, + + // valid request params + {"", "/", Parameters{"length": "", "name": "", "type": "", "if_modified": "", "": "", "q\x00we": "=a s\nd"}, nil, nil}, + {"", "/", Parameters{"length": "0"}, nil, nil}, + {"", "/", Parameters{"length": "1"}, nil, nil}, + {"", "/", Parameters{"length": "12345670089000000"}, nil, nil}, + {"", "/", Parameters{"name": "foobar"}, nil, nil}, + {"", "/", Parameters{"name": "x"}, nil, nil}, + {"", "/", Parameters{"name": "..-~!foo bar\nbaz\rquux=qwe\\asd"}, nil, nil}, + {"", "/", Parameters{"name": strings.Repeat("w", 1024*8)}, nil, nil}, + {"", "/", Parameters{"type": "qwe/asd"}, nil, nil}, + {"", "/", Parameters{"type": "qwe+asd/foo"}, nil, nil}, + {"", "/", Parameters{"type": "qwe-asd/foo"}, nil, nil}, + {"", "/", Parameters{"if_modified": "1970-01-01T00:00:00Z"}, nil, nil}, + {"", "/", Parameters{"if_modified": "0000-01-01T00:00:00Z"}, nil, nil}, + {"", "/", Parameters{"if_modified": "9999-12-31T23:59:59Z"}, nil, nil}, + {"", "/", Parameters{"if_modified": "0123-05-06T07:08:09Z"}, nil, nil}, + {"", "/", Parameters{"if_modified": "0000-02-29T00:00:00Z"}, nil, nil}, + {"", "/", Parameters{"if_modified": "2000-02-29T00:00:00Z"}, nil, nil}, +} + +func TestNewRequest(t *testing.T) { + for _, tst := range requestTests { + req, err := NewRequest(tst.h, tst.s, []byte{}) + if !errorEqual(tst.e, err) { + t.Errorf("NewRequest(%q, %q): expected error %+v, got %+v (%v)", tst.h, tst.s, tst.e, err, err) + continue + } + if tst.e == nil { + if tst.h != req.Host() { + t.Errorf("NewRequest(%q: %q): got unexpected host %q", tst.h, tst.s, req.Host()) + } else if Clean(tst.s) != req.Path() { + t.Errorf("NewRequest(%q: %q): got unexpected path %q", tst.h, tst.s, req.Path()) + } + } + } +} + +type requestURLTest struct { + u string + h, p string + e error +} + +var requestURLTests = []requestURLTest{ + // invalid + {"cnp:example.com/path/to/file", "", "", ErrorURL{}}, + {"http://example.com/path/to/file", "", "", ErrorURL{}}, + {"cnp://foo@bar:example.com/path/to/file", "", "", ErrorURL{}}, + {"cnp://foo:example.com/path/to/file", "", "", ErrorURL{}}, + {"", "", "/", ErrorURL{}}, + {"cnp://example.com/%5", "", "", ErrorURL{}}, + //{"cnp://example.com/?%5", "", "", ErrorURL{}}, + + // valid + {"cnp://example.com/path/to/file", "example.com", "/path/to/file", nil}, + {"cnp://2130706433/", "2130706433", "/", nil}, + //{"cnp://02130706433/", "2130706433", "/", nil}, + {"cnp://127.0.0.1/", "127.0.0.1", "/", nil}, + //{"cnp://0127.0.00.01/", "127.0.0.1", "/", nil}, + {"cnp://localhost", "localhost", "/", nil}, + {"cnp://[::1]/foo%20bar", "[::1]", "/foo bar", nil}, + {"cnp://[2001:db8::7334]/foo%0abar", "[2001:db8::7334]", "/foo\nbar", nil}, + //{"cnp://[2001:0db8:0000::7334]/foo%0abar", "[2001:db8::7334]", "/foo\nbar", nil}, + //{"cnp://example.com/qwe%20asd?foo=bar&baz=qu%20ux", "example.com", "/qwe asd?foo=bar&baz=qu ux", nil}, + {"cnp://example.com/qwe%20asd?foo=bar&baz=qu%20ux", "example.com", "/qwe asd", nil}, + {"cnp://localhost:25454", "localhost", "/", nil}, + {"cnp://localhost:12345/foo/bar", "localhost:12345", "/foo/bar", nil}, + {"cnp://localhost:25454/foo/bar", "localhost", "/foo/bar", nil}, + {"cnp://2130706433:12345/foo/bar", "2130706433:12345", "/foo/bar", nil}, + {"cnp://2130706433:25454/foo/bar", "2130706433", "/foo/bar", nil}, + {"cnp://127.0.0.1:12345/foo/bar", "127.0.0.1:12345", "/foo/bar", nil}, + {"cnp://127.0.0.1:25454/foo/bar", "127.0.0.1", "/foo/bar", nil}, + {"cnp://[::1]:12345/foo/bar", "[::1]:12345", "/foo/bar", nil}, + {"cnp://[::1]:25454/foo/bar", "[::1]", "/foo/bar", nil}, + {"//example.com/path/", "example.com", "/path/", nil}, + {"/example.com/path/", "", "/example.com/path/", nil}, + {"/foo/bar", "", "/foo/bar", nil}, + {"/", "", "/", nil}, + {"cnp://example.com/ ™☺)\n", "example.com", "/ ™☺)\n", nil}, + {"cnp://œ¤å₥¶ḹə.©°ɱ/baz/../foo/.//bar////.//../bar//", "œ¤å₥¶ḹə.©°ɱ", "/foo/bar/", nil}, + {"cnp://foo/bar/", "foo", "/bar/", nil}, + {"cnp:///foo/bar/", "", "/foo/bar/", nil}, + {"cnp:////////foo/bar/", "", "/foo/bar/", nil}, +} + +func TestNewRequestURL(t *testing.T) { + for _, tst := range requestURLTests { + req, err := NewRequestURL(tst.u, nil) + if !errorEqual(tst.e, err) { + t.Errorf("NewRequestURL(%q, nil): expected error %+v, got %+v (%v)", tst.u, tst.e, err, err) + continue + } + if tst.e == nil { + if req.Host() != tst.h { + t.Errorf("NewRequestURL(%q, nil): expected host %q, got %q", tst.u, tst.h, req.Host()) + } else if req.Path() != tst.p { + t.Errorf("NewRequestURL(%q, nil): expected path %q, got %q", tst.u, tst.p, req.Path()) + } + } + } +} + +func TestWriteParseRequest(t *testing.T) { + for _, tst := range requestTests { + if tst.e != nil { + continue // skip syntax errors but not invalid params + } + req := &Request{Message{Header{0, 3, "/", Parameters{}}, nil, nil}} + if err := req.SetHost(tst.h); err != nil { + t.Errorf("SetHost(%q) error: %v", tst.h, err) + continue + } + if err := req.SetPath(tst.s); err != nil { + t.Errorf("SetPath(%q) error: %v", tst.s, err) + continue + } + for k, v := range tst.p { + req.SetParam(k, v) + } + + var buf bytes.Buffer + if err := req.Write(&buf); err != nil { + t.Errorf("%+v.Write: error %v", req, err) + continue + } + + s := buf.String() + req2, err := ParseRequest(strings.NewReader(s)) + if err != nil { + t.Errorf("ParseRequest(%q) error: %v", s, err) + continue + } + if !msgEqual(&req.Message, &req2.Message) { + t.Errorf("Write/Parse: expected %+v, got %+v", req, req2) + continue + } + } +} + +func TestRequestValidate(t *testing.T) { + for _, tst := range requestTests { + if tst.e != nil { + continue + } + req := &Request{Message{Header{Intent: tst.h + tst.s, Parameters: tst.p}, nil, nil}} + if err := req.Validate(); !errorEqual(tst.v, err) { + t.Errorf("%+v.Validate(): expected error %+v, got %+v (%v)", req, tst.v, err, err) + } + } +} + +func TestRequestGetSet(t *testing.T) { + e := func(k, v string, expect, err error) { + if !errorEqual(err, expect) { + t.Errorf("setting param %q to %q: expected %+v, got %+v (%v)", k, v, expect, err, err) + } + } + c := func(k, v string, expect error, p string) { + if expect == nil && v != p { + t.Errorf("getting param %q: expected %q, got %q", k, v, p) + } + } + + for _, tst := range requestTests { + if tst.e != nil { + continue + } + req, _ := NewRequest(tst.h, tst.s, nil) + for k, v := range tst.p { + switch k { + case "length": + n, _ := strconv.ParseUint(v, 10, 63) + req.SetLength(int64(n)) + s := strconv.FormatInt(req.Length(), 10) + if s == "0" && v == "" { + s = "" + } + c(k, v, tst.v, s) + case "name": + e(k, v, tst.v, req.SetName(v)) + c(k, v, tst.v, req.Name()) + case "type": + e(k, v, tst.v, req.SetType(v)) + t := req.Type() + if t == "application/octet-stream" && v == "" { + t = "" + } + c(k, v, tst.v, t) + case "if_modified": + if tst.v != nil { + continue // invalid time format, can't even parse + } + var tm time.Time + if v != "" { + tm, _ = time.Parse(time.RFC3339, v) + } + req.SetIfModified(tm) + tm2 := "" + if !req.IfModified().IsZero() { + tm2 = req.IfModified().Format(time.RFC3339) + } + c(k, v, tst.v, tm2) + default: + req.SetParam(k, v) + c(k, v, tst.v, req.Param(k)) + } + } + } +} + +var urlTests = map[string]string{ + "œ¤å₥¶ḹə.©°ɱ/foo/bar": "cnp://%C5%93%C2%A4%C3%A5%E2%82%A5%C2%B6%E1%B8%B9%C9%99.%C2%A9%C2%B0%C9%B1/foo/bar", + "2130706433/": "cnp://2130706433/", + "example.com/path/to/file/": "cnp://example.com/path/to/file/", + "[::1]/foo bar": "cnp://[::1]/foo%20bar", + "[2001:db8::7334]/foo\nbar": "cnp://[2001:db8::7334]/foo%0Abar", + "example.com/qwe asd": "cnp://example.com/qwe%20asd", + "example.com/ ™☺)\n": "cnp://example.com/%20%E2%84%A2%E2%98%BA%29%0A", +} + +func TestRequestURL(t *testing.T) { + for k, v := range urlTests { + t.Run(v, func(t *testing.T) { + req, err := NewRequestURL(v, nil) + if err != nil { + t.Fatalf("NewRequestURL(%q): error: %v", v, err) + } + if req.Intent() != k { + t.Fatalf("NewRequestURL(%q).Intent(): expected %q, got %q", v, k, req.Intent()) + } + if req.URL().String() != v { + t.Fatalf("NewRequestURL(%q).URL(): expected %q, got %q", v, v, req.URL()) + } + }) + } +} + +var cleanTests = map[string]string{ + "/../.././//foo/bar/..": "/foo", + "//foo/bar/../": "/foo/", + "/": "/", + "//": "/", + "/..": "/", + "/../..": "/", + "/../": "/", + "/.": "/", + "/./": "/", + "/./.": "/", + "/././": "/", + "/./../": "/", + "/.././": "/", + "/foo/bar/../baz": "/foo/baz", + "/foo/bar/../baz/": "/foo/baz/", + "/foo/../foo/bar/../bar/baz/quux/../": "/foo/bar/baz/", + "/foo/../foo/bar/../bar/baz/quux/..": "/foo/bar/baz", +} + +func TestClean(t *testing.T) { + for k, v := range cleanTests { + t.Run(k, func(t *testing.T) { + c := Clean(k) + if c != v { + t.Fatalf("Clean(%q): expected %q, got %q", k, v, c) + } + }) + } +} -- cgit