char *abspath(char *dst,size_t dstSize,const char *path) { if(*path != '/') { /* translate "abc://def" to "/dev/abc/def" */ const char *p = path; while(*p) { if(p[0] == ':' && p[1] == '/' && p[2] == '/') { size_t slen = p - path; strncpy(dst,"/dev/",SSTRLEN("/dev/")); strncpy(dst + SSTRLEN("/dev/"),path,slen); strnzcpy(dst + SSTRLEN("/dev/") + slen,p + 2,dstSize - (SSTRLEN("/dev/") + slen)); return dst; } p++; } /* prepend CWD */ size_t len = getenvto(dst,dstSize,"CWD"); if(len < dstSize - 1 && dst[len - 1] != '/') { dst[len++] = '/'; dst[len] = '\0'; } strnzcpy(dst + len,path,dstSize - len); return dst; } return (char*)path; }
ostream& ostream::operator <<(bool n) { if(ios_base::flags() & ios_base::boolalpha) write(n ? "true" : "false",n ? SSTRLEN("true") : SSTRLEN("false")); else writeUnsigned(n); return *this; }
static int check_persistent(char *p) { char *conn = strstr(p, CONNECTION); char *h1_1 = strstr(p, " HTTP/1.1"); // HTTP/1.1 if ((NULL != h1_1 && (NULL == conn || 0 != SSTRNCMPI(CONNECTION_CLOSE, conn + SSTRLEN(CONNECTION)))) || // HTTP/1.0 (NULL == h1_1 && NULL != conn && 0 == SSTRNCMPI(CONNECTION_KEEPALIVE, conn + SSTRLEN(CONNECTION)))) return 1; else return 0; }
inline void http_server::checkPersistent(char *p) { char *conn = strstr(p, CONNECTION); char *h1_1 = strstr(p, " HTTP/1.1"); // HTTP/1.1 if ((NULL != h1_1 && (NULL == conn || 0 != SSTRNCMPI(CONNECTION_CLOSE, conn + SSTRLEN(CONNECTION)))) || // HTTP/1.0 (NULL == h1_1 && NULL != conn && 0 == SSTRNCMPI(CONNECTION_KEEPALIVE, conn + SSTRLEN(CONNECTION)))) persistent = true; else persistent = false; }
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); }
static void test_canonpath(void) { char path[MAX_PATH_LEN]; size_t count; test_caseStart("Testing canonpath"); setenv("CWD","/"); count = canonpath(path,sizeof(path),"/"); test_assertUInt(count,SSTRLEN("/")); test_assertStr(path,"/"); count = canonpath(path,sizeof(path),"/bin/ls"); test_assertUInt(count,SSTRLEN("/bin/ls")); test_assertStr(path,"/bin/ls"); count = canonpath(path,sizeof(path),"/../bin/../.././home"); test_assertUInt(count,SSTRLEN("/home")); test_assertStr(path,"/home"); count = canonpath(path,sizeof(path),"bin/..///.././home"); test_assertUInt(count,SSTRLEN("/home")); test_assertStr(path,"/home"); count = canonpath(path,sizeof(path),"bin/./ls"); test_assertUInt(count,SSTRLEN("/bin/ls")); test_assertStr(path,"/bin/ls"); setenv("CWD","/home"); count = canonpath(path,sizeof(path),"hrniels/./scripts"); test_assertUInt(count,SSTRLEN("/home/hrniels/scripts")); test_assertStr(path,"/home/hrniels/scripts"); setenv("CWD","/home/"); count = canonpath(path,sizeof(path),"hrniels/./scripts"); test_assertUInt(count,SSTRLEN("/home/hrniels/scripts")); test_assertStr(path,"/home/hrniels/scripts"); count = canonpath(path,sizeof(path),".."); test_assertUInt(count,SSTRLEN("/")); test_assertStr(path,"/"); count = canonpath(path,sizeof(path),"../../."); test_assertUInt(count,SSTRLEN("/")); test_assertStr(path,"/"); count = canonpath(path,sizeof(path),"./../bin"); test_assertUInt(count,SSTRLEN("/bin")); test_assertStr(path,"/bin"); count = canonpath(path,3,"/"); if(count > 3) test_caseFailed("Copied too much"); count = canonpath(path,8,"/bin/ls"); if(count > 8) test_caseFailed("Copied too much"); count = canonpath(path,8,"/bin/../home"); if(count > 8) test_caseFailed("Copied too much"); count = canonpath(path,8,"///../bin/ls"); if(count > 8) test_caseFailed("Copied too much"); test_caseSucceeded(); }
static void test_qsort(void) { test_caseStart("Testing qsort"); { int ints[] = {}; qsort(ints,ARRAY_SIZE(ints),sizeof(int),intCompare); } { int ints[] = {1,1,1}; qsort(ints,ARRAY_SIZE(ints),sizeof(int),intCompare); test_assertInt(ints[0],1); test_assertInt(ints[1],1); test_assertInt(ints[2],1); } { int ints[] = {1,2,3}; qsort(ints,ARRAY_SIZE(ints),sizeof(int),intCompare); test_assertInt(ints[0],1); test_assertInt(ints[1],2); test_assertInt(ints[2],3); } { int ints[] = {6,7,3,4,2,1,5}; qsort(ints,ARRAY_SIZE(ints),sizeof(int),intCompare); test_assertInt(ints[0],1); test_assertInt(ints[1],2); test_assertInt(ints[2],3); test_assertInt(ints[3],4); test_assertInt(ints[4],5); test_assertInt(ints[5],6); test_assertInt(ints[6],7); } { const char *strs[] = { "m3/m3-11.png", "m3/m3-03.png", "m3/m3-09.png", "m3/m3-20.png", "m3/m3-22.png", "m3/m3-17.png", "m3/m3-24.png", "m3/m3-12.png", "m3/m3-04.png", "m3/m3-23.png", "m3/m3-18.png", "m3/m3-01.png", "m3/m3-28.png", "m3/m3-14.png", "m3/m3-07.png", "m3/m3-26.png", "m3/m3-00.png", "m3/m3-05.png", "m3/m3-21.png", "m3/m3-16.png", "m3/m3-25.png", "m3/m3-08.png", "m3/m3-10.png", "m3/m3-02.png", "m3/m3-19.png", "m3/m3-06.png", "m3/m3-13.png", "m3/m3-15.png", "m3/m3-27.png", }; qsort(strs,ARRAY_SIZE(strs),sizeof(int),strCompare); for(size_t i = 0; i < ARRAY_SIZE(strs); ++i) { const char *sno = strs[i] + SSTRLEN("m3/m3-"); int no = atoi(sno); test_assertInt(no,i); } } test_caseSucceeded(); }
struct basic_epoll_event *http_server::headerClose() { header.memcpy(CRLFCRLF, SSTRLEN(CRLFCRLF)); return startWrite(); }
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); }