void rhizome_client_poll(struct sched_ent *alarm) { rhizome_http_request *r = (rhizome_http_request *)alarm; if (alarm->poll.revents == 0){ rhizome_server_free_http_request(r); return; } switch(r->request_type) { case RHIZOME_HTTP_REQUEST_RECEIVING: /* Keep reading until we have two CR/LFs in a row */ r->request[r->request_length] = '\0'; sigPipeFlag=0; int bytes = read_nonblock(r->alarm.poll.fd, &r->request[r->request_length], RHIZOME_HTTP_REQUEST_MAXLEN - r->request_length); /* If we got some data, see if we have found the end of the HTTP request */ if (bytes > 0) { // reset inactivity timer r->alarm.alarm = gettime_ms() + RHIZOME_IDLE_TIMEOUT; r->alarm.deadline = r->alarm.alarm + RHIZOME_IDLE_TIMEOUT; unschedule(&r->alarm); schedule(&r->alarm); r->request_length += bytes; if (http_header_complete(r->request, r->request_length, bytes + 4)) { /* We have the request. Now parse it to see if we can respond to it */ rhizome_server_parse_http_request(r); } } else { if (debug & DEBUG_RHIZOME_TX) DEBUG("Empty read, closing connection"); rhizome_server_free_http_request(r); return; } if (sigPipeFlag) { if (debug & DEBUG_RHIZOME_TX) DEBUG("Received SIGPIPE, closing connection"); rhizome_server_free_http_request(r); return; } break; default: /* Socket already has request -- so just try to send some data. */ rhizome_server_http_send_bytes(r); break; } return; }
static int rhizome_server_parse_http_request(rhizome_http_request *r) { /* Switching to writing, so update the call-back */ r->alarm.poll.events=POLLOUT; watch(&r->alarm); // Start building up a response. r->request_type = 0; // Parse the HTTP "GET" line. char *path = NULL; size_t pathlen = 0; if (str_startswith(r->request, "GET ", &path)) { char *p; // This loop is guaranteed to terminate before the end of the buffer, because we know that the // buffer contains at least "\n\n" and maybe "\r\n\r\n" at the end of the header block. for (p = path; !isspace(*p); ++p) ; pathlen = p - path; if ( str_startswith(p, " HTTP/1.", &p) && (str_startswith(p, "0", &p) || str_startswith(p, "1", &p)) && (str_startswith(p, "\r\n", &p) || str_startswith(p, "\n", &p)) ) path[pathlen] = '\0'; else path = NULL; } if (path) { char *id = NULL; INFOF("RHIZOME HTTP SERVER, GET %s", alloca_toprint(1024, path, pathlen)); if (strcmp(path, "/favicon.ico") == 0) { r->request_type = RHIZOME_HTTP_REQUEST_FAVICON; rhizome_server_http_response_header(r, 200, "image/vnd.microsoft.icon", favicon_len); } else if (strcmp(path, "/rhizome/groups") == 0) { /* Return the list of known groups */ rhizome_server_sql_query_http_response(r, "id", "groups", "from groups", 32, 1); } else if (strcmp(path, "/rhizome/files") == 0) { /* Return the list of known files */ rhizome_server_sql_query_http_response(r, "id", "files", "from files", 32, 1); } else if (strcmp(path, "/rhizome/bars") == 0) { /* Return the list of known BARs */ rhizome_server_sql_query_http_response(r, "bar", "manifests", "from manifests", 32, 0); } else if (str_startswith(path, "/rhizome/file/", &id)) { /* Stream the specified payload */ if (!rhizome_str_is_file_hash(id)) { rhizome_server_simple_http_response(r, 400, "<html><h1>Invalid payload ID</h1></html>\r\n"); } else { // TODO: Check for Range: header and return 206 if returning partial content str_toupper_inplace(id); long long rowid = -1; sqlite_exec_int64(&rowid, "select rowid from files where id='%s';", id); if (rowid >= 0 && sqlite3_blob_open(rhizome_db, "main", "files", "data", rowid, 0, &r->blob) != SQLITE_OK) rowid = -1; if (rowid == -1) { rhizome_server_simple_http_response(r, 404, "<html><h1>Payload not found</h1></html>\r\n"); } else { r->source_index = 0; r->blob_end = sqlite3_blob_bytes(r->blob); rhizome_server_http_response_header(r, 200, "application/binary", r->blob_end - r->source_index); r->request_type |= RHIZOME_HTTP_REQUEST_BLOB; } } } else if (str_startswith(path, "/rhizome/manifest/", &id)) { // TODO: Stream the specified manifest rhizome_server_simple_http_response(r, 500, "<html><h1>Not implemented</h1></html>\r\n"); } else { rhizome_server_simple_http_response(r, 404, "<html><h1>Not found</h1></html>\r\n"); } } else { if (debug & DEBUG_RHIZOME_TX) DEBUGF("Received malformed HTTP request: %s", alloca_toprint(120, (const char *)r->request, r->request_length)); rhizome_server_simple_http_response(r, 400, "<html><h1>Malformed request</h1></html>\r\n"); } /* Try sending data immediately. */ rhizome_server_http_send_bytes(r); return 0; }
int rhizome_direct_parse_http_request(rhizome_http_request *r) { const char *submitBareFileURI=confValueGet("rhizome.api.addfile.uri", NULL); DEBUGF("uri=%s", submitBareFileURI ? alloca_str_toprint(submitBareFileURI) : "NULL"); /* Switching to writing, so update the call-back */ r->alarm.poll.events=POLLOUT; watch(&r->alarm); // Parse the HTTP request into verb, path, protocol, headers and content. char *const request_end = r->request + r->request_length; char *verb = r->request; char *path = NULL; char *proto = NULL; size_t pathlen = 0; char *headers = NULL; int headerlen = 0; char *content = NULL; int contentlen = 0; char *p; if ((str_startswith(verb, "GET", &p) || str_startswith(verb, "POST", &p)) && isspace(*p)) { *p++ = '\0'; path = p; while (p < request_end && !isspace(*p)) ++p; if (p < request_end) { pathlen = p - path; *p++ = '\0'; proto = p; if ( str_startswith(p, "HTTP/1.", &p) && (str_startswith(p, "0", &p) || str_startswith(p, "1", &p)) && (str_startswith(p, "\r\n", &headers) || str_startswith(p, "\n", &headers)) ) { *p = '\0'; char *eoh = str_str(headers, "\r\n\r\n", request_end - p); if (eoh) { content = eoh + 4; headerlen = content - headers; contentlen = request_end - content; } } } } if (content == NULL) { if (debug & DEBUG_RHIZOME_TX) DEBUGF("Received malformed HTTP request %s", alloca_toprint(160, (const char *)r->request, r->request_length)); return rhizome_server_simple_http_response(r, 400, "<html><h1>Malformed request</h1></html>\r\n"); } INFOF("RHIZOME HTTP SERVER, %s %s %s", verb, alloca_toprint(-1, path, pathlen), proto); if (debug & DEBUG_RHIZOME_TX) DEBUGF("headers %s", alloca_toprint(-1, headers, headerlen)); if (strcmp(verb, "GET") == 0 && strcmp(path, "/favicon.ico") == 0) { r->request_type = RHIZOME_HTTP_REQUEST_FAVICON; rhizome_server_http_response_header(r, 200, "image/vnd.microsoft.icon", favicon_len); } else if (strcmp(verb, "POST") == 0 && ( strcmp(path, "/rhizome/import") == 0 || strcmp(path, "/rhizome/enquiry") == 0 || (submitBareFileURI && strcmp(path, submitBareFileURI) == 0) ) ) { const char *cl_str=str_str(headers,"Content-Length: ",headerlen); const char *ct_str=str_str(headers,"Content-Type: multipart/form-data; boundary=",headerlen); if (!cl_str) return rhizome_server_simple_http_response(r,400,"<html><h1>Missing Content-Length header</h1></html>\r\n"); if (!ct_str) return rhizome_server_simple_http_response(r,400,"<html><h1>Missing or unsupported Content-Type header</h1></html>\r\n"); /* ok, we have content-type and content-length, now make sure they are well formed. */ long long content_length; if (sscanf(cl_str,"Content-Length: %lld",&content_length)!=1) return rhizome_server_simple_http_response(r,400,"<html><h1>Malformed Content-Length header</h1></html>\r\n"); char boundary_string[1024]; int i; ct_str+=strlen("Content-Type: multipart/form-data; boundary="); for(i=0;i<1023&&*ct_str&&*ct_str!='\n'&&*ct_str!='\r';i++,ct_str++) boundary_string[i]=*ct_str; boundary_string[i] = '\0'; if (i<4||i>128) return rhizome_server_simple_http_response(r,400,"<html><h1>Malformed Content-Type header</h1></html>\r\n"); DEBUGF("content_length=%lld, boundary_string=%s contentlen=%d", (long long) content_length, alloca_str_toprint(boundary_string), contentlen); /* Now start receiving and parsing multi-part data. If we already received some of the post-header data, process that first. Tell the HTTP request that it has moved to multipart form data parsing, and what the actual requested action is. */ /* Remember boundary string and source path. Put the preceeding -- on the front to make our life easier when parsing the rest later. */ strbuf bs = strbuf_local(r->boundary_string, sizeof r->boundary_string); strbuf_puts(bs, "--"); strbuf_puts(bs, boundary_string); if (strbuf_overrun(bs)) return rhizome_server_simple_http_response(r,500,"<html><h1>Internal server error: Multipart boundary string too long</h1></html>\r\n"); strbuf ps = strbuf_local(r->path, sizeof r->path); strbuf_puts(ps, path); if (strbuf_overrun(ps)) return rhizome_server_simple_http_response(r,500,"<html><h1>Internal server error: Path too long</h1></html>\r\n"); r->boundary_string_length = strbuf_len(bs); r->source_index = 0; r->source_count = content_length; r->request_type = RHIZOME_HTTP_REQUEST_RECEIVING_MULTIPART; r->request_length = 0; r->source_flags = 0; /* Find the end of the headers and start of any body bytes that we have read so far. Copy the bytes to a separate buffer, because r->request and r->request_length get used internally in the parser. */ if (contentlen) { char buffer[contentlen]; bcopy(content, buffer, contentlen); rhizome_direct_process_post_multipart_bytes(r, buffer, contentlen); } /* Handle the rest of the transfer asynchronously. */ return 0; } else { rhizome_server_simple_http_response(r, 404, "<html><h1>Not found (OTHER)</h1></html>\r\n"); } /* Try sending data immediately. */ rhizome_server_http_send_bytes(r); return 0; }