static bool_t getPage(EFSRepoRef const repo, HTTPConnectionRef const conn, HTTPMethod const method, strarg_t const URI) { if(HTTP_GET != method) return false; size_t pathlen = prefix("/", URI); if(!pathlen) return false; if(!pathterm(URI, (size_t)pathlen)) return false; EFSSessionRef const session = auth(repo, conn, method, URI+pathlen); if(!session) { HTTPConnectionSendStatus(conn, 403); return true; } // TODO: Parse querystring `q` parameter EFSFilterRef const filter = EFSFilterCreate(EFSTypeFilter); EFSFilterAddStringArg(filter, "text/html; charset=utf-8"); EFSFileInfo *const files = EFSSessionCreateFileInfoList(session, filter, RESULTS_MAX); HTTPConnectionWriteResponse(conn, 200, "OK"); HTTPConnectionWriteHeader(conn, "Content-Type", "text/html; charset=utf-8"); HTTPConnectionWriteHeader(conn, "Transfer-Encoding", "chunked"); HTTPConnectionBeginBody(conn); // TODO: Page header uv_fs_t req = { .data = co_active(); } for(index_t i = 0; i < files->count; ++i) { uv_fs_open(loop, &req, path, O_RDONLY, 0400, async_fs_cb); co_switch(yield); uv_fs_req_cleanup(&req); uv_file const file = req.result; if(file < 0) continue; HTTPConnectionWriteChunkLength(conn, files->items[i].size); HTTPConnectionWriteFile(conn, file); uv_fs_close(loop, &req, file, async_fs_cb); co_switch(yield); uv_fs_req_cleanup(&req); HTTPConnectionWrite(conn, "\r\n", 2); } // TODO: Page trailer HTTPConnectionWriteChunkLength(conn, 0); HTTPConnectionWrite(conn, "\r\n", 2); HTTPConnectionEnd(conn); EFSFileInfoListFree(files); return true; }
int HTTPConnectionSendMessage(HTTPConnectionRef const conn, uint16_t const status, strarg_t const str) { size_t const len = strlen(str); int rc = 0; rc = rc < 0 ? rc : HTTPConnectionWriteResponse(conn, status, str); rc = rc < 0 ? rc : HTTPConnectionWriteHeader(conn, "Content-Type", "text/plain; charset=utf-8"); rc = rc < 0 ? rc : HTTPConnectionWriteContentLength(conn, len+1); rc = rc < 0 ? rc : HTTPConnectionBeginBody(conn); // TODO: Check how HEAD responses should look. if(HTTP_HEAD != conn->parser->method) { // TODO: Expose method? What? rc = rc < 0 ? rc : HTTPConnectionWrite(conn, (byte_t const *)str, len); rc = rc < 0 ? rc : HTTPConnectionWrite(conn, (byte_t const *)STR_LEN("\n")); } rc = rc < 0 ? rc : HTTPConnectionEnd(conn); // if(status >= 400) fprintf(stderr, "%s: %d %s\n", HTTPConnectionGetRequestURI(conn), (int)status, str); return rc; }
int HTTPConnectionWriteChunkLength(HTTPConnectionRef const conn, uint64_t const length) { if(!conn) return 0; str_t str[16]; int const slen = snprintf(str, sizeof(str), "%llx\r\n", (unsigned long long)length); if(slen < 0) return UV_UNKNOWN; return HTTPConnectionWrite(conn, (byte_t const *)str, slen); }
int HTTPConnectionWriteChunkFile(HTTPConnectionRef const conn, strarg_t const path) { bool worker = false; uv_file file = -1; byte_t *buf = NULL; int rc; async_pool_enter(NULL); worker = true; rc = async_fs_open(path, O_RDONLY, 0000); if(rc < 0) goto cleanup; file = rc; buf = malloc(BUFFER_SIZE); if(!buf) rc = UV_ENOMEM; if(rc < 0) goto cleanup; uv_buf_t const chunk = uv_buf_init((char *)buf, BUFFER_SIZE); ssize_t len = async_fs_readall_simple(file, &chunk); if(len < 0) rc = len; if(rc < 0) goto cleanup; // Fast path for small files. if(len < BUFFER_SIZE) { str_t pfx[16]; int const pfxlen = snprintf(pfx, sizeof(pfx), "%llx\r\n", (unsigned long long)len); if(pfxlen < 0) rc = UV_UNKNOWN; if(rc < 0) goto cleanup; uv_buf_t parts[] = { uv_buf_init(pfx, pfxlen), uv_buf_init((char *)buf, len), uv_buf_init((char *)STR_LEN("\r\n")), }; async_fs_close(file); file = -1; async_pool_leave(NULL); worker = false; rc = HTTPConnectionWritev(conn, parts, numberof(parts)); goto cleanup; } uv_fs_t req[1]; rc = async_fs_fstat(file, req); if(rc < 0) goto cleanup; if(0 == req->statbuf.st_size) goto cleanup; async_pool_leave(NULL); worker = false; // TODO: HACK, WriteFile continues from where we left off rc = rc < 0 ? rc : HTTPConnectionWriteChunkLength(conn, req->statbuf.st_size); rc = rc < 0 ? rc : HTTPConnectionWritev(conn, &chunk, 1); rc = rc < 0 ? rc : HTTPConnectionWriteFile(conn, file); rc = rc < 0 ? rc : HTTPConnectionWrite(conn, (byte_t const *)STR_LEN("\r\n")); cleanup: FREE(&buf); if(file >= 0) { async_fs_close(file); file = -1; } if(worker) { async_pool_leave(NULL); worker = false; } assert(file < 0); assert(!worker); return rc; }
static int listener0(void *ctx, HTTPServerRef const server, HTTPConnectionRef const conn) { HTTPMethod method; str_t URI[URI_MAX]; ssize_t len = HTTPConnectionReadRequest(conn, &method, URI, sizeof(URI)); if(UV_EOF == len) { // HACK: Force the connection to realize it's dead. // Otherwise there is a timeout period of like 15-20 seconds // and we can run out of file descriptors. I suspect this // is a bug with libuv, but I'm not sure. HTTPConnectionWrite(conn, (byte_t const *)STR_LEN("x")); HTTPConnectionFlush(conn); return 0; } if(UV_EMSGSIZE == len) return 414; // Request-URI Too Large if(len < 0) { fprintf(stderr, "Request error: %s\n", uv_strerror(len)); return 500; } HTTPHeadersRef headers; int rc = HTTPHeadersCreateFromConnection(conn, &headers); if(UV_EMSGSIZE == rc) return 431; // Request Header Fields Too Large if(rc < 0) return 500; strarg_t const host = HTTPHeadersGet(headers, "host"); str_t domain[1023+1]; domain[0] = '\0'; if(host) sscanf(host, "%1023[^:]", domain); // TODO: Verify Host header to prevent DNS rebinding. if(SERVER_PORT_TLS && server == server_raw) { // Redirect from HTTP to HTTPS if('\0' == domain[0]) return 400; strarg_t const port = SERVER_PORT_TLS; str_t loc[URI_MAX]; rc = snprintf(loc, sizeof(loc), "https://%s:%s%s", domain, port, URI); if(rc >= sizeof(loc)) 414; // Request-URI Too Large if(rc < 0) return 500; HTTPConnectionSendRedirect(conn, 301, loc); return 0; } strarg_t const cookie = HTTPHeadersGet(headers, "cookie"); SLNSessionCacheRef const cache = SLNRepoGetSessionCache(repo); SLNSessionRef session = NULL; rc = SLNSessionCacheCopyActiveSession(cache, cookie, &session); if(rc < 0) return 500; // Note: null session is valid (zero permissions). rc = -1; rc = rc >= 0 ? rc : SLNServerDispatch(repo, session, conn, method, URI, headers); rc = rc >= 0 ? rc : BlogDispatch(blog, session, conn, method, URI, headers); SLNSessionRelease(&session); HTTPHeadersFree(&headers); return rc; }
int HTTPConnectionWriteChunkv(HTTPConnectionRef const conn, uv_buf_t const parts[], unsigned int const count) { if(!conn) return 0; uint64_t total = 0; for(size_t i = 0; i < count; i++) total += parts[i].len; if(total <= 0) return 0; int rc = 0; rc = rc < 0 ? rc : HTTPConnectionWriteChunkLength(conn, total); rc = rc < 0 ? rc : async_write((uv_stream_t *)conn->stream, parts, count); rc = rc < 0 ? rc : HTTPConnectionWrite(conn, (byte_t const *)STR_LEN("\r\n")); return rc; }
int HTTPConnectionWriteChunkEnd(HTTPConnectionRef const conn) { if(!conn) return 0; return HTTPConnectionWrite(conn, (byte_t const *)STR_LEN("0\r\n\r\n")); }
int HTTPConnectionBeginBody(HTTPConnectionRef const conn) { if(!conn) return 0; return HTTPConnectionWrite(conn, (byte_t *)STR_LEN( "Connection: keep-alive\r\n" // TODO "\r\n")); }