Loading src/http.cpp +117 −0 Original line number Diff line number Diff line Loading @@ -4427,6 +4427,123 @@ size_t libhttppp::HttpRequest::getContentLength(){ return clen->at(0).getSizetValue(); } bool libhttppp::HttpRequest::isChunkedRequest(){ HttpHeader::HeaderData *te=getHeaderData("transfer-encoding"); if(!te) return false; for(HeaderData::Values *v=te->getfirstValue(); v; v=v->nextvalue()){ std::string val; std::transform(v->getvalue().begin(), v->getvalue().end(), std::back_inserter(val), [](unsigned char c){ return std::tolower(c); }); if(val.find("chunked") != std::string::npos) return true; } return false; } // Decode an HTTP/1.1 chunked request body sitting in RecvData (the header has // already been consumed by parse()). Idempotent across calls: while the full // chunked body has not yet arrived RecvData is left untouched so the server // loop can wait for more data, exactly like the Content-Length path. // returns 1 : body fully decoded; RecvData now holds the decoded body // (followed by any pipelined bytes) and Content-Length is set // 0 : incomplete, need more data (RecvData untouched) // -1 : malformed chunked framing int libhttppp::HttpRequest::decodeChunkedBody(){ const char *d = RecvData.data(); const size_t n = RecvData.size(); std::string body; size_t pos = 0; // Hard cap to avoid unbounded growth from a hostile peer (2 GiB). const size_t MAXBODY = static_cast<size_t>(1) << 31; auto findCRLF = [&](size_t from) -> size_t { for(size_t i = from; i + 1 < n; ++i){ if(d[i] == '\r' && d[i+1] == '\n') return i; } return std::string::npos; }; while(true){ size_t lineEnd = findCRLF(pos); if(lineEnd == std::string::npos) return 0; // chunk-size line not complete yet // Parse the hex chunk size, ignoring any ";chunk-extension". size_t sz = 0; bool anyDigit = false; for(size_t i = pos; i < lineEnd; ++i){ char c = d[i]; if(c == ';' || c == ' ' || c == '\t') break; int hv; if(c >= '0' && c <= '9') hv = c - '0'; else if(c >= 'a' && c <= 'f') hv = c - 'a' + 10; else if(c >= 'A' && c <= 'F') hv = c - 'A' + 10; else return -1; sz = sz * 16 + static_cast<size_t>(hv); anyDigit = true; if(sz > MAXBODY || body.size() > MAXBODY) return -1; } if(!anyDigit) return -1; size_t dataStart = lineEnd + 2; if(sz == 0){ // Last chunk. Consume any trailer header lines up to the final blank // line ("\r\n") that terminates the message. size_t tpos = dataStart; while(true){ size_t tEnd = findCRLF(tpos); if(tEnd == std::string::npos) return 0; // trailers / terminator not fully arrived if(tEnd == tpos){ // Empty line: end of body. Rebuild RecvData as decoded body plus // any trailing pipelined bytes after the terminator. size_t consumed = tEnd + 2; std::vector<char> trailing; if(consumed < n) trailing.assign(d + consumed, d + n); RecvData.clear(); if(!body.empty()) RecvData.append(body.data(), body.size()); if(!trailing.empty()) RecvData.append(trailing.data(), trailing.size()); // Replace framing headers so downstream code (webproxy, HttpForm) // uses the now-known Content-Length and never sees chunked framing. HeaderData *cl = setHeaderData("content-length"); if(cl){ cl->clear(); cl->push_back(std::to_string(body.size())); } HeaderData *te = getHeaderData("transfer-encoding"); if(te) te->clear(); return 1; } tpos = tEnd + 2; // skip one trailer line } } // Need the chunk data plus its trailing CRLF. if(dataStart + sz + 2 > n) return 0; if(d[dataStart + sz] != '\r' || d[dataStart + sz + 1] != '\n') return -1; body.append(d + dataStart, sz); if(body.size() > MAXBODY) return -1; pos = dataStart + sz + 2; } } size_t libhttppp::HttpRequest::getMaxUploadSize(){ return _MaxUploadSize; } Loading src/http.h +7 −0 Original line number Diff line number Diff line Loading @@ -358,6 +358,13 @@ namespace libhttppp { size_t getContentLength(); size_t getMaxUploadSize(); /* HTTP/1.1 chunked request-body support (server side). * Traefik and other reverse proxies downgrade HTTP/2 POSTs to HTTP/1.1 * using Transfer-Encoding: chunked (no Content-Length). These helpers let * the server loop de-chunk the body before dispatching RequestEvent. */ bool isChunkedRequest(); int decodeChunkedBody(); /*mobilphone switch*/ bool isMobile(); Loading src/httpd.cpp +30 −0 Original line number Diff line number Diff line Loading @@ -1544,6 +1544,21 @@ REQUESTHANDLING: cureq._RequestType=PARSEREQUEST; break; case PUTREQUEST: { if(cureq.isChunkedRequest()){ int dr = cureq.decodeChunkedBody(); if(dr == 0) break; // body incomplete, wait for more if(dr < 0){ cureq.clear(); libhttppp::HTTPException re; re[libhttppp::HTTPException::Error] << "malformed chunked request body!"; throw re; } size_t clen = cureq.getContentLength(); RequestEvent(cureq,tid,args); cureq.RecvData.erase(cureq.RecvData.begin(),cureq.RecvData.begin()+clen); cureq._RequestType=PARSEREQUEST; break; } size_t clen = cureq.getContentLength(); if(clen == 0){ RequestEvent(cureq,tid,args); Loading @@ -1556,6 +1571,21 @@ REQUESTHANDLING: break; } case POSTREQUEST: { if(cureq.isChunkedRequest()){ int dr = cureq.decodeChunkedBody(); if(dr == 0) break; // body incomplete, wait for more if(dr < 0){ cureq.clear(); libhttppp::HTTPException re; re[libhttppp::HTTPException::Error] << "malformed chunked request body!"; throw re; } size_t clen = cureq.getContentLength(); RequestEvent(cureq,tid,args); cureq.RecvData.erase(cureq.RecvData.begin(),cureq.RecvData.begin()+clen); cureq._RequestType=PARSEREQUEST; break; } size_t clen = cureq.getContentLength(); if(clen == 0){ RequestEvent(cureq,tid,args); Loading Loading
src/http.cpp +117 −0 Original line number Diff line number Diff line Loading @@ -4427,6 +4427,123 @@ size_t libhttppp::HttpRequest::getContentLength(){ return clen->at(0).getSizetValue(); } bool libhttppp::HttpRequest::isChunkedRequest(){ HttpHeader::HeaderData *te=getHeaderData("transfer-encoding"); if(!te) return false; for(HeaderData::Values *v=te->getfirstValue(); v; v=v->nextvalue()){ std::string val; std::transform(v->getvalue().begin(), v->getvalue().end(), std::back_inserter(val), [](unsigned char c){ return std::tolower(c); }); if(val.find("chunked") != std::string::npos) return true; } return false; } // Decode an HTTP/1.1 chunked request body sitting in RecvData (the header has // already been consumed by parse()). Idempotent across calls: while the full // chunked body has not yet arrived RecvData is left untouched so the server // loop can wait for more data, exactly like the Content-Length path. // returns 1 : body fully decoded; RecvData now holds the decoded body // (followed by any pipelined bytes) and Content-Length is set // 0 : incomplete, need more data (RecvData untouched) // -1 : malformed chunked framing int libhttppp::HttpRequest::decodeChunkedBody(){ const char *d = RecvData.data(); const size_t n = RecvData.size(); std::string body; size_t pos = 0; // Hard cap to avoid unbounded growth from a hostile peer (2 GiB). const size_t MAXBODY = static_cast<size_t>(1) << 31; auto findCRLF = [&](size_t from) -> size_t { for(size_t i = from; i + 1 < n; ++i){ if(d[i] == '\r' && d[i+1] == '\n') return i; } return std::string::npos; }; while(true){ size_t lineEnd = findCRLF(pos); if(lineEnd == std::string::npos) return 0; // chunk-size line not complete yet // Parse the hex chunk size, ignoring any ";chunk-extension". size_t sz = 0; bool anyDigit = false; for(size_t i = pos; i < lineEnd; ++i){ char c = d[i]; if(c == ';' || c == ' ' || c == '\t') break; int hv; if(c >= '0' && c <= '9') hv = c - '0'; else if(c >= 'a' && c <= 'f') hv = c - 'a' + 10; else if(c >= 'A' && c <= 'F') hv = c - 'A' + 10; else return -1; sz = sz * 16 + static_cast<size_t>(hv); anyDigit = true; if(sz > MAXBODY || body.size() > MAXBODY) return -1; } if(!anyDigit) return -1; size_t dataStart = lineEnd + 2; if(sz == 0){ // Last chunk. Consume any trailer header lines up to the final blank // line ("\r\n") that terminates the message. size_t tpos = dataStart; while(true){ size_t tEnd = findCRLF(tpos); if(tEnd == std::string::npos) return 0; // trailers / terminator not fully arrived if(tEnd == tpos){ // Empty line: end of body. Rebuild RecvData as decoded body plus // any trailing pipelined bytes after the terminator. size_t consumed = tEnd + 2; std::vector<char> trailing; if(consumed < n) trailing.assign(d + consumed, d + n); RecvData.clear(); if(!body.empty()) RecvData.append(body.data(), body.size()); if(!trailing.empty()) RecvData.append(trailing.data(), trailing.size()); // Replace framing headers so downstream code (webproxy, HttpForm) // uses the now-known Content-Length and never sees chunked framing. HeaderData *cl = setHeaderData("content-length"); if(cl){ cl->clear(); cl->push_back(std::to_string(body.size())); } HeaderData *te = getHeaderData("transfer-encoding"); if(te) te->clear(); return 1; } tpos = tEnd + 2; // skip one trailer line } } // Need the chunk data plus its trailing CRLF. if(dataStart + sz + 2 > n) return 0; if(d[dataStart + sz] != '\r' || d[dataStart + sz + 1] != '\n') return -1; body.append(d + dataStart, sz); if(body.size() > MAXBODY) return -1; pos = dataStart + sz + 2; } } size_t libhttppp::HttpRequest::getMaxUploadSize(){ return _MaxUploadSize; } Loading
src/http.h +7 −0 Original line number Diff line number Diff line Loading @@ -358,6 +358,13 @@ namespace libhttppp { size_t getContentLength(); size_t getMaxUploadSize(); /* HTTP/1.1 chunked request-body support (server side). * Traefik and other reverse proxies downgrade HTTP/2 POSTs to HTTP/1.1 * using Transfer-Encoding: chunked (no Content-Length). These helpers let * the server loop de-chunk the body before dispatching RequestEvent. */ bool isChunkedRequest(); int decodeChunkedBody(); /*mobilphone switch*/ bool isMobile(); Loading
src/httpd.cpp +30 −0 Original line number Diff line number Diff line Loading @@ -1544,6 +1544,21 @@ REQUESTHANDLING: cureq._RequestType=PARSEREQUEST; break; case PUTREQUEST: { if(cureq.isChunkedRequest()){ int dr = cureq.decodeChunkedBody(); if(dr == 0) break; // body incomplete, wait for more if(dr < 0){ cureq.clear(); libhttppp::HTTPException re; re[libhttppp::HTTPException::Error] << "malformed chunked request body!"; throw re; } size_t clen = cureq.getContentLength(); RequestEvent(cureq,tid,args); cureq.RecvData.erase(cureq.RecvData.begin(),cureq.RecvData.begin()+clen); cureq._RequestType=PARSEREQUEST; break; } size_t clen = cureq.getContentLength(); if(clen == 0){ RequestEvent(cureq,tid,args); Loading @@ -1556,6 +1571,21 @@ REQUESTHANDLING: break; } case POSTREQUEST: { if(cureq.isChunkedRequest()){ int dr = cureq.decodeChunkedBody(); if(dr == 0) break; // body incomplete, wait for more if(dr < 0){ cureq.clear(); libhttppp::HTTPException re; re[libhttppp::HTTPException::Error] << "malformed chunked request body!"; throw re; } size_t clen = cureq.getContentLength(); RequestEvent(cureq,tid,args); cureq.RecvData.erase(cureq.RecvData.begin(),cureq.RecvData.begin()+clen); cureq._RequestType=PARSEREQUEST; break; } size_t clen = cureq.getContentLength(); if(clen == 0){ RequestEvent(cureq,tid,args); Loading