Make major improvements to redbean

- lua server pages
- lua http library
- http v0.9 support
- request uri parsing
- fork failure recovery
- accelerated redirects
- http pipelining support
- lenient message framing
- html / uri / js escaping
- fix shutdown signal handling
This commit is contained in:
Justine Tunney 2021-03-25 02:21:13 -07:00
parent 6b90ff60cd
commit 09bcfa23d5
23 changed files with 2208 additions and 581 deletions

View file

@ -69,6 +69,7 @@
#define kZipLfileOffsetLastmodifieddate 12
#define kZipLfileOffsetCrc32 14
#define kZipLfileOffsetCompressedsize 18
#define kZipLfileOffsetUncompressedsize 22
#define kZipGflagUtf8 0x800
@ -140,7 +141,8 @@
#define ZIP_LFILE_CRC32(P) READ32LE((P) + kZipLfileOffsetCrc32)
#define ZIP_LFILE_COMPRESSEDSIZE(P) \
READ32LE((P) + kZipLfileOffsetCompressedsize)
#define ZIP_LFILE_UNCOMPRESSEDSIZE(P) READ32LE((P) + 22)
#define ZIP_LFILE_UNCOMPRESSEDSIZE(P) \
READ32LE((P) + kZipLfileOffsetUncompressedsize)
#define ZIP_LFILE_NAMESIZE(P) READ16LE((P) + 26)
#define ZIP_LFILE_EXTRASIZE(P) READ16LE((P) + 28)
#define ZIP_LFILE_NAME(P) ((const char *)(&(P)[30]))

21
net/http/escape.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef COSMOPOLITAN_NET_HTTP_ESCAPE_H_
#define COSMOPOLITAN_NET_HTTP_ESCAPE_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct EscapeResult {
char *data;
size_t size;
};
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 EscapeJsStringLiteral(const char *, size_t);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_NET_HTTP_ESCAPE_H_ */

81
net/http/escapehtml.c Normal file
View file

@ -0,0 +1,81 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/x/x.h"
#include "net/http/escape.h"
/**
* Escapes HTML entities.
*/
struct EscapeResult EscapeHtml(const char *data, size_t size) {
int c;
char *p;
size_t i;
struct EscapeResult r;
p = r.data = xmalloc(size * 6 + 1);
for (i = 0; i < size; ++i) {
switch ((c = data[i])) {
case '&':
p[0] = '&';
p[1] = 'a';
p[2] = 'm';
p[3] = 'p';
p[4] = ';';
p += 5;
break;
case '<':
p[0] = '&';
p[1] = 'l';
p[2] = 't';
p[3] = ';';
p += 4;
break;
case '>':
p[0] = '&';
p[1] = 'g';
p[2] = 't';
p[3] = ';';
p += 4;
break;
case '"':
p[0] = '&';
p[1] = 'q';
p[2] = 'u';
p[3] = 'o';
p[4] = 't';
p[5] = ';';
p += 6;
break;
case '\'':
p[0] = '&';
p[1] = '#';
p[2] = '3';
p[3] = '9';
p[4] = ';';
p += 5;
break;
default:
*p++ = c;
break;
}
}
r.size = p - r.data;
r.data = xrealloc(r.data, r.size + 1);
r.data[r.size] = '\0';
return r;
}

View file

