mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-24 23:09:02 +00:00
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:
parent
26ac6871da
commit
4effa23528
74 changed files with 3710 additions and 14246 deletions
examples
libc
net/http
encodeurl.cescape.hescapefragment.cescapehost.cescapehtml.cescapeip.cescapeparam.cescapepass.cescapepath.cescapesegment.cescapeurl.cescapeurlview.cescapeuser.cgethttpheader.gperfgethttpheader.incgethttpheadername.cgethttpmethod.cheaderhassubstring.chttp.hisacceptablehost.cisacceptablepath.cisacceptableport.cismimetype.cisvalidhttptoken.ckescapeauthority.ckescapefragment.ckescapeip.ckescapeparam.ckescapepath.ckescapesegment.ckhextoint.ckhttpmethod.ckhttprepeatable.ckhttptoken.cparsecontentlength.cparsehttprange.cparsehttprequest.cparseip.cparseurl.crfc2068rfc2396uri.huricspn-avx.Suricspn.curicspn.svgzuriparse.curiparse.rluriparse.svgzurl.h
test
libc/str
net/http
third_party
tool/net
111
examples/curl.c
111
examples/curl.c
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
144
net/http/encodeurl.c
Normal 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;
|
||||
}
|
|
@ -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_
|
||||
|
|
|
@ -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
28
net/http/escapehost.c
Normal 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);
|
||||
}
|
|
@ -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
30
net/http/escapeip.c
Normal 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);
|
||||
}
|
|
@ -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
28
net/http/escapepass.c
Normal 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
30
net/http/escapepath.c
Normal 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
31
net/http/escapesegment.c
Normal 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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
28
net/http/escapeuser.c
Normal 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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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) */
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 '.').
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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];
|
||||
}
|
|
@ -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.
|
||||
*
|
||||
|
|
46
net/http/kescapeauthority.c
Normal file
46
net/http/kescapeauthority.c
Normal 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
|
||||
};
|
|
@ -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
44
net/http/kescapeip.c
Normal 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
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
38
net/http/khextoint.c
Normal 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
|
||||
};
|
|
@ -18,7 +18,8 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "net/http/http.h"
|
||||
|
||||
const char kHttpMethod[17][8] = {
|
||||
const char kHttpMethod[18][8] = {
|
||||
"WUT", //
|
||||
"GET", //
|
||||
"HEAD", //
|
||||
"POST", //
|
||||
|
|
81
net/http/khttprepeatable.c
Normal file
81
net/http/khttprepeatable.c
Normal 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
42
net/http/khttptoken.c
Normal 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
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
52
net/http/parseip.c
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
9075
net/http/rfc2068
9075
net/http/rfc2068
File diff suppressed because it is too large
Load diff
2243
net/http/rfc2396
2243
net/http/rfc2396
File diff suppressed because it is too large
Load diff
118
net/http/uri.h
118
net/http/uri.h
|
@ -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_ */
|
|
@ -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
|
|
@ -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.
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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.
|
@ -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) */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
96
test/net/http/isacceptablehost_test.c
Normal file
96
test/net/http/isacceptablehost_test.c
Normal 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));
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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));
|
||||
}
|
2
third_party/chibicc/hashmap.c
vendored
2
third_party/chibicc/hashmap.c
vendored
|
@ -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.
|
||||
|
|
12
third_party/dlmalloc/dlmalloc.internal.h
vendored
12
third_party/dlmalloc/dlmalloc.internal.h
vendored
|
@ -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
11
tool/net/404.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
<!doctype html>
|
||||
<title>404 not found</title>
|
||||
|
||||
<pre aria-label="404 not found">
|
||||
_ _ ___ _ _ _ __ _
|
||||
| || | / _ \| || | _ __ ___ | |_ / _| ___ _ _ _ __ __| |
|
||||
| || |_| | | | || |_ | '_ \ / _ \| __| | |_ / _ \| | | | '_ \ / _` |
|
||||
|__ _| |_| |__ _| | | | | (_) | |_ | _| (_) | |_| | | | | (_| |
|
||||
|_| \___/ |_| |_| |_|\___/ \__| |_| \___/ \__,_|_| |_|\__,_|
|
||||
|
||||
</pre>
|
|
@ -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: \
|
||||
|
|
|
@ -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()
|
||||
|
|
1959
tool/net/redbean.c
1959
tool/net/redbean.c
File diff suppressed because it is too large
Load diff
|
@ -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
26
tool/net/seekable.txt
Normal 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
|
Loading…
Add table
Reference in a new issue