void Transport_parser::parse(const char* data, size_t len) { if(transport_ != nullptr) { transport_->load_cargo(data, len); } else { transport_ = std::make_unique<Transport>(Header::parse(data)); if(on_header) on_header(transport_->header()); len -= sizeof(Header); transport_->load_cargo(data + sizeof(Header), len); } if(transport_->is_complete()) on_complete(std::move(transport_)); }
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"); } } }