@ -0,0 +1,135 @@
/*-*- 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/thompike.h"
#include "libc/str/utf16.h"
#include "libc/x/x.h"
#include "net/http/escape.h"
/**
* Escapes UTF-8 data for JavaScript or JSON string literal.
*
* HTML entities and forward slash are escaped too for added safety.
*
* We assume the UTF-8 is well-formed and can be represented as UTF-16.
* Things that can't be decoded or encoded will be replaced with invalid
* code-point markers. This function is agnostic to numbers that have
* been used with malicious intent in the past under buggy software.
* Noncanonical encodings such as overlong NUL are canonicalized as NUL.
*/
struct EscapeResult EscapeJsStringLiteral(const char *data, size_t size) {
char *p;
size_t i;
unsigned n;
uint64_t w;
wint_t x, y;
struct EscapeResult r;
p = r.data = xmalloc(size * 6 + 6 + 1);
for (i = 0; i < size;) {
x = data[i++] & 0xff;
if (x >= 0200) {
if (x >= 0300) {
n = ThomPikeLen(x);
x = ThomPikeByte(x);
while (--n) {
if (i < size) {
y = data[i++] & 0xff;
if (ThomPikeCont(y)) {
x = ThomPikeMerge(x, y);
} else {
x = 0xFFFD;
break;
}
} else {
x = 0xFFFD;
break;
}
}
} else {
x = 0xFFFD;
}
}
switch (x) {
case '\t':
p[0] = '\\';
p[1] = 't';
p += 2;
break;
case '\n':
p[0] = '\\';
p[1] = 'n';
p += 2;
break;
case '\r':
p[0] = '\\';
p[1] = 'r';
p += 2;
break;
case '\f':
p[0] = '\\';
p[1] = 'f';
p += 2;
break;
case '\\':
p[0] = '\\';
p[1] = '\\';
p += 2;
break;
case '/':
p[0] = '\\';
p[1] = '/';
p += 2;
break;
case '"':
p[0] = '\\';
p[1] = '"';
p += 2;
break;
case '\'':
p[0] = '\\';
p[1] = '\'';
p += 2;
break;
default:
if (0x20 <= x && x < 0x7F) {
*p++ = x;
break;
}
/* fallthrough */
case '<':
case '>':
case '&':
case '=':
w = EncodeUtf16(x);
do {
p[0] = '\\';
p[1] = 'u';
p[2] = "0123456789ABCDEF"[(w & 0xF000) >> 014];
p[3] = "0123456789ABCDEF"[(w & 0x0F00) >> 010];
p[4] = "0123456789ABCDEF"[(w & 0x00F0) >> 004];
p[5] = "0123456789ABCDEF"[(w & 0x000F) >> 000];
p += 6;
} while ((w >>= 16));
break;
}
}
r.size = p - r.data;
r.data = xrealloc(r.data, r.size + 1);
r.data[r.size] = '\0';
return r;
}

61
net/http/escapeurl.c Normal file
View file

@ -0,0 +1,61 @@
/*-*- 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/x/x.h"
#include "net/http/escape.h"
/**
* Escapes URL component using generic table.
*
* This function is agnostic to the underlying charset.
* Always using UTF-8 is a good idea.
*
* @see EscapeUrlParam
* @see EscapeUrlFragment
* @see EscapeUrlPathSegment
*/
struct EscapeResult EscapeUrl(const char *data, size_t size,
const char xlat[hasatleast 256]) {
int c;
char *p;
size_t i;
struct EscapeResult r;
p = r.data = xmalloc(size * 6 + 1);
for (i = 0; i < size; ++i) {
switch (xlat[(c = data[i] & 0xff)]) {
case 0:
*p++ = c;
break;
case 1:
*p++ = '+';
break;
case 2:
p[0] = '%';
p[1] = "0123456789ABCDEF"[(c & 0xF0) >> 4];
p[2] = "0123456789ABCDEF"[(c & 0x0F) >> 0];
p += 3;
break;
default:
unreachable;
}
}
r.size = p - r.data;
r.data = xrealloc(r.data, r.size + 1);
r.data[r.size] = '\0';
return r;
}

View file

@ -0,0 +1,52 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/x/x.h"
#include "net/http/escape.h"
// url fragment dispatch
// - 0 is -/?.~_@:!$&'()*+,;=0-9A-Za-z
// - 2 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
static const char kEscapeUrlFragment[256] = {
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x00
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x10
2, 0, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, // 0x30
0, 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, 2, 2, 2, 2, 0, // 0x50
2, 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, 2, 2, 2, 0, 2, // 0x70
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x80
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x90
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xa0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xb0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0
};
/**
* Escapes URL fragment.
*/
struct EscapeResult EscapeUrlFragment(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeUrlFragment);
}

