mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-04-17 20:34:40 +00:00
Merge HTTP request / response parsing code
This change also fixes a bug so that DNS lookups work correctly when the first answer is a CNAME record.
This commit is contained in:
parent
5a6c0f27c3
commit
a68cc690ff
20 changed files with 561 additions and 616 deletions
|
@ -86,6 +86,7 @@ o/$(MODE)/examples/examples.pkg: \
|
||||||
$(EXAMPLES_OBJS) \
|
$(EXAMPLES_OBJS) \
|
||||||
$(foreach x,$(EXAMPLES_DIRECTDEPS),$($(x)_A).pkg)
|
$(foreach x,$(EXAMPLES_DIRECTDEPS),$($(x)_A).pkg)
|
||||||
|
|
||||||
|
o/$(MODE)/examples/zodiac.o \
|
||||||
o/$(MODE)/examples/unbourne.o: \
|
o/$(MODE)/examples/unbourne.o: \
|
||||||
OVERRIDE_CPPFLAGS += \
|
OVERRIDE_CPPFLAGS += \
|
||||||
-DSTACK_FRAME_UNLIMITED
|
-DSTACK_FRAME_UNLIMITED
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_DNS_CONSTS_H_
|
#ifndef COSMOPOLITAN_LIBC_DNS_CONSTS_H_
|
||||||
#define COSMOPOLITAN_LIBC_DNS_CONSTS_H_
|
#define COSMOPOLITAN_LIBC_DNS_CONSTS_H_
|
||||||
#include "libc/sock/sock.h"
|
#include "libc/sock/sock.h"
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
|
||||||
|
|
||||||
#define DNS_TYPE_A 0x01
|
#define DNS_TYPE_A 1
|
||||||
#define DNS_TYPE_PTR 0x0c
|
#define DNS_TYPE_NS 2
|
||||||
|
#define DNS_TYPE_CNAME 5
|
||||||
|
#define DNS_TYPE_SOA 6
|
||||||
|
#define DNS_TYPE_PTR 12
|
||||||
|
#define DNS_TYPE_MX 15
|
||||||
|
#define DNS_TYPE_TXT 16
|
||||||
|
|
||||||
#define DNS_CLASS_IN 1
|
#define DNS_CLASS_IN 1
|
||||||
|
|
||||||
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
|
||||||
#define kMinSockaddr4Size \
|
#define kMinSockaddr4Size \
|
||||||
(offsetof(struct sockaddr_in, sin_addr) + sizeof(struct in_addr))
|
(offsetof(struct sockaddr_in, sin_addr) + sizeof(struct in_addr))
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "libc/rand/rand.h"
|
#include "libc/rand/rand.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/sock/sock.h"
|
#include "libc/sock/sock.h"
|
||||||
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/af.h"
|
#include "libc/sysv/consts/af.h"
|
||||||
#include "libc/sysv/consts/ipproto.h"
|
#include "libc/sysv/consts/ipproto.h"
|
||||||
|
@ -49,12 +50,14 @@
|
||||||
*/
|
*/
|
||||||
int ResolveDns(const struct ResolvConf *resolvconf, int af, const char *name,
|
int ResolveDns(const struct ResolvConf *resolvconf, int af, const char *name,
|
||||||
struct sockaddr *addr, uint32_t addrsize) {
|
struct sockaddr *addr, uint32_t addrsize) {
|
||||||
|
int32_t ttl;
|
||||||
int rc, fd, n;
|
int rc, fd, n;
|
||||||
struct DnsQuestion q;
|
struct DnsQuestion q;
|
||||||
struct DnsHeader h, h2;
|
struct DnsHeader h, h2;
|
||||||
struct sockaddr_in *a4;
|
struct sockaddr_in *a4;
|
||||||
uint8_t *p, *pe, msg[512];
|
uint8_t *p, *pe, msg[512];
|
||||||
uint16_t rtype, rclass, rdlength;
|
uint16_t rtype, rclass, rdlength;
|
||||||
|
if (addrsize < kMinSockaddr4Size) return einval();
|
||||||
if (af != AF_INET && af != AF_UNSPEC) return eafnosupport();
|
if (af != AF_INET && af != AF_UNSPEC) return eafnosupport();
|
||||||
if (!resolvconf->nameservers.i) return 0;
|
if (!resolvconf->nameservers.i) return 0;
|
||||||
memset(&h, 0, sizeof(h));
|
memset(&h, 0, sizeof(h));
|
||||||
|
@ -75,38 +78,31 @@ int ResolveDns(const struct ResolvConf *resolvconf, int af, const char *name,
|
||||||
DeserializeDnsHeader(&h2, msg);
|
DeserializeDnsHeader(&h2, msg);
|
||||||
if (h2.id == h.id) {
|
if (h2.id == h.id) {
|
||||||
rc = 0;
|
rc = 0;
|
||||||
if (h2.ancount) {
|
p = msg + 12;
|
||||||
p = msg + 12;
|
pe = msg + n;
|
||||||
pe = msg + n;
|
while (p < pe && h2.qdcount--) {
|
||||||
while (p < pe && h2.qdcount) {
|
p += strnlen((char *)p, pe - p) + 1 + 4;
|
||||||
p += strnlen((char *)p, pe - p) + 1 + 4;
|
}
|
||||||
h2.qdcount--;
|
while (p < pe && h2.ancount--) {
|
||||||
|
if ((p[0] & 0xc0) == 0xc0) { /* name pointer */
|
||||||
|
p += 2;
|
||||||
|
} else {
|
||||||
|
p += strnlen((char *)p, pe - p) + 1;
|
||||||
}
|
}
|
||||||
if (p + 1 < pe) {
|
if (p + 10 <= pe) {
|
||||||
if ((p[0] & 0b11000000) == 0b11000000) { /* name pointer */
|
rtype = READ16BE(p);
|
||||||
p += 2;
|
rclass = READ16BE(p + 2);
|
||||||
} else {
|
ttl = READ32BE(p + 4);
|
||||||
p += strnlen((char *)p, pe - p) + 1;
|
rdlength = READ16BE(p + 8);
|
||||||
}
|
if (p + 10 + rdlength <= pe && rdlength == 4 &&
|
||||||
if (p + 2 + 2 + 4 + 2 < pe) {
|
rclass == DNS_CLASS_IN && rtype == DNS_TYPE_A) {
|
||||||
rtype = READ16BE(p), p += 2;
|
rc = 1;
|
||||||
rclass = READ16BE(p), p += 2;
|
a4 = (struct sockaddr_in *)addr;
|
||||||
/* ttl */ p += 4;
|
a4->sin_family = AF_INET;
|
||||||
rdlength = READ16BE(p), p += 2;
|
memcpy(&a4->sin_addr.s_addr, p + 10, 4);
|
||||||
if (p + rdlength <= pe && rdlength == 4 &&
|
break;
|
||||||
(rtype == DNS_TYPE_A && rclass == DNS_CLASS_IN)) {
|
|
||||||
rc = 1;
|
|
||||||
if (addrsize) {
|
|
||||||
if (addrsize >= kMinSockaddr4Size) {
|
|
||||||
a4 = (struct sockaddr_in *)addr;
|
|
||||||
a4->sin_family = AF_INET;
|
|
||||||
memcpy(&a4->sin_addr.s_addr, p, 4);
|
|
||||||
} else {
|
|
||||||
rc = einval();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
p += 10 + rdlength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,11 +125,11 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int PrintBacktrace(int fd, const struct StackFrame *bp) {
|
static int PrintBacktrace(int fd, const struct StackFrame *bp) {
|
||||||
if (!IsTiny()) {
|
/* if (!IsTiny()) { */
|
||||||
if (PrintBacktraceUsingAddr2line(fd, bp) != -1) {
|
if (PrintBacktraceUsingAddr2line(fd, bp) != -1) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
/* } */
|
||||||
return PrintBacktraceUsingSymbols(fd, bp, GetSymbolTable());
|
return PrintBacktraceUsingSymbols(fd, bp, GetSymbolTable());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,3 +85,11 @@ Uri, kHttpUri
|
||||||
Warning, kHttpWarning
|
Warning, kHttpWarning
|
||||||
WWW-Authenticate, kHttpWwwAuthenticate
|
WWW-Authenticate, kHttpWwwAuthenticate
|
||||||
Via, kHttpVia
|
Via, kHttpVia
|
||||||
|
Strict-Transport-Security, kHttpStrictTransportSecurity
|
||||||
|
X-Frame-Options, kHttpXFrameOptions
|
||||||
|
X-Content-Type-Options, kHttpXContentTypeOptions
|
||||||
|
Alt-Svc, kHttpAltSvc
|
||||||
|
Referrer-Policy, kHttpReferrerPolicy
|
||||||
|
X-XSS-Protection, kHttpXXssProtection
|
||||||
|
Accept-Ranges, kHttpAcceptRanges
|
||||||
|
Set-Cookie, kHttpSetCookie
|
||||||
|
|
|
@ -38,12 +38,12 @@
|
||||||
#line 12 "gethttpheader.gperf"
|
#line 12 "gethttpheader.gperf"
|
||||||
struct thatispacked HttpHeaderSlot { char *name; char code; };
|
struct thatispacked HttpHeaderSlot { char *name; char code; };
|
||||||
|
|
||||||
#define TOTAL_KEYWORDS 73
|
#define TOTAL_KEYWORDS 82
|
||||||
#define MIN_WORD_LENGTH 2
|
#define MIN_WORD_LENGTH 2
|
||||||
#define MAX_WORD_LENGTH 32
|
#define MAX_WORD_LENGTH 32
|
||||||
#define MIN_HASH_VALUE 6
|
#define MIN_HASH_VALUE 6
|
||||||
#define MAX_HASH_VALUE 142
|
#define MAX_HASH_VALUE 133
|
||||||
/* maximum key range = 137, duplicates = 0 */
|
/* maximum key range = 128, duplicates = 0 */
|
||||||
|
|
||||||
#ifndef GPERF_DOWNCASE
|
#ifndef GPERF_DOWNCASE
|
||||||
#define GPERF_DOWNCASE 1
|
#define GPERF_DOWNCASE 1
|
||||||
|
@ -72,7 +72,7 @@ static unsigned char gperf_downcase[256] =
|
||||||
|
|
||||||
#ifndef GPERF_CASE_STRNCMP
|
#ifndef GPERF_CASE_STRNCMP
|
||||||
#define GPERF_CASE_STRNCMP 1
|
#define GPERF_CASE_STRNCMP 1
|
||||||
static inline int
|
static int
|
||||||
gperf_case_strncmp (register const char *s1, register const char *s2, register size_t n)
|
gperf_case_strncmp (register const char *s1, register const char *s2, register size_t n)
|
||||||
{
|
{
|
||||||
for (; n > 0;)
|
for (; n > 0;)
|
||||||
|
@ -102,32 +102,32 @@ hash (register const char *str, register size_t len)
|
||||||
{
|
{
|
||||||
static const unsigned char asso_values[] =
|
static const unsigned char asso_values[] =
|
||||||
{
|
{
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 20, 134, 134, 134, 134,
|
||||||
143, 143, 143, 0, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 0, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 0, 143, 20, 75, 5,
|
134, 134, 134, 134, 134, 0, 134, 20, 95, 5,
|
||||||
35, 30, 15, 30, 143, 55, 15, 40, 0, 65,
|
10, 70, 15, 30, 134, 5, 15, 20, 0, 75,
|
||||||
30, 143, 35, 20, 0, 50, 10, 30, 55, 45,
|
40, 134, 35, 20, 0, 50, 25, 30, 0, 30,
|
||||||
143, 143, 143, 143, 143, 143, 143, 0, 143, 20,
|
134, 134, 134, 134, 134, 134, 134, 0, 134, 20,
|
||||||
75, 5, 35, 30, 15, 30, 143, 55, 15, 40,
|
95, 5, 10, 70, 15, 30, 134, 5, 15, 20,
|
||||||
0, 65, 30, 143, 35, 20, 0, 50, 10, 30,
|
0, 75, 40, 134, 35, 20, 0, 50, 25, 30,
|
||||||
55, 45, 143, 143, 143, 143, 143, 143, 143, 143,
|
0, 30, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
|
134, 134, 134, 134, 134, 134, 134, 134, 134, 134,
|
||||||
143, 143, 143, 143, 143, 143
|
134, 134, 134, 134, 134, 134
|
||||||
};
|
};
|
||||||
register unsigned int hval = len;
|
register unsigned int hval = len;
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ hash (register const char *str, register size_t len)
|
||||||
return hval + asso_values[(unsigned char)str[len - 1]];
|
return hval + asso_values[(unsigned char)str[len - 1]];
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline const struct thatispacked HttpHeaderSlot *
|
static const struct thatispacked HttpHeaderSlot *
|
||||||
LookupHttpHeader (register const char *str, register size_t len)
|
LookupHttpHeader (register const char *str, register size_t len)
|
||||||
{
|
{
|
||||||
static const struct thatispacked HttpHeaderSlot wordlist[] =
|
static const struct thatispacked HttpHeaderSlot wordlist[] =
|
||||||
|
@ -185,18 +185,31 @@ LookupHttpHeader (register const char *str, register size_t len)
|
||||||
{"Authorization", kHttpAuthorization},
|
{"Authorization", kHttpAuthorization},
|
||||||
#line 48 "gethttpheader.gperf"
|
#line 48 "gethttpheader.gperf"
|
||||||
{"Accept-Charset", kHttpAcceptCharset},
|
{"Accept-Charset", kHttpAcceptCharset},
|
||||||
{""}, {""}, {""}, {""},
|
{""},
|
||||||
|
#line 93 "gethttpheader.gperf"
|
||||||
|
{"X-XSS-Protection", kHttpXXssProtection},
|
||||||
|
#line 34 "gethttpheader.gperf"
|
||||||
|
{"X-CSRF-Token", kHttpXCsrfToken},
|
||||||
|
{""},
|
||||||
#line 14 "gethttpheader.gperf"
|
#line 14 "gethttpheader.gperf"
|
||||||
{"Host", kHttpHost},
|
{"Host", kHttpHost},
|
||||||
#line 18 "gethttpheader.gperf"
|
#line 18 "gethttpheader.gperf"
|
||||||
{"Accept-Language", kHttpAcceptLanguage},
|
{"Accept-Language", kHttpAcceptLanguage},
|
||||||
{""}, {""},
|
#line 32 "gethttpheader.gperf"
|
||||||
|
{"X-Forwarded-Host", kHttpXForwardedHost},
|
||||||
|
{""},
|
||||||
#line 73 "gethttpheader.gperf"
|
#line 73 "gethttpheader.gperf"
|
||||||
{"Location", kHttpLocation},
|
{"Location", kHttpLocation},
|
||||||
{""}, {""},
|
#line 72 "gethttpheader.gperf"
|
||||||
|
{"Link", kHttpLink},
|
||||||
|
#line 71 "gethttpheader.gperf"
|
||||||
|
{"Keep-Alive", kHttpKeepAlive},
|
||||||
#line 53 "gethttpheader.gperf"
|
#line 53 "gethttpheader.gperf"
|
||||||
{"Access-Control-MaxAge", kHttpAccessControlMaxAge},
|
{"Access-Control-MaxAge", kHttpAccessControlMaxAge},
|
||||||
{""}, {""},
|
#line 91 "gethttpheader.gperf"
|
||||||
|
{"Alt-Svc", kHttpAltSvc},
|
||||||
|
#line 87 "gethttpheader.gperf"
|
||||||
|
{"Via", kHttpVia},
|
||||||
#line 35 "gethttpheader.gperf"
|
#line 35 "gethttpheader.gperf"
|
||||||
{"Save-Data", kHttpSaveData},
|
{"Save-Data", kHttpSaveData},
|
||||||
#line 16 "gethttpheader.gperf"
|
#line 16 "gethttpheader.gperf"
|
||||||
|
@ -205,18 +218,21 @@ LookupHttpHeader (register const char *str, register size_t len)
|
||||||
{"Cookie", kHttpCookie},
|
{"Cookie", kHttpCookie},
|
||||||
#line 42 "gethttpheader.gperf"
|
#line 42 "gethttpheader.gperf"
|
||||||
{"Expires", kHttpExpires},
|
{"Expires", kHttpExpires},
|
||||||
{""}, {""},
|
#line 94 "gethttpheader.gperf"
|
||||||
|
{"Accept-Ranges", kHttpAcceptRanges},
|
||||||
|
#line 29 "gethttpheader.gperf"
|
||||||
|
{"From", kHttpFrom},
|
||||||
#line 46 "gethttpheader.gperf"
|
#line 46 "gethttpheader.gperf"
|
||||||
{"Allow", kHttpAllow},
|
{"Allow", kHttpAllow},
|
||||||
#line 25 "gethttpheader.gperf"
|
#line 31 "gethttpheader.gperf"
|
||||||
{"Pragma", kHttpPragma},
|
{"X-Requested-With", kHttpXRequestedWith},
|
||||||
#line 60 "gethttpheader.gperf"
|
#line 60 "gethttpheader.gperf"
|
||||||
{"Content-Base", kHttpContentBase},
|
{"Content-Base", kHttpContentBase},
|
||||||
#line 47 "gethttpheader.gperf"
|
#line 47 "gethttpheader.gperf"
|
||||||
{"Content-Range", kHttpContentRange},
|
{"Content-Range", kHttpContentRange},
|
||||||
#line 45 "gethttpheader.gperf"
|
|
||||||
{"ETag", kHttpEtag},
|
|
||||||
{""},
|
{""},
|
||||||
|
#line 95 "gethttpheader.gperf"
|
||||||
|
{"Set-Cookie", kHttpSetCookie},
|
||||||
#line 63 "gethttpheader.gperf"
|
#line 63 "gethttpheader.gperf"
|
||||||
{"Content-Language", kHttpContentLanguage},
|
{"Content-Language", kHttpContentLanguage},
|
||||||
#line 81 "gethttpheader.gperf"
|
#line 81 "gethttpheader.gperf"
|
||||||
|
@ -227,8 +243,8 @@ LookupHttpHeader (register const char *str, register size_t len)
|
||||||
{"Content-Description", kHttpContentDescription},
|
{"Content-Description", kHttpContentDescription},
|
||||||
#line 36 "gethttpheader.gperf"
|
#line 36 "gethttpheader.gperf"
|
||||||
{"Range", kHttpRange},
|
{"Range", kHttpRange},
|
||||||
#line 77 "gethttpheader.gperf"
|
#line 25 "gethttpheader.gperf"
|
||||||
{"Proxy-Connection", kHttpProxyConnection},
|
{"Pragma", kHttpPragma},
|
||||||
#line 28 "gethttpheader.gperf"
|
#line 28 "gethttpheader.gperf"
|
||||||
{"Sec-GPC", kHttpSecGpc},
|
{"Sec-GPC", kHttpSecGpc},
|
||||||
#line 15 "gethttpheader.gperf"
|
#line 15 "gethttpheader.gperf"
|
||||||
|
@ -239,15 +255,15 @@ LookupHttpHeader (register const char *str, register size_t len)
|
||||||
{"Access-Control-Request-Methods", kHttpAccessControlRequestMethods},
|
{"Access-Control-Request-Methods", kHttpAccessControlRequestMethods},
|
||||||
#line 86 "gethttpheader.gperf"
|
#line 86 "gethttpheader.gperf"
|
||||||
{"WWW-Authenticate", kHttpWwwAuthenticate},
|
{"WWW-Authenticate", kHttpWwwAuthenticate},
|
||||||
#line 82 "gethttpheader.gperf"
|
{""},
|
||||||
{"Transfer-Encoding", kHttpTransferEncoding},
|
|
||||||
#line 67 "gethttpheader.gperf"
|
#line 67 "gethttpheader.gperf"
|
||||||
{"If-Match", kHttpIfMatch},
|
{"If-Match", kHttpIfMatch},
|
||||||
#line 37 "gethttpheader.gperf"
|
#line 37 "gethttpheader.gperf"
|
||||||
{"Content-Length", kHttpContentLength},
|
{"Content-Length", kHttpContentLength},
|
||||||
{""},
|
#line 22 "gethttpheader.gperf"
|
||||||
#line 78 "gethttpheader.gperf"
|
{"X-Forwarded-For", kHttpXForwardedFor},
|
||||||
{"Public", kHttpPublic},
|
#line 77 "gethttpheader.gperf"
|
||||||
|
{"Proxy-Connection", kHttpProxyConnection},
|
||||||
#line 30 "gethttpheader.gperf"
|
#line 30 "gethttpheader.gperf"
|
||||||
{"If-Modified-Since", kHttpIfModifiedSince},
|
{"If-Modified-Since", kHttpIfModifiedSince},
|
||||||
#line 68 "gethttpheader.gperf"
|
#line 68 "gethttpheader.gperf"
|
||||||
|
@ -262,86 +278,87 @@ LookupHttpHeader (register const char *str, register size_t len)
|
||||||
{"Upgrade", kHttpUpgrade},
|
{"Upgrade", kHttpUpgrade},
|
||||||
#line 50 "gethttpheader.gperf"
|
#line 50 "gethttpheader.gperf"
|
||||||
{"Access-Control-Allow-Headers", kHttpAccessControlAllowHeaders},
|
{"Access-Control-Allow-Headers", kHttpAccessControlAllowHeaders},
|
||||||
|
#line 70 "gethttpheader.gperf"
|
||||||
|
{"If-Unmodified-Since", kHttpIfUnmodifiedSince},
|
||||||
|
{""},
|
||||||
|
#line 78 "gethttpheader.gperf"
|
||||||
|
{"Public", kHttpPublic},
|
||||||
|
#line 38 "gethttpheader.gperf"
|
||||||
|
{"Content-Type", kHttpContentType},
|
||||||
|
#line 51 "gethttpheader.gperf"
|
||||||
|
{"Access-Control-Allow-Methods", kHttpAccessControlAllowMethods},
|
||||||
|
#line 62 "gethttpheader.gperf"
|
||||||
|
{"Content-Disposition", kHttpContentDisposition},
|
||||||
|
{""}, {""},
|
||||||
|
#line 49 "gethttpheader.gperf"
|
||||||
|
{"Access-Control-Allow-Credentials", kHttpAccessControlAllowCredentials},
|
||||||
|
{""},
|
||||||
#line 76 "gethttpheader.gperf"
|
#line 76 "gethttpheader.gperf"
|
||||||
{"Proxy-Authorization", kHttpProxyAuthorization},
|
{"Proxy-Authorization", kHttpProxyAuthorization},
|
||||||
|
#line 89 "gethttpheader.gperf"
|
||||||
|
{"X-Frame-Options", kHttpXFrameOptions},
|
||||||
|
{""},
|
||||||
|
#line 21 "gethttpheader.gperf"
|
||||||
|
{"Referer", kHttpReferer},
|
||||||
|
#line 75 "gethttpheader.gperf"
|
||||||
|
{"Proxy-Authenticate", kHttpProxyAuthenticate},
|
||||||
|
#line 45 "gethttpheader.gperf"
|
||||||
|
{"ETag", kHttpEtag},
|
||||||
|
{""},
|
||||||
|
#line 23 "gethttpheader.gperf"
|
||||||
|
{"Origin", kHttpOrigin},
|
||||||
|
#line 90 "gethttpheader.gperf"
|
||||||
|
{"X-Content-Type-Options", kHttpXContentTypeOptions},
|
||||||
|
#line 84 "gethttpheader.gperf"
|
||||||
|
{"Uri", kHttpUri},
|
||||||
|
{""}, {""},
|
||||||
|
#line 79 "gethttpheader.gperf"
|
||||||
|
{"Retry-After", kHttpRetryAfter},
|
||||||
|
#line 74 "gethttpheader.gperf"
|
||||||
|
{"Max-Forwards", kHttpMaxForwards},
|
||||||
|
{""}, {""}, {""}, {""},
|
||||||
|
#line 82 "gethttpheader.gperf"
|
||||||
|
{"Transfer-Encoding", kHttpTransferEncoding},
|
||||||
|
{""}, {""}, {""}, {""},
|
||||||
|
#line 33 "gethttpheader.gperf"
|
||||||
|
{"X-Forwarded-Proto", kHttpXForwardedProto},
|
||||||
|
#line 27 "gethttpheader.gperf"
|
||||||
|
{"DNT", kHttpDnt},
|
||||||
|
{""},
|
||||||
|
#line 24 "gethttpheader.gperf"
|
||||||
|
{"Upgrade-Insecure-Requests", kHttpUpgradeInsecureRequests},
|
||||||
|
{""},
|
||||||
|
#line 52 "gethttpheader.gperf"
|
||||||
|
{"Access-Control-Allow-Origin", kHttpAccessControlAllowOrigin},
|
||||||
|
{""},
|
||||||
|
#line 40 "gethttpheader.gperf"
|
||||||
|
{"Date", kHttpDate},
|
||||||
#line 19 "gethttpheader.gperf"
|
#line 19 "gethttpheader.gperf"
|
||||||
{"Accept-Encoding", kHttpAcceptEncoding},
|
{"Accept-Encoding", kHttpAcceptEncoding},
|
||||||
#line 43 "gethttpheader.gperf"
|
#line 43 "gethttpheader.gperf"
|
||||||
{"Content-Encoding", kHttpContentEncoding},
|
{"Content-Encoding", kHttpContentEncoding},
|
||||||
#line 85 "gethttpheader.gperf"
|
#line 85 "gethttpheader.gperf"
|
||||||
{"Warning", kHttpWarning},
|
{"Warning", kHttpWarning},
|
||||||
#line 75 "gethttpheader.gperf"
|
|
||||||
{"Proxy-Authenticate", kHttpProxyAuthenticate},
|
|
||||||
#line 62 "gethttpheader.gperf"
|
|
||||||
{"Content-Disposition", kHttpContentDisposition},
|
|
||||||
{""},
|
|
||||||
#line 23 "gethttpheader.gperf"
|
|
||||||
{"Origin", kHttpOrigin},
|
|
||||||
#line 49 "gethttpheader.gperf"
|
|
||||||
{"Access-Control-Allow-Credentials", kHttpAccessControlAllowCredentials},
|
|
||||||
{""},
|
|
||||||
#line 72 "gethttpheader.gperf"
|
|
||||||
{"Link", kHttpLink},
|
|
||||||
#line 71 "gethttpheader.gperf"
|
|
||||||
{"Keep-Alive", kHttpKeepAlive},
|
|
||||||
#line 32 "gethttpheader.gperf"
|
|
||||||
{"X-Forwarded-Host", kHttpXForwardedHost},
|
|
||||||
#line 21 "gethttpheader.gperf"
|
|
||||||
{"Referer", kHttpReferer},
|
|
||||||
#line 27 "gethttpheader.gperf"
|
|
||||||
{"DNT", kHttpDnt},
|
|
||||||
#line 29 "gethttpheader.gperf"
|
|
||||||
{"From", kHttpFrom},
|
|
||||||
{""}, {""},
|
{""}, {""},
|
||||||
#line 38 "gethttpheader.gperf"
|
#line 88 "gethttpheader.gperf"
|
||||||
{"Content-Type", kHttpContentType},
|
{"Strict-Transport-Security", kHttpStrictTransportSecurity},
|
||||||
#line 84 "gethttpheader.gperf"
|
#line 64 "gethttpheader.gperf"
|
||||||
{"Uri", kHttpUri},
|
{"Content-Location", kHttpContentLocation},
|
||||||
#line 40 "gethttpheader.gperf"
|
{""}, {""}, {""}, {""},
|
||||||
{"Date", kHttpDate},
|
|
||||||
{""},
|
|
||||||
#line 79 "gethttpheader.gperf"
|
|
||||||
{"Retry-After", kHttpRetryAfter},
|
|
||||||
{""},
|
|
||||||
#line 51 "gethttpheader.gperf"
|
|
||||||
{"Access-Control-Allow-Methods", kHttpAccessControlAllowMethods},
|
|
||||||
#line 70 "gethttpheader.gperf"
|
|
||||||
{"If-Unmodified-Since", kHttpIfUnmodifiedSince},
|
|
||||||
{""},
|
|
||||||
#line 31 "gethttpheader.gperf"
|
|
||||||
{"X-Requested-With", kHttpXRequestedWith},
|
|
||||||
#line 52 "gethttpheader.gperf"
|
|
||||||
{"Access-Control-Allow-Origin", kHttpAccessControlAllowOrigin},
|
|
||||||
{""}, {""}, {""},
|
|
||||||
#line 54 "gethttpheader.gperf"
|
#line 54 "gethttpheader.gperf"
|
||||||
{"Access-Control-Method", kHttpAccessControlMethod},
|
{"Access-Control-Method", kHttpAccessControlMethod},
|
||||||
{""}, {""}, {""},
|
{""}, {""}, {""},
|
||||||
#line 24 "gethttpheader.gperf"
|
#line 92 "gethttpheader.gperf"
|
||||||
{"Upgrade-Insecure-Requests", kHttpUpgradeInsecureRequests},
|
{"Referrer-Policy", kHttpReferrerPolicy},
|
||||||
#line 64 "gethttpheader.gperf"
|
{""}, {""}, {""},
|
||||||
{"Content-Location", kHttpContentLocation},
|
|
||||||
{""}, {""},
|
|
||||||
#line 56 "gethttpheader.gperf"
|
#line 56 "gethttpheader.gperf"
|
||||||
{"Access-Control-Request-Method", kHttpAccessControlRequestMethod},
|
{"Access-Control-Request-Method", kHttpAccessControlRequestMethod},
|
||||||
{""},
|
{""},
|
||||||
#line 65 "gethttpheader.gperf"
|
#line 65 "gethttpheader.gperf"
|
||||||
{"Content-MD5", kHttpContentMd5},
|
{"Content-MD5", kHttpContentMd5},
|
||||||
#line 74 "gethttpheader.gperf"
|
|
||||||
{"Max-Forwards", kHttpMaxForwards},
|
|
||||||
{""}, {""},
|
|
||||||
#line 22 "gethttpheader.gperf"
|
|
||||||
{"X-Forwarded-For", kHttpXForwardedFor},
|
|
||||||
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
|
|
||||||
{""}, {""},
|
|
||||||
#line 34 "gethttpheader.gperf"
|
|
||||||
{"X-CSRF-Token", kHttpXCsrfToken},
|
|
||||||
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
|
|
||||||
{""}, {""}, {""}, {""}, {""}, {""},
|
{""}, {""}, {""}, {""}, {""}, {""},
|
||||||
#line 44 "gethttpheader.gperf"
|
#line 44 "gethttpheader.gperf"
|
||||||
{"Last-Modified", kHttpLastModified},
|
{"Last-Modified", kHttpLastModified}
|
||||||
{""}, {""}, {""},
|
|
||||||
#line 33 "gethttpheader.gperf"
|
|
||||||
{"X-Forwarded-Proto", kHttpXForwardedProto}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
|
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
|
||||||
|
|
|
@ -168,6 +168,22 @@ const char *GetHttpHeaderName(int h) {
|
||||||
return "WWW-Authenticate";
|
return "WWW-Authenticate";
|
||||||
case kHttpVia:
|
case kHttpVia:
|
||||||
return "Via";
|
return "Via";
|
||||||
|
case kHttpStrictTransportSecurity:
|
||||||
|
return "Strict-Transport-Security";
|
||||||
|
case kHttpXFrameOptions:
|
||||||
|
return "X-Frame-Options";
|
||||||
|
case kHttpXContentTypeOptions:
|
||||||
|
return "X-Content-Type-Options";
|
||||||
|
case kHttpAltSvc:
|
||||||
|
return "Alt-Svc";
|
||||||
|
case kHttpReferrerPolicy:
|
||||||
|
return "Referrer-Policy";
|
||||||
|
case kHttpXXssProtection:
|
||||||
|
return "X-XSS-Protection";
|
||||||
|
case kHttpAcceptRanges:
|
||||||
|
return "Accept-Ranges";
|
||||||
|
case kHttpSetCookie:
|
||||||
|
return "Set-Cookie";
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
/**
|
/**
|
||||||
* Returns true if standard header has substring.
|
* Returns true if standard header has substring.
|
||||||
*
|
*
|
||||||
* @param m is message parsed by ParseHttpRequest
|
* @param m is message parsed by ParseHttpMessage
|
||||||
* @param b is buffer that ParseHttpRequest parsed
|
* @param b is buffer that ParseHttpMessage parsed
|
||||||
* @param h is known header, e.g. kHttpAcceptEncoding
|
* @param h is known header, e.g. kHttpAcceptEncoding
|
||||||
* @param s should not contain comma
|
* @param s should not contain comma
|
||||||
* @param n is byte length of s where -1 implies strlen
|
* @param n is byte length of s where -1 implies strlen
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
#define COSMOPOLITAN_LIBC_HTTP_HTTP_H_
|
#define COSMOPOLITAN_LIBC_HTTP_HTTP_H_
|
||||||
#include "libc/time/struct/tm.h"
|
#include "libc/time/struct/tm.h"
|
||||||
|
|
||||||
|
#define kHttpRequest 0
|
||||||
|
#define kHttpResponse 1
|
||||||
|
|
||||||
#define kHttpGet 1
|
#define kHttpGet 1
|
||||||
#define kHttpHead 2
|
#define kHttpHead 2
|
||||||
#define kHttpPost 3
|
#define kHttpPost 3
|
||||||
|
@ -94,7 +97,15 @@
|
||||||
#define kHttpWarning 71
|
#define kHttpWarning 71
|
||||||
#define kHttpWwwAuthenticate 72
|
#define kHttpWwwAuthenticate 72
|
||||||
#define kHttpVia 73
|
#define kHttpVia 73
|
||||||
#define kHttpHeadersMax 74
|
#define kHttpStrictTransportSecurity 74
|
||||||
|
#define kHttpXFrameOptions 75
|
||||||
|
#define kHttpXContentTypeOptions 76
|
||||||
|
#define kHttpAltSvc 77
|
||||||
|
#define kHttpReferrerPolicy 78
|
||||||
|
#define kHttpXXssProtection 79
|
||||||
|
#define kHttpAcceptRanges 80
|
||||||
|
#define kHttpSetCookie 81
|
||||||
|
#define kHttpHeadersMax 82
|
||||||
|
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
@ -116,6 +127,7 @@ struct HttpHeaders {
|
||||||
struct HttpMessage {
|
struct HttpMessage {
|
||||||
int i, a, status;
|
int i, a, status;
|
||||||
unsigned char t;
|
unsigned char t;
|
||||||
|
unsigned char type;
|
||||||
unsigned char method;
|
unsigned char method;
|
||||||
unsigned char version;
|
unsigned char version;
|
||||||
struct HttpSlice k;
|
struct HttpSlice k;
|
||||||
|
@ -135,12 +147,9 @@ const char *GetHttpReason(int);
|
||||||
const char *GetHttpHeaderName(int);
|
const char *GetHttpHeaderName(int);
|
||||||
int GetHttpHeader(const char *, size_t);
|
int GetHttpHeader(const char *, size_t);
|
||||||
int GetHttpMethod(const char *, size_t);
|
int GetHttpMethod(const char *, size_t);
|
||||||
void InitHttpRequest(struct HttpMessage *);
|
void InitHttpMessage(struct HttpMessage *, int);
|
||||||
void DestroyHttpRequest(struct HttpMessage *);
|
void DestroyHttpMessage(struct HttpMessage *);
|
||||||
int ParseHttpRequest(struct HttpMessage *, const char *, size_t);
|
int ParseHttpMessage(struct HttpMessage *, const char *, size_t);
|
||||||
void InitHttpResponse(struct HttpMessage *);
|
|
||||||
void DestroyHttpResponse(struct HttpMessage *);
|
|
||||||
int ParseHttpResponse(struct HttpMessage *, const char *, size_t);
|
|
||||||
bool HeaderHas(struct HttpMessage *, const char *, int, const char *, size_t);
|
bool HeaderHas(struct HttpMessage *, const char *, int, const char *, size_t);
|
||||||
int64_t ParseContentLength(const char *, size_t);
|
int64_t ParseContentLength(const char *, size_t);
|
||||||
char *FormatHttpDateTime(char[hasatleast 30], struct tm *);
|
char *FormatHttpDateTime(char[hasatleast 30], struct tm *);
|
||||||
|
|
|
@ -19,11 +19,25 @@
|
||||||
#include "net/http/ip.h"
|
#include "net/http/ip.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if IPv4 address can come from the Internet.
|
* Returns true if IPv4 address is Globally Reachable.
|
||||||
*
|
*
|
||||||
* We intentionally omit TEST-NET here which can be used to simulate
|
* We intentionally omit TEST-NET here which can be used to simulate
|
||||||
* public Internet traffic using non-Internet IPs.
|
* public Internet traffic using non-Internet IPs.
|
||||||
|
*
|
||||||
|
* @return true 92.4499% of the time
|
||||||
|
* @see IANA IPv4 Special-Purpose Address Registry
|
||||||
*/
|
*/
|
||||||
bool IsPublicIp(uint32_t x) {
|
bool IsPublicIp(uint32_t x) {
|
||||||
return !IsLoopbackIp(x) && !IsPrivateIp(x);
|
return !((x & 0xffffffff) == 0x00000000 /* 0.0.0.0/32 */ ||
|
||||||
|
(x & 0xff000000) == 0x00000000 /* 0.0.0.0/8 */ ||
|
||||||
|
(x & 0xff000000) == 0x0a000000 /* 10.0.0.0/8 */ ||
|
||||||
|
(x & 0xff000000) == 0x7f000000 /* 127.0.0.0/8 */ ||
|
||||||
|
(x & 0xfff00000) == 0xac100000 /* 172.16.0.0/12 */ ||
|
||||||
|
(x & 0xffffff00) == 0xc0000000 /* 192.0.0.0/24 */ ||
|
||||||
|
(x & 0xffff0000) == 0xc0a80000 /* 192.168.0.0/16 */ ||
|
||||||
|
(x & 0xffff0000) == 0xa9fe0000 /* 169.254.0.0/16 */ ||
|
||||||
|
(x & 0xffc00000) == 0x64400000 /* 100.64.0.0/10 */ ||
|
||||||
|
(x & 0xfffe0000) == 0xc6120000 /* 198.18.0.0/15 */ ||
|
||||||
|
(x & 0xf0000000) == 0xf0000000 /* 240.0.0.0/4 */ ||
|
||||||
|
(x & 0xffffffff) == 0xffffffff /* 255.255.255.255/32 */);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/alg/alg.h"
|
#include "libc/alg/alg.h"
|
||||||
#include "libc/alg/arraylist.internal.h"
|
#include "libc/alg/arraylist.internal.h"
|
||||||
|
#include "libc/assert.h"
|
||||||
#include "libc/bits/bits.h"
|
#include "libc/bits/bits.h"
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
@ -30,19 +31,34 @@
|
||||||
|
|
||||||
#define LIMIT (SHRT_MAX - 2)
|
#define LIMIT (SHRT_MAX - 2)
|
||||||
|
|
||||||
enum { START, METHOD, URI, VERSION, HKEY, HSEP, HVAL, CR1, LF1, LF2 };
|
enum HttpMessageState {
|
||||||
|
START,
|
||||||
|
METHOD,
|
||||||
|
URI,
|
||||||
|
VERSION,
|
||||||
|
STATUS,
|
||||||
|
MESSAGE,
|
||||||
|
HKEY,
|
||||||
|
HSEP,
|
||||||
|
HVAL,
|
||||||
|
CR1,
|
||||||
|
LF1,
|
||||||
|
LF2,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes HTTP request parser.
|
* Initializes HTTP message parser.
|
||||||
*/
|
*/
|
||||||
void InitHttpRequest(struct HttpMessage *r) {
|
void InitHttpMessage(struct HttpMessage *r, int type) {
|
||||||
|
assert(type == kHttpRequest || type == kHttpResponse);
|
||||||
memset(r, 0, sizeof(*r));
|
memset(r, 0, sizeof(*r));
|
||||||
|
r->type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys HTTP request parser.
|
* Destroys HTTP message parser.
|
||||||
*/
|
*/
|
||||||
void DestroyHttpRequest(struct HttpMessage *r) {
|
void DestroyHttpMessage(struct HttpMessage *r) {
|
||||||
if (r->xheaders.p) {
|
if (r->xheaders.p) {
|
||||||
free(r->xheaders.p);
|
free(r->xheaders.p);
|
||||||
r->xheaders.p = NULL;
|
r->xheaders.p = NULL;
|
||||||
|
@ -51,7 +67,7 @@ void DestroyHttpRequest(struct HttpMessage *r) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses HTTP request.
|
* Parses HTTP request or response.
|
||||||
*
|
*
|
||||||
* This parser is responsible for determining the length of a message
|
* This parser is responsible for determining the length of a message
|
||||||
* and slicing the strings inside it. Performance is attained using
|
* and slicing the strings inside it. Performance is attained using
|
||||||
|
@ -83,7 +99,7 @@ void DestroyHttpRequest(struct HttpMessage *r) {
|
||||||
* @see HTTP/1.1 RFC2616 RFC2068
|
* @see HTTP/1.1 RFC2616 RFC2068
|
||||||
* @see HTTP/1.0 RFC1945
|
* @see HTTP/1.0 RFC1945
|
||||||
*/
|
*/
|
||||||
int ParseHttpRequest(struct HttpMessage *r, const char *p, size_t n) {
|
int ParseHttpMessage(struct HttpMessage *r, const char *p, size_t n) {
|
||||||
int c, h, i;
|
int c, h, i;
|
||||||
struct HttpHeader *x;
|
struct HttpHeader *x;
|
||||||
for (n = MIN(n, LIMIT); r->i < n; ++r->i) {
|
for (n = MIN(n, LIMIT); r->i < n; ++r->i) {
|
||||||
|
@ -92,7 +108,7 @@ int ParseHttpRequest(struct HttpMessage *r, const char *p, size_t n) {
|
||||||
case START:
|
case START:
|
||||||
if (c == '\r' || c == '\n') break; /* RFC7230 § 3.5 */
|
if (c == '\r' || c == '\n') break; /* RFC7230 § 3.5 */
|
||||||
if (!kHttpToken[c]) return ebadmsg();
|
if (!kHttpToken[c]) return ebadmsg();
|
||||||
r->t = METHOD;
|
r->t = r->type == kHttpRequest ? METHOD : VERSION;
|
||||||
r->a = r->i;
|
r->a = r->i;
|
||||||
break;
|
break;
|
||||||
case METHOD:
|
case METHOD:
|
||||||
|
@ -133,17 +149,57 @@ int ParseHttpRequest(struct HttpMessage *r, const char *p, size_t n) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case VERSION:
|
case VERSION:
|
||||||
if (c == '\r' || c == '\n') {
|
if (c == ' ' || c == '\r' || c == '\n') {
|
||||||
if (r->i - r->a == 8 &&
|
if (r->i - r->a == 8 &&
|
||||||
(READ64BE(p + r->a) & 0xFFFFFFFFFF00FF00) == 0x485454502F002E00 &&
|
(READ64BE(p + r->a) & 0xFFFFFFFFFF00FF00) == 0x485454502F002E00 &&
|
||||||
isdigit(p[r->a + 5]) && isdigit(p[r->a + 7])) {
|
isdigit(p[r->a + 5]) && isdigit(p[r->a + 7])) {
|
||||||
r->version = (p[r->a + 5] - '0') * 10 + (p[r->a + 7] - '0');
|
r->version = (p[r->a + 5] - '0') * 10 + (p[r->a + 7] - '0');
|
||||||
r->t = c == '\r' ? CR1 : LF1;
|
if (r->type == kHttpRequest) {
|
||||||
|
r->t = c == '\r' ? CR1 : LF1;
|
||||||
|
} else {
|
||||||
|
r->t = STATUS;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return ebadmsg();
|
return ebadmsg();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case STATUS:
|
||||||
|
for (;;) {
|
||||||
|
if (c == ' ' || c == '\r' || c == '\n') {
|
||||||
|
if (r->status < 100) return ebadmsg();
|
||||||
|
if (c == ' ') {
|
||||||
|
r->a = r->i + 1;
|
||||||
|
r->t = MESSAGE;
|
||||||
|
} else {
|
||||||
|
r->t = c == '\r' ? CR1 : LF1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else if ('0' <= c && c <= '9') {
|
||||||
|
r->status *= 10;
|
||||||
|
r->status += c - '0';
|
||||||
|
if (r->status > 999) return ebadmsg();
|
||||||
|
} else {
|
||||||
|
return ebadmsg();
|
||||||
|
}
|
||||||
|
if (++r->i == n) break;
|
||||||
|
c = p[r->i] & 0xff;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MESSAGE:
|
||||||
|
for (;;) {
|
||||||
|
if (c == '\r' || c == '\n') {
|
||||||
|
r->message.a = r->a;
|
||||||
|
r->message.b = r->i;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case CR1:
|
case CR1:
|
||||||
if (c != '\n') return ebadmsg();
|
if (c != '\n') return ebadmsg();
|
||||||
r->t = LF1;
|
r->t = LF1;
|
|
@ -1,187 +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/bits/bits.h"
|
|
||||||
#include "libc/limits.h"
|
|
||||||
#include "libc/macros.internal.h"
|
|
||||||
#include "libc/mem/mem.h"
|
|
||||||
#include "libc/str/str.h"
|
|
||||||
#include "libc/sysv/errfuns.h"
|
|
||||||
#include "net/http/http.h"
|
|
||||||
|
|
||||||
#define LIMIT (SHRT_MAX - 2)
|
|
||||||
|
|
||||||
enum { START, VERSION, STATUS, MESSAGE, HKEY, HSEP, HVAL, CR1, LF1, LF2 };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes HTTP response parser.
|
|
||||||
*/
|
|
||||||
void InitHttpResponse(struct HttpMessage *r) {
|
|
||||||
memset(r, 0, sizeof(*r));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys HTTP response parser.
|
|
||||||
*/
|
|
||||||
void DestroyHttpResponse(struct HttpMessage *r) {
|
|
||||||
if (r->xheaders.p) {
|
|
||||||
free(r->xheaders.p);
|
|
||||||
r->xheaders.p = NULL;
|
|
||||||
r->xheaders.n = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses HTTP response.
|
|
||||||
*/
|
|
||||||
int ParseHttpResponse(struct HttpMessage *r, const char *p, size_t n) {
|
|
||||||
int c, h, i;
|
|
||||||
struct HttpHeader *x;
|
|
||||||
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') break; /* RFC7230 § 3.5 */
|
|
||||||
if (c != 'H') return ebadmsg();
|
|
||||||
r->t = VERSION;
|
|
||||||
r->a = r->i;
|
|
||||||
break;
|
|
||||||
case VERSION:
|
|
||||||
if (c == ' ') {
|
|
||||||
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 = STATUS;
|
|
||||||
} else {
|
|
||||||
return ebadmsg();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case STATUS:
|
|
||||||
for (;;) {
|
|
||||||
if (c == ' ' || c == '\r' || c == '\n') {
|
|
||||||
if (r->status < 100) return ebadmsg();
|
|
||||||
if (c == ' ') {
|
|
||||||
r->a = r->i + 1;
|
|
||||||
r->t = MESSAGE;
|
|
||||||
} else {
|
|
||||||
r->t = c == '\r' ? CR1 : LF1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else if ('0' <= c && c <= '9') {
|
|
||||||
r->status *= 10;
|
|
||||||
r->status += c - '0';
|
|
||||||
if (r->status > 999) return ebadmsg();
|
|
||||||
} else {
|
|
||||||
return ebadmsg();
|
|
||||||
}
|
|
||||||
if (++r->i == n) break;
|
|
||||||
c = p[r->i] & 0xff;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MESSAGE:
|
|
||||||
for (;;) {
|
|
||||||
if (c == '\r' || c == '\n') {
|
|
||||||
r->message.a = r->a;
|
|
||||||
r->message.b = r->i;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CR1:
|
|
||||||
if (c != '\n') return ebadmsg();
|
|
||||||
r->t = LF1;
|
|
||||||
break;
|
|
||||||
case LF1:
|
|
||||||
if (c == '\r') {
|
|
||||||
r->t = LF2;
|
|
||||||
break;
|
|
||||||
} else if (c == '\n') {
|
|
||||||
return ++r->i;
|
|
||||||
} else if (!kHttpToken[c]) {
|
|
||||||
return ebadmsg(); /* RFC7230 § 3.2.4 */
|
|
||||||
}
|
|
||||||
r->k.a = r->i;
|
|
||||||
r->t = HKEY;
|
|
||||||
break;
|
|
||||||
case HKEY:
|
|
||||||
for (;;) {
|
|
||||||
if (c == ':') {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case HSEP:
|
|
||||||
if (c == ' ' || c == '\t') break;
|
|
||||||
r->a = r->i;
|
|
||||||
r->t = HVAL;
|
|
||||||
/* fallthrough */
|
|
||||||
case HVAL:
|
|
||||||
for (;;) {
|
|
||||||
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 &&
|
|
||||||
(!r->headers[h].a || !kHttpRepeatable[h])) {
|
|
||||||
r->headers[h].a = r->a;
|
|
||||||
r->headers[h].b = i;
|
|
||||||
} else if ((x = realloc(
|
|
||||||
r->xheaders.p,
|
|
||||||
(r->xheaders.n + 1) * sizeof(*r->xheaders.p)))) {
|
|
||||||
x[r->xheaders.n].k = r->k;
|
|
||||||
x[r->xheaders.n].v.a = r->a;
|
|
||||||
x[r->xheaders.n].v.b = i;
|
|
||||||
r->xheaders.p = x;
|
|
||||||
++r->xheaders.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;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LF2:
|
|
||||||
if (c == '\n') {
|
|
||||||
return ++r->i;
|
|
||||||
}
|
|
||||||
return ebadmsg();
|
|
||||||
default:
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (r->i < LIMIT) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return ebadmsg();
|
|
||||||
}
|
|
||||||
}
|
|
35
test/net/http/ismimetype_test.c
Normal file
35
test/net/http/ismimetype_test.c
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/*-*- 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(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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCH(IsMimeType, bench) {
|
||||||
|
EZBENCH2("IsMimeType", donothing,
|
||||||
|
IsMimeType("text/plain; charset=utf-8", -1, "text/plain"));
|
||||||
|
}
|
|
@ -38,41 +38,42 @@ static char *slice(const char *m, struct HttpSlice s) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetUp(void) {
|
|
||||||
InitHttpRequest(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TearDown(void) {
|
void TearDown(void) {
|
||||||
DestroyHttpRequest(req);
|
DestroyHttpMessage(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, soLittleState) {
|
TEST(ParseHttpMessage, soLittleState) {
|
||||||
|
InitHttpMessage(req, kHttpRequest);
|
||||||
ASSERT_LE(sizeof(struct HttpMessage), 512);
|
ASSERT_LE(sizeof(struct HttpMessage), 512);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testEmpty_tooShort) {
|
TEST(ParseHttpMessage, testEmpty_tooShort) {
|
||||||
EXPECT_EQ(0, ParseHttpRequest(req, "", 0));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(0, ParseHttpMessage(req, "", 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testTooShort) {
|
TEST(ParseHttpMessage, testTooShort) {
|
||||||
EXPECT_EQ(0, ParseHttpRequest(req, "\r\n", 2));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(0, ParseHttpMessage(req, "\r\n", 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testNoHeaders) {
|
TEST(ParseHttpMessage, testNoHeaders) {
|
||||||
static const char m[] = "GET /foo HTTP/1.0\r\n\r\n";
|
static const char m[] = "GET /foo HTTP/1.0\r\n\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_EQ(kHttpGet, req->method);
|
EXPECT_EQ(kHttpGet, req->method);
|
||||||
EXPECT_STREQ("/foo", gc(slice(m, req->uri)));
|
EXPECT_STREQ("/foo", gc(slice(m, req->uri)));
|
||||||
EXPECT_EQ(10, req->version);
|
EXPECT_EQ(10, req->version);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testSomeHeaders) {
|
TEST(ParseHttpMessage, testSomeHeaders) {
|
||||||
static const char m[] = "\
|
static const char m[] = "\
|
||||||
POST /foo?bar%20hi HTTP/1.0\r\n\
|
POST /foo?bar%20hi HTTP/1.0\r\n\
|
||||||
Host: foo.example\r\n\
|
Host: foo.example\r\n\
|
||||||
Content-Length: 0\r\n\
|
Content-Length: 0\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_EQ(kHttpPost, req->method);
|
EXPECT_EQ(kHttpPost, req->method);
|
||||||
EXPECT_STREQ("/foo?bar%20hi", gc(slice(m, req->uri)));
|
EXPECT_STREQ("/foo?bar%20hi", gc(slice(m, req->uri)));
|
||||||
EXPECT_EQ(10, req->version);
|
EXPECT_EQ(10, req->version);
|
||||||
|
@ -81,101 +82,113 @@ Content-Length: 0\r\n\
|
||||||
EXPECT_STREQ("", gc(slice(m, req->headers[kHttpEtag])));
|
EXPECT_STREQ("", gc(slice(m, req->headers[kHttpEtag])));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testHttp101) {
|
TEST(ParseHttpMessage, testHttp101) {
|
||||||
static const char m[] = "GET / HTTP/1.1\r\n\r\n";
|
static const char m[] = "GET / HTTP/1.1\r\n\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_EQ(kHttpGet, req->method);
|
EXPECT_EQ(kHttpGet, req->method);
|
||||||
EXPECT_STREQ("/", gc(slice(m, req->uri)));
|
EXPECT_STREQ("/", gc(slice(m, req->uri)));
|
||||||
EXPECT_EQ(11, req->version);
|
EXPECT_EQ(11, req->version);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testHttp100) {
|
TEST(ParseHttpMessage, testHttp100) {
|
||||||
static const char m[] = "GET / HTTP/1.0\r\n\r\n";
|
static const char m[] = "GET / HTTP/1.0\r\n\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_EQ(kHttpGet, req->method);
|
EXPECT_EQ(kHttpGet, req->method);
|
||||||
EXPECT_STREQ("/", gc(slice(m, req->uri)));
|
EXPECT_STREQ("/", gc(slice(m, req->uri)));
|
||||||
EXPECT_EQ(10, req->version);
|
EXPECT_EQ(10, req->version);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testUnknownMethod_canBeUsedIfYouWant) {
|
TEST(ParseHttpMessage, testUnknownMethod_canBeUsedIfYouWant) {
|
||||||
static const char m[] = "#%*+_^ / HTTP/1.0\r\n\r\n";
|
static const char m[] = "#%*+_^ / HTTP/1.0\r\n\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_FALSE(req->method);
|
EXPECT_FALSE(req->method);
|
||||||
EXPECT_STREQ("WUT", kHttpMethod[req->method]);
|
EXPECT_STREQ("WUT", kHttpMethod[req->method]);
|
||||||
EXPECT_STREQ("#%*+_^", gc(slice(m, req->xmethod)));
|
EXPECT_STREQ("#%*+_^", gc(slice(m, req->xmethod)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testIllegalMethod) {
|
TEST(ParseHttpMessage, testIllegalMethod) {
|
||||||
static const char m[] = "ehd@oruc / HTTP/1.0\r\n\r\n";
|
static const char m[] = "ehd@oruc / HTTP/1.0\r\n\r\n";
|
||||||
EXPECT_EQ(-1, ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(-1, ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_STREQ("WUT", kHttpMethod[req->method]);
|
EXPECT_STREQ("WUT", kHttpMethod[req->method]);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testIllegalMethodCasing_weAllowItAndPreserveIt) {
|
TEST(ParseHttpMessage, testIllegalMethodCasing_weAllowItAndPreserveIt) {
|
||||||
static const char m[] = "get / HTTP/1.0\r\n\r\n";
|
static const char m[] = "get / HTTP/1.0\r\n\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_STREQ("GET", kHttpMethod[req->method]);
|
EXPECT_STREQ("GET", kHttpMethod[req->method]);
|
||||||
EXPECT_STREQ("get", gc(slice(m, req->xmethod)));
|
EXPECT_STREQ("get", gc(slice(m, req->xmethod)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testEmptyMethod_isntAllowed) {
|
TEST(ParseHttpMessage, testEmptyMethod_isntAllowed) {
|
||||||
static const char m[] = " / HTTP/1.0\r\n\r\n";
|
static const char m[] = " / HTTP/1.0\r\n\r\n";
|
||||||
EXPECT_EQ(-1, ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(-1, ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_STREQ("WUT", kHttpMethod[req->method]);
|
EXPECT_STREQ("WUT", kHttpMethod[req->method]);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testEmptyUri_isntAllowed) {
|
TEST(ParseHttpMessage, testEmptyUri_isntAllowed) {
|
||||||
static const char m[] = "GET HTTP/1.0\r\n\r\n";
|
static const char m[] = "GET HTTP/1.0\r\n\r\n";
|
||||||
EXPECT_EQ(-1, ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(-1, ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_STREQ("GET", kHttpMethod[req->method]);
|
EXPECT_STREQ("GET", kHttpMethod[req->method]);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testHttp09) {
|
TEST(ParseHttpMessage, testHttp09) {
|
||||||
static const char m[] = "GET /\r\n\r\n";
|
static const char m[] = "GET /\r\n\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_EQ(kHttpGet, req->method);
|
EXPECT_EQ(kHttpGet, req->method);
|
||||||
EXPECT_STREQ("/", gc(slice(m, req->uri)));
|
EXPECT_STREQ("/", gc(slice(m, req->uri)));
|
||||||
EXPECT_EQ(9, req->version);
|
EXPECT_EQ(9, req->version);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testLeadingLineFeeds_areIgnored) {
|
TEST(ParseHttpMessage, testLeadingLineFeeds_areIgnored) {
|
||||||
static const char m[] = "\
|
static const char m[] = "\
|
||||||
\r\n\
|
\r\n\
|
||||||
GET /foo?bar%20hi HTTP/1.0\r\n\
|
GET /foo?bar%20hi HTTP/1.0\r\n\
|
||||||
User-Agent: hi\r\n\
|
User-Agent: hi\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_STREQ("/foo?bar%20hi", gc(slice(m, req->uri)));
|
EXPECT_STREQ("/foo?bar%20hi", gc(slice(m, req->uri)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testLineFolding_isRejected) {
|
TEST(ParseHttpMessage, testLineFolding_isRejected) {
|
||||||
static const char m[] = "\
|
static const char m[] = "\
|
||||||
GET /foo?bar%20hi HTTP/1.0\r\n\
|
GET /foo?bar%20hi HTTP/1.0\r\n\
|
||||||
User-Agent: hi\r\n\
|
User-Agent: hi\r\n\
|
||||||
there\r\n\
|
there\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(-1, ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(-1, ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_EQ(EBADMSG, errno);
|
EXPECT_EQ(EBADMSG, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testEmptyHeaderName_isRejected) {
|
TEST(ParseHttpMessage, testEmptyHeaderName_isRejected) {
|
||||||
static const char m[] = "\
|
static const char m[] = "\
|
||||||
GET /foo?bar%20hi HTTP/1.0\r\n\
|
GET /foo?bar%20hi HTTP/1.0\r\n\
|
||||||
User-Agent: hi\r\n\
|
User-Agent: hi\r\n\
|
||||||
: hi\r\n\
|
: hi\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(-1, ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(-1, ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_EQ(EBADMSG, errno);
|
EXPECT_EQ(EBADMSG, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testUnixNewlines) {
|
TEST(ParseHttpMessage, testUnixNewlines) {
|
||||||
static const char m[] = "\
|
static const char m[] = "\
|
||||||
POST /foo?bar%20hi HTTP/1.0\n\
|
POST /foo?bar%20hi HTTP/1.0\n\
|
||||||
Host: foo.example\n\
|
Host: foo.example\n\
|
||||||
Content-Length: 0\n\
|
Content-Length: 0\n\
|
||||||
\n\
|
\n\
|
||||||
\n";
|
\n";
|
||||||
EXPECT_EQ(strlen(m) - 1, ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m) - 1, ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_EQ(kHttpPost, req->method);
|
EXPECT_EQ(kHttpPost, req->method);
|
||||||
EXPECT_STREQ("/foo?bar%20hi", gc(slice(m, req->uri)));
|
EXPECT_STREQ("/foo?bar%20hi", gc(slice(m, req->uri)));
|
||||||
EXPECT_EQ(10, req->version);
|
EXPECT_EQ(10, req->version);
|
||||||
|
@ -184,7 +197,7 @@ Content-Length: 0\n\
|
||||||
EXPECT_STREQ("", gc(slice(m, req->headers[kHttpEtag])));
|
EXPECT_STREQ("", gc(slice(m, req->headers[kHttpEtag])));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testChromeMessage) {
|
TEST(ParseHttpMessage, testChromeMessage) {
|
||||||
static const char m[] = "\
|
static const char m[] = "\
|
||||||
GET /tool/net/redbean.png HTTP/1.1\r\n\
|
GET /tool/net/redbean.png HTTP/1.1\r\n\
|
||||||
Host: 10.10.10.124:8080\r\n\
|
Host: 10.10.10.124:8080\r\n\
|
||||||
|
@ -196,7 +209,8 @@ Referer: http://10.10.10.124:8080/\r\n\
|
||||||
Accept-Encoding: gzip, deflate\r\n\
|
Accept-Encoding: gzip, deflate\r\n\
|
||||||
Accept-Language: en-US,en;q=0.9\r\n\
|
Accept-Language: en-US,en;q=0.9\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_EQ(kHttpGet, req->method);
|
EXPECT_EQ(kHttpGet, req->method);
|
||||||
EXPECT_STREQ("/tool/net/redbean.png", gc(slice(m, req->uri)));
|
EXPECT_STREQ("/tool/net/redbean.png", gc(slice(m, req->uri)));
|
||||||
EXPECT_EQ(11, req->version);
|
EXPECT_EQ(11, req->version);
|
||||||
|
@ -207,35 +221,38 @@ Accept-Language: en-US,en;q=0.9\r\n\
|
||||||
EXPECT_STREQ("", gc(slice(m, req->headers[kHttpExpect])));
|
EXPECT_STREQ("", gc(slice(m, req->headers[kHttpExpect])));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testExtendedHeaders) {
|
TEST(ParseHttpMessage, testExtendedHeaders) {
|
||||||
static const char m[] = "\
|
static const char m[] = "\
|
||||||
GET /foo?bar%20hi HTTP/1.0\r\n\
|
GET /foo?bar%20hi HTTP/1.0\r\n\
|
||||||
X-User-Agent: hi\r\n\
|
X-User-Agent: hi\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
ASSERT_EQ(1, req->xheaders.n);
|
ASSERT_EQ(1, req->xheaders.n);
|
||||||
EXPECT_STREQ("X-User-Agent", gc(slice(m, req->xheaders.p[0].k)));
|
EXPECT_STREQ("X-User-Agent", gc(slice(m, req->xheaders.p[0].k)));
|
||||||
EXPECT_STREQ("hi", gc(slice(m, req->xheaders.p[0].v)));
|
EXPECT_STREQ("hi", gc(slice(m, req->xheaders.p[0].v)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testNormalHeaderOnMultipleLines_getsOverwritten) {
|
TEST(ParseHttpMessage, testNormalHeaderOnMultipleLines_getsOverwritten) {
|
||||||
static const char m[] = "\
|
static const char m[] = "\
|
||||||
GET / HTTP/1.1\r\n\
|
GET / HTTP/1.1\r\n\
|
||||||
Content-Type: text/html\r\n\
|
Content-Type: text/html\r\n\
|
||||||
Content-Type: text/plain\r\n\
|
Content-Type: text/plain\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_STREQ("text/plain", gc(slice(m, req->headers[kHttpContentType])));
|
EXPECT_STREQ("text/plain", gc(slice(m, req->headers[kHttpContentType])));
|
||||||
ASSERT_EQ(0, req->xheaders.n);
|
ASSERT_EQ(0, req->xheaders.n);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testCommaSeparatedOnMultipleLines_becomesLinear) {
|
TEST(ParseHttpMessage, testCommaSeparatedOnMultipleLines_becomesLinear) {
|
||||||
static const char m[] = "\
|
static const char m[] = "\
|
||||||
GET / HTTP/1.1\r\n\
|
GET / HTTP/1.1\r\n\
|
||||||
Accept: text/html\r\n\
|
Accept: text/html\r\n\
|
||||||
Accept: text/plain\r\n\
|
Accept: text/plain\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_STREQ("text/html", gc(slice(m, req->headers[kHttpAccept])));
|
EXPECT_STREQ("text/html", gc(slice(m, req->headers[kHttpAccept])));
|
||||||
ASSERT_EQ(1, req->xheaders.n);
|
ASSERT_EQ(1, req->xheaders.n);
|
||||||
EXPECT_STREQ("Accept", gc(slice(m, req->xheaders.p[0].k)));
|
EXPECT_STREQ("Accept", gc(slice(m, req->xheaders.p[0].k)));
|
||||||
|
@ -249,7 +266,8 @@ Accept-Encoding: deflate\r\n\
|
||||||
ACCEPT-ENCODING: gzip\r\n\
|
ACCEPT-ENCODING: gzip\r\n\
|
||||||
ACCEPT-encoding: bzip2\r\n\
|
ACCEPT-encoding: bzip2\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_TRUE(HeaderHas(req, m, kHttpAcceptEncoding, "gzip", -1));
|
EXPECT_TRUE(HeaderHas(req, m, kHttpAcceptEncoding, "gzip", -1));
|
||||||
EXPECT_TRUE(HeaderHas(req, m, kHttpAcceptEncoding, "deflate", -1));
|
EXPECT_TRUE(HeaderHas(req, m, kHttpAcceptEncoding, "deflate", -1));
|
||||||
EXPECT_FALSE(HeaderHas(req, m, kHttpAcceptEncoding, "funzip", -1));
|
EXPECT_FALSE(HeaderHas(req, m, kHttpAcceptEncoding, "funzip", -1));
|
||||||
|
@ -260,67 +278,116 @@ TEST(HeaderHas, testHeaderOnSameLIne) {
|
||||||
GET / HTTP/1.1\r\n\
|
GET / HTTP/1.1\r\n\
|
||||||
Accept-Encoding: deflate, gzip, bzip2\r\n\
|
Accept-Encoding: deflate, gzip, bzip2\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_TRUE(HeaderHas(req, m, kHttpAcceptEncoding, "gzip", -1));
|
EXPECT_TRUE(HeaderHas(req, m, kHttpAcceptEncoding, "gzip", -1));
|
||||||
EXPECT_TRUE(HeaderHas(req, m, kHttpAcceptEncoding, "deflate", -1));
|
EXPECT_TRUE(HeaderHas(req, m, kHttpAcceptEncoding, "deflate", -1));
|
||||||
EXPECT_FALSE(HeaderHas(req, m, kHttpAcceptEncoding, "funzip", -1));
|
EXPECT_FALSE(HeaderHas(req, m, kHttpAcceptEncoding, "funzip", -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testHeaderValuesWithWhitespace_getsTrimmed) {
|
TEST(ParseHttpMessage, testHeaderValuesWithWhitespace_getsTrimmed) {
|
||||||
static const char m[] = "\
|
static const char m[] = "\
|
||||||
OPTIONS * HTTP/1.0\r\n\
|
OPTIONS * HTTP/1.0\r\n\
|
||||||
User-Agent: \t hi there \t \r\n\
|
User-Agent: \t hi there \t \r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_STREQ("hi there", gc(slice(m, req->headers[kHttpUserAgent])));
|
EXPECT_STREQ("hi there", gc(slice(m, req->headers[kHttpUserAgent])));
|
||||||
EXPECT_STREQ("*", gc(slice(m, req->uri)));
|
EXPECT_STREQ("*", gc(slice(m, req->uri)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testAbsentHost_setsSliceToZero) {
|
TEST(ParseHttpMessage, testAbsentHost_setsSliceToZero) {
|
||||||
static const char m[] = "\
|
static const char m[] = "\
|
||||||
GET / HTTP/1.1\r\n\
|
GET / HTTP/1.1\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_EQ(0, req->headers[kHttpHost].a);
|
EXPECT_EQ(0, req->headers[kHttpHost].a);
|
||||||
EXPECT_EQ(0, req->headers[kHttpHost].b);
|
EXPECT_EQ(0, req->headers[kHttpHost].b);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testEmptyHost_setsSliceToNonzeroValue) {
|
TEST(ParseHttpMessage, testEmptyHost_setsSliceToNonzeroValue) {
|
||||||
static const char m[] = "\
|
static const char m[] = "\
|
||||||
GET / HTTP/1.1\r\n\
|
GET / HTTP/1.1\r\n\
|
||||||
Host:\r\n\
|
Host:\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_NE(0, req->headers[kHttpHost].a);
|
EXPECT_NE(0, req->headers[kHttpHost].a);
|
||||||
EXPECT_EQ(req->headers[kHttpHost].a, req->headers[kHttpHost].b);
|
EXPECT_EQ(req->headers[kHttpHost].a, req->headers[kHttpHost].b);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParseHttpRequest, testEmptyHost2_setsSliceToNonzeroValue) {
|
TEST(ParseHttpMessage, testEmptyHost2_setsSliceToNonzeroValue) {
|
||||||
static const char m[] = "\
|
static const char m[] = "\
|
||||||
GET / HTTP/1.1\r\n\
|
GET / HTTP/1.1\r\n\
|
||||||
Host: \r\n\
|
Host: \r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EXPECT_NE(0, req->headers[kHttpHost].a);
|
EXPECT_NE(0, req->headers[kHttpHost].a);
|
||||||
EXPECT_EQ(req->headers[kHttpHost].a, req->headers[kHttpHost].b);
|
EXPECT_EQ(req->headers[kHttpHost].a, req->headers[kHttpHost].b);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(IsMimeType, test) {
|
TEST(ParseHttpResponse, testEmpty_tooShort) {
|
||||||
ASSERT_TRUE(IsMimeType("text/plain", -1, "text/plain"));
|
InitHttpMessage(req, kHttpResponse);
|
||||||
ASSERT_TRUE(IsMimeType("TEXT/PLAIN", -1, "text/plain"));
|
EXPECT_EQ(0, ParseHttpMessage(req, "", 0));
|
||||||
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"));
|
TEST(ParseHttpResponse, testTooShort) {
|
||||||
ASSERT_FALSE(IsMimeType("", -1, "text/plain"));
|
InitHttpMessage(req, kHttpResponse);
|
||||||
|
EXPECT_EQ(0, ParseHttpMessage(req, "\r\n", 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseHttpResponse, testNoHeaders) {
|
||||||
|
static const char m[] = "HTTP/1.0 200 OK\r\n\r\n";
|
||||||
|
InitHttpMessage(req, kHttpResponse);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
|
EXPECT_EQ(200, req->status);
|
||||||
|
EXPECT_STREQ("OK", gc(slice(m, req->message)));
|
||||||
|
EXPECT_EQ(10, req->version);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseHttpResponse, testSomeHeaders) {
|
||||||
|
static const char m[] = "\
|
||||||
|
HTTP/1.0 200 OK\r\n\
|
||||||
|
Host: foo.example\r\n\
|
||||||
|
Content-Length: 0\r\n\
|
||||||
|
\r\n";
|
||||||
|
InitHttpMessage(req, kHttpResponse);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
|
EXPECT_EQ(200, req->status);
|
||||||
|
EXPECT_STREQ("OK", gc(slice(m, req->message)));
|
||||||
|
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])));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseHttpResponse, testHttp101) {
|
||||||
|
static const char m[] = "HTTP/1.1 300 OMG\r\n\r\n";
|
||||||
|
InitHttpMessage(req, kHttpResponse);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
|
EXPECT_EQ(300, req->status);
|
||||||
|
EXPECT_STREQ("OMG", gc(slice(m, req->message)));
|
||||||
|
EXPECT_EQ(11, req->version);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseHttpResponse, testHttp100) {
|
||||||
|
static const char m[] = "HTTP/1.0 404 Not Found\r\n\r\n";
|
||||||
|
InitHttpMessage(req, kHttpResponse);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
|
EXPECT_EQ(404, req->status);
|
||||||
|
EXPECT_STREQ("Not Found", gc(slice(m, req->message)));
|
||||||
|
EXPECT_EQ(10, req->version);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoTiniestHttpRequest(void) {
|
void DoTiniestHttpRequest(void) {
|
||||||
static const char m[] = "\
|
static const char m[] = "\
|
||||||
GET /\r\n\
|
GET /\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
InitHttpRequest(req);
|
InitHttpMessage(req, kHttpRequest);
|
||||||
ParseHttpRequest(req, m, sizeof(m));
|
ParseHttpMessage(req, m, sizeof(m));
|
||||||
DestroyHttpRequest(req);
|
DestroyHttpMessage(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoTinyHttpRequest(void) {
|
void DoTinyHttpRequest(void) {
|
||||||
|
@ -328,9 +395,9 @@ void DoTinyHttpRequest(void) {
|
||||||
GET /\r\n\
|
GET /\r\n\
|
||||||
Accept-Encoding: gzip\r\n\
|
Accept-Encoding: gzip\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
InitHttpRequest(req);
|
InitHttpMessage(req, kHttpRequest);
|
||||||
ParseHttpRequest(req, m, sizeof(m));
|
ParseHttpMessage(req, m, sizeof(m));
|
||||||
DestroyHttpRequest(req);
|
DestroyHttpMessage(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoStandardChromeRequest(void) {
|
void DoStandardChromeRequest(void) {
|
||||||
|
@ -345,9 +412,9 @@ Referer: http://10.10.10.124:8080/\r\n\
|
||||||
Accept-Encoding: gzip, deflate\r\n\
|
Accept-Encoding: gzip, deflate\r\n\
|
||||||
Accept-Language: en-US,en;q=0.9\r\n\
|
Accept-Language: en-US,en;q=0.9\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
InitHttpRequest(req);
|
InitHttpMessage(req, kHttpRequest);
|
||||||
CHECK_EQ(sizeof(m) - 1, ParseHttpRequest(req, m, sizeof(m)));
|
CHECK_EQ(sizeof(m) - 1, ParseHttpMessage(req, m, sizeof(m)));
|
||||||
DestroyHttpRequest(req);
|
DestroyHttpMessage(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoUnstandardChromeRequest(void) {
|
void DoUnstandardChromeRequest(void) {
|
||||||
|
@ -362,16 +429,88 @@ X-Referer: http://10.10.10.124:8080/\r\n\
|
||||||
X-Accept-Encoding: gzip, deflate\r\n\
|
X-Accept-Encoding: gzip, deflate\r\n\
|
||||||
X-Accept-Language: en-US,en;q=0.9\r\n\
|
X-Accept-Language: en-US,en;q=0.9\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
InitHttpRequest(req);
|
InitHttpMessage(req, kHttpRequest);
|
||||||
CHECK_EQ(sizeof(m) - 1, ParseHttpRequest(req, m, sizeof(m)));
|
CHECK_EQ(sizeof(m) - 1, ParseHttpMessage(req, m, sizeof(m)));
|
||||||
DestroyHttpRequest(req);
|
DestroyHttpMessage(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCH(ParseHttpRequest, bench) {
|
void DoTiniestHttpResponse(void) {
|
||||||
|
static const char m[] = "\
|
||||||
|
HTTP/1.0 200\r\n\
|
||||||
|
\r\n";
|
||||||
|
InitHttpMessage(req, kHttpResponse);
|
||||||
|
ParseHttpMessage(req, m, sizeof(m));
|
||||||
|
DestroyHttpMessage(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoTinyHttpResponse(void) {
|
||||||
|
static const char m[] = "\
|
||||||
|
HTTP/1.0 200\r\n\
|
||||||
|
Accept-Encoding: gzip\r\n\
|
||||||
|
\r\n";
|
||||||
|
InitHttpMessage(req, kHttpResponse);
|
||||||
|
ParseHttpMessage(req, m, sizeof(m));
|
||||||
|
DestroyHttpMessage(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoStandardHttpResponse(void) {
|
||||||
|
static const char m[] = "\
|
||||||
|
HTTP/1.1 200 OK\r\n\
|
||||||
|
Server: nginx\r\n\
|
||||||
|
Date: Sun, 27 Jun 2021 19:09:59 GMT\r\n\
|
||||||
|
Content-Type: text/html; charset=utf-8\r\n\
|
||||||
|
Transfer-Encoding: chunked\r\n\
|
||||||
|
Connection: keep-alive\r\n\
|
||||||
|
Vary: Accept-Encoding\r\n\
|
||||||
|
Cache-Control: private; max-age=0\r\n\
|
||||||
|
X-Frame-Options: DENY\r\n\
|
||||||
|
X-Content-Type-Options: nosniff\r\n\
|
||||||
|
X-XSS-Protection: 1; mode=block\r\n\
|
||||||
|
Referrer-Policy: origin\r\n\
|
||||||
|
Strict-Transport-Security: max-age=31556900\r\n\
|
||||||
|
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://cdnjs.cloudflare.com/; frame-src 'self' https://www.google.com/recaptcha/; style-src 'self' 'unsafe-inline'\r\n\
|
||||||
|
\r\n";
|
||||||
|
InitHttpMessage(req, kHttpResponse);
|
||||||
|
CHECK_EQ(sizeof(m) - 1, ParseHttpMessage(req, m, sizeof(m)));
|
||||||
|
DestroyHttpMessage(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoUnstandardHttpResponse(void) {
|
||||||
|
static const char m[] = "\
|
||||||
|
HTTP/1.1 200 OK\r\n\
|
||||||
|
date: Sun, 27 Jun 2021 19:00:36 GMT\r\n\
|
||||||
|
server: Apache\r\n\
|
||||||
|
x-frame-options: SAMEORIGIN\r\n\
|
||||||
|
x-xss-protection: 0\r\n\
|
||||||
|
vary: Accept-Encoding\r\n\
|
||||||
|
referrer-policy: no-referrer\r\n\
|
||||||
|
x-slack-backend: r\r\n\
|
||||||
|
strict-transport-security: max-age=31536000; includeSubDomains; preload\r\n\
|
||||||
|
set-cookie: b=5aboacm0axrlzntx5wfec7r42; expires=Fri, 27-Jun-2031 19:00:36 GMT; Max-Age=315532800; path=/; domain=.slack.com; secure; SameSite=None\r\n\
|
||||||
|
set-cookie: x=5aboacm0axrlzntx5wfec7r42.1624820436; expires=Sun, 27-Jun-2021 19:15:36 GMT; Max-Age=900; path=/; domain=.slack.com; secure; SameSite=None\r\n\
|
||||||
|
content-type: text/html; charset=utf-8\r\n\
|
||||||
|
x-envoy-upstream-service-time: 19\r\n\
|
||||||
|
x-backend: main_normal main_canary_with_overflow main_control_with_overflow\r\n\
|
||||||
|
x-server: slack-www-hhvm-main-iad-a9ic\r\n\
|
||||||
|
x-via: envoy-www-iad-qd3r, haproxy-edge-pdx-klqo\r\n\
|
||||||
|
x-slack-shared-secret-outcome: shared-secret\r\n\
|
||||||
|
via: envoy-www-iad-qd3r\r\n\
|
||||||
|
transfer-encoding: chunked\r\n\
|
||||||
|
\r\n";
|
||||||
|
InitHttpMessage(req, kHttpResponse);
|
||||||
|
CHECK_EQ(sizeof(m) - 1, ParseHttpMessage(req, m, sizeof(m)));
|
||||||
|
DestroyHttpMessage(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCH(ParseHttpMessage, bench) {
|
||||||
EZBENCH2("DoTiniestHttpRequest", donothing, DoTiniestHttpRequest());
|
EZBENCH2("DoTiniestHttpRequest", donothing, DoTiniestHttpRequest());
|
||||||
EZBENCH2("DoTinyHttpRequest", donothing, DoTinyHttpRequest());
|
EZBENCH2("DoTinyHttpRequest", donothing, DoTinyHttpRequest());
|
||||||
EZBENCH2("DoStandardChromeRequest", donothing, DoStandardChromeRequest());
|
EZBENCH2("DoStandardChromeRequest", donothing, DoStandardChromeRequest());
|
||||||
EZBENCH2("DoUnstandardChromeRequest", donothing, DoUnstandardChromeRequest());
|
EZBENCH2("DoUnstandardChromeRequest", donothing, DoUnstandardChromeRequest());
|
||||||
|
EZBENCH2("DoTiniestHttpResponse", donothing, DoTiniestHttpResponse());
|
||||||
|
EZBENCH2("DoTinyHttpResponse", donothing, DoTinyHttpResponse());
|
||||||
|
EZBENCH2("DoStandardHttpResponse", donothing, DoStandardHttpResponse());
|
||||||
|
EZBENCH2("DoUnstandardHttpResponse", donothing, DoUnstandardHttpResponse());
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCH(HeaderHas, bench) {
|
BENCH(HeaderHas, bench) {
|
||||||
|
@ -384,13 +523,12 @@ Accept-Encoding: deflate\r\n\
|
||||||
ACCEPT-ENCODING: gzip\r\n\
|
ACCEPT-ENCODING: gzip\r\n\
|
||||||
ACCEPT-encoding: bzip2\r\n\
|
ACCEPT-encoding: bzip2\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
EXPECT_EQ(strlen(m), ParseHttpRequest(req, m, strlen(m)));
|
InitHttpMessage(req, kHttpRequest);
|
||||||
|
EXPECT_EQ(strlen(m), ParseHttpMessage(req, m, strlen(m)));
|
||||||
EZBENCH2("HeaderHas text/plain", donothing,
|
EZBENCH2("HeaderHas text/plain", donothing,
|
||||||
HeaderHas(req, m, kHttpAccept, "text/plain", 7));
|
HeaderHas(req, m, kHttpAccept, "text/plain", 7));
|
||||||
EZBENCH2("HeaderHas deflate", donothing,
|
EZBENCH2("HeaderHas deflate", donothing,
|
||||||
HeaderHas(req, m, kHttpAcceptEncoding, "deflate", 7));
|
HeaderHas(req, m, kHttpAcceptEncoding, "deflate", 7));
|
||||||
EZBENCH2("HeaderHas gzip", donothing,
|
EZBENCH2("HeaderHas gzip", donothing,
|
||||||
HeaderHas(req, m, kHttpAcceptEncoding, "gzip", 4));
|
HeaderHas(req, m, kHttpAcceptEncoding, "gzip", 4));
|
||||||
EZBENCH2("IsMimeType", donothing,
|
|
||||||
IsMimeType("text/plain; charset=utf-8", -1, "text/plain"));
|
|
||||||
}
|
}
|
|
@ -1,175 +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/log/check.h"
|
|
||||||
#include "libc/str/str.h"
|
|
||||||
#include "libc/testlib/ezbench.h"
|
|
||||||
#include "libc/testlib/testlib.h"
|
|
||||||
#include "libc/x/x.h"
|
|
||||||
#include "net/http/http.h"
|
|
||||||
|
|
||||||
struct HttpMessage req[1];
|
|
||||||
|
|
||||||
static char *slice(const char *m, struct HttpSlice s) {
|
|
||||||
char *p;
|
|
||||||
p = xmalloc(s.b - s.a + 1);
|
|
||||||
memcpy(p, m + s.a, s.b - s.a);
|
|
||||||
p[s.b - s.a] = 0;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetUp(void) {
|
|
||||||
InitHttpResponse(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TearDown(void) {
|
|
||||||
DestroyHttpResponse(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ParseHttpResponse, soLittleState) {
|
|
||||||
ASSERT_LE(sizeof(struct HttpMessage), 512);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ParseHttpResponse, testEmpty_tooShort) {
|
|
||||||
EXPECT_EQ(0, ParseHttpResponse(req, "", 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ParseHttpResponse, testTooShort) {
|
|
||||||
EXPECT_EQ(0, ParseHttpResponse(req, "\r\n", 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ParseHttpResponse, testNoHeaders) {
|
|
||||||
static const char m[] = "HTTP/1.0 200 OK\r\n\r\n";
|
|
||||||
EXPECT_EQ(strlen(m), ParseHttpResponse(req, m, strlen(m)));
|
|
||||||
EXPECT_EQ(200, req->status);
|
|
||||||
EXPECT_STREQ("OK", gc(slice(m, req->message)));
|
|
||||||
EXPECT_EQ(10, req->version);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ParseHttpResponse, testSomeHeaders) {
|
|
||||||
static const char m[] = "\
|
|
||||||
HTTP/1.0 200 OK\r\n\
|
|
||||||
Host: foo.example\r\n\
|
|
||||||
Content-Length: 0\r\n\
|
|
||||||
\r\n";
|
|
||||||
EXPECT_EQ(strlen(m), ParseHttpResponse(req, m, strlen(m)));
|
|
||||||
EXPECT_EQ(200, req->status);
|
|
||||||
EXPECT_STREQ("OK", gc(slice(m, req->message)));
|
|
||||||
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])));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ParseHttpResponse, testHttp101) {
|
|
||||||
static const char m[] = "HTTP/1.1 300 OMG\r\n\r\n";
|
|
||||||
EXPECT_EQ(strlen(m), ParseHttpResponse(req, m, strlen(m)));
|
|
||||||
EXPECT_EQ(300, req->status);
|
|
||||||
EXPECT_STREQ("OMG", gc(slice(m, req->message)));
|
|
||||||
EXPECT_EQ(11, req->version);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ParseHttpResponse, testHttp100) {
|
|
||||||
static const char m[] = "HTTP/1.0 404 Not Found\r\n\r\n";
|
|
||||||
EXPECT_EQ(strlen(m), ParseHttpResponse(req, m, strlen(m)));
|
|
||||||
EXPECT_EQ(404, req->status);
|
|
||||||
EXPECT_STREQ("Not Found", gc(slice(m, req->message)));
|
|
||||||
EXPECT_EQ(10, req->version);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DoTiniestHttpResponse(void) {
|
|
||||||
static const char m[] = "\
|
|
||||||
HTTP/1.0 200\r\n\
|
|
||||||
\r\n";
|
|
||||||
InitHttpResponse(req);
|
|
||||||
ParseHttpResponse(req, m, sizeof(m));
|
|
||||||
DestroyHttpResponse(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DoTinyHttpResponse(void) {
|
|
||||||
static const char m[] = "\
|
|
||||||
HTTP/1.0 200\r\n\
|
|
||||||
Accept-Encoding: gzip\r\n\
|
|
||||||
\r\n";
|
|
||||||
InitHttpResponse(req);
|
|
||||||
ParseHttpResponse(req, m, sizeof(m));
|
|
||||||
DestroyHttpResponse(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DoStandardChromeResponse(void) {
|
|
||||||
static const char m[] = "\
|
|
||||||
HTTP/1.1 200 OK\r\n\
|
|
||||||
Host: 10.10.10.124:8080\r\n\
|
|
||||||
Connection: keep-alive\r\n\
|
|
||||||
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36\r\n\
|
|
||||||
DNT: \t1 \r\n\
|
|
||||||
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8\r\n\
|
|
||||||
Referer: http://10.10.10.124:8080/\r\n\
|
|
||||||
Accept-Encoding: gzip, deflate\r\n\
|
|
||||||
Accept-Language: en-US,en;q=0.9\r\n\
|
|
||||||
\r\n";
|
|
||||||
InitHttpResponse(req);
|
|
||||||
CHECK_EQ(sizeof(m) - 1, ParseHttpResponse(req, m, sizeof(m)));
|
|
||||||
DestroyHttpResponse(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DoUnstandardChromeResponse(void) {
|
|
||||||
static const char m[] = "\
|
|
||||||
HTTP/1.1 200 OK\r\n\
|
|
||||||
X-Host: 10.10.10.124:8080\r\n\
|
|
||||||
X-Connection: keep-alive\r\n\
|
|
||||||
X-User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36\r\n\
|
|
||||||
X-DNT: \t1 \r\n\
|
|
||||||
X-Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8\r\n\
|
|
||||||
X-Referer: http://10.10.10.124:8080/\r\n\
|
|
||||||
X-Accept-Encoding: gzip, deflate\r\n\
|
|
||||||
X-Accept-Language: en-US,en;q=0.9\r\n\
|
|
||||||
\r\n";
|
|
||||||
InitHttpResponse(req);
|
|
||||||
CHECK_EQ(sizeof(m) - 1, ParseHttpResponse(req, m, sizeof(m)));
|
|
||||||
DestroyHttpResponse(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
BENCH(ParseHttpResponse, bench) {
|
|
||||||
EZBENCH2("DoTiniestHttpResponse", donothing, DoTiniestHttpResponse());
|
|
||||||
EZBENCH2("DoTinyHttpResponse", donothing, DoTinyHttpResponse());
|
|
||||||
EZBENCH2("DoStandardChromeResponse", donothing, DoStandardChromeResponse());
|
|
||||||
EZBENCH2("DoUnstandardChromeResponse", donothing,
|
|
||||||
DoUnstandardChromeResponse());
|
|
||||||
}
|
|
||||||
|
|
||||||
BENCH(HeaderHas, bench) {
|
|
||||||
static const char m[] = "\
|
|
||||||
HTTP/1.1 200 OK\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), ParseHttpResponse(req, m, strlen(m)));
|
|
||||||
EZBENCH2("HeaderHas text/plain", donothing,
|
|
||||||
HeaderHas(req, m, kHttpAccept, "text/plain", 7));
|
|
||||||
EZBENCH2("HeaderHas deflate", donothing,
|
|
||||||
HeaderHas(req, m, kHttpAcceptEncoding, "deflate", 7));
|
|
||||||
EZBENCH2("HeaderHas gzip", donothing,
|
|
||||||
HeaderHas(req, m, kHttpAcceptEncoding, "gzip", 4));
|
|
||||||
EZBENCH2("IsMimeType", donothing,
|
|
||||||
IsMimeType("text/plain; charset=utf-8", -1, "text/plain"));
|
|
||||||
}
|
|
|
@ -21,12 +21,17 @@ TEST_NET_HTTP_CHECKS = \
|
||||||
|
|
||||||
TEST_NET_HTTP_DIRECTDEPS = \
|
TEST_NET_HTTP_DIRECTDEPS = \
|
||||||
NET_HTTP \
|
NET_HTTP \
|
||||||
|
LIBC_LOG \
|
||||||
LIBC_TESTLIB \
|
LIBC_TESTLIB \
|
||||||
THIRD_PARTY_MBEDTLS
|
THIRD_PARTY_MBEDTLS
|
||||||
|
|
||||||
TEST_NET_HTTP_DEPS := \
|
TEST_NET_HTTP_DEPS := \
|
||||||
$(call uniq,$(foreach x,$(TEST_NET_HTTP_DIRECTDEPS),$($(x))))
|
$(call uniq,$(foreach x,$(TEST_NET_HTTP_DIRECTDEPS),$($(x))))
|
||||||
|
|
||||||
|
o/$(MODE)/test/net/http/joyent_test.o: \
|
||||||
|
OVERRIDE_CPPFLAGS += \
|
||||||
|
-DSTACK_FRAME_UNLIMITED
|
||||||
|
|
||||||
o/$(MODE)/test/net/http/http.pkg: \
|
o/$(MODE)/test/net/http/http.pkg: \
|
||||||
$(TEST_NET_HTTP_OBJS) \
|
$(TEST_NET_HTTP_OBJS) \
|
||||||
$(foreach x,$(TEST_NET_HTTP_DIRECTDEPS),$($(x)_A).pkg)
|
$(foreach x,$(TEST_NET_HTTP_DIRECTDEPS),$($(x)_A).pkg)
|
||||||
|
|
|
@ -24,11 +24,14 @@
|
||||||
#include "net/http/escape.h"
|
#include "net/http/escape.h"
|
||||||
|
|
||||||
TEST(VisualizeControlCodes, test) {
|
TEST(VisualizeControlCodes, test) {
|
||||||
|
size_t n;
|
||||||
EXPECT_STREQ("hello", gc(VisualizeControlCodes("hello", -1, 0)));
|
EXPECT_STREQ("hello", gc(VisualizeControlCodes("hello", -1, 0)));
|
||||||
EXPECT_STREQ("hello\r\n", gc(VisualizeControlCodes("hello\r\n", -1, 0)));
|
EXPECT_STREQ("hello\r\n", gc(VisualizeControlCodes("hello\r\n", -1, 0)));
|
||||||
EXPECT_STREQ("hello␁␂␡", gc(VisualizeControlCodes("hello\1\2\177", -1, 0)));
|
EXPECT_STREQ("hello␁␂␡", gc(VisualizeControlCodes("hello\1\2\177", -1, 0)));
|
||||||
EXPECT_STREQ("hello\\u0085",
|
EXPECT_STREQ("hello\\u0085",
|
||||||
gc(VisualizeControlCodes("hello\302\205", -1, 0)));
|
gc(VisualizeControlCodes("hello\302\205", -1, 0)));
|
||||||
|
EXPECT_STREQ("hi", gc(VisualizeControlCodes("hi", -1, &n)));
|
||||||
|
EXPECT_EQ(2, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(VisualizeControlCodes, testOom_returnsNullAndSetsSizeToZero) {
|
TEST(VisualizeControlCodes, testOom_returnsNullAndSetsSizeToZero) {
|
||||||
|
|
|
@ -76,6 +76,7 @@ void lookup(const char *name) {
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
int i;
|
int i;
|
||||||
showcrashreports();
|
showcrashreports();
|
||||||
for (i = 1; i < argc; ++i) lookup(argv[i]);
|
lookup("time-a.timefreq.bldrdoc.gov");
|
||||||
|
/* for (i = 1; i < argc; ++i) lookup(argv[i]); */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ TOOL_NET_BINS = \
|
||||||
$(TOOL_NET_COMS:%=%.dbg)
|
$(TOOL_NET_COMS:%=%.dbg)
|
||||||
|
|
||||||
TOOL_NET_COMS = \
|
TOOL_NET_COMS = \
|
||||||
|
o/$(MODE)/tool/net/dig.com \
|
||||||
|
o/$(MODE)/tool/net/echoserver.com \
|
||||||
o/$(MODE)/tool/net/redbean.com \
|
o/$(MODE)/tool/net/redbean.com \
|
||||||
o/$(MODE)/tool/net/redbean-demo.com \
|
o/$(MODE)/tool/net/redbean-demo.com \
|
||||||
o/$(MODE)/tool/net/redbean-static.com \
|
o/$(MODE)/tool/net/redbean-static.com \
|
||||||
|
@ -127,10 +129,10 @@ o/$(MODE)/tool/net/redbean-demo.com: \
|
||||||
tool/net/demo/seekable.txt \
|
tool/net/demo/seekable.txt \
|
||||||
tool/net/demo/virtualbean.html \
|
tool/net/demo/virtualbean.html \
|
||||||
tool/net/redbean.c \
|
tool/net/redbean.c \
|
||||||
net/http/parsehttprequest.c \
|
net/http/parsehttpmessage.c \
|
||||||
net/http/parseurl.c \
|
net/http/parseurl.c \
|
||||||
net/http/encodeurl.c \
|
net/http/encodeurl.c \
|
||||||
test/net/http/parsehttprequest_test.c \
|
test/net/http/parsehttpmessage_test.c \
|
||||||
test/net/http/parseurl_test.c
|
test/net/http/parseurl_test.c
|
||||||
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
|
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
|
||||||
@$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-demo
|
@$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-demo
|
||||||
|
@ -143,7 +145,7 @@ o/$(MODE)/tool/net/redbean-demo.com: \
|
||||||
@echo "<-- check out this lua server page" | $(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -cqj $@ tool/net/demo/redbean.lua
|
@echo "<-- check out this lua server page" | $(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -cqj $@ tool/net/demo/redbean.lua
|
||||||
@$(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -qj $@ tool/net/demo/404.html tool/net/favicon.ico tool/net/redbean.png tool/net/demo/redbean-form.lua tool/net/demo/redbean-xhr.lua
|
@$(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -qj $@ tool/net/demo/404.html tool/net/favicon.ico tool/net/redbean.png tool/net/demo/redbean-form.lua tool/net/demo/redbean-xhr.lua
|
||||||
@echo Uncompressed for HTTP Range requests | $(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -cqj0 $@ tool/net/demo/seekable.txt
|
@echo Uncompressed for HTTP Range requests | $(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -cqj0 $@ tool/net/demo/seekable.txt
|
||||||
@$(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -q $@ tool/net/ tool/net/demo/ tool/net/demo/index.html tool/net/demo/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
|
@$(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -q $@ tool/net/ tool/net/demo/ tool/net/demo/index.html tool/net/demo/redbean.css tool/net/redbean.c net/http/parsehttpmessage.c net/http/parseurl.c net/http/encodeurl.c test/net/http/parsehttpmessage_test.c test/net/http/parseurl_test.c
|
||||||
@printf "<p>This is a live instance of <a href=https://justine.lol/redbean/>redbean</a>: a tiny multiplatform webserver that <a href=https://news.ycombinator.com/item?id=26271117>went viral</a> on hacker news a few months ago.\r\nSince then, we've added Lua dynamic serving, which also goes as fast as 1,000,000 requests per second on a core i9 (rather than a cheap virtual machine like this)\nin addition to SQLite and SSL. The text you're reading now is a PKZIP End Of Central Directory comment.\r\n<p>redbean aims to be production worthy across six operating systems, using a single executable file (this demo is hosted on FreeBSD 13). redbean has been enhanced to restore the APE header after startup.\r\nIt automatically generates this listing page based on your zip contents. If you use redbean as an application server / web development environment,\r\nthen you'll find other new and useful features like function call logging so you can get that sweet sweet microsecond scale latency." | $(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -z $@
|
@printf "<p>This is a live instance of <a href=https://justine.lol/redbean/>redbean</a>: a tiny multiplatform webserver that <a href=https://news.ycombinator.com/item?id=26271117>went viral</a> on hacker news a few months ago.\r\nSince then, we've added Lua dynamic serving, which also goes as fast as 1,000,000 requests per second on a core i9 (rather than a cheap virtual machine like this)\nin addition to SQLite and SSL. The text you're reading now is a PKZIP End Of Central Directory comment.\r\n<p>redbean aims to be production worthy across six operating systems, using a single executable file (this demo is hosted on FreeBSD 13). redbean has been enhanced to restore the APE header after startup.\r\nIt automatically generates this listing page based on your zip contents. If you use redbean as an application server / web development environment,\r\nthen you'll find other new and useful features like function call logging so you can get that sweet sweet microsecond scale latency." | $(COMPILE) -AZIP -T$@ o/$(MODE)/host/third_party/infozip/zip.com -z $@
|
||||||
@$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/virtualbean.justine.lol/
|
@$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/virtualbean.justine.lol/
|
||||||
@$(COMPILE) -ACP -T$@ cp tool/net/redbean.png o/$(MODE)/tool/net/virtualbean.justine.lol/redbean.png
|
@$(COMPILE) -ACP -T$@ cp tool/net/redbean.png o/$(MODE)/tool/net/virtualbean.justine.lol/redbean.png
|
||||||
|
|
|
@ -552,7 +552,7 @@ static void UnmapLater(int f, void *p, size_t n) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CollectGarbage(void) {
|
static void CollectGarbage(void) {
|
||||||
DestroyHttpRequest(&msg);
|
DestroyHttpMessage(&msg);
|
||||||
while (freelist.n) {
|
while (freelist.n) {
|
||||||
free(freelist.p[--freelist.n]);
|
free(freelist.p[--freelist.n]);
|
||||||
}
|
}
|
||||||
|
@ -3058,8 +3058,8 @@ static void GetDosLocalTime(int64_t utcunixts, uint16_t *out_time,
|
||||||
static bool IsUtf8(const void *data, size_t size) {
|
static bool IsUtf8(const void *data, size_t size) {
|
||||||
const unsigned char *p, *pe;
|
const unsigned char *p, *pe;
|
||||||
for (p = data, pe = p + size; p + 2 <= pe; ++p) {
|
for (p = data, pe = p + size; p + 2 <= pe; ++p) {
|
||||||
if (*p >= 0300) {
|
if (p[0] >= 0300) {
|
||||||
if (*p >= 0200 && *p < 0300) {
|
if (p[1] >= 0200 && p[1] < 0300) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -5236,7 +5236,7 @@ static bool HandleMessage(void) {
|
||||||
struct iovec iov[4];
|
struct iovec iov[4];
|
||||||
long actualcontentlength;
|
long actualcontentlength;
|
||||||
g_syscount = 0;
|
g_syscount = 0;
|
||||||
if ((rc = ParseHttpRequest(&msg, inbuf.p, amtread)) != -1) {
|
if ((rc = ParseHttpMessage(&msg, inbuf.p, amtread)) != -1) {
|
||||||
if (!rc) return false;
|
if (!rc) return false;
|
||||||
hdrsize = rc;
|
hdrsize = rc;
|
||||||
if (logmessages) LogMessage("received", inbuf.p, hdrsize);
|
if (logmessages) LogMessage("received", inbuf.p, hdrsize);
|
||||||
|
@ -5317,7 +5317,7 @@ static void InitRequest(void) {
|
||||||
outbuf.n = 0;
|
outbuf.n = 0;
|
||||||
luaheaderp = 0;
|
luaheaderp = 0;
|
||||||
contentlength = 0;
|
contentlength = 0;
|
||||||
InitHttpRequest(&msg);
|
InitHttpMessage(&msg, kHttpRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void HandleMessages(void) {
|
static void HandleMessages(void) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue