void http_server::on_connection(tcp_socket& socket) { http_request* request = new http_request(socket); request->connect_on_headers_end([this](http_request& request, http_response& response){ on_request(request, response); }); request->connect_on_body([this](http_request& request, const std::string& data, http_response& response){ if (data != "") { on_body(request, data, response, *this); } }); requests_.push_back(std::unique_ptr<http_request>(request)); }
/* Called repeatedly for the content body. The passed buffer has already had the transfer-encoding removed. */ int basic_parser::do_body (char const* in, std::size_t bytes) { on_body (in, bytes); return 0; }
void HttpServerConnection::read() { while(!is_closed()) { switch(read_state) { case LINE: // get the request line try { if(!async_read_in(line,sizeof(uri)-1)) return; } catch(EndOfStreamError*) { if(!count) throw; // so we get end-of-stream when keep-alive? no problem return; } if(!line.ends_with("\r\n",2)) HttpError::Throw(HttpError::ERequestURITooLong,*this); if(!memcmp(line.cstr(),"\r\n",3)) { // empty lines are ok before request line line.clear(); continue; } count++; read_state = HEADER; { const char* method = strtok(line.cstr()," "); strncpy(uri,strtok(NULL," \r"),sizeof(this->uri)); const char* v = strtok(NULL,"\r"); if(!memcmp(v,"HTTP/1.1",9)) version = HTTP_1_1; else if(!memcmp(v,"HTTP/1.0",9)) version = HTTP_1_0; else version = HTTP_0_9; in_encoding_chunked = false; in_content_length = -1; // not known keep_alive = out_encoding_chunked = (HTTP_1_1 == version); on_request(method,uri); } line.clear(); break; case HEADER: if(!async_read_in(line)) return; if(!memcmp("\r\n",line.cstr(),3)) { read_state = BODY; if(keep_alive && !in_encoding_chunked && (-1 == in_content_length)) in_content_length = 0; // length isn't specified, yet its keep-alive, so there is no content line.clear(); on_body(); break; } if(!line.ends_with("\r\n",2)) HttpError::Throw(HttpError::ERequestEntityTooLarge,*this); { const char* header = strtok(line.cstr()," "), *value = strtok(NULL,"\r"); if(!ends_with(header,":",1)) HttpError::Throw(HttpError::EBadRequest,*this); if((write_state == LINE) && !strcasecmp(header,"connection:") && !strcasecmp(value,"keep-alive")) keep_alive = true; else if(!strcasecmp(header,"content-length:")) { in_content_length = atoi(value); if(in_content_length < 0) HttpError::Throw(HttpError::EBadRequest,*this); } else if(!strcasecmp(header,"transfer-encoding:")) in_encoding_chunked = !strcasecmp(value,"chunked"); on_header(header,value); } line.clear(); break; case BODY: if(in_encoding_chunked) { //RFC2616-s4.4 says this overrides any explicit content-length header ThrowInternalError("in encoding chunked not implemented yet"); } else if(!keep_alive && (-1 == in_content_length)) { // read all available uint8_t* chunk; while(uint16_t len = async_read_buffered(chunk)) on_data(chunk,len); } else if(-1 != in_content_length) { // read all available while(in_content_length) { uint8_t* chunk; if(const uint16_t len = async_read_buffered(chunk,in_content_length)) { in_content_length -= len; on_data(chunk,len); } else return; } if(!keep_alive) { read_state = FINISHED; shutdown(fd,SHUT_RD); return; } read_state = LINE; } else ThrowInternalError("cannot cope with combination of keep_alive %d, content_length %d and encoding_chunked %d", keep_alive,in_content_length,in_encoding_chunked); break; default: ThrowInternalError("unexpected read_state"); } } }