51
net/http/escapeurlparam.c Normal file
View file

@ -0,0 +1,51 @@
/*-*- 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/x/x.h"
#include "net/http/escape.h"
// url query/form name/parameter dispatch
// - 0 is -.*_0-9A-Za-z
// - 1 is ' ' which becomes '+'
// - 2 is everything else which needs uppercase hex %XX
// note that unicode can still be wild
static const char kEscapeUrlParam[256] = {
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x00
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x10
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 0, 0, 2, // 0x20
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, // 0x30
2, 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, 2, 2, 2, 2, 0, // 0x50
2, 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, 2, 2, 2, 2, 2, // 0x70
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x80
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x90
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xa0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xb0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0
};
/**
* Escapes query/form name/parameter.
*/
struct EscapeResult EscapeUrlParam(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeUrlParam);
}

54
net/http/escapeurlpath.c Normal file
View file

@ -0,0 +1,54 @@
/*-*- 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/x/x.h"
#include "net/http/escape.h"
// url path dispatch
// - 0 is -.~_@:!$&'()*+,;=0-9A-Za-z/
// - 2 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
static const char kEscapeUrlPath[256] = {
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x00
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x10
2, 0, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 2, // 0x30
0, 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, 2, 2, 2, 2, 0, // 0x50
2, 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, 2, 2, 2, 0, 2, // 0x70
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x80
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x90
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xa0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xb0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0
};
/**
* Escapes URL path.
*
* This is the same as EscapeUrlPathSegment() except slash is allowed.
*/
struct EscapeResult EscapeUrlPath(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeUrlPath);
}

View file

@ -0,0 +1,55 @@
/*-*- 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/x/x.h"
#include "net/http/escape.h"
// url path segment dispatch
// - 0 is -.~_@:!$&'()*+,;=0-9A-Za-z
// - 2 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
static const char kEscapeUrlPathSegment[256] = {
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x00
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x10
2, 0, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, // 0x20
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 2, // 0x30
0, 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, 2, 2, 2, 2, 0, // 0x50
2, 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, 2, 2, 2, 0, 2, // 0x70
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x80
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x90
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xa0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xb0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 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.
*/
struct EscapeResult EscapeUrlPathSegment(const char *data, size_t size) {
return EscapeUrl(data, size, kEscapeUrlPathSegment);
}

View file

@ -16,6 +16,7 @@
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 "libc/time/struct/tm.h"
#include "libc/time/time.h"
@ -34,27 +35,28 @@ char *FormatHttpDateTime(char p[hasatleast 30], struct tm *tm) {
p = mempcpy(p, kWeekdayNameShort[tm->tm_wday], 3);
*p++ = ',';
*p++ = ' ';
i = tm->tm_mday;
i = MIN(MAX(tm->tm_mday, 0), 31);
*p++ = '0' + i / 10;
*p++ = '0' + i % 10;
*p++ = ' ';
p = mempcpy(p, kMonthNameShort[tm->tm_mon], 3);
i = MIN(MAX(tm->tm_mon, 0), 11);
p = mempcpy(p, kMonthNameShort[i], 3);
*p++ = ' ';
i = tm->tm_year + 1900;
i = MIN(MAX(tm->tm_year + 1900, 0), 9999);
*p++ = '0' + i / 1000;
*p++ = '0' + i / 100 % 10;
*p++ = '0' + i / 10 % 10;
*p++ = '0' + i % 10;
*p++ = ' ';
i = tm->tm_hour;
i = MIN(MAX(tm->tm_hour, 0), 23);
*p++ = '0' + i / 10;
*p++ = '0' + i % 10;
*p++ = ':';
i = tm->tm_min;
i = MIN(MAX(tm->tm_min, 0), 59);
*p++ = '0' + i / 10;
*p++ = '0' + i % 10;
*p++ = ':';
i = tm->tm_sec;
i = MIN(MAX(tm->tm_sec, 0), 59);
*p++ = '0' + i / 10;
*p++ = '0' + i % 10;
*p++ = ' ';

110
net/http/gethttpreason.c Normal file
View file

@ -0,0 +1,110 @@
/*-*- 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/fmt/itoa.h"
#include "libc/macros.internal.h"
#include "net/http/http.h"
static const struct HttpReason {
int code;
const char *name;
} kHttpReason[] = {
{100, "Continue"},
{101, "Switching Protocols"},
{102, "Processing"},
{200, "OK"},
{201, "Created"},
{202, "Accepted"},
{203, "Non-authoritative Information"},
{204, "No Content"},
{205, "Reset Content"},
{206, "Partial Content"},
{207, "Multi-Status"},
{208, "Already Reported"},
{226, "IM Used"},
{300, "Multiple Choices"},
{301, "Moved Permanently"},
{302, "Found"},
{303, "See Other"},
{304, "Not Modified"},
{305, "Use Proxy"},
{307, "Temporary Redirect"},
{308, "Permanent Redirect"},
{400, "Bad Request"},
{401, "Unauthorized"},
{402, "Payment Required"},
{403, "Forbidden"},
{404, "Not Found"},
{405, "Method Not Allowed"},
{406, "Not Acceptable"},
{407, "Proxy Authentication Required"},
{408, "Request Timeout"},
{409, "Conflict"},
{410, "Gone"},
{411, "Length Required"},
{412, "Precondition Failed"},
{413, "Payload Too Large"},
{414, "Request-URI Too Long"},
{415, "Unsupported Media Type"},
{416, "Requested Range Not Satisfiable"},
{417, "Expectation Failed"},
{418, "I'm a teapot"},
{421, "Misdirected Request"},
{422, "Unprocessable Entity"},
{423, "Locked"},
{424, "Failed Dependency"},
{426, "Upgrade Required"},
{428, "Precondition Required"},
{429, "Too Many Requests"},
{431, "Request Header Fields Too Large"},
{444, "Connection Closed Without Response"},
{451, "Unavailable For Legal Reasons"},
{499, "Client Closed Request"},
{500, "Internal Server Error"},
{501, "Not Implemented"},
{502, "Bad Gateway"},
{503, "Service Unavailable"},
{504, "Gateway Timeout"},
{505, "HTTP Version Not Supported"},
{506, "Variant Also Negotiates"},
{507, "Insufficient Storage"},
{508, "Loop Detected"},
{510, "Not Extended"},
{511, "Network Authentication Required"},
{599, "Network Connect Timeout Error"},
};
/**
* Returns string describing HTTP reason phrase.
*/
const char *GetHttpReason(int code) {
int m, l, r;
l = 0;
r = ARRAYLEN(kHttpReason) - 1;
while (l <= r) {
m = (l + r) >> 1;
if (kHttpReason[m].code < code) {
l = m + 1;
} else if (kHttpReason[m].code > code) {
r = m - 1;
} else {
return kHttpReason[m].name;
}
}
return "";
}

View file

@ -80,8 +80,8 @@ struct HttpRequestSlice {
};
struct HttpRequest {
int i, t, a, h;
int method;
int length;
struct HttpRequestSlice uri;
struct HttpRequestSlice version;
struct HttpRequestSlice scratch;
@ -92,14 +92,16 @@ extern const char kHttpMethod[17][8];
int GetHttpHeader(const char *, size_t);
int GetHttpMethod(const char *, size_t);
void InitHttpRequest(struct HttpRequest *);
int ParseHttpRequest(struct HttpRequest *, const char *, size_t);
int NegotiateHttpRequest(int, const char *, uint32_t *, char *, uint32_t *,
uint32_t *, bool, long double);
long ParseContentLength(const struct HttpRequest *, const char *);
ssize_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);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -19,16 +19,18 @@
#include "libc/str/str.h"
#include "net/http/http.h"
long ParseContentLength(const struct HttpRequest *req, const char *p) {
long i, r, n = 0;
for (i = req->headers[kHttpContentLength].a;
i < req->headers[kHttpContentLength].b; ++i) {
if (isdigit(p[i])) {
if (!__builtin_mul_overflow(n, 10, &r) &&
!__builtin_add_overflow(r, p[i] - '0', &r)) {
n = r;
/**
* Parses Content-Length header.
*
* @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 -1;
for (i = 0; i < n; ++i) {
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;
}
}
}
return n;
return r;
}

View file

@ -19,106 +19,119 @@
#include "libc/alg/alg.h"
#include "libc/alg/arraylist.internal.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
#include "libc/x/x.h"
#include "net/http/http.h"
enum ParseHttpRequestState {
METHOD,
URI,
VERSION,
HKEY,
HSEP,
HVAL,
CR1,
LF1,
LF2
};
#define LIMIT (SHRT_MAX - 1)
enum { START, METHOD, URI, VERSION, HKEY, HSEP, HVAL, CR1, LF1, LF2 };
/**
* Parses HTTP request header.
* Initializes HTTP request parser.
*/
int ParseHttpRequest(struct HttpRequest *req, const char *p, size_t n) {
int a, h, c, i, x;
enum ParseHttpRequestState t;
memset(req, 0, sizeof(*req));
a = h = 0;
t = METHOD;
if (n > SHRT_MAX - 1) n = SHRT_MAX - 1;
for (i = 0; i < n; ++i) {
c = p[i] & 0xFF;
switch (t) {
void InitHttpRequest(struct HttpRequest *r) {
memset(r, 0, sizeof(*r));
}
/**
* Parses HTTP request.
*/
int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
int c;
for (n = MIN(n, LIMIT); r->i < n; ++r->i) {
c = p[r->i] & 0xff;
switch (r->t) {
case START:
if (c == '\r' || c == '\n') {
++r->a; /* RFC7230 § 3.5 */
break;
}
r->t = METHOD;
/* fallthrough */
case METHOD:
if (c == '\r' || c == '\n') break; /* RFC7230 § 3.5 */
if (c == ' ') {
if (!i) return ebadmsg();
if ((x = GetHttpMethod(p, i)) == -1) return ebadmsg();
req->method = x;
req->uri.a = i + 1;
t = URI;
if ((r->method = GetHttpMethod(p + r->a, r->i - r->a)) != -1) {
r->uri.a = r->i + 1;
r->t = URI;
} else {
return ebadmsg();
}
}
break;
case URI:
if (c == ' ' || c == '\r' || c == '\n') {
if (r->i == r->uri.a) return ebadmsg();
r->uri.b = r->i;
if (c == ' ') {
req->uri.b = i;
req->version.a = i + 1;
if (req->uri.a == req->uri.b) return ebadmsg();
t = VERSION;
r->version.a = r->i + 1;
r->t = VERSION;
} else if (c == '\r') {
r->t = CR1;
} else {
r->t = LF1;
}
}
break;
case VERSION:
if (c == '\r' || c == '\n') {
req->version.b = i;
t = c == '\r' ? CR1 : LF1;
r->version.b = r->i;
r->t = c == '\r' ? CR1 : LF1;
}
break;
case CR1:
if (c != '\n') return ebadmsg();
t = LF1;
r->t = LF1;
break;
case LF1:
if (c == '\r') {
t = LF2;
r->t = LF2;
break;
} else if (c == '\n') {
return 0;
} else if (c == ' ' || c == '\t') { /* line folding!!! */
return eprotonosupport(); /* RFC7230 § 3.2.4 */
return ++r->i;
} else if (c == ':') {
return ebadmsg();
} else if (c == ' ' || c == '\t') {
return ebadmsg(); /* RFC7230 § 3.2.4 */
}
a = i;
t = HKEY;
/* εpsilon transition */
r->a = r->i;
r->t = HKEY;
break;
case HKEY:
if (c == ':') {
h = GetHttpHeader(p + a, i - a);
t = HSEP;
r->h = GetHttpHeader(p + r->a, r->i - r->a);
r->t = HSEP;
}
break;
case HSEP:
if (c == ' ' || c == '\t') break;
a = i;
t = HVAL;
/* εpsilon transition */
r->a = r->i;
r->t = HVAL;
/* fallthrough */
case HVAL:
if (c == '\r' || c == '\n') {
if (h != -1) {
req->headers[h].a = a;
req->headers[h].b = i;
if (r->h != -1) {
r->headers[r->h].a = r->a;
r->headers[r->h].b = r->i;
}
t = c == '\r' ? CR1 : LF1;
r->t = c == '\r' ? CR1 : LF1;
}
break;
case LF2:
if (c == '\n') {
req->length = i + 1;
return i + 1;
return ++r->i;
}
return ebadmsg();
default:
unreachable;
}
}
if (r->i < LIMIT) {
return 0;
} else {
return ebadmsg();
}
}

View file

@ -21,6 +21,7 @@
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;

View file

@ -0,0 +1,41 @@
/*-*- 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/runtime/gc.internal.h"
#include "libc/testlib/testlib.h"
#include "net/http/escape.h"
char *escapehtml(const char *s) {
struct EscapeResult r;
r = EscapeHtml(s, strlen(s));
ASSERT_EQ(strlen(r.data), r.size);
return r.data;
}
TEST(escapehtml, test) {
EXPECT_STREQ("abc&amp;&lt;&gt;&quot;&#39;\1\2",
gc(escapehtml("abc&<>\"'\1\2")));
}
TEST(escapehtml, testLargeGrowth) {
EXPECT_STREQ("&quot;&quot;&quot;", gc(escapehtml("\"\"\"")));
}
TEST(escapehtml, testEmpty) {
EXPECT_STREQ("", gc(escapehtml("")));
}

View file

@ -0,0 +1,32 @@
/*-*- 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(ParseContentLength, test) {
EXPECT_EQ(-1, ParseContentLength("", 0));
EXPECT_EQ(-1, ParseContentLength("-1", 2));
EXPECT_EQ(-1, ParseContentLength("-2", 2));
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));
}

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/errno.h"
#include "libc/mem/mem.h"
#include "libc/runtime/gc.internal.h"
#include "libc/stdio/stdio.h"
@ -24,6 +25,7 @@
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
#include "net/http/http.h"
#include "net/http/uri.h"
struct HttpRequest req[1];
@ -35,16 +37,23 @@ 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);
}
TEST(ParseHttpRequest, testEmpty_tooShort) {
InitHttpRequest(req);
EXPECT_EQ(0, ParseHttpRequest(req, "", 0));
}
TEST(ParseHttpRequest, testTooShort) {
InitHttpRequest(req);
EXPECT_EQ(0, ParseHttpRequest(req, "\r\n", 2));
}
TEST(ParseHttpRequest, testNoHeaders) {
static const char m[] = "GET /foo HTTP/1.0\r\n\r\n";
InitHttpRequest(req);
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_EQ(kHttpGet, req->method);
EXPECT_STREQ("/foo", gc(slice(m, req->uri)));
@ -57,6 +66,7 @@ POST /foo?bar%20hi HTTP/1.0\r\n\
Host: foo.example\r\n\
Content-Length: 0\r\n\
\r\n";
InitHttpRequest(req);
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_EQ(kHttpPost, req->method);
EXPECT_STREQ("/foo?bar%20hi", gc(slice(m, req->uri)));
@ -65,3 +75,83 @@ Content-Length: 0\r\n\
EXPECT_STREQ("0", gc(slice(m, req->headers[kHttpContentLength])));
EXPECT_STREQ("", gc(slice(m, req->headers[kHttpEtag])));
}
TEST(ParseHttpRequest, testHttp101) {
static const char m[] = "GET / HTTP/1.1\r\n\r\n";
InitHttpRequest(req);
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));
}
TEST(ParseHttpRequest, testHttp100) {
static const char m[] = "GET / HTTP/1.0\r\n\r\n";
InitHttpRequest(req);
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));
}
TEST(ParseHttpRequest, testHttp009) {
static const char m[] = "GET /\r\n\r\n";
InitHttpRequest(req);
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));
}
TEST(ParseHttpRequest, testLeadingLineFeeds_areIgnored) {
static const char m[] = "\
\r\n\
GET /foo?bar%20hi HTTP/1.0\r\n\
User-Agent: hi\r\n\
\r\n";
InitHttpRequest(req);
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
EXPECT_STREQ("/foo?bar%20hi", gc(slice(m, req->uri)));
}
TEST(ParseHttpRequest, testLineFolding_isRejected) {
static const char m[] = "\
GET /foo?bar%20hi HTTP/1.0\r\n\
User-Agent: hi\r\n\
there\r\n\
\r\n";
InitHttpRequest(req);
EXPECT_EQ(-1, ParseHttpRequest(req, m, strlen(m)));
EXPECT_EQ(EBADMSG, errno);
}
TEST(ParseHttpRequest, testEmptyHeaderName_isRejected) {
static const char m[] = "\
GET /foo?bar%20hi HTTP/1.0\r\n\
User-Agent: hi\r\n\
: hi\r\n\
\r\n";
InitHttpRequest(req);
EXPECT_EQ(-1, ParseHttpRequest(req, m, strlen(m)));
EXPECT_EQ(EBADMSG, errno);
}
TEST(ParseHttpRequest, testUnixNewlines) {
static const char m[] = "\
POST /foo?bar%20hi HTTP/1.0\n\
Host: foo.example\n\
Content-Length: 0\n\
\n\
\n";
InitHttpRequest(req);
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_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])));
}

View file

@ -350,7 +350,7 @@ static void PrintText(FILE *f, const char *s) {
bol = false;
break;
case '\'':
fprintf(f, "&apos;");
fprintf(f, "&#39;");
bol = false;
break;
case '`':

View file

@ -479,7 +479,7 @@ static int luaB_tostring (lua_State *L) {
static const luaL_Reg base_funcs[] = {
{"assert", luaB_assert},
{"collectgarbage", luaB_collectgarbage},
{"dofile", luaB_dofile},
/* {"dofile", luaB_dofile}, */
{"error", luaB_error},
{"getmetatable", luaB_getmetatable},
{"ipairs", luaB_ipairs},

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,48 @@
send('HTTP/1.1 200 OK\r\n'..
'Date: ' .. date() .. '\r\n'..
'Server: redbean/0.1\r\n'..
'Content-Type: text/plain\r\n'..
'Content-Length: 7\r\n'..
'\r\n'..
'hello\r\n')
-- redbean lua server page demo
local function main()
-- This check is optional.
-- We do this by default if you don't call GetMethod().
if GetMethod() ~= 'GET' and GetMethod() ~= 'HEAD' then
ServeError(405)
SetHeader('Allow', 'GET, HEAD')
return
end
-- These two lines are optional.
-- The default behavior is to do this if you don't.
SetStatus(200) -- Shorthand for SetStatus(200, "OK")
SetHeader('Content-Type', 'text/html; charset=utf-8')
-- 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')
-- 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')
for i = 1,#params do
Write('<dt>')
Write(EscapeHtml(params[i][1]))
Write('\n')
if params[i][2] then
Write('<dd>')
Write(EscapeHtml(params[i][2]))
Write('\n')
end
end
Write('</dl>\n')
else
Write('<p>\n')
Write('<em>none</em><br>\n')
Write('ProTip: Try <a href="')
Write(EscapeHtml(EscapePath(GetPath()) .. '?x=hi+there&y&z&z=' .. EscapeParam('&')))
Write('">clicking here</a>!\n')
end
end
main()