mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-01 20:13:31 +00:00
358 lines
10 KiB
C
358 lines
10 KiB
C
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||
|
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||
|
│ │
|
||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||
|
│ │
|
||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||
|
#include "libc/limits.h"
|
||
|
#include "libc/mem/mem.h"
|
||
|
#include "libc/rand/rand.h"
|
||
|
#include "libc/testlib/ezbench.h"
|
||
|
#include "libc/testlib/hyperion.h"
|
||
|
#include "libc/testlib/testlib.h"
|
||
|
#include "net/http/url.h"
|
||
|
|
||
|
TEST(ParseRequestUri, testEmpty) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri(0, 0, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(0, h.params.n);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testFragment) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("#x", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(0, h.path.n);
|
||
|
ASSERT_EQ(1, h.fragment.n);
|
||
|
ASSERT_BINEQ(u"x", h.fragment.p);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testFragmentAbsent_isNull) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(0, h.fragment.p);
|
||
|
ASSERT_EQ(0, h.fragment.n);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testFragmentEmpty_isNonNull) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("#", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_NE(0, h.fragment.p);
|
||
|
ASSERT_EQ(0, h.fragment.n);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testPathFragment) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("x#y", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(1, h.path.n);
|
||
|
ASSERT_EQ('x', h.path.p[0]);
|
||
|
ASSERT_EQ(1, h.fragment.n);
|
||
|
ASSERT_EQ('y', h.fragment.p[0]);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testAbsolutePath) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("/x/y", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(4, h.path.n);
|
||
|
ASSERT_BINEQ(u"/x/y", h.path.p);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testRelativePath1) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("x", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(1, h.path.n);
|
||
|
ASSERT_EQ('x', h.path.p[0]);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testOptions) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("*", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(1, h.path.n);
|
||
|
ASSERT_EQ('*', h.path.p[0]);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testRelativePath2) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("x/y", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(3, h.path.n);
|
||
|
ASSERT_BINEQ(u"x/y", h.path.p);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testRoot) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("/", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(1, h.path.n);
|
||
|
ASSERT_EQ('/', h.path.p[0]);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testSchemePath) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("x:y", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(1, h.scheme.n);
|
||
|
ASSERT_BINEQ(u"x", h.scheme.p);
|
||
|
ASSERT_EQ(1, h.path.n);
|
||
|
ASSERT_BINEQ(u"y", h.path.p);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testSchemeAuthority) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("x://y", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(1, h.scheme.n);
|
||
|
ASSERT_EQ('x', h.scheme.p[0]);
|
||
|
ASSERT_EQ(1, h.host.n);
|
||
|
ASSERT_EQ('y', h.host.p[0]);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testParamsQuestion_doesntTurnIntoSpace) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("x?+", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(1, h.path.n);
|
||
|
ASSERT_BINEQ(u"x", h.path.p);
|
||
|
ASSERT_EQ(1, h.params.n);
|
||
|
ASSERT_EQ(1, h.params.p[0].key.n);
|
||
|
ASSERT_EQ('+', h.params.p[0].key.p[0]);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testUrl) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("a://b:B@c:C/d?e#f", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(1, h.scheme.n);
|
||
|
ASSERT_EQ('a', h.scheme.p[0]);
|
||
|
ASSERT_EQ(1, h.user.n);
|
||
|
ASSERT_EQ('b', h.user.p[0]);
|
||
|
ASSERT_EQ(1, h.pass.n);
|
||
|
ASSERT_EQ('B', h.pass.p[0]);
|
||
|
ASSERT_EQ(1, h.host.n);
|
||
|
ASSERT_EQ('c', h.host.p[0]);
|
||
|
ASSERT_EQ(1, h.port.n);
|
||
|
ASSERT_EQ('C', h.port.p[0]);
|
||
|
ASSERT_EQ(2, h.path.n);
|
||
|
ASSERT_BINEQ(u"/d", h.path.p);
|
||
|
ASSERT_EQ(1, h.params.n);
|
||
|
ASSERT_EQ(1, h.params.p[0].key.n);
|
||
|
ASSERT_BINEQ(u"e", h.params.p[0].key.p);
|
||
|
ASSERT_EQ(SIZE_MAX, h.params.p[0].val.n);
|
||
|
ASSERT_EQ(1, h.fragment.n);
|
||
|
ASSERT_BINEQ(u"f", h.fragment.p);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testUrlWithoutScheme) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("//b@c/d?e#f", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(0, h.scheme.n);
|
||
|
ASSERT_EQ(1, h.user.n);
|
||
|
ASSERT_EQ('b', h.user.p[0]);
|
||
|
ASSERT_EQ(1, h.host.n);
|
||
|
ASSERT_EQ('c', h.host.p[0]);
|
||
|
ASSERT_EQ(2, h.path.n);
|
||
|
ASSERT_BINEQ(u"/d", h.path.p);
|
||
|
ASSERT_EQ(1, h.params.n);
|
||
|
ASSERT_EQ(1, h.params.p[0].key.n);
|
||
|
ASSERT_BINEQ(u"e", h.params.p[0].key.p);
|
||
|
ASSERT_EQ(SIZE_MAX, h.params.p[0].val.n);
|
||
|
ASSERT_EQ(1, h.fragment.n);
|
||
|
ASSERT_BINEQ(u"f", h.fragment.p);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testUrlWithoutUser) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("a://c/d?e#f", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(1, h.scheme.n);
|
||
|
ASSERT_EQ('a', h.scheme.p[0]);
|
||
|
ASSERT_EQ(0, h.user.n);
|
||
|
ASSERT_EQ(0, h.pass.n);
|
||
|
ASSERT_EQ(1, h.host.n);
|
||
|
ASSERT_EQ('c', h.host.p[0]);
|
||
|
ASSERT_EQ(0, h.port.n);
|
||
|
ASSERT_EQ(2, h.path.n);
|
||
|
ASSERT_BINEQ(u"/d", h.path.p);
|
||
|
ASSERT_EQ(1, h.params.n);
|
||
|
ASSERT_EQ(1, h.params.p[0].key.n);
|
||
|
ASSERT_EQ('e', h.params.p[0].key.p[0]);
|
||
|
ASSERT_EQ(SIZE_MAX, h.params.p[0].val.n);
|
||
|
ASSERT_EQ(1, h.fragment.n);
|
||
|
ASSERT_EQ('f', h.fragment.p[0]);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testLolv6) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("//[::1]:31337", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(3, h.host.n);
|
||
|
ASSERT_BINEQ(u"::1", h.host.p);
|
||
|
ASSERT_EQ(5, h.port.n);
|
||
|
ASSERT_BINEQ(u"31337", h.port.p);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testUrlWithoutParams) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("a://b@c/d#f", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(1, h.scheme.n);
|
||
|
ASSERT_EQ('a', h.scheme.p[0]);
|
||
|
ASSERT_EQ(1, h.user.n);
|
||
|
ASSERT_EQ('b', h.user.p[0]);
|
||
|
ASSERT_EQ(1, h.host.n);
|
||
|
ASSERT_EQ('c', h.host.p[0]);
|
||
|
ASSERT_EQ(2, h.path.n);
|
||
|
ASSERT_BINEQ(u"/d", h.path.p);
|
||
|
ASSERT_EQ(0, h.params.n);
|
||
|
ASSERT_EQ(1, h.fragment.n);
|
||
|
ASSERT_EQ('f', h.fragment.p[0]);
|
||
|
}
|
||
|
|
||
|
TEST(ParseUrl, testLatin1_doesNothing) {
|
||
|
struct Url h;
|
||
|
const char b[1] = {0377};
|
||
|
gc(ParseUrl(b, 1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(1, h.path.n);
|
||
|
ASSERT_EQ(0, memcmp("\377", h.path.p, 1));
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testLatin1_expandsMemoryToUtf8) {
|
||
|
struct Url h;
|
||
|
const char b[1] = {0377};
|
||
|
gc(ParseRequestUri(b, 1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(2, h.path.n);
|
||
|
ASSERT_EQ(0, memcmp("\303\277", h.path.p, 2));
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testPercentShrinkingMemory) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("%Ff", 3, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(1, h.path.n);
|
||
|
ASSERT_EQ(0, memcmp("\377", h.path.p, 1));
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testBadPercent_getsIgnored) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("%FZ", 3, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(3, h.path.n);
|
||
|
ASSERT_EQ(0, memcmp("%FZ", h.path.p, 3));
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testFileUrl) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("file:///etc/passwd", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(4, h.scheme.n);
|
||
|
ASSERT_BINEQ(u"file", h.scheme.p);
|
||
|
ASSERT_EQ(11, h.path.n);
|
||
|
ASSERT_BINEQ(u"/etc/passwd", h.path.p);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, testZipUri2) {
|
||
|
struct Url h;
|
||
|
gc(ParseRequestUri("zip:etc/passwd", -1, &h));
|
||
|
gc(h.params.p);
|
||
|
ASSERT_EQ(3, h.scheme.n);
|
||
|
ASSERT_BINEQ(u"zip", h.scheme.p);
|
||
|
ASSERT_EQ(10, h.path.n);
|
||
|
ASSERT_BINEQ(u"etc/passwd", h.path.p);
|
||
|
}
|
||
|
|
||
|
TEST(ParseParams, testEmpty) {
|
||
|
struct UrlParams h = {0};
|
||
|
gc(ParseParams(0, 0, &h));
|
||
|
gc(h.p);
|
||
|
ASSERT_EQ(0, h.n);
|
||
|
}
|
||
|
|
||
|
TEST(ParseParams, test) {
|
||
|
struct UrlParams h = {0};
|
||
|
gc(ParseParams("a=b&c&x+y%7A=", -1, &h));
|
||
|
gc(h.p);
|
||
|
ASSERT_EQ(3, h.n);
|
||
|
ASSERT_EQ(1, h.p[0].key.n);
|
||
|
ASSERT_EQ(1, h.p[0].val.n);
|
||
|
ASSERT_EQ(1, h.p[1].key.n);
|
||
|
ASSERT_EQ(SIZE_MAX, h.p[1].val.n);
|
||
|
ASSERT_EQ(4, h.p[2].key.n);
|
||
|
ASSERT_EQ(0, h.p[2].val.n);
|
||
|
EXPECT_EQ('a', h.p[0].key.p[0]);
|
||
|
EXPECT_EQ('b', h.p[0].val.p[0]);
|
||
|
EXPECT_EQ('c', h.p[1].key.p[0]);
|
||
|
EXPECT_BINEQ(u"x yz", h.p[2].key.p);
|
||
|
}
|
||
|
|
||
|
TEST(ParseParams, testLatin1_doesNothing) {
|
||
|
struct UrlParams h = {0};
|
||
|
gc(ParseParams("\200", -1, &h));
|
||
|
gc(h.p);
|
||
|
ASSERT_EQ(1, h.n);
|
||
|
ASSERT_EQ(1, h.p[0].key.n);
|
||
|
ASSERT_EQ(0200, h.p[0].key.p[0] & 255);
|
||
|
}
|
||
|
|
||
|
TEST(ParseParams, testUtf8_doesNothing) {
|
||
|
struct UrlParams h = {0};
|
||
|
gc(ParseParams("\300\200", -1, &h));
|
||
|
gc(h.p);
|
||
|
ASSERT_EQ(1, h.n);
|
||
|
ASSERT_EQ(2, h.p[0].key.n);
|
||
|
ASSERT_EQ(0300, h.p[0].key.p[0] & 255);
|
||
|
ASSERT_EQ(0200, h.p[0].key.p[1] & 255);
|
||
|
}
|
||
|
|
||
|
TEST(ParseRequestUri, fuzz) {
|
||
|
int i, j;
|
||
|
struct Url h;
|
||
|
char B[13], C[] = "/:#?%[]:@&=abc123xyz\200\300";
|
||
|
for (i = 0; i < 1024; ++i) {
|
||
|
for (j = 0; j < sizeof(B); ++j) {
|
||
|
B[j] = C[rand() % sizeof(C)];
|
||
|
}
|
||
|
free(ParseRequestUri(B, 8, &h));
|
||
|
free(h.params.p);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void A(void) {
|
||
|
struct UrlParams h = {0};
|
||
|
free(ParseParams(kHyperion, kHyperionSize, &h));
|
||
|
free(h.p);
|
||
|
}
|
||
|
|
||
|
BENCH(url, bench) {
|
||
|
struct Url h;
|
||
|
EZBENCH2("ParseParams", donothing, A());
|
||
|
EZBENCH2("URI a", donothing, free(ParseRequestUri("a", -1, &h)));
|
||
|
EZBENCH2("URI a://b@c/d#f", donothing,
|
||
|
free(ParseRequestUri("a://b@c/d#f", -1, &h)));
|
||
|
EZBENCH2("URI a://b@c/d?z#f", donothing, ({
|
||
|
free(ParseRequestUri("a://b@c/?zd#f", -1, &h));
|
||
|
free(h.params.p);
|
||
|
}));
|
||
|
}
|