diff --git a/net/http/parsecontentlength.c b/net/http/parsecontentlength.c index f10909e48..ddbae77d5 100644 --- a/net/http/parsecontentlength.c +++ b/net/http/parsecontentlength.c @@ -26,7 +26,7 @@ */ ssize_t ParseContentLength(const char *s, size_t n) { int i, r = 0; - if (!n) return -1; + if (!n) return 0; for (i = 0; i < n; ++i) { if (!isdigit(s[i])) return -1; if (__builtin_mul_overflow(r, 10, &r)) return -1; diff --git a/net/http/parsehttprange.c b/net/http/parsehttprange.c index ce15879db..78e9458ac 100644 --- a/net/http/parsehttprange.c +++ b/net/http/parsehttprange.c @@ -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 "net/http/http.h" @@ -48,14 +49,18 @@ bool ParseHttpRange(const char *p, size_t n, long resourcelength, } if (n && *p == '-') { ++p, --n; - length = 0; - while (n && '0' <= *p && *p <= '9') { - if (__builtin_mul_overflow(length, 10, &length)) return false; - if (__builtin_add_overflow(length, *p - '0', &length)) return false; - ++p, --n; + if (!n) { + length = MAX(start, resourcelength) - start; + } else { + length = 0; + while (n && '0' <= *p && *p <= '9') { + if (__builtin_mul_overflow(length, 10, &length)) return false; + if (__builtin_add_overflow(length, *p - '0', &length)) return false; + ++p, --n; + } + if (__builtin_add_overflow(length, 1, &length)) return false; + if (__builtin_sub_overflow(length, start, &length)) return false; } - if (__builtin_add_overflow(length, 1, &length)) return false; - if (__builtin_sub_overflow(length, start, &length)) return false; } else if (__builtin_sub_overflow(resourcelength, start, &length)) { return false; } diff --git a/net/http/parsehttprequest.c b/net/http/parsehttprequest.c index d0a8d9587..27c380e50 100644 --- a/net/http/parsehttprequest.c +++ b/net/http/parsehttprequest.c @@ -87,6 +87,8 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) { return ebadmsg(); } break; + } else if (!('A' <= c && c <= 'Z')) { + return ebadmsg(); } if (++r->i == n) break; c = p[r->i] & 0xff; diff --git a/test/net/http/parsecontentlength_test.c b/test/net/http/parsecontentlength_test.c index 097e69132..582bed94d 100644 --- a/test/net/http/parsecontentlength_test.c +++ b/test/net/http/parsecontentlength_test.c @@ -20,7 +20,7 @@ #include "net/http/http.h" TEST(ParseContentLength, test) { - EXPECT_EQ(-1, ParseContentLength("", 0)); + EXPECT_EQ(0, ParseContentLength("", 0)); EXPECT_EQ(-1, ParseContentLength("-1", 2)); EXPECT_EQ(-1, ParseContentLength("-2", 2)); EXPECT_EQ(0, ParseContentLength("0", 1)); diff --git a/test/net/http/parsehttprange_test.c b/test/net/http/parsehttprange_test.c index c1a3fcabe..75b9f00db 100644 --- a/test/net/http/parsehttprange_test.c +++ b/test/net/http/parsehttprange_test.c @@ -52,6 +52,14 @@ TEST(ParseHttpRange, testOffset) { EXPECT_EQ(10, length); } +TEST(ParseHttpRange, testEmptySecond) { + long start, length; + const char *s = "bytes=0-"; + EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length)); + EXPECT_EQ(0, start); + EXPECT_EQ(100, length); +} + TEST(ParseHttpRange, testToEnd) { long start, length; const char *s = "bytes=40"; diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 486227748..ddeb54cb6 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -463,7 +463,7 @@ static void ProgramRedirect(int code, const char *src, const char *dst) { } else { i = redirects.n; redirects.p = xrealloc(redirects.p, (i + 1) * sizeof(*redirects.p)); - for (j = i; j > 0; --j) { + for (j = i; j; --j) { if (CompareSlices(r.path, r.pathlen, redirects.p[j - 1].path, redirects.p[j - 1].pathlen) < 0) { redirects.p[j] = redirects.p[j - 1]; @@ -526,6 +526,10 @@ static const char *GetContentType2(const char *path, size_t n) { } static const char *GetContentType(struct Asset *a, const char *path, size_t n) { + const char *r; + if (a->file && (r = GetContentType2(a->file->path, strlen(a->file->path)))) { + return r; + } return firstnonnull( GetContentType2(path, n), firstnonnull(GetContentType2(ZIP_LFILE_NAME(zmap + a->lf), @@ -1114,6 +1118,19 @@ static void ParseRequestUri(void) { u.data = inbuf.p + msg.uri.a; u.size = msg.uri.b - msg.uri.a; memset(&request, 0, sizeof(request)); + if (u.size > 8 && !memcmp(u.data, "http", 4)) { + /* + * convert http://www.foo.com/index.html -> /www.foo.com/index.html + */ + if (u.data[4] == ':' && u.data[5] == '/' && u.data[6] == '/') { + u.data += 6; + u.size -= 6; + } else if (u.data[4] == 's' && u.data[5] == ':' && u.data[6] == '/' && + u.data[7] == '/') { + u.data += 7; + u.size -= 7; + } + } u.q = u.p = FreeLater(xmalloc(u.size * 2)); ParsePath(&u, &request.path); if (u.c == '?') ParseParams(&u, &request.params); @@ -1201,7 +1218,7 @@ static char *ServeError(int code, const char *reason) { contentlength = reasonlen + 2; stpcpy(stpcpy(content, reason), "\r\n"); WARNF("%s %s %`'.*s %d %s", clientaddrstr, kHttpMethod[msg.method], - request.path.n, request.path.p, code, reason); + msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a, code, reason); return p; } @@ -1233,9 +1250,10 @@ static char *AppendContentLength(char *p, size_t n) { static char *AppendContentRange(char *p, long rangestart, long rangelength, long contentlength) { long endrange; - CHECK_GE(rangestart + rangelength, rangestart); + CHECK_GT(rangelength, 0); + CHECK_GT(rangestart + rangelength, rangestart); CHECK_LE(rangestart + rangelength, contentlength); - if (__builtin_add_overflow(rangestart, rangelength, &endrange)) abort(); + endrange = rangestart + rangelength - 1; p = AppendHeaderName(p, "Content-Range"); p = stpcpy(p, "bytes "); p += uint64toarray_radix10(rangestart, p); @@ -1361,6 +1379,7 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) { if (ParseHttpRange(inbuf.p + msg.headers[kHttpRange].a, msg.headers[kHttpRange].b - msg.headers[kHttpRange].a, contentlength, &rangestart, &rangelength)) { + LOGF("rangestart = %ld rangelength = %ld", rangestart, rangelength); p = SetStatus(206, "Partial Content"); p = AppendContentRange(p, rangestart, rangelength, contentlength); content = AddRange(content, rangestart, rangelength); @@ -2139,7 +2158,7 @@ Content-Length: 0\r\n\ static void LogClose(const char *reason) { if (amtread) { - WARNF("%s %s with %,ld bytes unprocessed", clientaddrstr, reason); + WARNF("%s %s with %,ld bytes unprocessed", clientaddrstr, reason, amtread); } else { DEBUGF("%s %s", clientaddrstr, reason); } @@ -2301,25 +2320,22 @@ static char *HandleMessage(void) { if (httpversion > 101) { return ServeError(505, "HTTP Version Not Supported"); } - if (msg.method > kHttpOptions || + if (HasHeader(kHttpExpect) && !HeaderEquals(kHttpExpect, "100-continue")) { + return ServeError(417, "Expectation Failed"); + } + if (msg.method == kHttpConnect || (HasHeader(kHttpTransferEncoding) && !HeaderEquals(kHttpTransferEncoding, "identity"))) { return ServeError(501, "Not Implemented"); } - if (HasHeader(kHttpExpect) && !HeaderEquals(kHttpExpect, "100-continue")) { - return ServeError(417, "Expectation Failed"); + if (!HasHeader(kHttpContentLength) && + (msg.method == kHttpPost || msg.method == kHttpPut)) { + return ServeError(411, "Length Required"); } if ((cl = ParseContentLength(inbuf.p + msg.headers[kHttpContentLength].a, msg.headers[kHttpContentLength].b - msg.headers[kHttpContentLength].a)) == -1) { - if (HasHeader(kHttpContentLength)) { - return ServeError(400, "Bad Request"); - } else if (msg.method != kHttpGet && msg.method != kHttpHead && - msg.method != kHttpDelete && msg.method != kHttpOptions) { - return ServeError(411, "Length Required"); - } else { - cl = 0; - } + return ServeError(400, "Bad Request"); } need = hdrsize + cl; /* synchronization is possible */ if (need > inbuf.n) { @@ -2363,7 +2379,7 @@ static char *HandleMessage(void) { return ServeServerOptions(); } if (!IsAcceptableHttpRequestPath(request.path.p, request.path.n)) { - WARNF("%s could not parse request request %`'.*s", clientaddrstr, + WARNF("%s could not parse request %`'.*s", clientaddrstr, msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a); connectionclose = true; return ServeError(400, "Bad Request"); @@ -2399,7 +2415,9 @@ static bool HandleRequest(void) { p = HandleMessage(); } else { httpversion = 101; + connectionclose = true; p = ServeError(400, "Bad Request"); + DEBUGF("%s received garbage %`'.*s", clientaddrstr, amtread, inbuf.p); } if (!msgsize) { amtread = 0;