int HTTPConnectionSendFile(HTTPConnectionRef const conn, strarg_t const path, strarg_t const type, int64_t size) { int rc = async_fs_open(path, O_RDONLY, 0000); if(UV_ENOENT == rc) return HTTPConnectionSendStatus(conn, 404); if(rc < 0) return HTTPConnectionSendStatus(conn, 400); // TODO: Error conversion. uv_file file = rc; rc = 0; if(size < 0) { uv_fs_t req[1]; rc = async_fs_fstat(file, req); if(rc < 0) { rc = HTTPConnectionSendStatus(conn, 400); goto cleanup; } if(S_ISDIR(req->statbuf.st_mode)) { rc = UV_EISDIR; goto cleanup; } if(!S_ISREG(req->statbuf.st_mode)) { rc = HTTPConnectionSendStatus(conn, 403); goto cleanup; } size = req->statbuf.st_size; } rc = rc < 0 ? rc : HTTPConnectionWriteResponse(conn, 200, "OK"); rc = rc < 0 ? rc : HTTPConnectionWriteContentLength(conn, size); // TODO: Caching and other headers. if(type) rc = rc < 0 ? rc : HTTPConnectionWriteHeader(conn, "Content-Type", type); rc = rc < 0 ? rc : HTTPConnectionBeginBody(conn); rc = rc < 0 ? rc : HTTPConnectionWriteFile(conn, file); rc = rc < 0 ? rc : HTTPConnectionEnd(conn); cleanup: async_fs_close(file); file = -1; return rc; }
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; }
int HTTPConnectionSendFile(HTTPConnectionRef const conn, strarg_t const path, strarg_t const type, int64_t size) { uv_file const file = async_fs_open(path, O_RDONLY, 0000); if(UV_ENOENT == file) return HTTPConnectionSendStatus(conn, 404); if(file < 0) return HTTPConnectionSendStatus(conn, 400); // TODO: Error conversion. int rc = 0; if(size < 0) { uv_fs_t req[1]; rc = async_fs_fstat(file, req); if(rc < 0) return HTTPConnectionSendStatus(conn, 400); size = req->statbuf.st_size; } rc = rc < 0 ? rc : HTTPConnectionWriteResponse(conn, 200, "OK"); rc = rc < 0 ? rc : HTTPConnectionWriteContentLength(conn, size); // TODO: Caching and other headers. if(type) rc = rc < 0 ? rc : HTTPConnectionWriteHeader(conn, "Content-Type", type); rc = rc < 0 ? rc : HTTPConnectionBeginBody(conn); rc = rc < 0 ? rc : HTTPConnectionWriteFile(conn, file); rc = rc < 0 ? rc : HTTPConnectionEnd(conn); async_fs_close(file); return rc; }