Make more major improvements to redbean

- POSIX regular expressions for Lua
- Improved protocol parsing and encoding
- Additional APIs for ZIP storage retrieval
- Fix st_mode issue on NT for regular files
- Generalized APIs for URL and Host handling
- Worked out the kinks in resource resolution
- Allow for custom error pages like /404.html
This commit is contained in:
Justine Tunney 2021-04-20 19:14:21 -07:00
parent 26ac6871da
commit 4effa23528
74 changed files with 3710 additions and 14246 deletions

View file

@ -25,7 +25,8 @@
#include "libc/sysv/consts/shut.h"
#include "libc/sysv/consts/sock.h"
#include "libc/x/x.h"
#include "net/http/uri.h"
#include "net/http/http.h"
#include "net/http/url.h"
/**
* @fileoverview Downloads HTTP URL to stdout.
@ -35,50 +36,92 @@
*/
int main(int argc, char *argv[]) {
int sock;
ssize_t rc;
unsigned long need;
struct UriSlice path;
size_t i, got, toto, msglen;
char buf[1500], host[256], port[7];
const char *url, *msg, *pathstr, *crlfcrlf, *contentlength;
struct UriSlice us[16];
struct Uri u = {.segs.p = us, .segs.n = ARRAYLEN(us)};
/*
* Get argument.
*/
const char *urlarg;
if (argc != 2) {
fprintf(stderr, "USAGE: %s URL\n", argv[0]);
exit(1);
}
urlarg = argv[1];
/*
* Parse URL.
*/
struct Url url;
char *host, *port;
_gc(ParseUrl(urlarg, -1, &url));
_gc(url.params.p);
if (url.scheme.n &&
!(url.scheme.n == 4 && !memcasecmp(url.scheme.p, "http", 4))) {
fprintf(stderr, "ERROR: NOT AN HTTP URL: %s\n", urlarg);
exit(1);
}
host = firstnonnull(_gc(strndup(url.host.p, url.host.n)), "127.0.0.1");
port = url.port.n ? _gc(strndup(url.port.p, url.port.n)) : "80";
port = _gc(xasprintf("%hu", atoi(port)));
if (!IsAcceptableHost(host, -1)) {
fprintf(stderr, "ERROR: INVALID HOST: %s\n", urlarg);
exit(1);
}
url.fragment.p = 0, url.fragment.n = 0;
url.scheme.p = 0, url.scheme.n = 0;
url.user.p = 0, url.user.n = 0;
url.pass.p = 0, url.pass.n = 0;
url.host.p = 0, url.host.n = 0;
url.port.p = 0, url.port.n = 0;
if (!url.path.n || url.path.p[0] != '/') {
char *p = _gc(xmalloc(1 + url.path.n));
mempcpy(mempcpy(p, "/", 1), url.path.p, url.path.n);
url.path.p = p;
++url.path.n;
}
/*
* Create HTTP message.
*/
const char *msg;
msg = _gc(xasprintf("GET %s HTTP/1.1\r\n"
"Host: %s:%s\r\n"
"Connection: close\r\n"
"Content-Length: 0\r\n"
"Accept: text/plain; */*\r\n"
"Accept-Encoding: identity\r\n"
"User-Agent: github.com/jart/cosmopolitan\r\n"
"\r\n",
_gc(EncodeUrl(&url, 0)), host, port));
/*
* Perform DNS lookup.
*/
struct addrinfo *addr, *addrs;
struct addrinfo hints = {.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
.ai_flags = AI_NUMERICSERV};
if (argc != 2) {
fprintf(stderr, "USAGE: %s URL\n", argv[0]);
exit(1);
}
url = argv[1];
CHECK_NE(-1, uriparse(&u, url, strlen(url)), "BAD URL: %`'s", url);
CHECK_EQ(kUriSchemeHttp, urischeme(u.scheme, url));
urislice2cstr(host, sizeof(host), u.host, url, "127.0.0.1");
urislice2cstr(port, sizeof(port), u.port, url, "80");
path = uripath(&u);
pathstr = path.n ? url + path.i : "/";
msg = _gc(xstrcat("GET ", pathstr,
" HTTP/1.1\r\n"
"Host: ",
host,
"\r\n"
"Connection: close\r\n"
"Content-Length: 0\r\n"
"Accept: text/plain; */*\r\n"
"Accept-Encoding: identity\r\n"
"User-Agent: github.com/jart/cosmopolitan\r\n"
"\r\n"));
msglen = strlen(msg);
CHECK_EQ(EAI_SUCCESS, getaddrinfo(host, port, &hints, &addrs));
for (addr = addrs; addr; addr = addr->ai_next) {
/*
* Send HTTP Message.
*/
int sock;
CHECK_NE(-1, (sock = socket(addr->ai_family, addr->ai_socktype,
addr->ai_protocol)));
CHECK_NE(-1, connect(sock, addr->ai_addr, addr->ai_addrlen));
CHECK_EQ(msglen, write(sock, msg, msglen));
CHECK_EQ(strlen(msg), write(sock, msg, strlen(msg)));
shutdown(sock, SHUT_WR);
/*
* Handle response.
*/
ssize_t rc;
char buf[1500];
size_t got, toto;
unsigned long need;
const char *msg, *crlfcrlf;
buf[0] = '\0';
CHECK_NE(-1, (rc = read(sock, buf, sizeof(buf))));
got = rc;

View file

@ -150,10 +150,8 @@ void Draw(void) {
}
int main(int argc, char *argv[]) {
struct sigaction sa[2] = {
{.sa_handler = OnShutdown},
{.sa_handler = OnInvalidate, .sa_flags = SA_RESTART},
};
struct sigaction sa[2] = {{.sa_handler = OnShutdown},
{.sa_handler = OnInvalidate}};
showcrashreports();
Setup();
Enter();

View file

@ -30,7 +30,7 @@
#define SIG_DFL ((void *)0)
#define SIG_IGN ((void *)1)
#define MAP_FAILED ((void *)__SIZE_MAX__)
#define MAP_FAILED ((void *)-1)
#define ARCH_SET_GS 0x1001
#define ARCH_SET_FS 0x1002

View file

@ -39,22 +39,24 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *st) {
memset(st, 0, sizeof(*st));
switch (filetype) {
case kNtFileTypeChar:
st->st_mode = S_IFCHR | 0600;
st->st_mode = S_IFCHR | 0644;
break;
case kNtFileTypePipe:
st->st_mode = S_IFIFO | 0600;
st->st_mode = S_IFIFO | 0644;
break;
case kNtFileTypeDisk:
if (GetFileInformationByHandle(handle, &wst)) {
st->st_mode =
(S_IRUSR | S_IXUSR |
(!(wst.dwFileAttributes & kNtFileAttributeReadonly) ? S_IWUSR
: 0) |
((wst.dwFileAttributes & kNtFileAttributeNormal) ? S_IFREG : 0) |
((wst.dwFileAttributes & kNtFileFlagOpenReparsePoint) ? S_IFLNK
: 0) |
((wst.dwFileAttributes & kNtFileAttributeDirectory) ? S_IFDIR
: 0));
st->st_mode = 0555;
if (!(wst.dwFileAttributes & kNtFileAttributeReadonly)) {
st->st_mode |= 0200;
}
if (wst.dwFileAttributes & kNtFileAttributeDirectory) {
st->st_mode |= S_IFDIR;
} else if (wst.dwFileAttributes & kNtFileFlagOpenReparsePoint) {
st->st_mode |= S_IFLNK;
} else {
st->st_mode |= S_IFREG;
}
st->st_atim = FileTimeToTimeSpec(wst.ftLastAccessFileTime);
st->st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
st->st_ctim = FileTimeToTimeSpec(wst.ftCreationFileTime);

View file

@ -659,6 +659,7 @@ typedef uint64_t uintmax_t;
#pragma GCC diagnostic ignored "-Wformat" /* forces only gnu pf */
#pragma GCC diagnostic ignored "-Wunused-parameter" /* extreme prejudice */
#pragma GCC diagnostic ignored "-Wunused-function" /* contradicts dce! */
#pragma GCC diagnostic ignored "-Wunused-const-variable" /* let me dce */
#pragma GCC diagnostic ignored "-Wunused-variable" /* belongs in tidy */
#pragma GCC diagnostic ignored "-Wformat-extra-args" /* is also broken */
#pragma GCC diagnostic ignored "-Wparentheses" /* annoying tidy */

View file

@ -61,7 +61,7 @@ void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) {
struct DirectMap dm;
int i, x, n, m, a, b, f;
if (!size) return VIP(einval());
if (size > 0x0000010000000000) return VIP(enomem());
if (size > 0x0000010000000000ull) return VIP(enomem());
if (!ALIGNED(off)) return VIP(einval());
if (!ALIGNED(addr)) return VIP(einval());
if (!CANONICAL(addr)) return VIP(einval());

View file

@ -35,7 +35,7 @@ extern uint8_t __zip_end[]; /* αpε */
void mcount(void);
unsigned long getauxval(unsigned long);
void *mapanon(size_t) vallocesque attributeallocsize((1));
void *mapanon(size_t) attributeallocsize((1));
int setjmp(jmp_buf) libcesque returnstwice paramsnonnull();
void longjmp(jmp_buf, int) libcesque wontreturn paramsnonnull();
int _setjmp(jmp_buf) libcesque returnstwice paramsnonnull();

144
net/http/encodeurl.c Normal file
View file

@ -0,0 +1,144 @@
/*-*- 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/mem/mem.h"
#include "libc/str/str.h"
#include "net/http/escape.h"
#include "net/http/url.h"
static size_t DimensionUrl(struct Url *h) {
size_t i, n;
n = 0;
n += h->scheme.n;
n += 1;
n += 2;
n += h->user.n * 3;
n += 1;
n += h->pass.n * 3;
n += 1;
n += 1;
n += h->host.n * 3;
n += 1;
n += 1;
n += h->port.n * 3;
n += 1;
n += h->path.n * 3;
n += 1;
n += h->params.n;
for (i = 0; i < h->params.n; ++i) {
n += h->params.p[i].key.n * 3;
n += 1;
n += h->params.p[i].val.n * 3;
}
n += 1;
n += h->fragment.n * 3;
n += 1;
return n;
}
static bool NeedsSquareBrackets(struct Url *h) {
int c;
size_t i;
if (!memchr(h->host.p, ':', h->host.n)) return false;
if (h->pass.p) return true;
if (h->host.n >= 4 && h->host.p[0] == 'v' && h->host.p[2] == '.' &&
kHexToInt[h->host.p[1] & 0xFF] != -1) {
for (i = 3; i < h->host.n; ++i) {
if (kEscapeIp[h->host.p[i] & 0xFF]) {
return false;
}
}
} else {
for (i = 0; i < h->host.n; ++i) {
c = h->host.p[i] & 0xFF;
if (!(kHexToInt[c] || c == '.' || c == ':')) {
return false;
}
}
}
return true;
}
/**
* Encodes URL.
*
* @param z if not null receives string length of result
* @return nul-terminated url string needing free
* @see ParseUrl()
*/
char *EncodeUrl(struct Url *h, size_t *z) {
size_t i, n;
char *m, *p;
if ((p = m = malloc(DimensionUrl(h)))) {
if (h->scheme.n) {
p = mempcpy(p, h->scheme.p, h->scheme.n);
*p++ = ':';
}
if (h->host.p) {
*p++ = '/';
*p++ = '/';
if (h->user.p) {
p = EscapeUrlView(p, &h->user, kEscapeAuthority);
if (h->pass.p) {
*p++ = ':';
p = EscapeUrlView(p, &h->pass, kEscapeAuthority);
}
*p++ = '@';
}
if (h->host.p) {
if (NeedsSquareBrackets(h)) {
*p++ = '[';
p = EscapeUrlView(p, &h->host, kEscapeIp);
*p++ = ']';
} else {
p = EscapeUrlView(p, &h->host, kEscapeAuthority);
}
if (h->port.p) {
*p++ = ':';
p = EscapeUrlView(p, &h->port, kEscapeAuthority);
}
}
if (h->path.n && h->path.p[0] != '/') {
*p++ = '/';
}
}
p = EscapeUrlView(p, &h->path, kEscapePath);
if (h->params.p) {
*p++ = '?';
for (i = 0; i < h->params.n; ++i) {
if (i) *p++ = '&';
p = EscapeUrlView(p, &h->params.p[i].key, kEscapeParam);
if (h->params.p[i].val.p) {
*p++ = '=';
p = EscapeUrlView(p, &h->params.p[i].val, kEscapeParam);
}
}
}
if (h->fragment.p) {
*p++ = '#';
p = EscapeUrlView(p, &h->fragment, kEscapeFragment);
}
n = p - m;
*p++ = '\0';
if ((p = realloc(m, p - m))) m = p;
} else {
n = 0;
}
if (z) *z = n;
return m;
}

View file

@ -8,12 +8,24 @@ struct EscapeResult {
size_t size;
};
extern const signed char kHexToInt[256];
extern const char kEscapeAuthority[256];
extern const char kEscapeIp[256];
extern const char kEscapePath[256];
extern const char kEscapeSegment[256];
extern const char kEscapeParam[256];
extern const char kEscapeFragment[256];
struct EscapeResult EscapeHtml(const char *, size_t);
struct EscapeResult EscapeUrl(const char *, size_t, const char[hasatleast 256]);
struct EscapeResult EscapeUrlPath(const char *, size_t);
struct EscapeResult EscapeUrlParam(const char *, size_t);
struct EscapeResult EscapeUrlFragment(const char *, size_t);
struct EscapeResult EscapeUrlPathSegment(const char *, size_t);
struct EscapeResult EscapeUser(const char *, size_t);
struct EscapeResult EscapePass(const char *, size_t);
struct EscapeResult EscapeIp(const char *, size_t);
struct EscapeResult EscapeHost(const char *, size_t);
struct EscapeResult EscapePath(const char *, size_t);
struct EscapeResult EscapeParam(const char *, size_t);
struct EscapeResult EscapeFragment(const char *, size_t);
struct EscapeResult EscapeSegment(const char *, size_t);
struct EscapeResult EscapeJsStringLiteral(const char *, size_t);
COSMOPOLITAN_C_END_

View file

@ -1,7 +1,7 @@
/*-*- 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 2020 Justine Alexandra Roberts Tunney
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
@ -16,15 +16,13 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "net/http/uri.h"
#include "net/http/escape.h"
struct UriSlice uripath(const struct Uri *uri) {
if (uri->segs.i) {
return (struct UriSlice){
uri->segs.p[0].i,
(uri->segs.p[uri->segs.i - 1].n +
(uri->segs.p[uri->segs.i - 1].i - uri->segs.p[0].i))};
} else {
return (struct UriSlice){0, 0};
}
/**
* Escapes URL fragment.
*
* @param size if -1 implies strlen
*/
struct EscapeResult EscapeFragment(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeFragment);
}

28
net/http/escapehost.c Normal file
View file

@ -0,0 +1,28 @@
/*-*- 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 "net/http/escape.h"
/**
* Escapes URL host or registry name.
*
* @param size if -1 implies strlen
*/
struct EscapeResult EscapeHost(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeAuthority);
}

View file

@ -16,17 +16,21 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/str/str.h"
#include "libc/x/x.h"
#include "net/http/escape.h"
/**
* Escapes HTML entities.
*
* @param size if -1 implies strlen
*/
struct EscapeResult EscapeHtml(const char *data, size_t size) {
int c;
char *p;
size_t i;
struct EscapeResult r;
if (size == -1) size = data ? strlen(data) : 0;
p = r.data = xmalloc(size * 6 + 1);
for (i = 0; i < size; ++i) {
switch ((c = data[i])) {

30
net/http/escapeip.c Normal file
View file

@ -0,0 +1,30 @@
/*-*- 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 "net/http/escape.h"
/**
* Escapes URL IP-literal.
*
* This is the same as EscapeHost except colon is permitted.
*
* @param size if -1 implies strlen
*/
struct EscapeResult EscapeIp(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeAuthority);
}

View file

@ -1,7 +1,7 @@
/*-*- 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 2020 Justine Alexandra Roberts Tunney
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
@ -16,14 +16,13 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
#include "net/http/uri.h"
#include "net/http/escape.h"
TEST(urischeme, test) {
EXPECT_EQ(kUriSchemeSip, urischeme((struct UriSlice){0, 3}, "sips"));
}
BENCH(urischeme, bench) {
EZBENCH(donothing, urischeme((struct UriSlice){0, 3}, "sips"));
/**
* Escapes query/form name/parameter.
*
* @param size if -1 implies strlen
*/
struct EscapeResult EscapeParam(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeParam);
}

28
net/http/escapepass.c Normal file
View file

@ -0,0 +1,28 @@
/*-*- 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 "net/http/escape.h"
/**
* Escapes URL password.
*
* @param size if -1 implies strlen
*/
struct EscapeResult EscapePass(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeAuthority);
}

30
net/http/escapepath.c Normal file
View file

@ -0,0 +1,30 @@
/*-*- 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 "net/http/escape.h"
/**
* Escapes URL path.
*
* This is the same as EscapePathSegment() except slash is allowed.
*
* @param size if -1 implies strlen
*/
struct EscapeResult EscapePath(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapePath);
}

31
net/http/escapesegment.c Normal file
View file

@ -0,0 +1,31 @@
/*-*- 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 "net/http/escape.h"
/**
* Escapes URL path segment.
*
* Please note this will URI encode the slash character. That's because
* segments are the labels between the slashes in a path.
*
* @param size if -1 implies strlen
*/
struct EscapeResult EscapeSegment(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeSegment);
}

View file

@ -18,6 +18,7 @@
*/
#include "libc/x/x.h"
#include "net/http/escape.h"
#include "net/http/url.h"
/**
* Escapes URL component using generic table.
@ -26,29 +27,22 @@
* Always using UTF-8 is a good idea.
*
* @param size if -1 implies strlen
* @see EscapeUrlParam
* @see EscapeUrlFragment
* @see EscapeUrlPathSegment
* @see kEscapeAuthority
* @see kEscapeIpLiteral
* @see kEscapePath
* @see kEscapePathSegment
* @see kEscapeParam
* @see kEscapeFragment
*/
struct EscapeResult EscapeUrl(const char *data, size_t size,
const char xlat[hasatleast 256]) {
int c;
char *p;
size_t i;
struct UrlView v;
struct EscapeResult r;
if (size == -1) size = data ? strlen(data) : 0;
p = r.data = xmalloc(size * 6 + 1);
for (i = 0; i < size; ++i) {
if (!xlat[(c = data[i] & 0xff)]) {
*p++ = c;
} else {
p[0] = '%';
p[1] = "0123456789ABCDEF"[(c & 0xF0) >> 4];
p[2] = "0123456789ABCDEF"[(c & 0x0F) >> 0];
p += 3;
}
}
r.size = p - r.data;
v.p = data;
v.n = size;
r.data = xmalloc(size * 6 + 1);
r.size = EscapeUrlView(r.data, &v, xlat) - r.data;
r.data = xrealloc(r.data, r.size + 1);
r.data[r.size] = '\0';
return r;

View file

@ -1,7 +1,7 @@
/*-*- 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 2020 Justine Alexandra Roberts Tunney
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
@ -16,20 +16,23 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "net/http/geturischeme.inc"
#include "net/http/uri.h"
#include "net/http/url.h"
/**
* Returns nonzero numeric code for resource paradigms we like.
*
* Lookups are case-insensitive and performed using a hash table that's
* literally perfect.
* Escapes URL component using generic table w/ stpcpy() api.
*/
enum UriScheme urischeme(struct UriSlice scheme, const char *str) {
const struct UriSchemeSlot *slot;
if ((slot = in_word_set(str + scheme.i, scheme.n))) {
return slot->code;
} else {
return 0;
char *EscapeUrlView(char *p, struct UrlView *v, const char T[256]) {
int c;
size_t i;
for (i = 0; i < v->n; ++i) {
if (!T[(c = v->p[i] & 0xFF)]) {
*p++ = c;
} else {
p[0] = '%';
p[1] = "0123456789ABCDEF"[(c & 0xF0) >> 4];
p[2] = "0123456789ABCDEF"[(c & 0x0F) >> 0];
p += 3;
}
}
return p;
}

28
net/http/escapeuser.c Normal file
View file

@ -0,0 +1,28 @@
/*-*- 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 "net/http/escape.h"
/**
* Escapes URL user name.
*
* @param size if -1 implies strlen
*/
struct EscapeResult EscapeUser(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeAuthority);
}

View file

@ -20,14 +20,14 @@ Allow, kHttpAllow
Authorization, kHttpAuthorization
Cache-Control, kHttpCacheControl
Chunked, kHttpChunked
Close, kHttpClose
Link, kHttpLink
Connection, kHttpConnection
Content-Base, kHttpContentBase
Content-Encoding, kHttpContentEncoding
Content-Language, kHttpContentLanguage
Content-Length, kHttpContentLength
Content-Location, kHttpContentLocation
Content-Md5, kHttpContentMd5
Content-MD5, kHttpContentMd5
Content-Range, kHttpContentRange
Content-Type, kHttpContentType
Date, kHttpDate
@ -60,7 +60,6 @@ Vary, kHttpVary
Warning, kHttpWarning
WWW-Authenticate, kHttpWwwAuthenticate
Last-Modified, kHttpLastModified
Cookie, kHttpCookie
Trailer, kHttpTrailer
TE, kHttpTe
DNT, kHttpDnt
@ -69,3 +68,4 @@ Content-Disposition, kHttpContentDisposition
Content-Description, kHttpContentDescription
Origin, kHttpOrigin
Upgrade-Insecure-Requests, kHttpUpgradeInsecureRequests
URI, kHttpUri

View file

@ -1,7 +1,6 @@
/* ANSI-C code produced by gperf version 3.1 */
/* Command-line: gperf gethttpheader.gperf */
/* Computed positions: -k'3-4,10' */
/* clang-format off */
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
&& ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
@ -108,13 +107,13 @@ hash (register const char *str, register size_t len)
98, 98, 98, 98, 98, 98, 98, 98, 98, 98,
98, 98, 98, 98, 98, 30, 98, 98, 98, 98,
98, 98, 98, 98, 98, 98, 98, 98, 98, 98,
98, 98, 98, 98, 98, 5, 5, 30, 55, 0,
35, 30, 0, 35, 98, 40, 0, 30, 0, 20,
55, 98, 0, 5, 10, 5, 0, 5, 20, 30,
98, 98, 98, 98, 98, 98, 98, 5, 5, 30,
55, 0, 35, 30, 0, 35, 98, 40, 0, 30,
0, 20, 55, 98, 0, 5, 10, 5, 0, 5,
20, 30, 98, 98, 98, 98, 98, 98, 98, 98,
98, 98, 98, 98, 98, 5, 0, 30, 55, 0,
0, 10, 5, 30, 98, 0, 0, 15, 0, 15,
51, 98, 30, 55, 10, 5, 35, 20, 25, 10,
98, 98, 98, 98, 98, 98, 98, 5, 0, 30,
55, 0, 0, 10, 5, 30, 98, 0, 0, 15,
0, 15, 51, 98, 30, 55, 10, 5, 35, 20,
25, 10, 98, 98, 98, 98, 98, 98, 98, 98,
98, 98, 98, 98, 98, 98, 98, 98, 98, 98,
98, 98, 98, 98, 98, 98, 98, 98, 98, 98,
98, 98, 98, 98, 98, 98, 98, 98, 98, 98,
@ -153,144 +152,146 @@ hash (register const char *str, register size_t len)
return hval;
}
static const struct HttpHeaderSlot *
const struct HttpHeaderSlot *
LookupHttpHeader (register const char *str, register size_t len)
{
static const struct HttpHeaderSlot wordlist[] =
{
{""}, {""},
#line 65 "gethttpheader.gperf"
#line 64 "gethttpheader.gperf"
{"TE", kHttpTe},
#line 18 "gethttpheader.gperf"
{"Age", kHttpAge},
{""}, {""},
#line 58 "gethttpheader.gperf"
{"Server", kHttpServer},
#line 60 "gethttpheader.gperf"
{"Warning", kHttpWarning},
#line 23 "gethttpheader.gperf"
{"Link", kHttpLink},
{""},
#line 56 "gethttpheader.gperf"
{"Public", kHttpPublic},
#line 50 "gethttpheader.gperf"
{"Referer", kHttpReferer},
#line 54 "gethttpheader.gperf"
{"Via", kHttpVia},
{""},
#line 24 "gethttpheader.gperf"
{"Connection", kHttpConnection},
#line 56 "gethttpheader.gperf"
{"Public", kHttpPublic},
{""},
#line 22 "gethttpheader.gperf"
{"Chunked", kHttpChunked},
#line 66 "gethttpheader.gperf"
#line 65 "gethttpheader.gperf"
{"DNT", kHttpDnt},
#line 33 "gethttpheader.gperf"
{"Date", kHttpDate},
{""}, {""}, {""}, {""},
#line 37 "gethttpheader.gperf"
{"Host", kHttpHost},
#line 53 "gethttpheader.gperf"
{"User-Agent", kHttpUserAgent},
#line 57 "gethttpheader.gperf"
{"Retry-After", kHttpRetryAfter},
#line 49 "gethttpheader.gperf"
{"Range", kHttpRange},
{""}, {""}, {""},
#line 34 "gethttpheader.gperf"
{"ETag", kHttpEtag},
#line 19 "gethttpheader.gperf"
{"Allow", kHttpAllow},
#line 45 "gethttpheader.gperf"
{"Pragma", kHttpPragma},
#line 51 "gethttpheader.gperf"
{"Transfer-Encoding", kHttpTransferEncoding},
{""},
#line 28 "gethttpheader.gperf"
{"Content-Length", kHttpContentLength},
#line 19 "gethttpheader.gperf"
{"Allow", kHttpAllow},
{""},
#line 26 "gethttpheader.gperf"
{"Content-Encoding", kHttpContentEncoding},
#line 25 "gethttpheader.gperf"
{"Content-Base", kHttpContentBase},
#line 31 "gethttpheader.gperf"
{"Content-Range", kHttpContentRange},
#line 69 "gethttpheader.gperf"
#line 68 "gethttpheader.gperf"
{"Content-Description", kHttpContentDescription},
#line 23 "gethttpheader.gperf"
{"Close", kHttpClose},
{""},
#line 27 "gethttpheader.gperf"
{"Content-Language", kHttpContentLanguage},
#line 32 "gethttpheader.gperf"
{"Content-Type", kHttpContentType},
#line 71 "gethttpheader.gperf"
{"URI", kHttpUri},
#line 36 "gethttpheader.gperf"
{"From", kHttpFrom},
{""},
#line 20 "gethttpheader.gperf"
{"Authorization", kHttpAuthorization},
#line 59 "gethttpheader.gperf"
{"Vary", kHttpVary},
#line 49 "gethttpheader.gperf"
{"Range", kHttpRange},
#line 14 "gethttpheader.gperf"
{"Accept", kHttpAccept},
#line 52 "gethttpheader.gperf"
{"Upgrade", kHttpUpgrade},
#line 41 "gethttpheader.gperf"
{"If-Range", kHttpIfRange},
#line 34 "gethttpheader.gperf"
{"ETag", kHttpEtag},
{""},
#line 45 "gethttpheader.gperf"
{"Pragma", kHttpPragma},
#line 50 "gethttpheader.gperf"
{"Referer", kHttpReferer},
#line 55 "gethttpheader.gperf"
{"Location", kHttpLocation},
{""},
#line 17 "gethttpheader.gperf"
{"Accept-Language", kHttpAcceptLanguage},
#line 60 "gethttpheader.gperf"
{"Warning", kHttpWarning},
#line 20 "gethttpheader.gperf"
{"Authorization", kHttpAuthorization},
{""}, {""},
#line 29 "gethttpheader.gperf"
{"Content-Location", kHttpContentLocation},
#line 64 "gethttpheader.gperf"
#line 63 "gethttpheader.gperf"
{"Trailer", kHttpTrailer},
#line 55 "gethttpheader.gperf"
{"Location", kHttpLocation},
#line 59 "gethttpheader.gperf"
{"Vary", kHttpVary},
#line 17 "gethttpheader.gperf"
{"Accept-Language", kHttpAcceptLanguage},
#line 69 "gethttpheader.gperf"
{"Origin", kHttpOrigin},
#line 52 "gethttpheader.gperf"
{"Upgrade", kHttpUpgrade},
#line 40 "gethttpheader.gperf"
{"If-None-Match", kHttpIfNoneMatch},
#line 15 "gethttpheader.gperf"
{"Accept-Charset", kHttpAcceptCharset},
#line 53 "gethttpheader.gperf"
{"User-Agent", kHttpUserAgent},
#line 57 "gethttpheader.gperf"
{"Retry-After", kHttpRetryAfter},
{""},
#line 38 "gethttpheader.gperf"
{"If-Match", kHttpIfMatch},
#line 42 "gethttpheader.gperf"
{"If-Unmodified-Since", kHttpIfUnmodifiedSince},
{""},
#line 61 "gethttpheader.gperf"
{"WWW-Authenticate", kHttpWwwAuthenticate},
#line 32 "gethttpheader.gperf"
{"Content-Type", kHttpContentType},
#line 21 "gethttpheader.gperf"
{"Cache-Control", kHttpCacheControl},
#line 36 "gethttpheader.gperf"
{"From", kHttpFrom},
#line 71 "gethttpheader.gperf"
{"Upgrade-Insecure-Requests", kHttpUpgradeInsecureRequests},
#line 48 "gethttpheader.gperf"
{"Proxy-Connection", kHttpProxyConnection},
#line 66 "gethttpheader.gperf"
{"Expect", kHttpExpect},
#line 21 "gethttpheader.gperf"
{"Cache-Control", kHttpCacheControl},
#line 67 "gethttpheader.gperf"
{"Content-Disposition", kHttpContentDisposition},
{""},
#line 43 "gethttpheader.gperf"
{"Keep-Alive", kHttpKeepAlive},
#line 39 "gethttpheader.gperf"
{"If-Modified-Since", kHttpIfModifiedSince},
#line 46 "gethttpheader.gperf"
{"Proxy-Authenticate", kHttpProxyAuthenticate},
#line 47 "gethttpheader.gperf"
{"Proxy-Authorization", kHttpProxyAuthorization},
{""},
#line 67 "gethttpheader.gperf"
{"Expect", kHttpExpect},
#line 44 "gethttpheader.gperf"
{"Max-Forwards", kHttpMaxForwards},
#line 62 "gethttpheader.gperf"
{"Last-Modified", kHttpLastModified},
#line 68 "gethttpheader.gperf"
{"Content-Disposition", kHttpContentDisposition},
#line 43 "gethttpheader.gperf"
{"Keep-Alive", kHttpKeepAlive},
#line 63 "gethttpheader.gperf"
{"Cookie", kHttpCookie},
{""},
#line 38 "gethttpheader.gperf"
{"If-Match", kHttpIfMatch},
{""}, {""},
#line 70 "gethttpheader.gperf"
{"Origin", kHttpOrigin},
{"Upgrade-Insecure-Requests", kHttpUpgradeInsecureRequests},
#line 61 "gethttpheader.gperf"
{"WWW-Authenticate", kHttpWwwAuthenticate},
{""},
#line 41 "gethttpheader.gperf"
{"If-Range", kHttpIfRange},
#line 37 "gethttpheader.gperf"
{"Host", kHttpHost},
{""},
#line 58 "gethttpheader.gperf"
{"Server", kHttpServer},
{""}, {""}, {""},
#line 16 "gethttpheader.gperf"
{"Accept-Encoding", kHttpAcceptEncoding},
#line 30 "gethttpheader.gperf"
{"Content-Md5", kHttpContentMd5},
#line 39 "gethttpheader.gperf"
{"If-Modified-Since", kHttpIfModifiedSince},
{"Content-MD5", kHttpContentMd5},
{""},
#line 62 "gethttpheader.gperf"
{"Last-Modified", kHttpLastModified},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""},
#line 42 "gethttpheader.gperf"
{"If-Unmodified-Since", kHttpIfUnmodifiedSince},
{""}, {""}, {""}, {""}, {""}, {""}, {""},
#line 35 "gethttpheader.gperf"
{"Expires", kHttpExpires}
{"Expires", kHttpExpires},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
#line 44 "gethttpheader.gperf"
{"Max-Forwards", kHttpMaxForwards}
};
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)

View file

@ -38,8 +38,8 @@ const char *GetHttpHeaderName(int h) {
return "Cache-Control";
case kHttpChunked:
return "Chunked";
case kHttpClose:
return "Close";
case kHttpLink:
return "Link";
case kHttpConnection:
return "Connection";
case kHttpContentBase:
@ -53,7 +53,7 @@ const char *GetHttpHeaderName(int h) {
case kHttpContentLocation:
return "Content-Location";
case kHttpContentMd5:
return "Content-Md5";
return "Content-MD5";
case kHttpContentRange:
return "Content-Range";
case kHttpContentType:
@ -118,8 +118,6 @@ const char *GetHttpHeaderName(int h) {
return "WWW-Authenticate";
case kHttpLastModified:
return "Last-Modified";
case kHttpCookie:
return "Cookie";
case kHttpTrailer:
return "Trailer";
case kHttpTe:
@ -136,6 +134,8 @@ const char *GetHttpHeaderName(int h) {
return "Origin";
case kHttpUpgradeInsecureRequests:
return "Upgrade-Insecure-Requests";
case kHttpUri:
return "URI";
default:
return NULL;
}

View file

@ -20,13 +20,13 @@
#include "net/http/http.h"
/**
* Returns small number for HTTP method, or -1 if not found.
* Returns small number for HTTP method, or 0 if not found.
*/
int GetHttpMethod(const char *str, size_t len) {
const struct HttpMethodSlot *slot;
if ((slot = LookupHttpMethod(str, len))) {
return slot->code;
} else {
return -1;
return 0;
}
}

View file

@ -1,7 +1,7 @@
/*-*- 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 2020 Justine Alexandra Roberts Tunney
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
@ -17,54 +17,38 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/sysv/errfuns.h"
#include "net/http/uri.h"
#include "libc/str/str.h"
#include "net/http/http.h"
/* TODO(jart): Rewrite in C */
#define static
/* clang-format off */
%% machine uricspn;
%% write data;
/* clang-format on */
int uricspn(const char *data, size_t size) {
int uricspn$avx(const char *, size_t) hidden;
const char *p, *pe;
int cs;
assert(data || !size);
assert(size <= 0x7ffff000);
assert(size <= 0x7ffff000);
if (X86_HAVE(AVX)) {
return uricspn$avx(data, size);
}
p = data;
pe = data + size;
/* clang-format off */
%%{
mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")";
reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ",";
unreserved = alnum | mark;
uric = reserved | unreserved | "%";
machina := uric*;
}%%
%% write init;
cs = uricspn_en_machina;
%% write exec;
/* clang-format on */
if (cs >= uricspn_first_final) {
return p - data;
} else {
return einval();
/**
* Returns true if standard header has substring.
*
* @param m is message parsed by ParseHttpRequest
* @param b is buffer that ParseHttpRequest parsed
* @param h is known header, e.g. kHttpAcceptEncoding
* @param s should not contain comma
* @param n is byte length of s where -1 implies strlen
* @return true if substring present
*/
bool HeaderHasSubstring(struct HttpRequest *m, const char *b, int h,
const char *s, size_t n) {
size_t i;
assert(0 <= h && h < kHttpHeadersMax);
if (n == -1) n = s ? strlen(s) : 0;
if (m->headers[h].a) {
if (memmem(b + m->headers[h].a, m->headers[h].b - m->headers[h].a, s, n)) {
return true;
}
if (kHttpRepeatable[h]) {
for (i = 0; i < m->xheaders.n; ++i) {
if (GetHttpHeader(b + m->xheaders.p[i].k.a,
m->xheaders.p[i].k.b - m->xheaders.p[i].k.a) == h &&
memmem(b + m->xheaders.p[i].v.a,
m->xheaders.p[i].v.b - m->xheaders.p[i].v.a, s, n)) {
return true;
}
}
}
}
return false;
}

View file

@ -3,23 +3,23 @@
#include "libc/alg/alg.h"
#include "libc/time/struct/tm.h"
#define kHttpGet 0
#define kHttpHead 1
#define kHttpPost 2
#define kHttpPut 3
#define kHttpDelete 4
#define kHttpOptions 5
#define kHttpConnect 6
#define kHttpTrace 7
#define kHttpCopy 8
#define kHttpLock 9
#define kHttpMerge 10
#define kHttpMkcol 11
#define kHttpMove 12
#define kHttpNotify 13
#define kHttpPatch 14
#define kHttpReport 15
#define kHttpUnlock 16
#define kHttpGet 1
#define kHttpHead 2
#define kHttpPost 3
#define kHttpPut 4
#define kHttpDelete 5
#define kHttpOptions 6
#define kHttpConnect 7
#define kHttpTrace 8
#define kHttpCopy 9
#define kHttpLock 10
#define kHttpMerge 11
#define kHttpMkcol 12
#define kHttpMove 13
#define kHttpNotify 14
#define kHttpPatch 15
#define kHttpReport 16
#define kHttpUnlock 17
#define kHttpAccept 0
#define kHttpAcceptCharset 1
@ -30,7 +30,7 @@
#define kHttpAuthorization 6
#define kHttpCacheControl 7
#define kHttpChunked 8
#define kHttpClose 9
#define kHttpLink 9
#define kHttpConnection 10
#define kHttpContentBase 11
#define kHttpContentEncoding 12
@ -70,15 +70,15 @@
#define kHttpWarning 46
#define kHttpWwwAuthenticate 47
#define kHttpLastModified 48
#define kHttpCookie 49
#define kHttpTrailer 50
#define kHttpTe 51
#define kHttpDnt 52
#define kHttpExpect 53
#define kHttpContentDisposition 54
#define kHttpContentDescription 55
#define kHttpOrigin 56
#define kHttpUpgradeInsecureRequests 57
#define kHttpTrailer 49
#define kHttpTe 50
#define kHttpDnt 51
#define kHttpExpect 52
#define kHttpContentDisposition 53
#define kHttpContentDescription 54
#define kHttpOrigin 55
#define kHttpUpgradeInsecureRequests 56
#define kHttpUri 57
#define kHttpHeadersMax 58
#if !(__ASSEMBLER__ + __LINKER__ + 0)
@ -89,14 +89,17 @@ struct HttpRequestSlice {
};
struct HttpRequest {
int i, t, a, method;
int i, a;
unsigned char t;
unsigned char method;
unsigned char version;
struct HttpRequestSlice k;
struct HttpRequestSlice uri;
struct HttpRequestSlice version;
struct HttpRequestSlice scratch;
struct HttpRequestSlice headers[kHttpHeadersMax];
struct HttpRequestSlice xmethod;
struct HttpRequestHeaders {
size_t n;
unsigned n;
struct HttpRequestHeader {
struct HttpRequestSlice k;
struct HttpRequestSlice v;
@ -104,19 +107,22 @@ struct HttpRequest {
} xheaders;
};
extern const char kHttpMethod[17][8];
extern const char kHttpToken[256];
extern const char kHttpMethod[18][8];
extern const bool kHttpRepeatable[kHttpHeadersMax];
int GetHttpHeader(const char *, size_t);
int GetHttpMethod(const char *, size_t);
void InitHttpRequest(struct HttpRequest *);
void DestroyHttpRequest(struct HttpRequest *);
int ParseHttpRequest(struct HttpRequest *, const char *, size_t);
bool HeaderHasSubstring(struct HttpRequest *, const char *, int, const char *,
size_t);
int NegotiateHttpRequest(int, const char *, uint32_t *, char *, uint32_t *,
uint32_t *, bool, long double);
ssize_t ParseContentLength(const char *, size_t);
int64_t ParseContentLength(const char *, size_t);
char *FormatHttpDateTime(char[hasatleast 30], struct tm *);
bool ParseHttpRange(const char *, size_t, long, long *, long *);
unsigned ParseHttpVersion(const char *, size_t);
int64_t ParseHttpDateTime(const char *, size_t);
const char *GetHttpReason(int);
const char *GetHttpHeaderName(int);
@ -126,7 +132,10 @@ char *EncodeHttpHeaderValue(const char *, size_t, size_t *);
char *VisualizeControlCodes(const char *, size_t, size_t *);
char *IndentLines(const char *, size_t, size_t *, size_t);
bool IsAcceptablePath(const char *, size_t);
bool IsAcceptableHostPort(const char *, size_t);
bool IsAcceptableHost(const char *, size_t);
bool IsAcceptablePort(const char *, size_t);
int64_t ParseIp(const char *, size_t);
bool IsMimeType(const char *, size_t, const char *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -20,87 +20,62 @@
#include "net/http/http.h"
/**
* Returns true if HOST[:PORT] seems legit.
* Returns true if host seems legit.
*
* This parser is permissive and imposes the subset of restrictions
* that'll make things easier for the caller. For example, only one
* colon is allowed to appear, which makes memchr() so much easier.
* This function may be called after ParseUrl() or ParseHost() has
* already handled things like percent encoding. There's currently
* no support for IPv6 and IPv7.
*
* Here's examples of permitted inputs:
*
* - ""
* - 1.2.3.4
* - 1.2.3.4.arpa
* - 1.2.3.4:8080
* - localservice
* - hello.example
* - _hello.example
* - -hello.example
* - hi-there.example
* - hello.example:443
*
* Here's some examples of forbidden inputs:
*
* - :443
* - ::1
* - 1.2.3
* - 1.2.3.4.5
* - [::1]:8080
* - .hi.example
* - hi..example
* - hi.example::80
* - hi.example:-80
* - hi.example:65536
*
* @param n if -1 implies strlen
*/
bool IsAcceptableHostPort(const char *s, size_t n) {
bool IsAcceptableHost(const char *s, size_t n) {
size_t i;
bool isip;
int c, t, p, b, j;
int c, b, j;
if (n == -1) n = s ? strlen(s) : 0;
if (!n) return false;
for (isip = true, b = j = p = t = i = 0; i < n; ++i) {
if (!n) return true;
for (isip = true, b = j = i = 0; i < n; ++i) {
c = s[i] & 255;
if (!t) {
if (c == ':') {
if (!i || s[i - 1] == '.') {
return false;
} else {
t = 1;
}
} else if (c == '.' && (!i || s[i - 1] == '.')) {
return false;
} else if (!(isalnum(c) || c == '-' || c == '_' || c == '.')) {
return false;
}
if (isip) {
if (isdigit(c)) {
b *= 10;
b += c - '0';
if (b > 255) {
return false;
}
} else if (c == '.') {
b = 0;
++j;
} else {
isip = false;
}
}
} else {
if (c == ':') {
return false;
} else if ('0' <= c && c <= '9') {
p *= 10;
p += c - '0';
if (p > 65535) {
if (c == '.' && (!i || s[i - 1] == '.')) {
return false;
} else if (!(isalnum(c) || c == '-' || c == '_' || c == '.')) {
return false;
}
if (isip) {
if (isdigit(c)) {
b *= 10;
b += c - '0';
if (b > 255) {
return false;
}
} else if (c == '.') {
b = 0;
++j;
} else {
return false;
isip = false;
}
}
}
if (isip && j != 3) return false;
if (!t && s[i - 1] == '.') return false;
if (i && s[i - 1] == '.') return false;
return true;
}

View file

@ -21,7 +21,7 @@
#include "net/http/http.h"
/**
* Returns true if request path seems legit.
* Returns true if path seems legit.
*
* 1. The substring "//" is disallowed.
* 2. We won't serve hidden files (segment starts with '.').

View file

@ -1,7 +1,7 @@
/*-*- 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 2020 Justine Alexandra Roberts Tunney
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
@ -16,30 +16,41 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/str/str.h"
#include "net/http/uri.h"
#include "net/http/http.h"
/* TODO(jart): Unescape */
char *urislice2cstr(char *buf, size_t size, struct UriSlice slice,
const char *uristr, const char *defaultval) {
size_t n;
const char *p;
if (size) {
if (slice.n) {
p = uristr + slice.i;
n = slice.n;
} else if (defaultval) {
p = defaultval;
n = strlen(defaultval);
/**
* Returns true if port seems legit.
*
* Here's examples of permitted inputs:
*
* - ""
* - 0
* - 65535
*
* Here's some examples of forbidden inputs:
*
* - -1
* - 65536
* - https
*
* @param n if -1 implies strlen
*/
bool IsAcceptablePort(const char *s, size_t n) {
int p, c;
size_t i;
if (n == -1) n = s ? strlen(s) : 0;
for (p = i = 0; i < n; ++i) {
c = s[i] & 255;
if ('0' <= c && c <= '9') {
p *= 10;
p += c - '0';
if (p > 65535) {
return false;
}
} else {
p = NULL;
n = 0;
return false;
}
n = MIN(n, size - 1);
memcpy(buf, p, n);
buf[n] = '\0';
}
return buf;
return true;
}

View file

@ -1,7 +1,7 @@
/*-*- 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 2020 Justine Alexandra Roberts Tunney
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
@ -16,18 +16,18 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/str/str.h"
#include "net/http/http.h"
unsigned ParseHttpVersion(const char *p, size_t n) {
unsigned x;
if (!n) return 9;
if (n >= 8 && READ32LE(p) == ('H' | 'T' << 8 | 'T' << 16 | 'P' << 24)) {
if (READ32LE(p + 4) == ('/' | '1' << 8 | '.' << 16 | '1' << 24)) {
return 101;
} else if (READ32LE(p + 4) == ('/' | '1' << 8 | '.' << 16 | '0' << 24)) {
return 100;
}
/**
* Returns true if content-type 𝑡 has mime-type 𝑠.
*/
bool IsMimeType(const char *t, size_t n, const char *s) {
size_t i;
if (n == -1) n = t ? strlen(t) : 0;
for (i = 0; i < n; ++i) {
if (!s[i]) return !kHttpToken[t[i] & 0xFF];
if (kToLower[s[i] & 0xFF] != kToLower[t[i] & 0xFF]) return false;
}
return -1;
return !s[i];
}

View file

@ -19,28 +19,6 @@
#include "libc/str/str.h"
#include "net/http/http.h"
// http/1.1 token dispatch
// 0 is CTLs, SP, ()<>@,;:\"/[]?={}
// 1 is what remains of ascii
static const char kHttpToken[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 0x20
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, // 0x30
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 0x50
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 0x70
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xc0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xd0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xe0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xf0
};
/**
* Returns true if string is ASCII without delimiters.
*

View file

@ -0,0 +1,46 @@
/*-*- 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 "net/http/escape.h"
// [user[:pass]@]host[:port]|reg_name dispatch
// - 0 is -_.!~*'();&=+$,0-9A-Za-z
// - 1 is everything else which needs uppercase hex %XX
// note that '& can break html
// note that '() can break css urls
// note that unicode can still be wild
// note that IPv6+ can't be encoded this way
// note that user can look deceptively like host
const char kEscapeAuthority[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10
1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // 0x20
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, // 0x30
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, // 0x50
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, // 0x70
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xe0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xf0
};

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/x/x.h"
#include "net/http/escape.h"
// url fragment dispatch
@ -25,7 +24,7 @@
// note that '& can break html
// note that '() can break css urls
// note that unicode can still be wild
static const char kEscapeUrlFragment[256] = {
const char kEscapeFragment[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10
1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20
@ -43,12 +42,3 @@ static const char kEscapeUrlFragment[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xe0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xf0
};
/**
* Escapes URL fragment.
*
* @param size if -1 implies strlen
*/
struct EscapeResult EscapeUrlFragment(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeUrlFragment);
}

44
net/http/kescapeip.c Normal file
View file

@ -0,0 +1,44 @@
/*-*- 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 "net/http/escape.h"
// Square Bracket IP-literal dispatch
// - 0 is -_.!~*'();&=+$,0-9A-Za-z:
// - 1 shouldn't be there; exceptions exist; escape it
// same as kEscapeAuthority but with colon
// note that '& can break html
// note that '() can break css urls
const char kEscapeIp[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10
1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // 0x20
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, // 0x30
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, // 0x50
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, // 0x70
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xe0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xf0
};

View file

@ -16,14 +16,13 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/x/x.h"
#include "net/http/escape.h"
// url query/form name/parameter dispatch
// - 0 is -.*_0-9A-Za-z
// - 1 is everything else which needs uppercase hex %XX
// note that unicode can still be wild
static const char kEscapeUrlParam[256] = {
const char kEscapeParam[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, // 0x20
@ -41,12 +40,3 @@ static const char kEscapeUrlParam[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xe0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xf0
};
/**
* Escapes query/form name/parameter.
*
* @param size if -1 implies strlen
*/
struct EscapeResult EscapeUrlParam(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeUrlParam);
}

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/x/x.h"
#include "net/http/escape.h"
// url path dispatch
@ -25,7 +24,7 @@
// note that '& can break html
// note that '() can break css urls
// note that unicode can still be wild
static const char kEscapeUrlPath[256] = {
const char kEscapePath[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10
1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20
@ -43,14 +42,3 @@ static const char kEscapeUrlPath[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xe0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xf0
};
/**
* Escapes URL path.
*
* This is the same as EscapeUrlPathSegment() except slash is allowed.
*
* @param size if -1 implies strlen
*/
struct EscapeResult EscapeUrlPath(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeUrlPath);
}

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/x/x.h"
#include "net/http/escape.h"
// url path segment dispatch
@ -25,7 +24,7 @@
// note that '& can break html
// note that '() can break css urls
// note that unicode can still be wild
static const char kEscapeUrlPathSegment[256] = {
const char kEscapeSegment[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10
1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // 0x20
@ -43,15 +42,3 @@ static const char kEscapeUrlPathSegment[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xe0
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xf0
};
/**
* Escapes URL path segment.
*
* Please note this will URI encode the slash character. That's because
* segments are the labels between the slashes in a path.
*
* @param size if -1 implies strlen
*/
struct EscapeResult EscapeUrlPathSegment(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeUrlPathSegment);
}

38
net/http/khextoint.c Normal file
View file

@ -0,0 +1,38 @@
/*-*- 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 "net/http/escape.h"
const signed char kHexToInt[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x20
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 0x30
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x40
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x50
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x60
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x70
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x80
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x90
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xa0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xb0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xc0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xd0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xe0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xf0
};

View file

@ -18,7 +18,8 @@
*/
#include "net/http/http.h"
const char kHttpMethod[17][8] = {
const char kHttpMethod[18][8] = {
"WUT", //
"GET", //
"HEAD", //
"POST", //

View file

@ -0,0 +1,81 @@
/*-*- 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 "net/http/http.h"
/**
* Set of standard comma-separate HTTP headers that may span lines.
*
* These headers may specified on multiple lines, e.g.
*
* Allow: GET
* Allow: POST
*
* Is the same as:
*
* Allow: GET, POST
*
* Standard headers that aren't part of this set will be overwritten in
* the event that they're specified multiple times. For example,
*
* Content-Type: application/octet-stream
* Content-Type: text/plain; charset=utf-8
*
* Is the same as:
*
* Content-Type: text/plain; charset=utf-8
*
* This set exists to optimize header lookups and parsing. The existence
* of standard headers that aren't in this set is an O(1) operation. The
* repeatable headers in this list require an O(1) operation if they are
* not present, otherwise the extended headers list needs to be crawled.
*
* Please note non-standard headers exist, e.g. Cookie, that may span
* multiple lines, even though they're not comma-delimited. For those
* headers we simply don't add them to the perfect hash table.
*
* @note we choose to not recognize this grammar for kHttpConnection
* @note `grep '[A-Z][a-z]*".*":"' rfc2616`
* @note `grep ':.*#' rfc2616`
* @see RFC7230 § 4.2
*/
const bool kHttpRepeatable[kHttpHeadersMax] = {
[kHttpAcceptCharset] = true,
[kHttpAcceptEncoding] = true,
[kHttpAcceptLanguage] = true,
[kHttpAccept] = true,
[kHttpAllow] = true,
[kHttpCacheControl] = true,
[kHttpContentEncoding] = true,
[kHttpContentLanguage] = true,
[kHttpExpect] = true,
[kHttpIfMatch] = true,
[kHttpIfNoneMatch] = true,
[kHttpPragma] = true,
[kHttpProxyAuthenticate] = true,
[kHttpPublic] = true,
[kHttpTe] = true,
[kHttpTrailer] = true,
[kHttpTransferEncoding] = true,
[kHttpUpgrade] = true,
[kHttpUri] = true,
[kHttpVary] = true,
[kHttpVia] = true,
[kHttpWarning] = true,
[kHttpWwwAuthenticate] = true,
};

42
net/http/khttptoken.c Normal file
View file

@ -0,0 +1,42 @@
/*-*- 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 "net/http/http.h"
// http/1.1 token dispatch
// 0 is CTLs, SP, ()<>@,;:\"/[]?={} which are illegal
// 1 is everything else in ASCII which is legal
// note that &" can break html
const char kHttpToken[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 0x20
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, // 0x30
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 0x50
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 0x70
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xc0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xd0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xe0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xf0
};

View file

@ -19,18 +19,25 @@
#include "libc/str/str.h"
#include "net/http/http.h"
#define MAXIMUM (1024L * 1024L * 1024L * 1024L)
/**
* Parses Content-Length header.
*
* @param size is byte length and -1 implies strlen
* @return -1 on invalid or overflow, otherwise >=0 value
*/
ssize_t ParseContentLength(const char *s, size_t n) {
int i, r = 0;
if (!n) return 0;
for (i = 0; i < n; ++i) {
int64_t ParseContentLength(const char *s, size_t n) {
size_t i;
int64_t r;
if (n == -1) n = s ? strlen(s) : 0;
if (!n) return -1;
for (r = i = 0; i < n; ++i) {
if (s[i] == ',' && i > 0) break;
if (!isdigit(s[i])) return -1;
if (__builtin_mul_overflow(r, 10, &r)) return -1;
if (__builtin_add_overflow(r, s[i] - '0', &r)) return -1;
r *= 10;
r += s[i] - '0';
if (r >= MAXIMUM) return -1;
}
return r;
}

View file

@ -22,6 +22,15 @@
/**
* Parses HTTP Range request header.
*
* Here are some example values:
*
* Range: bytes=0- (everything)
* Range: bytes=0-499 (first 500 bytes)
* Range: bytes=500-999 (second 500 bytes)
* Range: bytes=-500 (final 500 bytes)
* Range: bytes=0-0,-1 (first and last and always)
* Range: bytes=500-600,601-999 (overlong but legal)
*/
bool ParseHttpRange(const char *p, size_t n, long resourcelength,
long *out_start, long *out_length) {
@ -67,10 +76,10 @@ bool ParseHttpRange(const char *p, size_t n, long resourcelength,
}
if (n) return false;
if (start < 0) return false;
if (length < 0) return false;
*out_start = start;
*out_length = length;
if (length < 1) return false;
if (__builtin_add_overflow(start, length, &ending)) return false;
if (ending > resourcelength) return false;
*out_start = start;
*out_length = length;
return true;
}

View file

@ -18,6 +18,7 @@
*/
#include "libc/alg/alg.h"
#include "libc/alg/arraylist.internal.h"
#include "libc/bits/bits.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
@ -58,11 +59,19 @@ void DestroyHttpRequest(struct HttpRequest *r) {
* messages. Line folding is forbidden. State persists across calls so
* that fragmented messages can be handled efficiently. A limitation on
* message size is imposed to make the header data structures smaller.
* All other things are permissive to the greatest extent possible.
* Further functions are provided for the interpretation, validation,
* and sanitization of various fields.
*
* kHttpRepeatable defines which standard header fields are O(1) and
* which ones may have comma entries spilled over into xheaders. For
* most headers it's sufficient to simply check the static slice. If
* r->headers[kHttpFoo].a is zero then the header is totally absent.
*
* This parser takes about 300 nanoseconds (900 cycles) to parse a 403
* byte Chrome HTTP request under MODE=rel on a Core i9 which is about
* gigabyte per second of throughput per core.
*
* @note we assume p points to a buffer that has >=SHRT_MAX bytes
* @see HTTP/1.1 RFC2616 RFC2068
* @see HTTP/1.0 RFC1945
*/
int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
int c, h, i;
@ -71,23 +80,21 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
c = p[r->i] & 0xff;
switch (r->t) {
case START:
if (c == '\r' || c == '\n') {
++r->a; /* RFC7230 § 3.5 */
break;
}
if (c == '\r' || c == '\n') break; /* RFC7230 § 3.5 */
if (!kHttpToken[c]) return ebadmsg();
r->t = METHOD;
/* fallthrough */
r->a = r->i;
break;
case METHOD:
for (;;) {
if (c == ' ') {
if ((r->method = GetHttpMethod(p + r->a, r->i - r->a)) != -1) {
r->uri.a = r->i + 1;
r->t = URI;
} else {
return ebadmsg();
}
r->method = GetHttpMethod(p + r->a, r->i - r->a);
r->xmethod.a = r->a;
r->xmethod.b = r->i;
r->a = r->i + 1;
r->t = URI;
break;
} else if (!('A' <= c && c <= 'Z')) {
} else if (!kHttpToken[c]) {
return ebadmsg();
}
if (++r->i == n) break;
@ -97,17 +104,19 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
case URI:
for (;;) {
if (c == ' ' || c == '\r' || c == '\n') {
if (r->i == r->uri.a) return ebadmsg();
if (r->i == r->a) return ebadmsg();
r->uri.a = r->a;
r->uri.b = r->i;
if (c == ' ') {
r->version.a = r->i + 1;
r->a = r->i + 1;
r->t = VERSION;
} else if (c == '\r') {
r->t = CR1;
} else {
r->t = LF1;
r->version = 9;
r->t = c == '\r' ? CR1 : LF1;
}
break;
} else if (c < 0x20 || (0x7F <= c && c < 0xA0)) {
return ebadmsg();
}
if (++r->i == n) break;
c = p[r->i] & 0xff;
@ -115,8 +124,14 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
break;
case VERSION:
if (c == '\r' || c == '\n') {
r->version.b = r->i;
r->t = c == '\r' ? CR1 : LF1;
if (r->i - r->a == 8 &&
(READ64BE(p + r->a) & 0xFFFFFFFFFF00FF00) == 0x485454502F002E00 &&
isdigit(p[r->a + 5]) && isdigit(p[r->a + 7])) {
r->version = (p[r->a + 5] - '0') * 10 + (p[r->a + 7] - '0');
r->t = c == '\r' ? CR1 : LF1;
} else {
return ebadmsg();
}
}
break;
case CR1:
@ -129,9 +144,7 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
break;
} else if (c == '\n') {
return ++r->i;
} else if (c == ':') {
return ebadmsg();
} else if (c == ' ' || c == '\t') {
} else if (!kHttpToken[c]) {
return ebadmsg(); /* RFC7230 § 3.2.4 */
}
r->k.a = r->i;
@ -143,6 +156,8 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
r->k.b = r->i;
r->t = HSEP;
break;
} else if (!kHttpToken[c]) {
return ebadmsg();
}
if (++r->i == n) break;
c = p[r->i] & 0xff;
@ -158,7 +173,8 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
if (c == '\r' || c == '\n') {
i = r->i;
while (i > r->a && (p[i - 1] == ' ' || p[i - 1] == '\t')) --i;
if ((h = GetHttpHeader(p + r->k.a, r->k.b - r->k.a)) != -1) {
if ((h = GetHttpHeader(p + r->k.a, r->k.b - r->k.a)) != -1 &&
(!r->headers[h].a || !kHttpRepeatable[h])) {
r->headers[h].a = r->a;
r->headers[h].b = i;
} else if ((x = realloc(
@ -172,6 +188,8 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
}
r->t = c == '\r' ? CR1 : LF1;
break;
} else if ((c < 0x20 && c != '\t') || (0x7F <= c && c < 0xA0)) {
return ebadmsg();
}
if (++r->i == n) break;
c = p[r->i] & 0xff;

52
net/http/parseip.c Normal file
View file

@ -0,0 +1,52 @@
/*-*- 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/str/str.h"
#include "net/http/http.h"
/**
* Parse IPv4 address.
*
* @param n if -1 implies strlen
* @return -1 on failure, otherwise 32-bit host-order unsigned integer
*/
int64_t ParseIp(const char *s, size_t n) {
size_t i;
uint32_t x;
int b, c, j;
if (n == -1) n = s ? strlen(s) : 0;
for (b = x = j = i = 0; i < n; ++i) {
c = s[i] & 255;
if (isdigit(c)) {
b *= 10;
b += c - '0';
if (b > 255) return -1;
} else if (c == '.') {
x <<= 8;
x |= b;
b = 0;
++j;
} else {
return -1;
}
}
x <<= 8;
x |= b;
if (j != 3) return -1;
return x;
}

View file

@ -20,6 +20,7 @@
#include "libc/limits.h"
#include "libc/str/str.h"
#include "libc/x/x.h"
#include "net/http/escape.h"
#include "net/http/url.h"
struct UrlParser {
@ -29,29 +30,11 @@ struct UrlParser {
int size;
bool isform;
bool islatin1;
bool isopaque;
char *p;
char *q;
};
static const signed char kHexToInt[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x20
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 0x30
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x40
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x50
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x60
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x70
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x80
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x90
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xa0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xb0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xc0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xd0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xe0
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xf0
};
static void EmitLatin1(struct UrlParser *u, int c) {
u->p[0] = 0300 | c >> 6;
u->p[1] = 0200 | c & 077;
@ -67,10 +50,10 @@ static void EmitKey(struct UrlParser *u, struct UrlParams *h) {
static void EmitVal(struct UrlParser *u, struct UrlParams *h, bool t) {
if (!t) {
if (u->p > u->q) {
if (u->p > u->q || u->c != '?') {
EmitKey(u, h);
h->p[h->n - 1].val.p = NULL;
h->p[h->n - 1].val.n = SIZE_MAX;
h->p[h->n - 1].val.n = 0;
}
} else {
h->p[h->n - 1].val.p = u->q;
@ -80,14 +63,14 @@ static void EmitVal(struct UrlParser *u, struct UrlParams *h, bool t) {
}
static void ParseEscape(struct UrlParser *u) {
int a, b;
int a, b, c = '%';
if (u->i + 2 <= u->size &&
((a = kHexToInt[u->data[u->i + 0] & 0xff]) != -1 &&
(b = kHexToInt[u->data[u->i + 1] & 0xff]) != -1)) {
u->c = a << 4 | b;
c = a << 4 | b;
u->i += 2;
}
*u->p++ = u->c;
*u->p++ = c;
}
static bool ParseScheme(struct UrlParser *u, struct Url *h) {
@ -98,18 +81,22 @@ static bool ParseScheme(struct UrlParser *u, struct Url *h) {
++u->i;
return true;
} else {
*u->p++ = u->c;
*u->p++ = '/';
return false;
}
} else if (u->c == ':') {
} else if (u->c == ':' && u->i > 1) {
h->scheme.p = u->q;
h->scheme.n = u->p - u->q;
u->q = u->p;
if (u->i + 2 <= u->size &&
(u->data[u->i + 1] == '/' && u->data[u->i + 1] == '/')) {
u->i += 2;
return true;
if (u->i < u->size && u->data[u->i] == '/') {
if (u->i + 1 < u->size && u->data[u->i + 1] == '/') {
u->i += 2;
return true;
} else {
return false;
}
} else {
u->isopaque = true;
return false;
}
} else if (u->c == '#' || u->c == '?') {
@ -119,10 +106,21 @@ static bool ParseScheme(struct UrlParser *u, struct Url *h) {
return false;
} else if (u->c == '%') {
ParseEscape(u);
return false;
} else if (u->c >= 0200 && u->islatin1) {
EmitLatin1(u, u->c);
return false;
} else {
*u->p++ = u->c;
if (u->i == 1) {
if (!isalpha(u->c)) {
return false;
}
} else {
if (!isalnum(u->c) && u->c != '+' && u->c != '-' && u->c != '.') {
return false;
}
}
}
}
return false;
@ -180,7 +178,9 @@ static void ParseAuthority(struct UrlParser *u, struct Url *h) {
static void ParsePath(struct UrlParser *u, struct UrlView *h) {
while (u->i < u->size) {
u->c = u->data[u->i++] & 0xff;
if (u->c == '#' || u->c == '?') {
if (u->c == '#') {
break;
} else if (u->c == '?' && !u->isopaque) {
break;
} else if (u->c == '%') {
ParseEscape(u);
@ -195,8 +195,9 @@ static void ParsePath(struct UrlParser *u, struct UrlView *h) {
u->q = u->p;
}
static void ParseKeyValues(struct UrlParser *u, struct UrlParams *h) {
static void ParseQuery(struct UrlParser *u, struct UrlParams *h) {
bool t = false;
if (!h->p) h->p = xmalloc(0);
while (u->i < u->size) {
u->c = u->data[u->i++] & 0xff;
if (u->c == '#') {
@ -210,10 +211,8 @@ static void ParseKeyValues(struct UrlParser *u, struct UrlParams *h) {
t = false;
} else if (u->c == '=') {
if (!t) {
if (u->p > u->q) {
EmitKey(u, h);
t = true;
}
EmitKey(u, h);
t = true;
} else {
*u->p++ = '=';
}
@ -251,13 +250,14 @@ static char *ParseUrlImpl(const char *data, size_t size, struct Url *h,
u.c = 0;
u.isform = false;
u.islatin1 = latin1;
u.isopaque = false;
u.data = data;
u.size = size;
memset(h, 0, sizeof(*h));
u.q = u.p = m = xmalloc(u.size * 2);
u.q = u.p = m = xmalloc(latin1 ? u.size * 2 : u.size);
if (ParseScheme(&u, h)) ParseAuthority(&u, h);
if (u.c != '#' && u.c != '?') ParsePath(&u, &h->path);
if (u.c == '?') ParseKeyValues(&u, &h->params);
if (u.c == '?') ParseQuery(&u, &h->params);
if (u.c == '#') ParseFragment(&u, &h->fragment);
return xrealloc(m, u.p - m);
}
@ -265,22 +265,33 @@ static char *ParseUrlImpl(const char *data, size_t size, struct Url *h,
/**
* Parses URL.
*
* This parser is charset agnostic. Percent encoded bytes are decoded
* for all fields. Returned values might contain things like NUL
* characters, spaces, control codes, and non-canonical encodings.
* Absent can be discerned from empty by checking if the pointer is set.
*
* There's no failure condition for this routine. This is a permissive
* parser that doesn't impose character restrictions beyond what is
* necessary for parsing. This doesn't normalize path segments like `.`
* or `..`. Use IsAcceptablePath() to check for those.
* parser. This doesn't normalize path segments like `.` or `..` so use
* IsAcceptablePath() to check for those. No restrictions are imposed
* beyond that which is strictly necessary for parsing. All the data
* that is provided will be consumed to the one of the fields. Strict
* conformance is enforced on some fields more than others, like scheme,
* since it's the most non-deterministically defined field of them all.
*
* This parser is charset agnostic. Returned values might contain things
* like NUL characters, control codes, and non-canonical encodings.
*
* This parser doesn't support the ability to accurately parse path
* segments which contain percent-encoded slash. There's also no support
* for semicolon parameters at the moment.
* Please note this is a URL parser, not a URI parser. Which means we
* support everything everything the URI spec says we should do except
* for the things we won't do, like tokenizing path segments into an
* array and then nesting another array beneath each of those for
* storing semicolon parameters. So this parser won't make SIP easy.
* What it can do is parse HTTP URLs and most URIs like data:opaque,
* better in fact than most things which claim to be URI parsers.
*
* @param data is value like `/hi?x=y&z` or `http://a.example/hi#x`
* @param size is byte length and -1 implies strlen
* @param h is assumed to be uninitialized
* @return memory backing UrlView needing free (and h.params.p too)
* @see URI Generic Syntax RFC3986 RFC2396
* @see EncodeUrl()
*/
char *ParseUrl(const char *data, size_t size, struct Url *h) {
return ParseUrlImpl(data, size, h, false);
@ -293,15 +304,13 @@ char *ParseUrl(const char *data, size_t size, struct Url *h) {
* assume percent-encoded bytes are expressed as UTF-8. Returned values
* might contain things like NUL characters, C0, and C1 control codes.
* UTF-8 isn't checked for validity and may contain overlong values.
* Absent can be discerned from empty by checking if the pointer is set.
*
* There's no failure condition for this routine. This is a permissive
* parser that doesn't impose character restrictions beyond what is
* necessary for parsing. This doesn't normalize path segments like `.`
* or `..`. Use IsAcceptablePath() to check for those.
*
* This parser doesn't support the ability to accurately parse path
* segments which contain percent-encoded slash.
*
* @param data is value like `/hi?x=y&z` or `http://a.example/hi#x`
* @param size is byte length and -1 implies strlen
* @param h is assumed to be uninitialized
@ -319,7 +328,8 @@ char *ParseRequestUri(const char *data, size_t size, struct Url *h) {
* for this is application/x-www-form-urlencoded.
*
* This parser is charset agnostic. Returned values might contain things
* like NUL characters, control codes, and non-canonical encodings.
* like NUL characters, NUL, control codes, and non-canonical encodings.
* Absent can be discerned from empty by checking if the pointer is set.
*
* There's no failure condition for this routine. This is a permissive
* parser that doesn't impose character restrictions beyond what is
@ -335,12 +345,53 @@ char *ParseParams(const char *data, size_t size, struct UrlParams *h) {
struct UrlParser u;
if (size == -1) size = data ? strlen(data) : 0;
u.i = 0;
u.c = 0;
u.c = '?';
u.isform = true;
u.islatin1 = false;
u.isopaque = false;
u.data = data;
u.size = size;
u.q = u.p = m = xmalloc(u.size);
ParseKeyValues(&u, h);
ParseQuery(&u, h);
return m;
}
/**
* Parses HTTP Host header.
*
* The input is ISO-8859-1 which is transcoded to UTF-8. Therefore we
* assume percent-encoded bytes are expressed as UTF-8. Returned values
* might contain things like NUL characters, C0, and C1 control codes.
* UTF-8 isn't checked for validity and may contain overlong values.
* Absent can be discerned from empty by checking if the pointer is set.
*
* This function turns an HTTP header HOST[:PORT] into two strings, one
* for host and the other for port. You may then call IsAcceptableHost()
* and IsAcceptablePort() to see if they are valid values. After that a
* function like sscanf() can be used to do the thing you likely thought
* this function would do.
*
* This function doesn't initialize h since it's assumed this will be
* called conditionally after ParseRequestUri() if the host is absent.
* Fields unrelated to authority won't be impacted by this function.
*
* @param data is value like `127.0.0.1` or `foo.example:80`
* @param size is byte length and -1 implies strlen
* @param h is needs to be initialized by caller
* @return memory backing UrlView needing free
*/
char *ParseHost(const char *data, size_t size, struct Url *h) {
char *m;
struct UrlParser u;
if (size == -1) size = data ? strlen(data) : 0;
u.i = 0;
u.c = 0;
u.isform = false;
u.islatin1 = true;
u.isopaque = false;
u.data = data;
u.size = size;
u.q = u.p = m = xmalloc(u.size * 2);
ParseAuthority(&u, h);
return xrealloc(m, u.p - m);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,118 +0,0 @@
#ifndef COSMOPOLITAN_NET_HTTP_URI_H_
#define COSMOPOLITAN_NET_HTTP_URI_H_
#include "libc/dns/dns.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
enum UriScheme {
kUriSchemeHttp = 1,
kUriSchemeHttps,
kUriSchemeFile,
kUriSchemeData,
kUriSchemeZip,
kUriSchemeSip,
kUriSchemeSips,
kUriSchemeTel,
kUriSchemeSsh,
kUriSchemeGs,
kUriSchemeS3
};
struct UriSlice {
/*
* !i && !n means absent
* i && !n means empty
*/
unsigned i, n;
};
struct UriSlices {
unsigned i, n;
struct UriSlice *p;
};
struct UriKeyval {
struct UriSlice k, v;
};
struct UriKeyvals {
unsigned i, n;
struct UriKeyval * p;
};
struct UriRef {
unsigned r;
};
struct UriRefs {
unsigned i, n;
struct UriRef * p;
};
struct Uri {
/*
* e.g. "", "http", "sip", "http", "dns+http", etc.
*/
struct UriSlice scheme;
/*
* Holds remainder for exotic URI schemes, e.g. data.
*/
struct UriSlice opaque;
/*
* e.g. sip:user@host, //user:pass@host
*/
struct UriSlice userinfo;
/*
* e.g. "", "example.com", "1.2.3.4", "::1", etc.
*/
struct UriSlice host;
/*
* e.g. "", "5060", "80", etc.
*/
struct UriSlice port;
/*
* e.g. /dir/index.html means
* - memcmp("/dir/index.html",
* p + segs.p[0].i,
* (segs.p[segs.i - 1].n +
* (segs.p[segs.i - 1].i -
* segs.p[0].i))) == 0
* - memcmp("/dir", p + segs.p[0].i, segs.p[0].n) == 0
* - memcmp("/index.html", p + segs.p[1].i, segs.p[1].n) == 0
*/
struct UriSlices segs;
/* e.g. ;lr;isup-oli=00;day=tuesday */
struct UriKeyvals params;
/*
* e.g. /dir;super=rare/index.html
*
* let 𝑖 [0,params.i)
* paramsegs.p[𝑖].r [0,segs.i]
*/
struct UriRefs paramsegs;
/* e.g. ?boop&subject=project%20x&lol=cat */
struct UriKeyvals queries;
/* e.g. #anchor */
struct UriSlice fragment;
};
int uricspn(const char *data, size_t size);
int uriparse(struct Uri *, const char *, size_t) paramsnonnull((1));
enum UriScheme urischeme(struct UriSlice, const char *)
paramsnonnull() nosideeffect;
struct UriSlice uripath(const struct Uri *) paramsnonnull() nosideeffect;
char *urislice2cstr(char *, size_t, struct UriSlice, const char *, const char *)
paramsnonnull((1, 4));
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_NET_HTTP_URI_H_ */

View file

@ -1,61 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 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/macros.internal.h"
// Verifies buffer contains only URI characters.
//
// @param %rdi is data which should be 32-byte aligned
// @param %rsi is byte length of data
// @return number of kosher bytes
// @cost 10x faster than fastest Ragel code
uricspn$avx:
.leafprologue
.profilable
vmovaps .Luric(%rip),%xmm0
mov $14,%eax
mov %rsi,%rdx
xor %esi,%esi
0: vmovdqu (%rdi,%rsi),%xmm1
vmovdqu 16(%rdi,%rsi),%xmm2
vpcmpestri $0b00010100,%xmm1,%xmm0
jc 1f
jo 1f
add $16,%rsi
sub $16,%rdx
vpcmpestri $0b00010100,%xmm2,%xmm0
jc 1f
jo 1f
add $16,%rsi
sub $16,%rdx
jmp 0b
1: lea (%rsi,%rcx),%rax
.leafepilogue
.endfn uricspn$avx,globl,hidden
.rodata.cst16
.Luric: .byte '!','!'
.byte '$',';'
.byte '=','='
.byte '?','Z'
.byte '_','_'
.byte 'a','z'
.byte '~','~'
.byte 0,0
.endobj .Luric
.previous

View file

@ -1,185 +0,0 @@
/*-*- 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/assert.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/sysv/errfuns.h"
#include "net/http/uri.h"
/*
* GENERATED BY
*
* ragel -o net/http/uricspn.c net/http/uricspn.rl
*
* TODO(jart): Rewrite in normal C.
*/
#define static
/* clang-format off */
#line 29 "net/http/uricspn.rl"
#line 34 "build/bootstrap/net/http/uricspn.c"
static const char _uricspn_key_offsets[] = {
0, 0
};
static const char _uricspn_trans_keys[] = {
33, 61, 95, 126, 36, 59, 63, 90,
97, 122, 0
};
static const char _uricspn_single_lengths[] = {
0, 4
};
static const char _uricspn_range_lengths[] = {
0, 3
};
static const char _uricspn_index_offsets[] = {
0, 0
};
static const char _uricspn_trans_targs[] = {
1, 1, 1, 1, 1, 1, 1, 0,
0
};
static const int uricspn_start = 1;
static const int uricspn_first_final = 1;
static const int uricspn_error = 0;
static const int uricspn_en_machina = 1;
#line 30 "net/http/uricspn.rl"
/* clang-format on */
int uricspn(const char *data, size_t size) {
int uricspn$avx(const char *, size_t) hidden;
const char *p, *pe;
int cs;
assert(data || !size);
assert(size <= 0x7ffff000);
assert(size <= 0x7ffff000);
if (X86_HAVE(AVX)) {
return uricspn$avx(data, size);
}
p = data;
pe = data + size;
/* clang-format off */
#line 56 "net/http/uricspn.rl"
#line 94 "build/bootstrap/net/http/uricspn.c"
{
cs = uricspn_start;
}
#line 59 "net/http/uricspn.rl"
cs = uricspn_en_machina;
#line 102 "build/bootstrap/net/http/uricspn.c"
{
int _klen;
unsigned int _trans;
const char *_keys;
if ( p == pe )
goto _test_eof;
if ( cs == 0 )
goto _out;
_resume:
_keys = _uricspn_trans_keys + _uricspn_key_offsets[cs];
_trans = _uricspn_index_offsets[cs];
_klen = _uricspn_single_lengths[cs];
if ( _klen > 0 ) {
const char *_lower = _keys;
const char *_mid;
const char *_upper = _keys + _klen - 1;
while (1) {
if ( _upper < _lower )
break;
_mid = _lower + ((_upper-_lower) >> 1);
if ( (*p) < *_mid )
_upper = _mid - 1;
else if ( (*p) > *_mid )
_lower = _mid + 1;
else {
_trans += (unsigned int)(_mid - _keys);
goto _match;
}
}
_keys += _klen;
_trans += _klen;
}
_klen = _uricspn_range_lengths[cs];
if ( _klen > 0 ) {
const char *_lower = _keys;
const char *_mid;
const char *_upper = _keys + (_klen<<1) - 2;
while (1) {
if ( _upper < _lower )
break;
_mid = _lower + (((_upper-_lower) >> 1) & ~1);
if ( (*p) < _mid[0] )
_upper = _mid - 2;
else if ( (*p) > _mid[1] )
_lower = _mid + 2;
else {
_trans += (unsigned int)((_mid - _keys)>>1);
goto _match;
}
}
_trans += _klen;
}
_match:
cs = _uricspn_trans_targs[_trans];
if ( cs == 0 )
goto _out;
if ( ++p != pe )
goto _resume;
_test_eof: {}
_out: {}
}
#line 61 "net/http/uricspn.rl"
/* clang-format on */
if (cs >= uricspn_first_final) {
return p - data;
} else {
return einval();
}
}

Binary file not shown.

View file

@ -1,724 +0,0 @@
/*-*- 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/assert.h"
#include "libc/dce.h"
#include "libc/dns/dns.h"
#include "libc/log/log.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
#include "net/http/uri.h"
/*
* GENERATED BY
*
* ragel -o net/http/uriparse.c net/http/uriparse.rl
*
* TODO(jart): Rewrite in normal C.
*/
#define static
/* clang-format off */
#line 32 "net/http/uriparse.rl"
#line 37 "build/bootstrap/net/http/uriparse.c"
static const char _uriparse_actions[] = {
0, 1, 0, 1, 1, 1, 2, 1,
3, 1, 4, 1, 5, 1, 6, 1,
8, 1, 11, 1, 12, 2, 0, 2,
2, 4, 8, 2, 5, 8, 2, 6,
9, 2, 6, 10, 2, 7, 9, 2,
7, 10, 2, 8, 0, 2, 11, 0,
3, 4, 8, 0, 3, 5, 8, 0,
3, 6, 9, 0, 3, 7, 9, 0
};
static const short _uriparse_key_offsets[] = {
0, 0, 6, 12, 18, 24, 37, 43,
49, 64, 70, 76, 91, 97, 103, 118,
124, 130, 145, 151, 157, 169, 188, 202,
208, 214, 224, 226, 233, 241, 256, 273,
279, 285, 302, 308, 314, 326, 332, 338,
357, 371, 377, 383, 393, 395, 410, 416,
422, 437, 443, 449, 456, 464, 479, 494,
509, 520, 531, 546, 564, 581, 598, 614,
625, 630, 634, 653, 671, 689, 707, 727,
728, 739, 742, 759, 775, 777, 797
};
static const char _uriparse_trans_keys[] = {
48, 57, 65, 70, 97, 102, 48, 57,
65, 70, 97, 102, 48, 57, 65, 70,
97, 102, 48, 57, 65, 70, 97, 102,
33, 37, 61, 95, 126, 36, 46, 48,
58, 64, 90, 97, 122, 48, 57, 65,
70, 97, 102, 48, 57, 65, 70, 97,
102, 33, 37, 93, 95, 126, 36, 43,
45, 46, 48, 58, 65, 91, 97, 122,
48, 57, 65, 70, 97, 102, 48, 57,
65, 70, 97, 102, 33, 37, 93, 95,
126, 36, 43, 45, 46, 48, 58, 65,
91, 97, 122, 48, 57, 65, 70, 97,
102, 48, 57, 65, 70, 97, 102, 33,
36, 37, 63, 93, 95, 126, 39, 43,
45, 58, 65, 91, 97, 122, 48, 57,
65, 70, 97, 102, 48, 57, 65, 70,
97, 102, 33, 36, 37, 63, 93, 95,
126, 39, 43, 45, 58, 65, 91, 97,
122, 48, 57, 65, 70, 97, 102, 48,
57, 65, 70, 97, 102, 33, 37, 47,
61, 95, 126, 36, 58, 64, 90, 97,
122, 33, 37, 43, 58, 61, 63, 91,
95, 126, 36, 44, 45, 46, 48, 57,
65, 90, 97, 122, 33, 37, 61, 64,
95, 126, 36, 46, 48, 58, 63, 90,
97, 122, 48, 57, 65, 70, 97, 102,
48, 57, 65, 70, 97, 102, 43, 91,
45, 46, 48, 57, 65, 90, 97, 122,
48, 57, 46, 48, 58, 65, 70, 97,
102, 46, 93, 48, 58, 65, 70, 97,
102, 33, 37, 58, 61, 64, 95, 126,
36, 46, 48, 57, 63, 90, 97, 122,
33, 37, 38, 44, 47, 61, 64, 91,
93, 95, 126, 36, 58, 63, 90, 97,
122, 48, 57, 65, 70, 97, 102, 48,
57, 65, 70, 97, 102, 33, 37, 38,
44, 47, 61, 64, 91, 93, 95, 126,
36, 58, 63, 90, 97, 122, 48, 57,
65, 70, 97, 102, 48, 57, 65, 70,
97, 102, 33, 37, 47, 61, 95, 126,
36, 59, 63, 90, 97, 122, 48, 57,
65, 70, 97, 102, 48, 57, 65, 70,
97, 102, 33, 37, 43, 58, 61, 63,
91, 95, 126, 36, 44, 45, 46, 48,
57, 65, 90, 97, 122, 33, 37, 61,
64, 95, 126, 36, 46, 48, 58, 63,
90, 97, 122, 48, 57, 65, 70, 97,
102, 48, 57, 65, 70, 97, 102, 43,
91, 45, 46, 48, 57, 65, 90, 97,
122, 48, 57, 33, 37, 93, 95, 126,
36, 43, 45, 46, 48, 58, 65, 91,
97, 122, 48, 57, 65, 70, 97, 102,
48, 57, 65, 70, 97, 102, 33, 37,
93, 95, 126, 36, 43, 45, 46, 48,
58, 65, 91, 97, 122, 48, 57, 65,
70, 97, 102, 48, 57, 65, 70, 97,
102, 46, 48, 58, 65, 70, 97, 102,
46, 93, 48, 58, 65, 70, 97, 102,
33, 37, 58, 61, 64, 95, 126, 36,
46, 48, 57, 63, 90, 97, 122, 33,
35, 37, 47, 59, 61, 64, 95, 126,
36, 57, 65, 90, 97, 122, 33, 35,
37, 47, 59, 61, 63, 95, 126, 36,
57, 64, 90, 97, 122, 33, 37, 61,
95, 126, 36, 59, 63, 90, 97, 122,
33, 37, 61, 95, 126, 36, 59, 63,
90, 97, 122, 33, 35, 37, 47, 59,
61, 63, 95, 126, 36, 58, 64, 90,
97, 122, 33, 35, 37, 47, 59, 61,
63, 93, 95, 126, 36, 43, 45, 58,
65, 91, 97, 122, 33, 35, 37, 47,
59, 63, 93, 95, 126, 36, 43, 45,
58, 65, 91, 97, 122, 33, 35, 37,
38, 61, 63, 93, 95, 126, 36, 43,
45, 58, 65, 91, 97, 122, 33, 35,
37, 38, 63, 93, 95, 126, 36, 43,
45, 58, 65, 91, 97, 122, 35, 43,
47, 58, 63, 45, 57, 65, 90, 97,
122, 35, 47, 63, 48, 57, 35, 47,
58, 63, 33, 35, 37, 43, 47, 58,
61, 63, 64, 95, 126, 36, 44, 45,
57, 65, 90, 97, 122, 33, 35, 37,
47, 58, 61, 63, 64, 95, 126, 36,
46, 48, 57, 65, 90, 97, 122, 33,
35, 37, 38, 44, 47, 61, 64, 91,
93, 95, 126, 36, 58, 63, 90, 97,
122, 33, 35, 37, 38, 44, 47, 61,
64, 91, 93, 95, 126, 36, 58, 63,
90, 97, 122, 33, 35, 37, 43, 47,
58, 59, 61, 63, 64, 95, 126, 36,
44, 45, 57, 65, 90, 97, 122, 35,
43, 58, 59, 45, 46, 48, 57, 65,
90, 97, 122, 59, 48, 57, 33, 37,
59, 61, 93, 95, 126, 36, 43, 45,
46, 48, 58, 65, 91, 97, 122, 33,
37, 59, 93, 95, 126, 36, 43, 45,
46, 48, 58, 65, 91, 97, 122, 58,
59, 33, 37, 43, 58, 59, 61, 63,
64, 95, 126, 36, 44, 45, 46, 48,
57, 65, 90, 97, 122, 33, 37, 58,
59, 61, 64, 95, 126, 36, 46, 48,
57, 63, 90, 97, 122, 0
};
static const char _uriparse_single_lengths[] = {
0, 0, 0, 0, 0, 5, 0, 0,
5, 0, 0, 5, 0, 0, 7, 0,
0, 7, 0, 0, 6, 9, 6, 0,
0, 2, 0, 1, 2, 7, 11, 0,
0, 11, 0, 0, 6, 0, 0, 9,
6, 0, 0, 2, 0, 5, 0, 0,
5, 0, 0, 1, 2, 7, 9, 9,
5, 5, 9, 10, 9, 9, 8, 5,
3, 4, 11, 10, 12, 12, 12, 1,
3, 1, 7, 6, 2, 10, 8
};
static const char _uriparse_range_lengths[] = {
0, 3, 3, 3, 3, 4, 3, 3,
5, 3, 3, 5, 3, 3, 4, 3,
3, 4, 3, 3, 3, 5, 4, 3,
3, 4, 1, 3, 3, 4, 3, 3,
3, 3, 3, 3, 3, 3, 3, 5,
4, 3, 3, 4, 1, 5, 3, 3,
5, 3, 3, 3, 3, 4, 3, 3,
3, 3, 3, 4, 4, 4, 4, 3,
1, 0, 4, 4, 3, 3, 4, 0,
4, 1, 5, 5, 0, 5, 4
};
static const short _uriparse_index_offsets[] = {
0, 0, 4, 8, 12, 16, 26, 30,
34, 45, 49, 53, 64, 68, 72, 84,
88, 92, 104, 108, 112, 122, 137, 148,
152, 156, 163, 165, 170, 176, 188, 203,
207, 211, 226, 230, 234, 244, 248, 252,
267, 278, 282, 286, 293, 295, 306, 310,
314, 325, 329, 333, 338, 344, 356, 369,
382, 391, 400, 413, 428, 442, 456, 469,
478, 483, 488, 504, 519, 535, 551, 568,
570, 578, 581, 594, 606, 609, 625
};
static const unsigned char _uriparse_indicies[] = {
0, 0, 0, 1, 2, 2, 2, 1,
3, 3, 3, 1, 4, 4, 4, 1,
5, 6, 5, 5, 5, 5, 5, 5,
5, 1, 7, 7, 7, 1, 5, 5,
5, 1, 8, 9, 8, 8, 8, 8,
8, 8, 8, 8, 1, 10, 10, 10,
1, 11, 11, 11, 1, 12, 13, 12,
12, 12, 12, 12, 12, 12, 12, 1,
14, 14, 14, 1, 15, 15, 15, 1,
16, 16, 17, 16, 16, 16, 16, 16,
16, 16, 16, 1, 18, 18, 18, 1,
19, 19, 19, 1, 20, 20, 21, 20,
20, 20, 20, 20, 20, 20, 20, 1,
22, 22, 22, 1, 23, 23, 23, 1,
5, 6, 24, 5, 5, 5, 5, 5,
5, 1, 25, 26, 27, 25, 25, 25,
28, 25, 25, 25, 27, 27, 27, 27,
1, 29, 30, 29, 31, 29, 29, 29,
29, 29, 29, 1, 32, 32, 32, 1,
29, 29, 29, 1, 33, 28, 33, 33,
33, 33, 1, 34, 1, 35, 35, 35,
35, 1, 36, 37, 36, 36, 36, 1,
29, 30, 29, 29, 31, 29, 29, 29,
38, 29, 29, 1, 39, 40, 29, 29,
16, 29, 31, 16, 16, 39, 39, 39,
39, 39, 1, 41, 41, 41, 1, 42,
42, 42, 1, 43, 44, 29, 29, 20,
29, 31, 20, 20, 43, 43, 43, 43,
43, 1, 45, 45, 45, 1, 46, 46,
46, 1, 47, 48, 49, 47, 47, 47,
47, 47, 47, 1, 50, 50, 50, 1,
47, 47, 47, 1, 51, 52, 53, 51,
51, 51, 54, 51, 51, 51, 53, 53,
53, 53, 1, 55, 56, 55, 57, 55,
55, 55, 55, 55, 55, 1, 58, 58,
58, 1, 55, 55, 55, 1, 59, 60,
59, 59, 59, 59, 1, 61, 1, 62,
63, 62, 62, 62, 62, 62, 62, 62,
62, 1, 64, 64, 64, 1, 65, 65,
65, 1, 66, 67, 66, 66, 66, 66,
66, 66, 66, 66, 1, 68, 68, 68,
1, 69, 69, 69, 1, 70, 70, 70,
70, 1, 71, 72, 71, 71, 71, 1,
55, 56, 55, 55, 57, 55, 55, 55,
73, 55, 55, 1, 74, 75, 76, 49,
74, 74, 74, 74, 74, 74, 77, 77,
1, 4, 78, 79, 80, 4, 4, 81,
4, 4, 4, 4, 4, 1, 82, 83,
82, 82, 82, 82, 82, 82, 1, 2,
84, 2, 2, 2, 2, 2, 2, 1,
5, 78, 6, 80, 85, 5, 81, 5,
5, 5, 5, 5, 1, 11, 86, 87,
88, 89, 90, 91, 11, 11, 11, 11,
11, 11, 11, 1, 15, 92, 93, 94,
95, 96, 15, 15, 15, 15, 15, 15,
15, 1, 19, 97, 98, 99, 100, 19,
19, 19, 19, 19, 19, 19, 19, 1,
23, 101, 102, 103, 23, 23, 23, 23,
23, 23, 23, 23, 1, 104, 105, 106,
107, 108, 105, 105, 105, 1, 109, 110,
112, 111, 1, 113, 114, 115, 116, 1,
29, 104, 30, 117, 106, 118, 29, 119,
31, 29, 29, 29, 117, 117, 117, 1,
29, 109, 30, 110, 29, 29, 121, 31,
29, 29, 29, 120, 29, 29, 1, 42,
97, 122, 123, 29, 19, 124, 31, 19,
19, 42, 42, 42, 42, 42, 1, 46,
101, 125, 126, 29, 23, 29, 31, 23,
23, 46, 46, 46, 46, 46, 1, 4,
78, 79, 127, 80, 128, 4, 4, 81,
4, 4, 4, 4, 127, 127, 127, 1,
75, 1, 129, 130, 131, 129, 129, 129,
129, 1, 133, 132, 1, 65, 134, 135,
136, 65, 65, 65, 65, 65, 65, 65,
65, 1, 69, 137, 138, 69, 69, 69,
69, 69, 69, 69, 69, 1, 139, 140,
1, 55, 56, 141, 142, 131, 55, 55,
57, 55, 55, 55, 141, 141, 141, 141,
1, 55, 56, 55, 133, 55, 57, 55,
55, 55, 143, 55, 55, 1, 0
};
static const char _uriparse_trans_targs[] = {
2, 0, 57, 4, 55, 58, 6, 7,
59, 9, 10, 59, 60, 12, 13, 60,
61, 15, 16, 61, 62, 18, 19, 62,
21, 22, 23, 66, 27, 22, 23, 25,
24, 63, 64, 28, 28, 65, 67, 68,
31, 32, 68, 69, 34, 35, 69, 71,
37, 20, 38, 40, 41, 77, 51, 40,
41, 43, 42, 72, 51, 73, 74, 46,
47, 74, 75, 49, 50, 75, 52, 52,
76, 78, 55, 56, 3, 70, 56, 3,
5, 14, 57, 1, 1, 8, 56, 9,
5, 8, 11, 14, 56, 12, 5, 8,
14, 56, 15, 14, 17, 56, 18, 14,
56, 63, 5, 26, 14, 56, 5, 64,
14, 56, 5, 26, 14, 66, 29, 30,
67, 30, 31, 30, 33, 34, 30, 70,
36, 72, 44, 45, 73, 45, 46, 45,
48, 49, 45, 44, 45, 77, 53, 78
};
static const char _uriparse_trans_actions[] = {
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 0, 0, 1, 1, 0, 0,
1, 1, 0, 0, 1, 1, 0, 0,
0, 1, 1, 1, 0, 0, 0, 7,
0, 1, 1, 1, 0, 9, 1, 1,
1, 0, 0, 1, 1, 0, 0, 19,
0, 1, 0, 1, 1, 1, 1, 0,
0, 7, 0, 1, 0, 1, 1, 1,
0, 0, 1, 1, 0, 0, 1, 0,
9, 1, 1, 0, 1, 1, 17, 0,
45, 17, 1, 1, 0, 17, 30, 0,
56, 30, 13, 30, 36, 0, 60, 36,
36, 33, 0, 33, 13, 39, 0, 39,
24, 0, 48, 9, 24, 27, 52, 0,
27, 15, 42, 0, 15, 0, 9, 24,
0, 27, 0, 33, 13, 0, 39, 0,
3, 0, 9, 9, 0, 11, 0, 30,
13, 0, 36, 0, 0, 0, 9, 0
};
static const char _uriparse_eof_actions[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 17,
21, 5, 17, 30, 36, 33, 39, 24,
27, 15, 24, 27, 33, 39, 17, 0,
9, 11, 30, 36, 0, 9, 11
};
static const int uriparse_start = 54;
static const int uriparse_first_final = 54;
static const int uriparse_error = 0;
static const int uriparse_en_sip = 39;
static const int uriparse_en_uri = 54;
#line 33 "net/http/uriparse.rl"
/* clang-format on */
/**
* Parses URI.
*
* This is a general URL parser. It's typically used for HTTP. Support
* for the bonus syntax needed by SIP is provided. The whirlwhind tour
* of the URI rabbit hole is as follows:
*
* /foo.html
* //justine.local/foo.html
* http://justine.local/foo.html
* http://bettersearchengine.local/search.cgi?q=my%20query
* file:///etc/passwd
* gs://bucket/object.txt
* zip:///usr/share/zoneinfo/GMT
* sip:127.0.0.1:5060;lr
* sip:+12125650666@gateway.example
* sip:bob%20barker:priceisright@[dead:beef::666]:5060;isup-oli=00
* data:video/mpeg;base64,gigabytesofhex
*
* This parser operates on slices rather than C strings. It performs
* slicing and validation only. Operations like turning "%20"" " or
* "80"80 and perfect hashing can be done later, if needed.
*
* The Uri object is owned by the caller; it has a lifecycle like the
* following:
*
* struct Uri uri;
* memset(&uri, 0, sizeof(uri));
*
* uriparse(&uri, s1, strlen(s1));
* CHECK_EQ(kUriSchemeHttp, urischeme(uri->scheme, s1));
*
* uriparse(&uri, s2, strlen(s2));
* printf("host = %`.*s\n", uri->host.n, s2 + uri->host.i);
*
* Inner arrays may be granted memory by the caller. The uri->𝐴.i field
* is cleared at the mark of this function. No more than uri->𝐴.n items
* can be inserted. If we need more than that, then ENOMEM is returned
* rather than dynamically extending uri->𝐴.p. However, if uri->𝐴.n==0,
* we assume caller doesn't care about uri->𝐴 and its data is discarded.
*
* @param uri is owned by caller
* @param p is caller-owned uri string; won't copy/alias/mutate
* @return 0 on success, or -1 w/ errno
* @see RFC2396: Uniform Resource Identifiers (URI): Generic Syntax
* @see RFC3261: SIP: Session Initiation Protocol
*/
int uriparse(struct Uri *uri, const char *p, size_t size) {
unsigned zero, cs;
struct UriKeyval kv;
const char *pe, *eof, *buf, *mark;
assert(p || !size);
assert(size <= 0x7ffff000);
#define ABSENT ((struct UriSlice){zero, zero})
#define SLICE ((struct UriSlice){mark - buf, p - mark})
cs = zero = VEIL("r", 0u);
eof = pe = (mark = buf = p) + size;
uri->scheme = ABSENT;
uri->opaque = ABSENT;
uri->userinfo = ABSENT;
uri->host = ABSENT;
uri->port = ABSENT;
uri->fragment = ABSENT;
uri->segs.i = zero;
uri->paramsegs.i = zero;
uri->params.i = zero;
uri->queries.i = zero;
/* clang-format off */
#line 229 "net/http/uriparse.rl"
#line 435 "build/bootstrap/net/http/uriparse.c"
{
cs = uriparse_start;
}
#line 232 "net/http/uriparse.rl"
cs = uriparse_en_uri;
#line 443 "build/bootstrap/net/http/uriparse.c"
{
int _klen;
unsigned int _trans;
const char *_acts;
unsigned int _nacts;
const char *_keys;
if ( p == pe )
goto _test_eof;
if ( cs == 0 )
goto _out;
_resume:
_keys = _uriparse_trans_keys + _uriparse_key_offsets[cs];
_trans = _uriparse_index_offsets[cs];
_klen = _uriparse_single_lengths[cs];
if ( _klen > 0 ) {
const char *_lower = _keys;
const char *_mid;
const char *_upper = _keys + _klen - 1;
while (1) {
if ( _upper < _lower )
break;
_mid = _lower + ((_upper-_lower) >> 1);
if ( (*p) < *_mid )
_upper = _mid - 1;
else if ( (*p) > *_mid )
_lower = _mid + 1;
else {
_trans += (unsigned int)(_mid - _keys);
goto _match;
}
}
_keys += _klen;
_trans += _klen;
}
_klen = _uriparse_range_lengths[cs];
if ( _klen > 0 ) {
const char *_lower = _keys;
const char *_mid;
const char *_upper = _keys + (_klen<<1) - 2;
while (1) {
if ( _upper < _lower )
break;
_mid = _lower + (((_upper-_lower) >> 1) & ~1);
if ( (*p) < _mid[0] )
_upper = _mid - 2;
else if ( (*p) > _mid[1] )
_lower = _mid + 2;
else {
_trans += (unsigned int)((_mid - _keys)>>1);
goto _match;
}
}
_trans += _klen;
}
_match:
_trans = _uriparse_indicies[_trans];
cs = _uriparse_trans_targs[_trans];
if ( _uriparse_trans_actions[_trans] == 0 )
goto _again;
_acts = _uriparse_actions + _uriparse_trans_actions[_trans];
_nacts = (unsigned int) *_acts++;
while ( _nacts-- > 0 )
{
switch ( *_acts++ )
{
case 0:
#line 110 "net/http/uriparse.rl"
{ mark = p; }
break;
case 1:
#line 111 "net/http/uriparse.rl"
{ uri->scheme = SLICE; }
break;
case 3:
#line 113 "net/http/uriparse.rl"
{ uri->userinfo = SLICE; }
break;
case 4:
#line 114 "net/http/uriparse.rl"
{ uri->host = SLICE; }
break;
case 5:
#line 115 "net/http/uriparse.rl"
{ uri->port = SLICE; }
break;
case 6:
#line 117 "net/http/uriparse.rl"
{
kv.k = SLICE;
kv.v = (struct UriSlice){zero, zero};
}
break;
case 7:
#line 122 "net/http/uriparse.rl"
{
kv.v = SLICE;
}
break;
case 8:
#line 126 "net/http/uriparse.rl"
{
uri->segs.i = zero;
uri->paramsegs.i = zero;
}
break;
case 9:
#line 131 "net/http/uriparse.rl"
{
if (uri->params.n) {
if (uri->params.i < uri->params.n) {
uri->params.p[uri->params.i++] = kv;
} else {
return enomem();
}
}
}
break;
case 10:
#line 141 "net/http/uriparse.rl"
{
if (uri->queries.n) {
if (uri->queries.i < uri->queries.n) {
uri->queries.p[uri->queries.i++] = kv;
} else {
return enomem();
}
}
}
break;
case 11:
#line 151 "net/http/uriparse.rl"
{
if (p > mark && uri->segs.n) {
if (uri->segs.i < uri->segs.n) {
uri->segs.p[uri->segs.i++] = SLICE;
} else {
return enomem();
}
}
}
break;
case 12:
#line 161 "net/http/uriparse.rl"
{
switch (urischeme(uri->scheme, buf)) {
case kUriSchemeSip:
case kUriSchemeSips:
--p;
{cs = 39;goto _again;}
default:
if (uricspn(p, pe - p) == pe - p) {
uri->opaque = (struct UriSlice){p - buf, pe - p};
return zero;
} else {
return einval();
}
}
}
break;
#line 611 "build/bootstrap/net/http/uriparse.c"
}
}
_again:
if ( cs == 0 )
goto _out;
if ( ++p != pe )
goto _resume;
_test_eof: {}
if ( p == eof )
{
const char *__acts = _uriparse_actions + _uriparse_eof_actions[cs];
unsigned int __nacts = (unsigned int) *__acts++;
while ( __nacts-- > 0 ) {
switch ( *__acts++ ) {
case 0:
#line 110 "net/http/uriparse.rl"
{ mark = p; }
break;
case 2:
#line 112 "net/http/uriparse.rl"
{ uri->fragment = SLICE; }
break;
case 4:
#line 114 "net/http/uriparse.rl"
{ uri->host = SLICE; }
break;
case 5:
#line 115 "net/http/uriparse.rl"
{ uri->port = SLICE; }
break;
case 6:
#line 117 "net/http/uriparse.rl"
{
kv.k = SLICE;
kv.v = (struct UriSlice){zero, zero};
}
break;
case 7:
#line 122 "net/http/uriparse.rl"
{
kv.v = SLICE;
}
break;
case 8:
#line 126 "net/http/uriparse.rl"
{
uri->segs.i = zero;
uri->paramsegs.i = zero;
}
break;
case 9:
#line 131 "net/http/uriparse.rl"
{
if (uri->params.n) {
if (uri->params.i < uri->params.n) {
uri->params.p[uri->params.i++] = kv;
} else {
return enomem();
}
}
}
break;
case 10:
#line 141 "net/http/uriparse.rl"
{
if (uri->queries.n) {
if (uri->queries.i < uri->queries.n) {
uri->queries.p[uri->queries.i++] = kv;
} else {
return enomem();
}
}
}
break;
case 11:
#line 151 "net/http/uriparse.rl"
{
if (p > mark && uri->segs.n) {
if (uri->segs.i < uri->segs.n) {
uri->segs.p[uri->segs.i++] = SLICE;
} else {
return enomem();
}
}
}
break;
#line 699 "build/bootstrap/net/http/uriparse.c"
}
}
}
_out: {}
}
#line 234 "net/http/uriparse.rl"
/* clang-format on */
if (cs >= uriparse_first_final) {
if (uri->host.n <= DNS_NAME_MAX && uri->port.n <= 6) {
return zero;
} else {
return eoverflow();
}
} else {
return einval();
}
}

View file

@ -1,247 +0,0 @@
/*-*- 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 2020 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/assert.h"
#include "libc/dce.h"
#include "libc/dns/dns.h"
#include "libc/log/log.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
#include "net/http/uri.h"
/* TODO(jart): Rewrite in C */
#define static
/* clang-format off */
%% machine uriparse;
%% write data;
/* clang-format on */
/**
* Parses URI.
*
* This is a general URL parser. It's typically used for HTTP. Support
* for the bonus syntax needed by SIP is provided. The whirlwhind tour
* of the URI rabbit hole is as follows:
*
* /foo.html
* //justine.local/foo.html
* http://justine.local/foo.html
* http://bettersearchengine.local/search.cgi?q=my%20query
* file:///etc/passwd
* gs://bucket/object.txt
* zip:///usr/share/zoneinfo/GMT
* sip:127.0.0.1:5060;lr
* sip:+12125650666@gateway.example
* sip:bob%20barker:priceisright@[dead:beef::666]:5060;isup-oli=00
* data:video/mpeg;base64,gigabytesofhex
*
* This parser operates on slices rather than C strings. It performs
* slicing and validation only. Operations like turning "%20"→" " or
* "80"→80 and perfect hashing can be done later, if needed.
*
* The Uri object is owned by the caller; it has a lifecycle like the
* following:
*
* struct Uri uri;
* memset(&uri, 0, sizeof(uri));
*
* uriparse(&uri, s1, strlen(s1));
* CHECK_EQ(kUriSchemeHttp, urischeme(uri->scheme, s1));
*
* uriparse(&uri, s2, strlen(s2));
* printf("host = %`.*s\n", uri->host.n, s2 + uri->host.i);
*
* Inner arrays may be granted memory by the caller. The uri->𝐴.i field
* is cleared at the mark of this function. No more than uri->𝐴.n items
* can be inserted. If we need more than that, then ENOMEM is returned
* rather than dynamically extending uri->𝐴.p. However, if uri->𝐴.n==0,
* we assume caller doesn't care about uri->𝐴 and its data is discarded.
*
* @param uri is owned by caller
* @param p is caller-owned uri string; won't copy/alias/mutate
* @return 0 on success, or -1 w/ errno
* @see RFC2396: Uniform Resource Identifiers (URI): Generic Syntax
* @see RFC3261: SIP: Session Initiation Protocol
*/
int uriparse(struct Uri *uri, const char *p, size_t size) {
unsigned zero, cs;
struct UriKeyval kv;
const char *pe, *eof, *buf, *mark;
assert(p || !size);
assert(size <= 0x7ffff000);
#define ABSENT ((struct UriSlice){zero, zero})
#define SLICE ((struct UriSlice){mark - buf, p - mark})
cs = zero = VEIL("r", 0u);
eof = pe = (mark = buf = p) + size;
uri->scheme = ABSENT;
uri->opaque = ABSENT;
uri->userinfo = ABSENT;
uri->host = ABSENT;
uri->port = ABSENT;
uri->fragment = ABSENT;
uri->segs.i = zero;
uri->paramsegs.i = zero;
uri->params.i = zero;
uri->queries.i = zero;
/* clang-format off */
%%{
action Mark { mark = p; }
action SetScheme { uri->scheme = SLICE; }
action SetFragment { uri->fragment = SLICE; }
action SetUserinfo { uri->userinfo = SLICE; }
action SetHost { uri->host = SLICE; }
action SetPort { uri->port = SLICE; }
action SetKey {
kv.k = SLICE;
kv.v = (struct UriSlice){zero, zero};
}
action SetVal {
kv.v = SLICE;
}
action RestartSegs {
uri->segs.i = zero;
uri->paramsegs.i = zero;
}
action AppendParam {
if (uri->params.n) {
if (uri->params.i < uri->params.n) {
uri->params.p[uri->params.i++] = kv;
} else {
return enomem();
}
}
}
action AppendQuery {
if (uri->queries.n) {
if (uri->queries.i < uri->queries.n) {
uri->queries.p[uri->queries.i++] = kv;
} else {
return enomem();
}
}
}
action AppendSegment {
if (p > mark && uri->segs.n) {
if (uri->segs.i < uri->segs.n) {
uri->segs.p[uri->segs.i++] = SLICE;
} else {
return enomem();
}
}
}
action HandleOpaquePart {
switch (urischeme(uri->scheme, buf)) {
case kUriSchemeSip:
case kUriSchemeSips:
--p;
fgoto sip;
default:
if (uricspn(p, pe - p) == pe - p) {
uri->opaque = (struct UriSlice){p - buf, pe - p};
return zero;
} else {
return einval();
}
}
}
mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")";
reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ",";
unreserved = alnum | mark;
ipv4c = digit | ".";
ipv6c = xdigit | "." | ":";
hostc = alnum | "-" | ".";
telc = digit | "+" | "-";
schemec = alnum | "+" | "-" | ".";
userinfoc = unreserved | "&" | "=" | "+" | "$" | "," | "?" | ":";
paramc = unreserved | "[" | "]" | ":" | "&" | "+" | "$";
queryc = unreserved | "[" | "]" | "/" | "?" | ":" | "+" | "$";
pathc = unreserved | ":" | "@" | "&" | "=" | "+" | "$" | ",";
relc = unreserved | ";" | "@" | "&" | "=" | "+" | "$" | ",";
uric = reserved | unreserved;
escaped = "%" xdigit xdigit;
pathchar = escaped | pathc;
urichar = escaped | uric;
relchar = escaped | relc;
userinfochar = escaped | userinfoc;
paramchar = escaped | paramc;
querychar = escaped | queryc;
paramkey = paramchar+ >Mark %SetKey;
paramval = paramchar+ >Mark %SetVal;
param = ";" paramkey ( "=" paramval )? %AppendParam;
querykey = querychar+ >Mark %SetKey;
queryval = querychar+ >Mark %SetVal;
query = querykey ( "=" queryval )? %AppendQuery;
queries = "?" query ( "&" query )*;
scheme = ( alpha @Mark schemec* ) ":" @SetScheme;
userinfo = userinfochar+ >Mark "@" @SetUserinfo;
host6 = "[" ( ipv6c+ >Mark %SetHost ) "]";
host = host6 | ( ( ipv4c | hostc | telc )+ >Mark %SetHost );
port = digit+ >Mark %SetPort;
hostport = host ( ":" port )?;
authority = userinfo? hostport;
segment = pathchar+ %AppendSegment param*;
rel_segment = relchar+ >Mark %AppendSegment;
path_segments = segment ( "/" @Mark segment )*;
abs_path = "/" @Mark path_segments;
net_path = "//" authority abs_path? >RestartSegs;
hier_part = ( net_path | abs_path ) queries?;
rel_path = rel_segment abs_path?;
opaque_part = ( urichar -- "/" ) @HandleOpaquePart;
fragment = "#" urichar* >Mark %SetFragment;
relativeURI = ( net_path | abs_path | rel_path ) queries?;
absoluteURI = scheme ( hier_part | opaque_part );
sip := authority >Mark param*;
uri := ( relativeURI | absoluteURI )? fragment?;
}%%
%% write init;
cs = uriparse_en_uri;
%% write exec;
/* clang-format on */
if (cs >= uriparse_first_final) {
if (uri->host.n <= DNS_NAME_MAX && uri->port.n <= 6) {
return zero;
} else {
return eoverflow();
}
} else {
return einval();
}
}

Binary file not shown.

View file

@ -5,31 +5,34 @@ COSMOPOLITAN_C_START_
struct UrlView {
size_t n;
char *p; /* not allocated; not nul terminated */
char *p;
};
struct UrlParams {
size_t n;
struct Param {
struct UrlParam {
struct UrlView key;
struct UrlView val; /* val.n may be SIZE_MAX */
struct UrlView val;
} * p;
};
struct Url {
struct UrlView scheme;
struct UrlView user;
struct UrlView pass;
struct UrlView host;
struct UrlView port;
struct UrlView path;
struct UrlView scheme; /* must be [A-Za-z][-+.0-9A-Za-z]* or empty */
struct UrlView user; /* depends on host non-absence */
struct UrlView pass; /* depends on user non-absence */
struct UrlView host; /* or reg_name */
struct UrlView port; /* depends on host non-absence */
struct UrlView path; /* or opaque_part */
struct UrlParams params;
struct UrlView fragment;
};
char *EncodeUrl(struct Url *, size_t *);
char *ParseUrl(const char *, size_t, struct Url *);
char *ParseParams(const char *, size_t, struct UrlParams *);
char *ParseRequestUri(const char *, size_t, struct Url *);
char *ParseHost(const char *, size_t, struct Url *);
char *EscapeUrlView(char *, struct UrlView *, const char[256]);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/str/str.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
#include "third_party/regex/regex.h"
@ -30,3 +31,125 @@ TEST(regex, test) {
EXPECT_EQ(REG_NOMATCH, regexec(&rx, "0", 0, NULL, 0));
regfree(&rx);
}
TEST(regex, testDns) {
regex_t rx;
EXPECT_EQ(REG_OK, regcomp(&rx, "^[-._0-9A-Za-z]*$", REG_EXTENDED));
EXPECT_EQ(REG_OK, regexec(&rx, "", 0, NULL, 0));
EXPECT_EQ(REG_OK, regexec(&rx, "foo.com", 0, NULL, 0));
EXPECT_EQ(REG_NOMATCH, regexec(&rx, "bar@example", 0, NULL, 0));
regfree(&rx);
}
TEST(regex, testIpBasic) {
regex_t rx;
EXPECT_EQ(REG_OK, regcomp(&rx,
"^"
"\\([0-9][0-9]*\\)\\."
"\\([0-9][0-9]*\\)\\."
"\\([0-9][0-9]*\\)\\."
"\\([0-9][0-9]*\\)"
"$",
0));
const char *s = "127.0.0.1";
regmatch_t *m = gc(calloc(rx.re_nsub + 1, sizeof(regmatch_t)));
ASSERT_EQ(4, rx.re_nsub);
EXPECT_EQ(REG_OK, regexec(&rx, s, rx.re_nsub + 1, m, 0));
EXPECT_STREQ("127", gc(strndup(s + m[1].rm_so, m[1].rm_eo - m[1].rm_so)));
EXPECT_STREQ("0", gc(strndup(s + m[2].rm_so, m[2].rm_eo - m[2].rm_so)));
EXPECT_STREQ("0", gc(strndup(s + m[3].rm_so, m[3].rm_eo - m[3].rm_so)));
EXPECT_STREQ("1", gc(strndup(s + m[4].rm_so, m[4].rm_eo - m[4].rm_so)));
regfree(&rx);
}
TEST(regex, testIpExtended) {
regex_t rx;
EXPECT_EQ(REG_OK, regcomp(&rx,
"^"
"([0-9]{1,3})\\."
"([0-9]{1,3})\\."
"([0-9]{1,3})\\."
"([0-9]{1,3})"
"$",
REG_EXTENDED));
const char *s = "127.0.0.1";
regmatch_t *m = gc(calloc(rx.re_nsub + 1, sizeof(regmatch_t)));
ASSERT_EQ(4, rx.re_nsub);
EXPECT_EQ(REG_OK, regexec(&rx, s, rx.re_nsub + 1, m, 0));
EXPECT_STREQ("127", gc(strndup(s + m[1].rm_so, m[1].rm_eo - m[1].rm_so)));
EXPECT_STREQ("0", gc(strndup(s + m[2].rm_so, m[2].rm_eo - m[2].rm_so)));
EXPECT_STREQ("0", gc(strndup(s + m[3].rm_so, m[3].rm_eo - m[3].rm_so)));
EXPECT_STREQ("1", gc(strndup(s + m[4].rm_so, m[4].rm_eo - m[4].rm_so)));
regfree(&rx);
}
void A(void) {
regex_t rx;
regcomp(&rx, "^[-._0-9A-Za-z]*$", REG_EXTENDED);
regexec(&rx, "foo.com", 0, NULL, 0);
regfree(&rx);
}
void B(regex_t *rx) {
regexec(rx, "foo.com", 0, NULL, 0);
}
void C(void) {
regex_t rx;
regcomp(&rx, "^[-._0-9A-Za-z]*$", 0);
regexec(&rx, "foo.com", 0, NULL, 0);
regfree(&rx);
}
void D(regex_t *rx, regmatch_t *m) {
regexec(rx, "127.0.0.1", rx->re_nsub + 1, m, 0);
}
BENCH(regex, bench) {
regex_t rx;
regmatch_t *m;
regcomp(&rx, "^[-._0-9A-Za-z]*$", REG_EXTENDED);
EZBENCH2("precompiled extended", donothing, B(&rx));
regfree(&rx);
EZBENCH2("easy api extended", donothing, A());
EZBENCH2("easy api basic", donothing, C());
EXPECT_EQ(REG_OK, regcomp(&rx,
"^"
"\\([0-9][0-9]*\\)\\."
"\\([0-9][0-9]*\\)\\."
"\\([0-9][0-9]*\\)\\."
"\\([0-9][0-9]*\\)"
"$",
0));
m = calloc(rx.re_nsub + 1, sizeof(regmatch_t));
EZBENCH2("precompiled basic match", donothing, D(&rx, m));
free(m);
regfree(&rx);
EXPECT_EQ(REG_OK, regcomp(&rx,
"^"
"([0-9]{1,3})\\."
"([0-9]{1,3})\\."
"([0-9]{1,3})\\."
"([0-9]{1,3})"
"$",
REG_EXTENDED));
m = calloc(rx.re_nsub + 1, sizeof(regmatch_t));
EZBENCH2("precompiled extended match", donothing, D(&rx, m));
free(m);
regfree(&rx);
EXPECT_EQ(REG_OK, regcomp(&rx,
"^"
"([0-9]{1,3})\\."
"([0-9]{1,3})\\."
"([0-9]{1,3})\\."
"([0-9]{1,3})"
"$",
REG_EXTENDED | REG_NOSUB));
m = calloc(rx.re_nsub + 1, sizeof(regmatch_t));
EZBENCH2("precompiled nosub match", donothing, D(&rx, m));
free(m);
regfree(&rx);
}

View file

@ -25,29 +25,29 @@
char *escapeparam(const char *s) {
struct EscapeResult r;
r = EscapeUrlParam(s, -1);
r = EscapeParam(s, -1);
ASSERT_EQ(strlen(r.data), r.size);
return r.data;
}
TEST(EscapeUrlParam, test) {
TEST(EscapeParam, test) {
EXPECT_STREQ("abc%20%26%3C%3E%22%27%01%02",
gc(escapeparam("abc &<>\"'\1\2")));
}
TEST(EscapeUrlParam, testLargeGrowth) {
TEST(EscapeParam, testLargeGrowth) {
EXPECT_STREQ("%22%22%22", gc(escapeparam("\"\"\"")));
}
TEST(EscapeUrlParam, testEmpty) {
TEST(EscapeParam, testEmpty) {
EXPECT_STREQ("", gc(escapeparam("")));
}
TEST(EscapeUrlParam, testAstralPlanes_usesUtf8HexEncoding) {
TEST(EscapeParam, testAstralPlanes_usesUtf8HexEncoding) {
EXPECT_STREQ("%F0%90%8C%B0", escapeparam("𐌰"));
}
BENCH(EscapeUrlParam, bench) {
EZBENCH2("EscapeUrlParam", donothing,
free(EscapeUrlParam(kHyperion, kHyperionSize).data));
BENCH(EscapeParam, bench) {
EZBENCH2("EscapeParam", donothing,
free(EscapeParam(kHyperion, kHyperionSize).data));
}

View file

@ -0,0 +1,96 @@
/*-*- 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/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
#include "net/http/http.h"
TEST(IsAcceptableHost, test) {
EXPECT_TRUE(IsAcceptableHost("", -1));
EXPECT_TRUE(IsAcceptableHost("0.0.0.0", -1));
EXPECT_FALSE(IsAcceptableHost("1.2.3", -1));
EXPECT_TRUE(IsAcceptableHost("1.2.3.4", -1));
EXPECT_FALSE(IsAcceptableHost("1.2.3.4.5", -1));
EXPECT_TRUE(IsAcceptableHost("1.2.3.4.5.arpa", -1));
EXPECT_TRUE(IsAcceptableHost("255.255.255.255", -1));
EXPECT_FALSE(IsAcceptableHost("255.255.255", -1));
EXPECT_FALSE(IsAcceptableHost("256.255.255.255", -1));
EXPECT_TRUE(IsAcceptableHost("hello.example", -1));
EXPECT_FALSE(IsAcceptableHost("hello..example", -1));
EXPECT_TRUE(IsAcceptableHost("hello", -1));
EXPECT_FALSE(IsAcceptableHost("hello\177", -1));
EXPECT_FALSE(IsAcceptableHost("hello.example\300\200", -1));
EXPECT_FALSE(IsAcceptableHost(".", -1));
EXPECT_FALSE(IsAcceptableHost(".e", -1));
EXPECT_FALSE(IsAcceptableHost("e.", -1));
EXPECT_FALSE(IsAcceptableHost(".hi.example", -1));
EXPECT_FALSE(IsAcceptableHost("hi..example", -1));
EXPECT_TRUE(IsAcceptableHost("hi-there.example", -1));
EXPECT_TRUE(IsAcceptableHost("_there.example", -1));
EXPECT_TRUE(IsAcceptableHost("-there.example", -1));
EXPECT_TRUE(IsAcceptableHost("there-.example", -1));
EXPECT_FALSE(IsAcceptableHost("ther#e.example", -1));
EXPECT_TRUE(IsAcceptableHost("localhost", -1));
}
TEST(IsAcceptablePort, test) {
EXPECT_TRUE(IsAcceptablePort("", -1));
EXPECT_TRUE(IsAcceptablePort("0", -1));
EXPECT_TRUE(IsAcceptablePort("65535", -1));
EXPECT_FALSE(IsAcceptablePort("65536", -1));
EXPECT_FALSE(IsAcceptablePort("-1", -1));
EXPECT_FALSE(IsAcceptablePort("http", -1));
}
TEST(ParseIp, test) {
EXPECT_EQ(-1, ParseIp("", -1));
EXPECT_EQ(0x00000000, ParseIp("0.0.0.0", -1));
EXPECT_EQ(0x01020304, ParseIp("1.2.3.4", -1));
EXPECT_EQ(0x80020304, ParseIp("128.2.3.4", -1));
EXPECT_EQ(0xFFFFFFFF, ParseIp("255.255.255.255", -1));
EXPECT_EQ(0xcb007100, ParseIp("203.0.113.0", -1));
EXPECT_EQ(0x00000000, ParseIp("...", -1)); /* meh */
EXPECT_EQ(-1, ParseIp("128.2..3.4", -1));
EXPECT_EQ(-1, ParseIp("1.2.3", -1));
EXPECT_EQ(-1, ParseIp("256.255.255.255", -1));
EXPECT_EQ(-1, ParseIp("1.2.3.4.5", -1));
EXPECT_EQ(-1, ParseIp("1.2.3.4.5.arpa", -1));
EXPECT_EQ(-1, ParseIp("255.255.255", -1));
EXPECT_EQ(-1, ParseIp("hello", -1));
EXPECT_EQ(-1, ParseIp("hello\177", -1));
EXPECT_EQ(-1, ParseIp("hello.example\300\200", -1));
EXPECT_EQ(-1, ParseIp(".", -1));
EXPECT_EQ(-1, ParseIp(".e", -1));
EXPECT_EQ(-1, ParseIp("e.", -1));
EXPECT_EQ(-1, ParseIp(".hi.example", -1));
EXPECT_EQ(-1, ParseIp("hi..example", -1));
EXPECT_EQ(-1, ParseIp("hi-there.example", -1));
EXPECT_EQ(-1, ParseIp("_there.example", -1));
EXPECT_EQ(-1, ParseIp("-there.example", -1));
EXPECT_EQ(-1, ParseIp("there-.example", -1));
EXPECT_EQ(-1, ParseIp("ther#e.example", -1));
EXPECT_EQ(-1, ParseIp("localhost", -1));
EXPECT_EQ(-1, ParseIp("hello.example", -1));
EXPECT_EQ(-1, ParseIp("hello..example", -1));
}
BENCH(IsAcceptableHost, bench) {
EZBENCH2("IsAcceptableHost 127.0.0.1", donothing,
IsAcceptableHost("127.0.0.1", 9));
EZBENCH2("IsAcceptablePort 80", donothing, IsAcceptablePort("80", 2));
}

View file

@ -1,59 +0,0 @@
/*-*- 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/testlib/testlib.h"
#include "net/http/http.h"
TEST(IsAcceptableHostPort, test) {
EXPECT_FALSE(IsAcceptableHostPort("", -1));
EXPECT_FALSE(IsAcceptableHostPort(":", -1));
EXPECT_FALSE(IsAcceptableHostPort(":80", -1));
EXPECT_TRUE(IsAcceptableHostPort("0.0.0.0", -1));
EXPECT_FALSE(IsAcceptableHostPort("1.2.3", -1));
EXPECT_TRUE(IsAcceptableHostPort("1.2.3.4", -1));
EXPECT_FALSE(IsAcceptableHostPort("1.2.3.4.5", -1));
EXPECT_TRUE(IsAcceptableHostPort("1.2.3.4.5.arpa", -1));
EXPECT_TRUE(IsAcceptableHostPort("255.255.255.255", -1));
EXPECT_FALSE(IsAcceptableHostPort("255.255.255", -1));
EXPECT_FALSE(IsAcceptableHostPort("256.255.255.255", -1));
EXPECT_TRUE(IsAcceptableHostPort("hello.example", -1));
EXPECT_FALSE(IsAcceptableHostPort("hello..example", -1));
EXPECT_TRUE(IsAcceptableHostPort("hello.example:80", -1));
EXPECT_FALSE(IsAcceptableHostPort("hello.example:80:", -1));
EXPECT_FALSE(IsAcceptableHostPort("hello.example::80", -1));
EXPECT_FALSE(IsAcceptableHostPort("hello.example:-80", -1));
EXPECT_FALSE(IsAcceptableHostPort(":80", -1));
EXPECT_TRUE(IsAcceptableHostPort("hello.example:65535", -1));
EXPECT_FALSE(IsAcceptableHostPort("hello.example:65536", -1));
EXPECT_FALSE(IsAcceptableHostPort("hello.example:-80", -1));
EXPECT_FALSE(IsAcceptableHostPort(" hello .example:80", -1));
EXPECT_FALSE(IsAcceptableHostPort("hello.example:80h", -1));
EXPECT_TRUE(IsAcceptableHostPort("hello", -1));
EXPECT_FALSE(IsAcceptableHostPort("hello\177", -1));
EXPECT_FALSE(IsAcceptableHostPort("hello.example\300\200:80", -1));
EXPECT_FALSE(IsAcceptableHostPort(".", -1));
EXPECT_FALSE(IsAcceptableHostPort(".e", -1));
EXPECT_FALSE(IsAcceptableHostPort("e.", -1));
EXPECT_FALSE(IsAcceptableHostPort(".hi.example", -1));
EXPECT_FALSE(IsAcceptableHostPort("hi..example", -1));
EXPECT_TRUE(IsAcceptableHostPort("hi-there.example", -1));
EXPECT_TRUE(IsAcceptableHostPort("_there.example", -1));
EXPECT_TRUE(IsAcceptableHostPort("-there.example", -1));
EXPECT_TRUE(IsAcceptableHostPort("there-.example", -1));
EXPECT_FALSE(IsAcceptableHostPort("ther#e.example", -1));
}

View file

@ -20,13 +20,16 @@
#include "net/http/http.h"
TEST(ParseContentLength, test) {
EXPECT_EQ(0, ParseContentLength("", 0));
EXPECT_EQ(-1, ParseContentLength(0, 0));
EXPECT_EQ(-1, ParseContentLength("", 0));
EXPECT_EQ(-1, ParseContentLength("-1", 2));
EXPECT_EQ(-1, ParseContentLength("-2", 2));
EXPECT_EQ(-1, ParseContentLength("e", -1));
EXPECT_EQ(-1, ParseContentLength(",", -1));
EXPECT_EQ(-1, ParseContentLength("\0", 1));
EXPECT_EQ(0, ParseContentLength("0", 1));
EXPECT_EQ(1, ParseContentLength("1", 1));
EXPECT_EQ(0x7fffffff, ParseContentLength("2147483647", 10));
EXPECT_EQ(-1, ParseContentLength("2147483648", 10));
EXPECT_EQ(-1, ParseContentLength("9223372036854775808", 19));
EXPECT_EQ(-1, ParseContentLength("88223372036854775808", 20));
EXPECT_EQ(42, ParseContentLength("42, 42", -1)); /* RFC7230 § 3.3.2 */
EXPECT_EQ(0x000000ffffffffff, ParseContentLength("1099511627775", -1));
EXPECT_EQ(-1, ParseContentLength("1099511627776", -1));
}

View file

@ -20,11 +20,11 @@
#include "libc/testlib/testlib.h"
#include "net/http/http.h"
TEST(ParseHttpRange, testEmptyHack) {
TEST(ParseHttpRange, testEmptyHack_refusedBecauseItWontEncodeInContentRange) {
long start, length;
const char *s = "bytes=-0";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(100, start);
EXPECT_FALSE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(0, length);
}
@ -36,6 +36,22 @@ TEST(ParseHttpRange, testEmptyRange_isntEmpty) {
EXPECT_EQ(1, length);
}
TEST(ParseHttpRange, testEmptyRangeOfOneByteFile_itWorks) {
long start, length;
const char *s = "bytes=0-0";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 1, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(1, length);
}
TEST(ParseHttpRange, testEmptyRangeOfEmptyFile_outOfRange) {
long start, length;
const char *s = "bytes=0-0";
EXPECT_FALSE(ParseHttpRange(s, strlen(s), 0, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(0, length);
}
TEST(ParseHttpRange, testInclusiveIndexing) {
long start, length;
const char *s = "bytes=0-10";
@ -81,7 +97,7 @@ TEST(ParseHttpRange, testOutOfRange) {
const char *s = "bytes=0-100";
EXPECT_FALSE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(101, length);
EXPECT_EQ(0, length);
}
TEST(ParseHttpRange, testInvalidRange) {
@ -104,6 +120,14 @@ TEST(ParseHttpRange, testOverflow_duringAddition_setsErrorRange) {
long start, length;
const char *s = "bytes=4611686018427387904-4611686018427387915";
EXPECT_FALSE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(4611686018427387904, start);
EXPECT_EQ(12, length);
EXPECT_EQ(0, start);
EXPECT_EQ(0, length);
}
TEST(ParseHttpRange, testMultipartRange_notImplemented) {
long start, length;
const char *s = "bytes=0-100,200-300";
EXPECT_FALSE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(0, length);
}

View file

@ -27,7 +27,6 @@
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
#include "net/http/http.h"
#include "net/http/uri.h"
struct HttpRequest req[1];
@ -39,10 +38,6 @@ static char *slice(const char *m, struct HttpRequestSlice s) {
return p;
}
static unsigned version(const char *m) {
return ParseHttpVersion(m + req->version.a, req->version.b - req->version.a);
}
void SetUp(void) {
InitHttpRequest(req);
}
@ -51,9 +46,9 @@ void TearDown(void) {
DestroyHttpRequest(req);
}
/* TEST(ParseHttpRequest, soLittleState) { */
/* ASSERT_EQ(280, sizeof(struct HttpRequest)); */
/* } */
TEST(ParseHttpRequest, soLittleState) {
ASSERT_LE(sizeof(struct HttpRequest), 512);
}
TEST(ParseHttpRequest, testEmpty_tooShort) {
EXPECT_EQ(0, ParseHttpRequest(req, "", 0));
@ -68,7 +63,7 @@ TEST(ParseHttpRequest, testNoHeaders) {
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_EQ(kHttpGet, req->method);
EXPECT_STREQ("/foo", gc(slice(m, req->uri)));
EXPECT_STREQ("HTTP/1.0", gc(slice(m, req->version)));
EXPECT_EQ(10, req->version);
}
TEST(ParseHttpRequest, testSomeHeaders) {
@ -80,7 +75,7 @@ Content-Length: 0\r\n\
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_EQ(kHttpPost, req->method);
EXPECT_STREQ("/foo?bar%20hi", gc(slice(m, req->uri)));
EXPECT_STREQ("HTTP/1.0", gc(slice(m, req->version)));
EXPECT_EQ(10, req->version);
EXPECT_STREQ("foo.example", gc(slice(m, req->headers[kHttpHost])));
EXPECT_STREQ("0", gc(slice(m, req->headers[kHttpContentLength])));
EXPECT_STREQ("", gc(slice(m, req->headers[kHttpEtag])));
@ -91,8 +86,7 @@ TEST(ParseHttpRequest, testHttp101) {
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_EQ(kHttpGet, req->method);
EXPECT_STREQ("/", gc(slice(m, req->uri)));
EXPECT_STREQ("HTTP/1.1", gc(slice(m, req->version)));
EXPECT_EQ(101, version(m));
EXPECT_EQ(11, req->version);
}
TEST(ParseHttpRequest, testHttp100) {
@ -100,17 +94,48 @@ TEST(ParseHttpRequest, testHttp100) {
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_EQ(kHttpGet, req->method);
EXPECT_STREQ("/", gc(slice(m, req->uri)));
EXPECT_STREQ("HTTP/1.0", gc(slice(m, req->version)));
EXPECT_EQ(100, version(m));
EXPECT_EQ(10, req->version);
}
TEST(ParseHttpRequest, testHttp009) {
TEST(ParseHttpRequest, testUnknownMethod_canBeUsedIfYouWant) {
static const char m[] = "#%*+_^ / HTTP/1.0\r\n\r\n";
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_FALSE(req->method);
EXPECT_STREQ("WUT", kHttpMethod[req->method]);
EXPECT_STREQ("#%*+_^", gc(slice(m, req->xmethod)));
}
TEST(ParseHttpRequest, testIllegalMethod) {
static const char m[] = "ehd@oruc / HTTP/1.0\r\n\r\n";
EXPECT_EQ(-1, ParseHttpRequest(req, m, strlen(m)));
EXPECT_STREQ("WUT", kHttpMethod[req->method]);
}
TEST(ParseHttpRequest, testIllegalMethodCasing_weAllowItAndPreserveIt) {
static const char m[] = "get / HTTP/1.0\r\n\r\n";
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_STREQ("GET", kHttpMethod[req->method]);
EXPECT_STREQ("get", gc(slice(m, req->xmethod)));
}
TEST(ParseHttpRequest, testEmptyMethod_isntAllowed) {
static const char m[] = " / HTTP/1.0\r\n\r\n";
EXPECT_EQ(-1, ParseHttpRequest(req, m, strlen(m)));
EXPECT_STREQ("WUT", kHttpMethod[req->method]);
}
TEST(ParseHttpRequest, testEmptyUri_isntAllowed) {
static const char m[] = "GET HTTP/1.0\r\n\r\n";
EXPECT_EQ(-1, ParseHttpRequest(req, m, strlen(m)));
EXPECT_STREQ("GET", kHttpMethod[req->method]);
}
TEST(ParseHttpRequest, testHttp09) {
static const char m[] = "GET /\r\n\r\n";
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_EQ(kHttpGet, req->method);
EXPECT_STREQ("/", gc(slice(m, req->uri)));
EXPECT_STREQ("", gc(slice(m, req->version)));
EXPECT_EQ(9, version(m));
EXPECT_EQ(9, req->version);
}
TEST(ParseHttpRequest, testLeadingLineFeeds_areIgnored) {
@ -153,7 +178,7 @@ Content-Length: 0\n\
EXPECT_EQ(strlen(m) - 1, ParseHttpRequest(req, m, strlen(m)));
EXPECT_EQ(kHttpPost, req->method);
EXPECT_STREQ("/foo?bar%20hi", gc(slice(m, req->uri)));
EXPECT_STREQ("HTTP/1.0", gc(slice(m, req->version)));
EXPECT_EQ(10, req->version);
EXPECT_STREQ("foo.example", gc(slice(m, req->headers[kHttpHost])));
EXPECT_STREQ("0", gc(slice(m, req->headers[kHttpContentLength])));
EXPECT_STREQ("", gc(slice(m, req->headers[kHttpEtag])));
@ -174,7 +199,7 @@ Accept-Language: en-US,en;q=0.9\r\n\
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_EQ(kHttpGet, req->method);
EXPECT_STREQ("/tool/net/redbean.png", gc(slice(m, req->uri)));
EXPECT_STREQ("HTTP/1.1", gc(slice(m, req->version)));
EXPECT_EQ(11, req->version);
EXPECT_STREQ("10.10.10.124:8080", gc(slice(m, req->headers[kHttpHost])));
EXPECT_STREQ("1", gc(slice(m, req->headers[kHttpDnt])));
EXPECT_STREQ("", gc(slice(m, req->headers[kHttpExpect])));
@ -193,6 +218,54 @@ X-User-Agent: hi\r\n\
EXPECT_STREQ("hi", gc(slice(m, req->xheaders.p[0].v)));
}
TEST(ParseHttpRequest, testNormalHeaderOnMultipleLines_getsOverwritten) {
static const char m[] = "\
GET / HTTP/1.1\r\n\
Content-Type: text/html\r\n\
Content-Type: text/plain\r\n\
\r\n";
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_STREQ("text/plain", gc(slice(m, req->headers[kHttpContentType])));
ASSERT_EQ(0, req->xheaders.n);
}
TEST(ParseHttpRequest, testCommaSeparatedOnMultipleLines_becomesLinear) {
static const char m[] = "\
GET / HTTP/1.1\r\n\
Accept: text/html\r\n\
Accept: text/plain\r\n\
\r\n";
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_STREQ("text/html", gc(slice(m, req->headers[kHttpAccept])));
ASSERT_EQ(1, req->xheaders.n);
EXPECT_STREQ("Accept", gc(slice(m, req->xheaders.p[0].k)));
EXPECT_STREQ("text/plain", gc(slice(m, req->xheaders.p[0].v)));
}
TEST(HeaderHasSubstring, testHeaderSpansMultipleLines) {
static const char m[] = "\
GET / HTTP/1.1\r\n\
Accept-Encoding: deflate\r\n\
ACCEPT-ENCODING: gzip\r\n\
ACCEPT-encoding: bzip2\r\n\
\r\n";
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_TRUE(HeaderHasSubstring(req, m, kHttpAcceptEncoding, "gzip", -1));
EXPECT_TRUE(HeaderHasSubstring(req, m, kHttpAcceptEncoding, "deflate", -1));
EXPECT_FALSE(HeaderHasSubstring(req, m, kHttpAcceptEncoding, "funzip", -1));
}
TEST(HeaderHasSubstring, testHeaderOnSameLIne) {
static const char m[] = "\
GET / HTTP/1.1\r\n\
Accept-Encoding: deflate, gzip, bzip2\r\n\
\r\n";
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_TRUE(HeaderHasSubstring(req, m, kHttpAcceptEncoding, "gzip", -1));
EXPECT_TRUE(HeaderHasSubstring(req, m, kHttpAcceptEncoding, "deflate", -1));
EXPECT_FALSE(HeaderHasSubstring(req, m, kHttpAcceptEncoding, "funzip", -1));
}
TEST(ParseHttpRequest, testHeaderValuesWithWhitespace_getsTrimmed) {
static const char m[] = "\
OPTIONS * HTTP/1.0\r\n\
@ -231,6 +304,15 @@ Host: \r\n\
EXPECT_EQ(req->headers[kHttpHost].a, req->headers[kHttpHost].b);
}
TEST(IsMimeType, test) {
ASSERT_TRUE(IsMimeType("text/plain", -1, "text/plain"));
ASSERT_TRUE(IsMimeType("TEXT/PLAIN", -1, "text/plain"));
ASSERT_TRUE(IsMimeType("TEXT/PLAIN ", -1, "text/plain"));
ASSERT_TRUE(IsMimeType("text/plain; charset=utf-8", -1, "text/plain"));
ASSERT_FALSE(IsMimeType("TEXT/PLAI ", -1, "text/plain"));
ASSERT_FALSE(IsMimeType("", -1, "text/plain"));
}
void DoTiniestHttpRequest(void) {
static const char m[] = "\
GET /\r\n\
@ -290,3 +372,24 @@ BENCH(ParseHttpRequest, bench) {
EZBENCH2("DoStandardChromeRequest", donothing, DoStandardChromeRequest());
EZBENCH2("DoUnstandardChromeRequest", donothing, DoUnstandardChromeRequest());
}
BENCH(HeaderHasSubstring, bench) {
static const char m[] = "\
GET / HTTP/1.1\r\n\
X-In-Your-Way-A: a\r\n\
X-In-Your-Way-B: b\r\n\
X-In-Your-Way-C: b\r\n\
Accept-Encoding: deflate\r\n\
ACCEPT-ENCODING: gzip\r\n\
ACCEPT-encoding: bzip2\r\n\
\r\n";
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EZBENCH2("HeaderHasSubstring text/plain", donothing,
HeaderHasSubstring(req, m, kHttpAccept, "text/plain", 7));
EZBENCH2("HeaderHasSubstring deflate", donothing,
HeaderHasSubstring(req, m, kHttpAcceptEncoding, "deflate", 7));
EZBENCH2("HeaderHasSubstring gzip", donothing,
HeaderHasSubstring(req, m, kHttpAcceptEncoding, "gzip", 4));
EZBENCH2("IsMimeType", donothing,
IsMimeType("text/plain; charset=utf-8", -1, "text/plain"));
}

View file

@ -24,122 +24,135 @@
#include "libc/testlib/testlib.h"
#include "net/http/url.h"
TEST(ParseRequestUri, testEmpty) {
TEST(ParseUrl, testEmpty) {
struct Url h;
gc(ParseRequestUri(0, 0, &h));
gc(ParseUrl(0, 0, &h));
gc(h.params.p);
ASSERT_EQ(0, h.params.n);
ASSERT_STREQ("", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testFragment) {
TEST(ParseUrl, testFragment) {
struct Url h;
gc(ParseRequestUri("#x", -1, &h));
gc(ParseUrl("#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);
ASSERT_STREQ("#x", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testFragmentAbsent_isNull) {
TEST(ParseUrl, testFragmentAbsent_isNull) {
struct Url h;
gc(ParseRequestUri("", -1, &h));
gc(ParseUrl("", -1, &h));
gc(h.params.p);
ASSERT_EQ(0, h.fragment.p);
ASSERT_EQ(0, h.fragment.n);
ASSERT_STREQ("", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testFragmentEmpty_isNonNull) {
TEST(ParseUrl, testFragmentEmpty_isNonNull) {
struct Url h;
gc(ParseRequestUri("#", -1, &h));
gc(ParseUrl("#", -1, &h)); /* python's uri parser is wrong here */
gc(h.params.p);
ASSERT_NE(0, h.fragment.p);
ASSERT_EQ(0, h.fragment.n);
ASSERT_STREQ("#", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testPathFragment) {
TEST(ParseUrl, testPathFragment) {
struct Url h;
gc(ParseRequestUri("x#y", -1, &h));
gc(ParseUrl("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]);
ASSERT_STREQ("x#y", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testAbsolutePath) {
TEST(ParseUrl, testAbsolutePath) {
struct Url h;
gc(ParseRequestUri("/x/y", -1, &h));
gc(ParseUrl("/x/y", -1, &h));
gc(h.params.p);
ASSERT_EQ(4, h.path.n);
ASSERT_BINEQ(u"/x/y", h.path.p);
ASSERT_STREQ("/x/y", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testRelativePath1) {
TEST(ParseUrl, testRelativePath1) {
struct Url h;
gc(ParseRequestUri("x", -1, &h));
gc(ParseUrl("x", -1, &h));
gc(h.params.p);
ASSERT_EQ(1, h.path.n);
ASSERT_EQ('x', h.path.p[0]);
ASSERT_STREQ("x", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testOptions) {
TEST(ParseUrl, testOptions) {
struct Url h;
gc(ParseRequestUri("*", -1, &h));
gc(ParseUrl("*", -1, &h));
gc(h.params.p);
ASSERT_EQ(1, h.path.n);
ASSERT_EQ('*', h.path.p[0]);
ASSERT_STREQ("*", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testRelativePath2) {
TEST(ParseUrl, testRelativePath2) {
struct Url h;
gc(ParseRequestUri("x/y", -1, &h));
gc(ParseUrl("x/y", -1, &h));
gc(h.params.p);
ASSERT_EQ(3, h.path.n);
ASSERT_BINEQ(u"x/y", h.path.p);
ASSERT_STREQ("x/y", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testRoot) {
TEST(ParseUrl, testRoot) {
struct Url h;
gc(ParseRequestUri("/", -1, &h));
gc(ParseUrl("/", -1, &h));
gc(h.params.p);
ASSERT_EQ(1, h.path.n);
ASSERT_EQ('/', h.path.p[0]);
ASSERT_STREQ("/", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testSchemePath) {
TEST(ParseUrl, testSchemePath) {
struct Url h;
gc(ParseRequestUri("x:y", -1, &h));
gc(ParseUrl("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);
ASSERT_STREQ("x:y", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testSchemeAuthority) {
TEST(ParseUrl, testSchemeAuthority) {
struct Url h;
gc(ParseRequestUri("x://y", -1, &h));
gc(ParseUrl("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]);
ASSERT_STREQ("x://y", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testParamsQuestion_doesntTurnIntoSpace) {
TEST(ParseUrl, testParamsQuestion_doesntTurnIntoSpace) {
struct Url h;
gc(ParseRequestUri("x?+", -1, &h));
gc(ParseUrl("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]);
ASSERT_STREQ("x?%2B", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testUrl) {
TEST(ParseUrl, testUrl) {
struct Url h;
gc(ParseRequestUri("a://b:B@c:C/d?e#f", -1, &h));
gc(ParseUrl("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]);
@ -156,14 +169,40 @@ TEST(ParseRequestUri, testUrl) {
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(0, h.params.p[0].val.n);
ASSERT_EQ(0, h.params.p[0].val.p);
ASSERT_EQ(1, h.fragment.n);
ASSERT_BINEQ(u"f", h.fragment.p);
ASSERT_STREQ("a://b:B@c:C/d?e#f", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testUrlWithoutScheme) {
TEST(ParseUrl, testEmptyQueryKeyVal_decodesToEmptyStrings) {
struct Url h;
gc(ParseRequestUri("//b@c/d?e#f", -1, &h));
gc(ParseUrl("?=", -1, &h));
gc(h.params.p);
ASSERT_EQ(1, h.params.n);
ASSERT_EQ(0, h.params.p[0].key.n);
ASSERT_NE(0, h.params.p[0].key.p);
ASSERT_EQ(0, h.params.p[0].val.n);
ASSERT_NE(0, h.params.p[0].val.p);
ASSERT_STREQ("?=", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testMultipleEquals_goesIntoValue) {
struct Url h;
gc(ParseUrl("?==", -1, &h));
gc(h.params.p);
ASSERT_EQ(1, h.params.n);
ASSERT_EQ(0, h.params.p[0].key.n);
ASSERT_NE(0, h.params.p[0].key.p);
ASSERT_EQ(1, h.params.p[0].val.n);
ASSERT_EQ('=', h.params.p[0].val.p[0]);
ASSERT_STREQ("?=%3D", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testUrlWithoutScheme) {
struct Url h;
gc(ParseUrl("//b@c/d?e#f", -1, &h));
gc(h.params.p);
ASSERT_EQ(0, h.scheme.n);
ASSERT_EQ(1, h.user.n);
@ -175,14 +214,16 @@ TEST(ParseRequestUri, testUrlWithoutScheme) {
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(0, h.params.p[0].val.n);
ASSERT_EQ(0, h.params.p[0].val.p);
ASSERT_EQ(1, h.fragment.n);
ASSERT_BINEQ(u"f", h.fragment.p);
ASSERT_STREQ("//b@c/d?e#f", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testUrlWithoutUser) {
TEST(ParseUrl, testUrlWithoutUser) {
struct Url h;
gc(ParseRequestUri("a://c/d?e#f", -1, &h));
gc(ParseUrl("a://c/d?e#f", -1, &h));
gc(h.params.p);
ASSERT_EQ(1, h.scheme.n);
ASSERT_EQ('a', h.scheme.p[0]);
@ -196,24 +237,159 @@ TEST(ParseRequestUri, testUrlWithoutUser) {
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(0, h.params.p[0].val.n);
ASSERT_EQ(0, h.params.p[0].val.p);
ASSERT_EQ(1, h.fragment.n);
ASSERT_EQ('f', h.fragment.p[0]);
ASSERT_STREQ("a://c/d?e#f", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testLolv6) {
TEST(ParseUrl, testEmptyParams_absentCanBeDiscerned) {
struct Url h;
gc(ParseRequestUri("//[::1]:31337", -1, &h));
gc(ParseUrl("", -1, &h));
gc(h.params.p);
ASSERT_EQ(0, h.params.n);
ASSERT_EQ(NULL, h.params.p);
gc(ParseUrl("?", -1, &h)); /* python's uri parser is wrong here */
gc(h.params.p);
ASSERT_EQ(0, h.params.n);
ASSERT_NE(NULL, h.params.p);
}
TEST(ParseUrl, testWeirdAmps_areReprodicible) {
struct Url h;
gc(ParseUrl("?&&", -1, &h));
gc(h.params.p);
ASSERT_EQ(3, h.params.n);
ASSERT_EQ(0, h.params.p[0].key.n);
ASSERT_NE(0, h.params.p[0].key.p);
ASSERT_EQ(0, h.params.p[0].val.n);
ASSERT_EQ(0, h.params.p[0].val.p);
ASSERT_EQ(0, h.params.p[1].key.n);
ASSERT_NE(0, h.params.p[1].key.p);
ASSERT_EQ(0, h.params.p[1].val.n);
ASSERT_EQ(0, h.params.p[1].val.p);
ASSERT_EQ(0, h.params.p[2].key.n);
ASSERT_NE(0, h.params.p[2].key.p);
ASSERT_EQ(0, h.params.p[2].val.n);
ASSERT_EQ(0, h.params.p[2].val.p);
ASSERT_STREQ("?&&", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testOpaquePart_canLetQuestionMarkGoInPath) {
struct Url h; /* python's uri parser is wrong here */
gc(ParseUrl("s:o!$%&'()*+,-./09:;=?@AZ_az#fragged", -1, &h));
gc(h.params.p);
ASSERT_EQ(26, h.path.n);
ASSERT_EQ(0, memcmp(h.path.p, "o!$%&'()*+,-./09:;=?@AZ_az", 26));
ASSERT_EQ(0, h.params.n);
ASSERT_EQ(NULL, h.params.p);
ASSERT_STREQ("s:o!$%25&'()*+,-./09:;=%3F@AZ_az#fragged",
gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testSchemePathWithoutAuthority_paramsAreAllowed) {
struct Url h;
gc(ParseUrl("s:/o!$%&'()*+,-./09:;=?@AZ_az#fragged", -1, &h));
gc(h.params.p);
ASSERT_EQ(20, h.path.n);
ASSERT_EQ(0, memcmp(h.path.p, "/o!$%&'()*+,-./09:;=", 20));
ASSERT_EQ(1, h.params.n);
ASSERT_STREQ("s:/o!$%25&'()*+,-./09:;=?%40AZ_az#fragged",
gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testOpaquePart_permitsPercentEncoding) {
struct Url h;
gc(ParseUrl("s:%2Fo!$%&'()*+,-./09:;=?@AZ_az#fragged", -1, &h));
gc(h.params.p);
ASSERT_EQ(27, h.path.n);
ASSERT_EQ(0, memcmp(h.path.p, "/o!$%&'()*+,-./09:;=?@AZ_az", 27));
ASSERT_EQ(0, h.params.n);
ASSERT_STREQ("s:/o!$%25&\'()*+,-./09:;=%3F@AZ_az#fragged",
gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testTelephone) {
struct Url h;
gc(ParseUrl("tel:+1-212-867-5309", -1, &h));
gc(h.params.p);
ASSERT_EQ(15, h.path.n);
ASSERT_BINEQ(u"+1-212-867-5309", h.path.p);
ASSERT_STREQ("tel:+1-212-867-5309", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testLolv6) {
struct Url h;
gc(ParseUrl("//[::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);
ASSERT_STREQ("//[::1]:31337", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testUrlWithoutParams) {
TEST(ParseUrl, testLolV6_withoutPort) {
struct Url h;
gc(ParseRequestUri("a://b@c/d#f", -1, &h));
gc(ParseUrl("//[::1]", -1, &h));
gc(h.params.p);
ASSERT_STREQ("//[::1]", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testLolv7) {
struct Url h;
gc(ParseUrl("//[vf.::1]", -1, &h));
gc(h.params.p);
ASSERT_EQ(6, h.host.n);
ASSERT_BINEQ(u"vf.::1", h.host.p);
ASSERT_EQ(0, h.port.n);
ASSERT_EQ(0, h.port.p);
ASSERT_STREQ("//[vf.::1]", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testLolv7WithoutColon_weCantProduceLegalEncodingSadly) {
struct Url h;
gc(ParseUrl("//[v7.7.7.7]", -1, &h));
gc(h.params.p);
ASSERT_STREQ("//v7.7.7.7", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testObviouslyIllegalIpLiteral_getsTreatedAsRegName) {
struct Url h;
gc(ParseUrl("//[vf.::1%00]", -1, &h));
gc(h.params.p);
ASSERT_STREQ("//vf.%3A%3A1%00", gc(EncodeUrl(&h, 0)));
}
TEST(ParseHost, test) {
struct Url h = {0};
gc(ParseHost("foo.example:80", -1, &h));
gc(h.params.p);
ASSERT_EQ(11, h.host.n);
ASSERT_BINEQ(u"foo.example", h.host.p);
ASSERT_EQ(2, h.port.n);
ASSERT_BINEQ(u"80", h.port.p);
ASSERT_STREQ("//foo.example:80", gc(EncodeUrl(&h, 0)));
}
TEST(ParseHost, testObviouslyIllegalIpLiteral_getsTreatedAsRegName) {
struct Url h = {0};
gc(ParseHost("[vf.::1%00]", -1, &h));
gc(h.params.p);
ASSERT_STREQ("//vf.%3A%3A1%00", gc(EncodeUrl(&h, 0)));
}
TEST(EncodeUrl, testHostPortPlacedInHostField_ungoodIdea) {
struct Url h = {0};
h.host.n = strlen("foo.example:80");
h.host.p = "foo.example:80";
ASSERT_STREQ("//foo.example%3A80", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testUrlWithoutParams) {
struct Url h;
gc(ParseUrl("a://b@c/d#f", -1, &h));
gc(h.params.p);
ASSERT_EQ(1, h.scheme.n);
ASSERT_EQ('a', h.scheme.p[0]);
@ -226,6 +402,7 @@ TEST(ParseRequestUri, testUrlWithoutParams) {
ASSERT_EQ(0, h.params.n);
ASSERT_EQ(1, h.fragment.n);
ASSERT_EQ('f', h.fragment.p[0]);
ASSERT_STREQ("a://b@c/d#f", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testLatin1_doesNothing) {
@ -235,6 +412,7 @@ TEST(ParseUrl, testLatin1_doesNothing) {
gc(h.params.p);
ASSERT_EQ(1, h.path.n);
ASSERT_EQ(0, memcmp("\377", h.path.p, 1));
ASSERT_STREQ("%FF", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testLatin1_expandsMemoryToUtf8) {
@ -246,40 +424,202 @@ TEST(ParseRequestUri, testLatin1_expandsMemoryToUtf8) {
ASSERT_EQ(0, memcmp("\303\277", h.path.p, 2));
}
TEST(ParseRequestUri, testPercentShrinkingMemory) {
TEST(ParseUrl, testPercentShrinkingMemory) {
struct Url h;
gc(ParseRequestUri("%Ff", 3, &h));
gc(ParseUrl("%Ff", 3, &h));
gc(h.params.p);
ASSERT_EQ(1, h.path.n);
ASSERT_EQ(0, memcmp("\377", h.path.p, 1));
ASSERT_STREQ("%FF", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testBadPercent_getsIgnored) {
TEST(ParseUrl, testEscapingWontOverrun) {
struct Url h;
gc(ParseRequestUri("%FZ", 3, &h));
char b[1] = {'%'};
gc(ParseUrl(b, 1, &h));
gc(h.params.p);
ASSERT_EQ(1, h.path.n);
ASSERT_EQ(0, memcmp("%", h.path.p, 1));
ASSERT_STREQ("%25", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testBadPercent_getsIgnored) {
struct Url h;
gc(ParseUrl("%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) {
TEST(ParseUrl, testFileUrl) {
struct Url h;
gc(ParseRequestUri("file:///etc/passwd", -1, &h));
gc(ParseUrl("file:///etc/passwd", -1, &h));
gc(h.params.p);
ASSERT_EQ(4, h.scheme.n);
ASSERT_BINEQ(u"file", h.scheme.p);
ASSERT_EQ(0, h.host.n);
ASSERT_NE(0, h.host.p);
ASSERT_EQ(0, h.port.n);
ASSERT_EQ(0, h.port.p);
ASSERT_EQ(11, h.path.n);
ASSERT_BINEQ(u"/etc/passwd", h.path.p);
ASSERT_STREQ("file:///etc/passwd", gc(EncodeUrl(&h, 0)));
}
TEST(ParseRequestUri, testZipUri2) {
TEST(EncodeUrl, testModifyingParseResultAndReencoding_addsStructure) {
size_t n;
struct Url h;
gc(ParseRequestUri("zip:etc/passwd", -1, &h));
gc(ParseUrl("rel", -1, &h));
gc(h.params.p);
h.host.n = 7;
h.host.p = "justine";
ASSERT_STREQ("//justine/rel", gc(EncodeUrl(&h, &n)));
ASSERT_EQ(13, n);
}
TEST(EncodeUrl, testTortureCharacters_doesWhatYouAskItToDoButSchemeCantEscape) {
size_t n;
struct Url h;
memset(&h, 0, sizeof(h));
h.scheme.n = 1;
h.scheme.p = "/";
h.user.n = 1;
h.user.p = "";
h.pass.n = 1;
h.pass.p = "";
h.host.n = 1;
h.host.p = "";
h.port.n = 1;
h.port.p = "";
h.path.n = 1;
h.path.p = "";
h.params = (struct UrlParams){.n = 1,
.p = (struct UrlParam[]){{
.key = (struct UrlView){.n = 1, .p = ""},
.val = (struct UrlView){.n = 1, .p = ""},
}}};
h.fragment.n = 1;
h.fragment.p = "";
ASSERT_STREQ("/://%00:%00@%00:%00/%00?%00=%00#%00", gc(EncodeUrl(&h, &n)));
ASSERT_EQ(35, n);
}
TEST(EncodeUrl, testUserPassPort_allDependOnHostNonAbsence) {
size_t n;
struct Url h;
memset(&h, 0, sizeof(h));
h.scheme.n = 1;
h.scheme.p = "/";
h.user.n = 1;
h.user.p = "";
h.pass.n = 1;
h.pass.p = "";
h.host.n = 0;
h.host.p = 0;
h.port.n = 1;
h.port.p = "";
h.path.n = 1;
h.path.p = "";
h.params = (struct UrlParams){.n = 1,
.p = (struct UrlParam[]){{
.key = (struct UrlView){.n = 1, .p = ""},
.val = (struct UrlView){.n = 1, .p = ""},
}}};
h.fragment.n = 1;
h.fragment.p = "";
ASSERT_STREQ("/:%00?%00=%00#%00", gc(EncodeUrl(&h, 0)));
}
TEST(EncodeUrl, testEmptyRegName_isLegal) {
size_t n;
struct Url h;
memset(&h, 0, sizeof(h));
h.scheme.n = 1;
h.scheme.p = "/";
h.user.n = 1;
h.user.p = "";
h.pass.n = 1;
h.pass.p = "";
h.host.n = 0;
h.host.p = "";
h.port.n = 1;
h.port.p = "";
h.path.n = 1;
h.path.p = "";
h.params = (struct UrlParams){.n = 1,
.p = (struct UrlParam[]){{
.key = (struct UrlView){.n = 1, .p = ""},
.val = (struct UrlView){.n = 1, .p = ""},
}}};
h.fragment.n = 1;
h.fragment.p = "";
ASSERT_STREQ("/://%00:%00@:%00/%00?%00=%00#%00", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testEmptyScheme_isNotPossible) {
struct Url h;
gc(ParseUrl(":", -1, &h));
gc(h.params.p);
ASSERT_EQ(0, h.scheme.n);
ASSERT_EQ(0, h.scheme.p);
ASSERT_EQ(1, h.path.n);
ASSERT_EQ(':', h.path.p[0]);
ASSERT_STREQ(":", gc(EncodeUrl(&h, 0)));
gc(ParseUrl("://hi", -1, &h));
gc(h.params.p);
ASSERT_EQ(0, h.scheme.n);
ASSERT_EQ(0, h.scheme.p);
ASSERT_EQ(5, h.path.n);
ASSERT_BINEQ(u"://hi", h.path.p);
ASSERT_STREQ("://hi", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testZipUri2) {
struct Url h;
gc(ParseUrl("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);
ASSERT_STREQ("zip:etc/passwd", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testZipUri3) {
struct Url h;
gc(ParseUrl("zip:/etc/passwd", -1, &h));
gc(h.params.p);
ASSERT_EQ(0, h.host.n);
ASSERT_EQ(0, h.host.p);
ASSERT_EQ(3, h.scheme.n);
ASSERT_BINEQ(u"zip", h.scheme.p);
ASSERT_EQ(11, h.path.n);
ASSERT_BINEQ(u"/etc/passwd", h.path.p);
ASSERT_STREQ("zip:/etc/passwd", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testDataUri) {
struct Url h;
gc(ParseUrl("data:image/png;base64,09AZaz+/==", -1, &h));
gc(h.params.p);
ASSERT_EQ(0, h.host.n);
ASSERT_EQ(0, h.host.p);
ASSERT_EQ(4, h.scheme.n);
ASSERT_BINEQ(u"data", h.scheme.p);
ASSERT_EQ(27, h.path.n);
ASSERT_BINEQ(u"image/png;base64,09AZaz+/==", h.path.p);
ASSERT_STREQ("data:image/png;base64,09AZaz+/==", gc(EncodeUrl(&h, 0)));
}
TEST(ParseUrl, testBadSchemeCharacter_parserAssumesItsPath) {
struct Url h;
gc(ParseUrl("fil\e://hi", -1, &h));
gc(h.params.p);
ASSERT_EQ(0, h.scheme.n);
ASSERT_EQ(0, h.scheme.p);
ASSERT_EQ(9, h.path.n);
ASSERT_BINEQ(u"fil←://hi", h.path.p);
ASSERT_STREQ("fil%1B://hi", gc(EncodeUrl(&h, 0)));
}
TEST(ParseParams, testEmpty) {
@ -297,7 +637,9 @@ TEST(ParseParams, test) {
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_NE(0, h.p[1].key.p);
ASSERT_EQ(0, h.p[1].val.n);
ASSERT_EQ(0, h.p[1].val.p);
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]);
@ -344,14 +686,28 @@ void A(void) {
free(h.p);
}
BENCH(url, bench) {
BENCH(ParseUrl, 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));
EZBENCH2("ParseParams hyperion", donothing, A());
EZBENCH2("ParseUrl a", donothing, free(ParseUrl("a", -1, &h)));
EZBENCH2("ParseUrl a://b@c/d#f", donothing,
free(ParseUrl("a://b@c/d#f", -1, &h)));
EZBENCH2("ParseUrl a://b@c/d?z#f", donothing, ({
free(ParseUrl("a://b@c/?zd#f", -1, &h));
free(h.params.p);
}));
}
BENCH(EncodeUrl, bench) {
struct Url h;
gc(ParseUrl("a", -1, &h));
EZBENCH2("EncodeUrl a", donothing, free(EncodeUrl(&h, 0)));
gc(ParseUrl("a://b@c/d#f", -1, &h));
EZBENCH2("EncodeUrl a://b@c/d#f", donothing, free(EncodeUrl(&h, 0)));
gc(ParseUrl("a://b@c/?zd#f", -1, &h));
gc(h.params.p);
EZBENCH2("EncodeUrl a://b@c/d?z#f", donothing, free(EncodeUrl(&h, 0)));
gc(ParseUrl(kHyperion, kHyperionSize, &h));
gc(h.params.p);
EZBENCH2("EncodeUrl hyperion", donothing, free(EncodeUrl(&h, 0)));
}

View file

@ -1,59 +0,0 @@
/*-*- 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 2020 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/str/str.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
#include "net/http/uri.h"
_Alignas(32) const char kWinsockIcoPngBase64[] = "\
base64,iVBORw0KGgoAAAANSUhEUgAAAJcAAACXCAYAAAAYn8l5AAAABmJLR0QA/\
wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4woLByMP6uwgW\
QAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAMeSURBVHja7\
d1BcuIwEAVQPMW94GbAzeBkZJepUXpw01ixDO+tE0PML+lHtsV0v9/vO+jgj1OAc\
CFcIFwIF8IFP+wrvzRNkzP3y7a4YmTkQrgQLnitc/XqA3GX+9SrU/+ei8vl8uMnT\
qeTkQvTIggXwoVC36mOJhZa3Upm5ALhQrjQuV6jT2HkQrgQLhAuNlLo+96Z6q5XI\
xcIF8KFzrXbWTDt0jTf4AkrIxfChXCBcCFcCBcIF8LFO9iP/gajx9jXMvrj80Yuh\
AuEC52r2q9G6jnRxWQX7Y1cCBfCBcLFxxb6tsBH5f12uz08xvV6Lb328Xh8+nfO5\
/NsyVfwjVwIF8IFa3auzALpXL96pRst0dWinta+loVWIxfChXCBcCFcCBcIF8LFe\
xn+6Z+5xc5oYTOzQJr5mcrFbYxcCBfCBcKFQv9AexdC9U7UueMueWwjFwgXwoVwO\
QUIF8IFwkV3e6dgfdETQ5knmIxcmBZBuBAuUOgH1Rb6LRZ8IxfChXDBt+le2N9nq\
a0a222VRn/aJrp5sO1CS22XlPkC9fa1R/tuIiMXwoVwgXDx5oV+ruCPJlrI7LXfa\
XsuMouo1YXWXv8IGLkwLSJcMGbnyrzWmqK/s31/Ue+pdJr2uNECbrvoXP0cen2eR\
i5MiwgXCBf9DX8n6ta+lCmzkFkp+FGhb89N9Yu52uMs9eVYRi5MiwgXbKdzba0TV\
h7NjzpY5i7Tpb78tD1OZrE408GMXJgWES4QLhT6zRf8qAxXFlqXKu+Vgp/5xyX6u\
41cmBYRLvg7dS5xJyqPzW2HFH0Ev9mxKjJ3wRq5MC0iXCBc9FdaRM38DzD6o/kjF\
frRy7uRC+FCuOBlpUVUnjzJhQvXo+8PaxEV0yLCBU9xs+Cg2ies1+5g0RPfRi5Mi\
wgXCBcK/UeYe3Ims6ia2RN1zfJu5MK0iHDBQy5cj/AhFLZd6inarskWSpgWES4QL\
sZkEXUAS227VJU5ti2UMC0iXKBzfUIPW3vbqrm96qP3Z+TCtIhwgXCh0POfAt1T5\
i6Nw+Ew+/6MXJgWES7Quejf74xcdPMFQQsgQ0YEZnUAAAAASUVORK5CYII=";
size_t size;
void SetUp(void) {
size = strlen(kWinsockIcoPngBase64);
}
BENCH(strlen, bench) {
EZBENCH(donothing, (size = strlen(kWinsockIcoPngBase64)));
}
TEST(uricspn, test) {
EXPECT_EQ(size, uricspn(kWinsockIcoPngBase64, size));
}
BENCH(uricspn, bench) {
EZBENCH(donothing, uricspn(kWinsockIcoPngBase64, size));
}

View file

@ -1,141 +0,0 @@
/*-*- 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 2020 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/bits/bits.h"
#include "libc/bits/initializer.internal.h"
#include "libc/errno.h"
#include "libc/log/log.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/gc.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
#include "net/http/uri.h"
#define URIPARSE(URI) uriparse(&uri, (p = URI), (size = sizeof(URI) - 1))
static const char kHttpCosmopolitanVideoUrl[] =
"http://cosmopolitan.storage.googleapis.com/pub/vid/blankspace.mpg";
static const char kSipPriceIsTortureUri[] =
"sip:bob%20barker:priceisright@[dead:beef::666]:5060;isup-oli=00";
static const char kWinsockIcoPngBase64[] = "\
base64,iVBORw0KGgoAAAANSUhEUgAAAJcAAACXCAYAAAAYn8l5AAAABmJLR0QA/\
wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4woLByMP6uwgW\
QAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAMeSURBVHja7\
d1BcuIwEAVQPMW94GbAzeBkZJepUXpw01ixDO+tE0PML+lHtsV0v9/vO+jgj1OAc\
CFcIFwIF8IFP+wrvzRNkzP3y7a4YmTkQrgQLnitc/XqA3GX+9SrU/+ei8vl8uMnT\
qeTkQvTIggXwoVC36mOJhZa3Upm5ALhQrjQuV6jT2HkQrgQLhAuNlLo+96Z6q5XI\
xcIF8KFzrXbWTDt0jTf4AkrIxfChXCBcCFcCBcIF8LFO9iP/gajx9jXMvrj80Yuh\
AuEC52r2q9G6jnRxWQX7Y1cCBfCBcLFxxb6tsBH5f12uz08xvV6Lb328Xh8+nfO5\
/NsyVfwjVwIF8IFa3auzALpXL96pRst0dWinta+loVWIxfChXCBcCFcCBcIF8LFe\
xn+6Z+5xc5oYTOzQJr5mcrFbYxcCBfCBcKFQv9AexdC9U7UueMueWwjFwgXwoVwO\
QUIF8IFwkV3e6dgfdETQ5knmIxcmBZBuBAuUOgH1Rb6LRZ8IxfChXDBt+le2N9nq\
a0a222VRn/aJrp5sO1CS22XlPkC9fa1R/tuIiMXwoVwgXDx5oV+ruCPJlrI7LXfa\
XsuMouo1YXWXv8IGLkwLSJcMGbnyrzWmqK/s31/Ue+pdJr2uNECbrvoXP0cen2eR\
i5MiwgXCBf9DX8n6ta+lCmzkFkp+FGhb89N9Yu52uMs9eVYRi5MiwgXbKdzba0TV\
h7NjzpY5i7Tpb78tD1OZrE408GMXJgWES4QLhT6zRf8qAxXFlqXKu+Vgp/5xyX6u\
41cmBYRLvg7dS5xJyqPzW2HFH0Ev9mxKjJ3wRq5MC0iXCBc9FdaRM38DzD6o/kjF\
frRy7uRC+FCuOBlpUVUnjzJhQvXo+8PaxEV0yLCBU9xs+Cg2ies1+5g0RPfRi5Mi\
wgXCBcK/UeYe3Ims6ia2RN1zfJu5MK0iHDBQy5cj/AhFLZd6inarskWSpgWES4QL\
sZkEXUAS227VJU5ti2UMC0iXKBzfUIPW3vbqrm96qP3Z+TCtIhwgXCh0POfAt1T5\
i6Nw+Ew+/6MXJgWES7Quejf74xcdPMFQQsgQ0YEZnUAAAAASUVORK5CYII=";
static size_t size;
static const char *p;
static struct Uri uri;
static struct UriMem {
struct UriSlice segs[8];
struct UriRef paramsegs[8];
struct UriKeyval params[4], queries[4];
} urimem_;
static textstartup void init() {
uri.segs.n = ARRAYLEN(urimem_.segs);
uri.segs.p = urimem_.segs;
uri.params.n = ARRAYLEN(urimem_.params);
uri.params.p = urimem_.params;
uri.queries.n = ARRAYLEN(urimem_.queries);
uri.queries.p = urimem_.queries;
uri.paramsegs.n = ARRAYLEN(urimem_.paramsegs);
uri.paramsegs.p = urimem_.paramsegs;
}
const void *const g_name_ctor[] initarray = {init};
TEST(uriparse, sipPstnUri) {
EXPECT_NE(-1, URIPARSE("sip:+12125650666"));
EXPECT_STREQ("sip", gc(strndup(p + uri.scheme.i, uri.scheme.n)));
EXPECT_STREQ("+12125650666", gc(strndup(p + uri.host.i, uri.host.n)));
EXPECT_STREQ("", gc(strndup(p + uri.opaque.i, uri.opaque.n)));
}
TEST(uriparse, printVideoUrl) {
EXPECT_NE(-1, URIPARSE(kHttpCosmopolitanVideoUrl));
EXPECT_STREQ("http", gc(strndup(p + uri.scheme.i, uri.scheme.n)));
EXPECT_STREQ("cosmopolitan.storage.googleapis.com",
gc(strndup(p + uri.host.i, uri.host.n)));
EXPECT_STREQ("", gc(strndup(p + uri.port.i, uri.port.n)));
EXPECT_STREQ("/pub/vid/blankspace.mpg",
gc(strndup(p + uri.segs.p[0].i,
(uri.segs.p[uri.segs.i - 1].n +
(uri.segs.p[uri.segs.i - 1].i - uri.segs.p[0].i)))));
}
TEST(uriparse, localRelativeFile) {
EXPECT_NE(-1, URIPARSE("blankspace.mpg"));
EXPECT_STREQ("", gc(strndup(p + uri.scheme.i, uri.scheme.n)));
EXPECT_STREQ("", gc(strndup(p + uri.host.i, uri.host.n)));
EXPECT_STREQ("", gc(strndup(p + uri.port.i, uri.port.n)));
EXPECT_STREQ("blankspace.mpg",
gc(strndup(p + uri.segs.p[0].i,
(uri.segs.p[uri.segs.i - 1].n +
(uri.segs.p[uri.segs.i - 1].i - uri.segs.p[0].i)))));
}
TEST(uriparse, badPort_einval) {
EXPECT_EQ(-1, URIPARSE("http://hello.example:http/"));
EXPECT_EQ(EINVAL, errno);
}
TEST(uriparse, datauri) {
size = strlen((p = gc(xstrcat("data:image/png;", kWinsockIcoPngBase64))));
EXPECT_NE(-1, uriparse(&uri, p, size));
EXPECT_EQ(5, uri.opaque.i);
EXPECT_EQ(size - 5, uri.opaque.n);
}
////////////////////////////////////////////////////////////////////////////////
BENCH(uriparse, bench) {
EZBENCH(donothing, URIPARSE("sip:+12125650666"));
EZBENCH(donothing, URIPARSE("http://hello.example"));
EZBENCH(donothing, URIPARSE(kHttpCosmopolitanVideoUrl));
EZBENCH(donothing, URIPARSE(kSipPriceIsTortureUri));
}
BENCH(uriparse, bigWinsockIcoPngUri) {
const char *BigDataIconUri;
BigDataIconUri = gc(xstrcat("data:image/png;", kWinsockIcoPngBase64));
size = strlen(kWinsockIcoPngBase64);
EZBENCH(donothing, uriparse(&uri, BigDataIconUri, size));
}

View file

@ -26,7 +26,7 @@ static void rehash(HashMap *map) {
nkeys++;
}
}
size_t cap = map->capacity;
int cap = MAX(8, map->capacity);
while ((nkeys * 100) / cap >= LOW_WATERMARK) cap = cap * 2;
assert(cap > 0);
// Create a new hashmap and copy all key-values.

View file

@ -1230,12 +1230,12 @@ forceinline msegmentptr segment_holding(mstate m, char *addr) {
#define check_malloc_state(M) do_check_malloc_state(M)
#endif /* DEBUG */
void do_check_free_chunk(mstate m, mchunkptr p) hidden;
void do_check_inuse_chunk(mstate m, mchunkptr p) hidden;
void do_check_top_chunk(mstate m, mchunkptr p) hidden;
void do_check_malloced_chunk(mstate m, void *mem, size_t s) hidden;
void do_check_mmapped_chunk(mstate m, mchunkptr p) hidden;
void do_check_malloc_state(mstate m) hidden;
void do_check_free_chunk(mstate, mchunkptr) hidden;
void do_check_inuse_chunk(mstate, mchunkptr) hidden;
void do_check_top_chunk(mstate, mchunkptr) hidden;
void do_check_malloced_chunk(mstate, void *, size_t) hidden;
void do_check_mmapped_chunk(mstate, mchunkptr) hidden;
void do_check_malloc_state(mstate) hidden;
/* ─────────────────────────── prototypes ──────────────────────────────── */

11
tool/net/404.html Normal file
View file

@ -0,0 +1,11 @@
<!doctype html>
<title>404 not found</title>
<pre aria-label="404 not found">
_ _ ___ _ _ _ __ _
| || | / _ \| || | _ __ ___ | |_ / _| ___ _ _ _ __ __| |
| || |_| | | | || |_ | '_ \ / _ \| __| | |_ / _ \| | | | '_ \ / _` |
|__ _| |_| |__ _| | | | | (_) | |_ | _| (_) | |_| | | | | (_| |
|_| \___/ |_| |_| |_|\___/ \__| |_| \___/ \__,_|_| |_|\__,_|
</pre>

View file

@ -42,6 +42,7 @@ TOOL_NET_DIRECTDEPS = \
NET_HTTP \
THIRD_PARTY_GETOPT \
THIRD_PARTY_LUA \
THIRD_PARTY_REGEX \
THIRD_PARTY_ZLIB \
TOOL_DECODE_LIB
@ -80,17 +81,54 @@ o/$(MODE)/tool/net/redbean.com: \
o/$(MODE)/tool/net/redbean-demo.com: \
o/$(MODE)/tool/net/redbean.com \
tool/net/redbean.mk \
tool/net/net.mk \
tool/net/404.html \
tool/net/index.html \
tool/net/redbean.css \
tool/net/redbean.lua \
tool/net/redbean-form.lua \
tool/net/redbean-xhr.lua \
$(TOOL_NET_HDRS) \
$(TOOL_NET_SRCS)
tool/net/seekable.txt \
tool/net/redbean.c \
net/http/parsehttprequest.c \
net/http/parseurl.c \
net/http/encodeurl.c \
test/net/http/parsehttprequest_test.c \
test/net/http/parseurl_test.c
@$(COMPILE) -ACP -T$@ cp $< $@
@$(COMPILE) -AZIP -T$@ zip -qj $@ tool/net/redbean.lua tool/net/redbean-form.lua tool/net/redbean-xhr.lua
@$(COMPILE) -AZIP -T$@ zip -q $@ tool/net tool/net/index.html tool/net/redbean.css $(TOOL_NET_HDRS) $(TOOL_NET_SRCS)
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.ape bs=64 count=11 conv=notrunc 2>/dev/null
@$(COMPILE) -AZIP -T$@ zip -qj $@ o/$(MODE)/tool/net/.ape tool/net/404.html tool/net/redbean.lua tool/net/redbean-form.lua tool/net/redbean-xhr.lua
@$(COMPILE) -AZIP -T$@ zip -qj0 $@ tool/net/seekable.txt
@$(COMPILE) -AZIP -T$@ zip -q $@ tool/net tool/net/index.html tool/net/redbean.css tool/net/redbean.c net/http/parsehttprequest.c net/http/parseurl.c net/http/encodeurl.c test/net/http/parsehttprequest_test.c test/net/http/parseurl_test.c
o/$(MODE)/tool/net/redbean-static.com: \
o/$(MODE)/tool/net/redbean-static.com.dbg \
tool/net/favicon.ico \
tool/net/redbean.png
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.ape bs=64 count=11 conv=notrunc 2>/dev/null
@$(COMPILE) -AZIP -T$@ zip -qj $@ o/$(MODE)/tool/net/.ape tool/net/favicon.ico tool/net/redbean.png
o/$(MODE)/tool/net/redbean-bench.com.dbg: \
$(TOOL_NET_DEPS) \
o/$(MODE)/tool/net/redbean.o \
o/$(MODE)/tool/net/index.html.zip.o \
o/$(MODE)/tool/net/redbean.lua.zip.o \
o/$(MODE)/tool/net/net.pkg \
$(CRT) \
$(APE)
@$(APELINK)
o/$(MODE)/tool/net/redbean-static.com.dbg: \
$(TOOL_NET_DEPS) \
o/$(MODE)/tool/net/redbean-static.o \
o/$(MODE)/tool/net/net.pkg \
$(CRT) \
$(APE)
@$(APELINK)
o/$(MODE)/tool/net/redbean-static.o: tool/net/redbean.c
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -DSTATIC $(OUTPUT_OPTION) $<
.PHONY: o/$(MODE)/tool/net
o/$(MODE)/tool/net: \

View file

@ -8,9 +8,9 @@ local function main()
end
SetStatus(200)
SetHeader('Content-Type', 'text/html; charset=utf-8')
Write('<!doctype html>\n')
Write('<title>redbean</title>\n')
Write('<h3>POST Request HTML Form Handler Demo</h3>\n')
Write('<!doctype html>\r\n')
Write('<title>redbean</title>\r\n')
Write('<h3>POST Request HTML Form Handler Demo</h3>\r\n')
Write('<p>')
firstname = GetParam('firstname')
@ -24,47 +24,47 @@ local function main()
Write('Thank you for using redbean.')
end
Write('<dl>\n')
Write('<dl>\r\n')
Write('<dt>Params\n')
Write('<dd>\n')
Write('<dl>\n')
Write('<dt>Params\r\n')
Write('<dd>\r\n')
Write('<dl>\r\n')
params = GetParams()
for i = 1,#params do
Write('<dt>')
Write(EscapeHtml(params[i][1]))
Write('\n')
Write('\r\n')
if params[i][2] then
Write('<dd>')
Write(EscapeHtml(params[i][2]))
Write('\n')
Write('\r\n')
end
end
Write('</dl>\n')
Write('</dl>\r\n')
Write('<dt>Headers\n')
Write('<dd>\n')
Write('<dl>\n')
Write('<dt>Headers\r\n')
Write('<dd>\r\n')
Write('<dl>\r\n')
for k,v in pairs(GetHeaders()) do
Write('<dt>')
Write(EscapeHtml(k))
Write('\n')
Write('\r\n')
Write('<dd>')
Write(EscapeHtml(v))
Write('\n')
Write('\r\n')
end
Write('</dl>\n')
Write('</dl>\r\n')
Write('<dt>Payload\n')
Write('<dt>Payload\r\n')
Write('<dd><p>')
Write(EscapeHtml(GetPayload()))
Write('\n')
Write('\r\n')
Write('</dl>\n')
Write('</dl>\r\n')
Write('<p>')
Write('<a href="redbean.lua">Click here</a> ')
Write('to return to the previous page.\n')
Write('to return to the previous page.\r\n')
end
main()

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,19 @@
-- redbean lua server page demo
local function DescribeIp(ip)
Write(' <small>[')
if IsPrivateIp(ip) then
Write('PRIVATE')
elseif IsTestIp(ip) then
Write('TESTNET')
elseif IsLocalIp(ip) then
Write('LOCALNET')
else
Write('PUBLIC')
end
Write(']</small>')
end
local function main()
-- This is the best way to print data to the console or log file.
Log(kLogWarn, "hello from \e[1mlua\e[0m!")
@ -18,9 +32,9 @@ local function main()
-- Response data is buffered until the script finishes running.
-- Compression is applied automatically, based on your headers.
Write('<!doctype html>\n')
Write('<title>redbean</title>\n')
Write('<h1>redbean lua server page demo</h1>\n')
Write('<!doctype html>\r\n')
Write('<title>redbean</title>\r\n')
Write('<h1>redbean lua server page demo</h1>\r\n')
-- Prevent caching.
-- We need this because we're doing things like putting the client's
@ -28,43 +42,49 @@ local function main()
SetHeader('Expires', FormatHttpDateTime(GetDate()))
SetHeader('Cache-Control', 'no-cache, must-revalidate, max-age=0')
-- GetParams() returns an ordered list of Request-URI query params.
Write('<h3>request uri parameters</h3>\n')
params = GetParams()
if #params > 0 then
Write('<dl>\n')
-- Roundtripping information can make it safer.
Write('<p>Thank you for visiting ')
Write(EscapeHtml(EncodeUrl(ParseUrl(GetUrl()))))
Write('\r\n')
-- GetParam(NAME) is the fastest easiest way to get URL and FORM params
-- If you want the RequestURL query params specifically in full do this
Write('<h3>request url parameters</h3>\r\n')
params = ParseUrl(GetUrl()).params -- like GetParams() but w/o form body
if params and #params>0 then
Write('<dl>\r\n')
for i = 1,#params do
Write('<dt>')
Write(EscapeHtml(params[i][1]))
Write('\n')
Write('\r\n')
if params[i][2] then
Write('<dd>')
Write(EscapeHtml(params[i][2]))
Write('\n')
Write('\r\n')
end
end
Write('</dl>\n')
Write('</dl>\r\n')
else
Write('<p>\n')
Write('<em>none</em><br>\n')
Write('<p>\r\n')
Write('<em>none</em><br>\r\n')
Write('ProTip: Try <a href="')
Write(EscapeHtml(EscapePath(GetPath()) .. '?x=hi%20there&y&z&z=' .. EscapeParam('&')))
Write('">clicking here</a>!\n')
Write('">clicking here</a>!\r\n')
end
-- Access redbean command line arguments.
-- These are the ones that come *after* the redbean server arguments.
Write('<h3>command line arguments</h3>\n')
-- redbean command line arguments
-- these come *after* the c getopt server arguments
Write('<h3>command line arguments</h3>\r\n')
if #argv > 0 then
Write('<ul>\n')
Write('<ul>\r\n')
for i = 1,#argv do
Write('<li>')
Write(EscapeHtml(argv[i]))
Write('\n')
Write('\r\n')
end
Write('</ul>\n')
Write('</ul>\r\n')
else
Write('<p><em>none</em>\n')
Write('<p><em>none</em>\r\n')
end
Write([[
@ -100,16 +120,227 @@ local function main()
</script>
]])
Write('<h3>extra information</h3>\n')
Write('<dt>GetClientAddr()\n')
Write('<h3>statistics</h3>\r\n')
Write('<dl>\r\n')
Write('<dt>GetStatistics().workers\r\n')
Write('<dd>')
Write(GetClientAddr())
Write('\n')
Write('<dt>GetServerAddr()\n')
Write(tostring(GetStatistics().workers))
Write('\r\n')
Write('<dt>GetStatistics().requestshandled\r\n')
Write('<dd>')
Write(GetServerAddr())
Write('\n')
Write('</dl>\n')
Write(tostring(GetStatistics().requestshandled))
Write('\r\n')
Write('<dt>GetStatistics().uptime\r\n')
Write('<dd>')
Write(tostring(GetStatistics().uptime))
Write(' seconds\r\n')
Write('</dl>\r\n')
-- fast redbean apis for accessing already parsed request data
Write('<h3>extra information</h3>\r\n')
Write('<dl>\r\n')
Write('<dt>GetMethod()\r\n')
Write('<dd>')
Write(EscapeHtml(GetMethod())) -- & and ' are legal in http methods
Write('\r\n')
if GetUser() then
Write('<dt>GetUser()\r\n')
Write('<dd>')
Write(EscapeHtml(GetUser()))
Write('\r\n')
end
if GetScheme() then
Write('<dt>GetScheme()\r\n')
Write('<dd>')
Write(GetScheme())
Write('\r\n')
end
if GetPass() then
Write('<dt>GetPass()\r\n')
Write('<dd>')
Write(EscapeHtml(GetPass()))
Write('\r\n')
end
Write('<dt>GetHost() <small>(from HTTP Request-URL or Host header)</small>\r\n')
Write('<dd>')
Write(EscapeHtml(GetHost()))
Write('\r\n')
Write('<dt>GetPort() <small>(from HTTP Request-URL or Host header)</small>\r\n')
Write('<dd>')
Write(tostring(GetPort()))
Write('\r\n')
Write('<dt>GetPath()\r\n')
Write('<dd>')
Write(EscapeHtml(GetPath()))
Write('\r\n')
if GetFragment() then
Write('<dt>GetFragment()\r\n')
Write('<dd>')
Write(EscapeHtml(GetFragment()))
Write('\r\n')
end
Write('<dt>GetClientIp()\r\n')
Write('<dd>')
Write(FormatIp(GetClientIp()))
DescribeIp(GetClientIp())
Write('\r\n')
Write('<dt>GetClientPort()\r\n')
Write('<dd>')
Write(tostring(GetClientPort()))
Write('\r\n')
Write('<dt>GetServerIp()\r\n')
Write('<dd>')
Write(FormatIp(GetServerIp()))
DescribeIp(GetServerIp())
Write('\r\n')
Write('<dt>GetServerPort()\r\n')
Write('<dd>')
Write(tostring(GetServerPort()))
Write('\r\n')
Write('</dl>\r\n')
-- redbean apis for generalized parsing and encoding
referer = GetHeader('Referer')
if referer then
url = ParseUrl(referer)
if url.scheme then
url.scheme = string.upper(url.scheme)
end
Write('<h3>referer url</h3>\r\n')
Write('<p>\r\n')
Write(EscapeHtml(EncodeUrl(url)))
Write('<dl>\r\n')
if url.scheme then
Write('<dt>scheme\r\n')
Write('<dd>\r\n')
Write(EscapeHtml(url.scheme))
end
if url.user then
Write('<dt>user\r\n')
Write('<dd>\r\n')
Write(EscapeHtml(url.user))
end
if url.pass then
Write('<dt>pass\r\n')
Write('<dd>\r\n')
Write(EscapeHtml(url.pass))
end
if url.host then
Write('<dt>host\r\n')
Write('<dd>\r\n')
Write(EscapeHtml(url.host))
end
if url.port then
Write('<dt>port\r\n')
Write('<dd>\r\n')
Write(EscapeHtml(url.port))
end
if url.path then
Write('<dt>path\r\n')
Write('<dd>\r\n')
Write(EscapeHtml(url.path))
end
if url.params then
Write('<dt>params\r\n')
Write('<dd>\r\n')
Write('<dl>\r\n')
for i = 1,#url.params do
Write('<dt>')
Write(EscapeHtml(url.params[i][1]))
Write('\r\n')
if url.params[i][2] then
Write('<dd>')
Write(EscapeHtml(url.params[i][2]))
Write('\r\n')
end
end
Write('</dl>\r\n')
end
if url.fragment then
Write('<dt>fragment\r\n')
Write('<dd>\r\n')
Write(EscapeHtml(url.fragment))
end
Write('</dl>\r\n')
end
Write('<h3>posix extended regular expressions</h3>\r\n')
s = 'my ' .. FormatIp(GetClientIp()) .. ' ip'
r = CompileRegex('([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})', 'e')
m,a,b,c,d = ExecuteRegex(r, s)
Write('<pre>\r\n')
Write(string.format([[m,a,b,c,d = ExecuteRegex(CompileRegex('([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})', 'e'), %q)]], s))
Write('</pre>\r\n')
ReleaseRegex(r)
Write('<dl>\r\n')
Write('<dt>m\r\n')
Write('<dd>')
Write(EscapeHtml(tostring(m)))
Write('\r\n')
Write('<dt>a\r\n')
Write('<dd>')
Write(EscapeHtml(tostring(a)))
Write('\r\n')
Write('<dt>b\r\n')
Write('<dd>')
Write(EscapeHtml(tostring(b)))
Write('\r\n')
Write('<dt>c\r\n')
Write('<dd>')
Write(EscapeHtml(tostring(c)))
Write('\r\n')
Write('<dt>d\r\n')
Write('<dd>')
Write(EscapeHtml(tostring(d)))
Write('\r\n')
Write('</dl>\r\n')
-- redbean zip assets
Write('<h3>zip assets</h3>\r\n')
paths = GetZipPaths()
if #paths > 0 then
Write('<ul>\r\n')
for i = 1,#paths do
Write('<li>\r\n')
Write('<a href="')
Write(EscapeHtml(EscapePath(paths[i])))
Write('">')
Write(EscapeHtml(paths[i]))
Write('</a>')
if IsHiddenPath(paths[i]) then
Write(' <small>[HIDDEN]</small>')
end
if not IsAcceptablePath(paths[i]) then
Write(' <small>[BLOCKED]</small>')
end
if not IsCompressed(paths[i]) then
Write(' <small>[UNCOMPRESSED]</small>')
end
if (GetAssetMode(paths[i]) & 0xF000) == 0x4000 then
Write(' <small>[DIRECTORY]</small>')
end
Write('<br>\r\n')
Write('Modified: ')
Write(FormatHttpDateTime(GetLastModifiedTime(paths[i])))
Write('<br>\r\n')
Write('Mode: ')
Write(string.format("0%o", GetAssetMode(paths[i])))
Write('<br>\r\n')
Write('Size: ')
Write(tostring(GetAssetSize(paths[i])))
Write('<br>\r\n')
if GetComment(paths[i]) then
Write('Comment: ')
Write(EscapeHtml(GetComment(paths[i])))
Write('<br>\r\n')
end
Write('\r\n')
end
Write('</ul>\r\n')
else
Write('<p><em>none</em>\r\n')
end
end
main()

26
tool/net/seekable.txt Normal file
View file

@ -0,0 +1,26 @@
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z