struct basic_epoll_event *handle_request() { if (0 == SSTRNCMP(URI_TEST1, URI)) { return response(HTTP_STATUS_200, HTTP_CONTENT_TYPE_TEXT_PLAIN); } URI::decode(URI); const char *file = (URI[1] == 0 ? "." : URI + 1); int ffd = open(file, O_RDONLY); if (0 > ffd) return response(HTTP_STATUS_404, HTTP_CONTENT_TYPE_TEXT_PLAIN); http_server *h = (http_server *)events_array.get(ffd); if (0 > h->sendFile(this)) { struct stat st; int res = fstat(ffd, &st); ::close(ffd); if (res == 0 && S_ISDIR(st.st_mode) && 0 == generateDirList(file)) { return response(HTTP_STATUS_200, HTTP_CONTENT_TYPE_TEXT_HTML); } else return response(HTTP_STATUS_500, HTTP_CONTENT_TYPE_TEXT_PLAIN); } headerStartMime(HTTP_STATUS_200, file); headerContentLength(); return headerClose(); }
static void http_server_process_request(char *uri, char *headers) { struct http_server_context *ctx = http_server_get_context(); ctx->headers = headers; char *query = strchrnul(uri, '?'); if (*query) *query++ = 0; ctx->query = query; static const char HTTP[] = "http://"; if (0 == SSTRNCMP(HTTP, uri)) { uri += SSTRLEN(HTTP); uri = strchrnul(uri, '/'); } ctx->uri = uri; epoll_worker_ignore_events(ctx->fd); ctx->server->user_func(); }
void http_client_fiber_main(void) { struct http_client_context *ctx = (struct http_client_context *)current_ctx->reserved; int fd = current_ctx->fd; struct epoll_worker_fd_data *fd_data = epoll_worker_fd_map + fd; TIMEOUT_HANDLER_REMOVE_FD_DATA(fd_data); int persistent = 0; epoll_worker_set_last_fd(fd); /* needed in the case where epoll_wait never occured */ /* * write request */ int res; for (; (res = vmbuf_write(&ctx->request, fd)) == 0; http_client_yield()); if (0 > res) { LOGGER_PERROR("write"); CLIENT_ERROR(); } /* * HTTP header */ uint32_t eoh_ofs; char *data; char *eoh; res = vmbuf_read(&ctx->response, fd); *vmbuf_wloc(&ctx->response) = 0; READ_MORE_DATA_STR(NULL == (eoh = strstr(data = vmbuf_data(&ctx->response), CRLFCRLF))); eoh_ofs = eoh - data + SSTRLEN(CRLFCRLF); *eoh = 0; char *p = strstr(data, CONNECTION); if (p != NULL) { p += SSTRLEN(CONNECTION); persistent = (0 == SSTRNCMPI(CONNECTION_CLOSE, p) ? 0 : 1); } SSTRL(HTTP, "HTTP/"); if (0 != SSTRNCMP(HTTP, data)) CLIENT_ERROR(); p = strchrnul(data, ' '); int code = (*p ? atoi(p + 1) : 0); if (0 == code) CLIENT_ERROR(); do { if (code == 204 || code == 304) /* No Content, Not Modified */ break; /* * content length */ char *content_len_str = strstr(data, CONTENT_LENGTH); if (NULL != content_len_str) { content_len_str += SSTRLEN(CONTENT_LENGTH); size_t content_end = eoh_ofs + atoi(content_len_str); READ_MORE_DATA(vmbuf_wlocpos(&ctx->response) < content_end); break; } /* * chunked encoding */ char *transfer_encoding_str = strstr(data, TRANSFER_ENCODING); if (NULL != transfer_encoding_str && 0 == SSTRNCMP(transfer_encoding_str + SSTRLEN(TRANSFER_ENCODING), "chunked")) { size_t chunk_start = eoh_ofs; size_t data_start = eoh_ofs; char *p; for (;;) { READ_MORE_DATA_STR(*(p = strchrnul((data = vmbuf_data(&ctx->response)) + chunk_start, '\r')) == 0); if (0 != SSTRNCMP(CRLF, p)) CLIENT_ERROR(); uint32_t s = strtoul(data + chunk_start, NULL, 16); if (0 == s) { vmbuf_wlocset(&ctx->response, data_start); break; } chunk_start = p - data + SSTRLEN(CRLF); size_t chunk_end = chunk_start + s + SSTRLEN(CRLF); READ_MORE_DATA(vmbuf_wlocpos(&ctx->response) < chunk_end); memmove(vmbuf_data(&ctx->response) + data_start, vmbuf_data(&ctx->response) + chunk_start, s); data_start += s; chunk_start = chunk_end; } break; } /* * older versions of HTTP, terminated by disconnect */ for (;; yield()) { if ((res = vmbuf_read(&ctx->response, fd)) < 0) CLIENT_ERROR(); if (0 == res) break; /* remote side closed connection */ } } while (0); ctx->content = vmbuf_data_ofs(&ctx->response, eoh_ofs); ctx->content_length = vmbuf_wlocpos(&ctx->response) - eoh_ofs; ctx->http_status_code = code; vmbuf_data_ofs(&ctx->response, eoh_ofs - SSTRLEN(CRLFCRLF))[0] = CR; *vmbuf_wloc(&ctx->response) = 0; ctx->persistent = persistent; if (!persistent) close(fd); }
void http_server_fiber_main(void) { struct http_server_context *ctx = http_server_get_context(); struct http_server *server = ctx->server; int fd = ctx->fd; char *URI; char *headers; char *content; size_t content_length; int res; ctx->persistent = 0; vmbuf_init(&ctx->request, server->init_request_size); vmbuf_init(&ctx->header, server->init_header_size); vmbuf_init(&ctx->payload, server->init_payload_size); size_t max_req_size = server->max_req_size; for (;; http_server_yield()) { READ_FROM_SOCKET(); if (vmbuf_wlocpos(&ctx->request) > MIN_HTTP_REQ_SIZE) break; } do { if (0 == SSTRNCMP(GET, vmbuf_data(&ctx->request)) || 0 == SSTRNCMP(HEAD, vmbuf_data(&ctx->request))) { /* GET or HEAD */ while (0 != SSTRNCMP(CRLFCRLF, vmbuf_wloc(&ctx->request) - SSTRLEN(CRLFCRLF))) { http_server_yield(); READ_FROM_SOCKET(); } /* make sure the string is \0 terminated */ /* this will overwrite the first CR */ *(vmbuf_wloc(&ctx->request) - SSTRLEN(CRLFCRLF)) = 0; char *p = vmbuf_data(&ctx->request); ctx->persistent = check_persistent(p); URI = strchrnul(p, ' '); /* can't be NULL GET and HEAD constants have space at the end */ *URI = 0; ++URI; // skip the space p = strchrnul(URI, '\r'); /* HTTP/1.0 */ headers = p; if (0 != *headers) /* are headers present? */ headers += SSTRLEN(CRLF); /* skip the new line */ *p = 0; p = strchrnul(URI, ' '); /* truncate the version part */ *p = 0; /* \0 at the end of URI */ ctx->content = NULL; ctx->content_len = 0; /* minimal parsing and call user function */ http_server_process_request(URI, headers); } else if (0 == SSTRNCMP(POST, vmbuf_data(&ctx->request)) || 0 == SSTRNCMP(PUT, vmbuf_data(&ctx->request))) { /* POST or PUT */ for (;;) { *vmbuf_wloc(&ctx->request) = 0; /* wait until we have the header */ if (NULL != (content = strstr(vmbuf_data(&ctx->request), CRLFCRLF))) break; http_server_yield(); READ_FROM_SOCKET(); } *content = 0; /* terminate at the first CR like in GET */ content += SSTRLEN(CRLFCRLF); size_t content_ofs = content - vmbuf_data(&ctx->request); if (strstr(vmbuf_data(&ctx->request), EXPECT_100)) { vmbuf_sprintf(&ctx->header, "%s %s\r\n\r\n", HTTP_SERVER_VER, HTTP_STATUS_100); if (0 > vmbuf_write(&ctx->header, fd)) { close(fd); return; } vmbuf_reset(&ctx->header); } ctx->persistent = check_persistent(vmbuf_data(&ctx->request)); /* parse the content length */ char *p = strcasestr(vmbuf_data(&ctx->request), CONTENT_LENGTH); if (NULL == p) { http_server_response(HTTP_STATUS_411, HTTP_CONTENT_TYPE_TEXT_PLAIN); break; } p += SSTRLEN(CONTENT_LENGTH); content_length = atoi(p); for (;;) { if (content_ofs + content_length <= vmbuf_wlocpos(&ctx->request)) break; http_server_yield(); READ_FROM_SOCKET(); } p = vmbuf_data(&ctx->request); URI = strchrnul(p, ' '); /* can't be NULL PUT and POST constants have space at the end */ *URI = 0; ++URI; /* skip the space */ p = strchrnul(URI, '\r'); /* HTTP/1.0 */ headers = p; if (0 != *headers) /* are headers present? */ headers += SSTRLEN(CRLF); /* skip the new line */ *p = 0; p = strchrnul(URI, ' '); /* truncate http version */ *p = 0; /* \0 at the end of URI */ ctx->content = vmbuf_data_ofs(&ctx->request, content_ofs); *(ctx->content + content_length) = 0; ctx->content_len = content_length; /* minimal parsing and call user function */ http_server_process_request(URI, headers); } else { http_server_response(HTTP_STATUS_501, HTTP_CONTENT_TYPE_TEXT_PLAIN); break; } } while(0); if (vmbuf_wlocpos(&ctx->header) > 0) { epoll_worker_resume_events(fd); http_server_write(); } if (ctx->persistent) { struct epoll_worker_fd_data *fd_data = epoll_worker_fd_map + fd; fd_data->ctx = server->idle_ctx; timeout_handler_add_fd_data(&server->timeout_handler, fd_data); } else close(fd); }
struct basic_epoll_event *http_server::onRead() { int res = inbuf.read(fd); if (0 >= res) return this->close(); // remote side closed or other error occured if (inbuf.wlocpos() > max_req_size) return response(HTTP_STATUS_413, HTTP_CONTENT_TYPE_TEXT_PLAIN); // parse http request // if (inbuf.wlocpos() > MIN_HTTP_REQ_SIZE) { if (0 == SSTRNCMP(GET, inbuf.data()) || 0 == SSTRNCMP(HEAD, inbuf.data())) { if (0 == SSTRNCMP(CRLFCRLF, inbuf.wloc() - SSTRLEN(CRLFCRLF))) { // GET or HEAD requests // make sure the string is \0 terminated // this will overwrite the first CR *(inbuf.wloc() - SSTRLEN(CRLFCRLF)) = 0; char *p = inbuf.data(); checkPersistent(p); URI = strchrnul(p, ' '); // can't be NULL GET and HEAD constants have space at the end *URI = 0; ++URI; // skip the space p = strchrnul(URI, '\r'); // HTTP/1.0 headers = p; if (0 != *headers) // are headers present? headers += SSTRLEN(CRLF); // skip the new line *p = 0; p = strchrnul(URI, ' '); // truncate the version part *p = 0; // \0 at the end of URI content = NULL; content_length = 0; return process_request(); } } else if (0 == SSTRNCMP(POST, inbuf.data()) || 0 == SSTRNCMP(PUT, inbuf.data())) { // POST if (0 == eoh) { *inbuf.wloc() = 0; content = strstr(inbuf.data(), CRLFCRLF); if (NULL != content) { eoh = content - inbuf.data() + SSTRLEN(CRLFCRLF); *content = 0; // terminate at the first CR like in GET content += SSTRLEN(CRLFCRLF); if (strstr(inbuf.data(), EXPECT_100)) { header.sprintf("%s %s%s", HTTP_SERVER_VER, HTTP_STATUS_100, CRLFCRLF); if (0 > header.write(fd)) return this->close(); header.reset(); } checkPersistent(inbuf.data()); // parse the content length char *p = strcasestr(inbuf.data(), CONTENT_LENGTH); if (NULL == p) return response(HTTP_STATUS_411, HTTP_CONTENT_TYPE_TEXT_PLAIN); p += SSTRLEN(CONTENT_LENGTH); content_length = atoi(p); } else return epoll::yield(epoll::server_timeout_chain, this); // back to epoll, wait for more data } else content = inbuf.data() + eoh; if (content + content_length <= inbuf.wloc()) { // POST or PUT requests char *p = inbuf.data(); URI = strchrnul(p, ' '); // can't be NULL PUT and POST constants have space at the end *URI = 0; ++URI; // skip the space p = strchrnul(URI, '\r'); // HTTP/1.0 headers = p; if (0 != *headers) // are headers present? headers += SSTRLEN(CRLF); // skip the new line *p = 0; p = strchrnul(URI, ' '); // truncate http version *p = 0; // \0 at the end of URI *(content + content_length) = 0; return process_request(); } } else { return response(HTTP_STATUS_501, HTTP_CONTENT_TYPE_TEXT_PLAIN); } } // wait for more data return epoll::yield(epoll::server_timeout_chain, this); }