Loading src/http.cpp +193 −139 Original line number Diff line number Diff line Loading @@ -816,14 +816,9 @@ const std::vector<char> libhttppp::HttpClient::Post(libhttppp::HttpRequest &nreq return _h2Request("POST", nreq, &post); } netplus::buffer data(BLOCKSIZE); std::vector<char> ret; try { size_t hsize = 0; int cpos = 0, rlen = 0; bool chunked = false; std::stringstream host; host << _url.getHost() << ":" << _url.getPort(); Loading @@ -834,7 +829,7 @@ const std::vector<char> libhttppp::HttpClient::Post(libhttppp::HttpRequest &nreq nreq.setRequestVersion(HTTPVERSION(1.1)); // IMPORTANT: length as string nreq.setHeaderData("Content-Length")->push_back(std::to_string(post.size())); nreq.setHeaderData("content-length")->push_back(std::to_string(post.size())); // (optionally) set content-type: // nreq.setHeaderData("Content-Type")->push_back("application/json"); Loading @@ -852,174 +847,233 @@ const std::vector<char> libhttppp::HttpClient::Post(libhttppp::HttpRequest &nreq throw HTTPException()[HTTPException::Error] << e.what(); } int recv = 0; int chunklen = 0; // --- Read HTTP response header (with timeout) --- using namespace std::chrono; auto start = steady_clock::now(); auto timeout = seconds(5); try { // --------------------------------------------------------- // helper: recv one chunk with Note-retry // --------------------------------------------------------- auto recv_blocking = [&](netplus::buffer& b) -> size_t { for (;;) { if (steady_clock::now() - start > timeout) { throw netplus::NetException() [netplus::NetException::Error] << "Timeout waiting for server response header (5s limit exceeded)."; try { size_t n = _cltsock->recvData(b, 0); return n; } catch (netplus::NetException& e) { if (e.getErrorType() == netplus::NetException::Note) { sleep_note(); continue; } throw; } } }; try { recv = _cltsock->recvData(data); // --------------------------------------------------------- // Read until we have full header (\r\n\r\n) // --------------------------------------------------------- std::vector<char> raw; raw.reserve(8192); // immediate EOF after request if (recv <= 0) { throw netplus::NetException() [netplus::NetException::Error] << "Server closed connection before sending response."; } size_t header_end = std::string::npos; for (;;) { if (raw.size() >= 4) { for (size_t i = 0; i + 3 < raw.size(); ++i) { if (raw[i] == '\r' && raw[i+1] == '\n' && raw[i+2] == '\r' && raw[i+3] == '\n') { header_end = i + 4; break; } catch (netplus::NetException &re) { if (re.getErrorType() != netplus::NetException::Note) { throw re; } else { std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; } } } } catch (netplus::NetException &e) { libhttppp::HTTPException he; he[libhttppp::HTTPException::Error] << e.what(); throw he; if (header_end != std::string::npos) break; netplus::buffer buf(BLOCKSIZE); size_t n = recv_blocking(buf); if (n == 0) { netplus::NetException ne; ne[netplus::NetException::Error] << "HTTP POST: EOF while reading response header"; throw ne; } raw.insert(raw.end(), buf.data.buf, buf.data.buf + n); } // --------------------------------------------------------- // Parse header // --------------------------------------------------------- libhttppp::HttpResponse res; hsize = res.parse(data.data.buf, recv); // header length in bytes size_t parsed_hsize = res.parse(raw.data(), raw.size()); if (hsize > static_cast<size_t>(recv)) { libhttppp::HTTPException ee; ee[libhttppp::HTTPException::Error] << "nginxfiler: header size bigger than recv'd bytes"; throw ee; if (parsed_hsize == 0 || parsed_hsize > raw.size()) { libhttppp::HTTPException he; he[libhttppp::HTTPException::Error] << "HTTP POST: response header parse failed"; throw he; } // number of body bytes already read in this recv recv -= static_cast<int>(hsize); if (recv > 0) { std::memmove(data.data.buf, // dest data.data.buf + hsize, // src static_cast<size_t>(recv)); // count } size_t body_off = parsed_hsize; cpos = 0; // --------------------------------------------------------- // Determine transfer mode // --------------------------------------------------------- bool chunked = false; ptrdiff_t content_len = -1; // Determine if response is chunked or has Content-Length try { for (libhttppp::HttpHeader::HeaderData::Values *chdat = res.getTransferEncoding(); chdat; chdat = chdat->nextvalue()) for (libhttppp::HttpHeader::HeaderData::Values *v = res.getTransferEncoding(); v; v = v->nextvalue()) { std::string clstr = chdat->getvalue(); std::transform(clstr.begin(), clstr.end(), clstr.begin(), [](unsigned char c){ return static_cast<char>(std::tolower(c)); }); if (clstr == "chunked") std::string val = tolower_copy(v->getvalue()); if (val == "chunked") { chunked = true; break; } } if (!chunked) throw "e"; } catch (...) { rlen = res.getContentLength(); // ignore — no Transfer-Encoding header } // --- Non-Chunked Body Reading --- if (!chunked) { do { try { std::copy(data.data.buf + cpos, data.data.buf + recv, std::back_inserter(ret)); rlen -= recv; if (rlen > 0) { for (;;) { cpos = 0; try { recv = _cltsock->recvData(data); content_len = res.getContentLength(); if (content_len < 0) content_len = -1; } catch (...) { content_len = -1; } } if (recv <= 0) { throw netplus::NetException() [netplus::NetException::Error] << "Premature EOF while reading HTTP response body."; // --------------------------------------------------------- // Helper: append from raw buffer // --------------------------------------------------------- auto append_from_raw = [&](size_t& take_pos, size_t take_len) { if (take_len == 0) return; ret.insert(ret.end(), raw.begin() + (ptrdiff_t)take_pos, raw.begin() + (ptrdiff_t)(take_pos + take_len)); take_pos += take_len; }; // ========================================================= // BODY: Content-Length // ========================================================= if (!chunked && content_len >= 0) { size_t remaining = (size_t)content_len; size_t pos = body_off; size_t can = (std::min)(remaining, raw.size() - pos); append_from_raw(pos, can); remaining -= can; while (remaining > 0) { netplus::buffer buf(BLOCKSIZE); size_t n = recv_blocking(buf); if (n == 0) { netplus::NetException ne; ne[netplus::NetException::Error] << "HTTP POST: EOF before Content-Length complete"; throw ne; } size_t take = (std::min)(remaining, n); ret.insert(ret.end(), buf.data.buf, buf.data.buf + take); remaining -= take; } } break; } catch (netplus::NetException &e) { if (e.getErrorType() == netplus::NetException::Note) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; // ========================================================= // BODY: chunked // ========================================================= else if (chunked) { size_t pos = body_off; auto ensure_bytes = [&](size_t need) { while (raw.size() - pos < need) { netplus::buffer buf(BLOCKSIZE); size_t n = recv_blocking(buf); if (n == 0) { netplus::NetException ne; ne[netplus::NetException::Error] << "HTTP POST: EOF while reading chunked body"; throw ne; } e[netplus::NetException::Error] << "nginxfiler: can't reach nginx server !"; throw e; raw.insert(raw.end(), buf.data.buf, buf.data.buf + n); } }; auto read_line = [&]() -> std::string { for (;;) { for (size_t i = pos; i + 1 < raw.size(); ++i) { if (raw[i] == '\r' && raw[i+1] == '\n') { std::string line(raw.begin() + (ptrdiff_t)pos, raw.begin() + (ptrdiff_t)i); pos = i + 2; return line; } } } catch (netplus::NetException &e) { libhttppp::HTTPException ee; ee[libhttppp::HTTPException::Error] << e.what(); throw ee; netplus::buffer buf(BLOCKSIZE); size_t n = recv_blocking(buf); if (n == 0) { netplus::NetException ne; ne[netplus::NetException::Error] << "HTTP POST: EOF while reading chunk size line"; throw ne; } } while (rlen > 0); raw.insert(raw.end(), buf.data.buf, buf.data.buf + n); } }; // --- Chunked Body Reading (Transfer-Encoding: chunked) --- else { int readed = 0; for (;;) { std::string szline = read_line(); auto sem = szline.find(';'); if (sem != std::string::npos) szline.resize(sem); size_t chunk_size = 0; try { chunk_size = (size_t)std::stoul(szline, nullptr, 16); } catch (...) { libhttppp::HTTPException he; he[libhttppp::HTTPException::Error] << "HTTP POST: invalid chunk size line: " << szline; throw he; } if (chunk_size == 0) { for (;;) { if (recv - cpos > 0) { if (readed == chunklen) { chunklen = readchunk(data.data.buf, recv, cpos); if (chunklen == 0) { // final chunk: DO NOT copy 0\r\n\r\n into ret std::string tline = read_line(); if (tline.empty()) break; } readed = 0; break; } int len = (chunklen - readed < recv - cpos) ? (chunklen - readed) : (recv - cpos); ensure_bytes(chunk_size + 2); std::copy(data.data.buf + cpos, data.data.buf + cpos + len, std::back_inserter(ret)); cpos += len; readed += len; } else { cpos = 0; for (;;) { try { recv = _cltsock->recvData(data); ret.insert(ret.end(), raw.begin() + (ptrdiff_t)pos, raw.begin() + (ptrdiff_t)(pos + chunk_size)); pos += chunk_size; if (recv <= 0) { throw netplus::NetException() [netplus::NetException::Error] << "Premature EOF while reading chunked body."; if (raw[pos] != '\r' || raw[pos+1] != '\n') { libhttppp::HTTPException he; he[libhttppp::HTTPException::Error] << "HTTP POST: bad chunk terminator"; throw he; } break; } catch (netplus::NetException &e) { if (e.getErrorType() != netplus::NetException::Note) { libhttppp::HTTPException ee; ee[libhttppp::HTTPException::Error] << e.what(); throw ee; } else { std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; pos += 2; } } // ========================================================= // BODY: no length, not chunked -> read until EOF // ========================================================= else { size_t pos = body_off; if (raw.size() > pos) { ret.insert(ret.end(), raw.begin() + (ptrdiff_t)pos, raw.end()); } for (;;) { netplus::buffer buf(BLOCKSIZE); size_t n = 0; try { n = recv_blocking(buf); } catch (...) { throw; } if (n == 0) break; ret.insert(ret.end(), buf.data.buf, buf.data.buf + n); } } } catch (netplus::NetException &e) { Loading Loading
src/http.cpp +193 −139 Original line number Diff line number Diff line Loading @@ -816,14 +816,9 @@ const std::vector<char> libhttppp::HttpClient::Post(libhttppp::HttpRequest &nreq return _h2Request("POST", nreq, &post); } netplus::buffer data(BLOCKSIZE); std::vector<char> ret; try { size_t hsize = 0; int cpos = 0, rlen = 0; bool chunked = false; std::stringstream host; host << _url.getHost() << ":" << _url.getPort(); Loading @@ -834,7 +829,7 @@ const std::vector<char> libhttppp::HttpClient::Post(libhttppp::HttpRequest &nreq nreq.setRequestVersion(HTTPVERSION(1.1)); // IMPORTANT: length as string nreq.setHeaderData("Content-Length")->push_back(std::to_string(post.size())); nreq.setHeaderData("content-length")->push_back(std::to_string(post.size())); // (optionally) set content-type: // nreq.setHeaderData("Content-Type")->push_back("application/json"); Loading @@ -852,174 +847,233 @@ const std::vector<char> libhttppp::HttpClient::Post(libhttppp::HttpRequest &nreq throw HTTPException()[HTTPException::Error] << e.what(); } int recv = 0; int chunklen = 0; // --- Read HTTP response header (with timeout) --- using namespace std::chrono; auto start = steady_clock::now(); auto timeout = seconds(5); try { // --------------------------------------------------------- // helper: recv one chunk with Note-retry // --------------------------------------------------------- auto recv_blocking = [&](netplus::buffer& b) -> size_t { for (;;) { if (steady_clock::now() - start > timeout) { throw netplus::NetException() [netplus::NetException::Error] << "Timeout waiting for server response header (5s limit exceeded)."; try { size_t n = _cltsock->recvData(b, 0); return n; } catch (netplus::NetException& e) { if (e.getErrorType() == netplus::NetException::Note) { sleep_note(); continue; } throw; } } }; try { recv = _cltsock->recvData(data); // --------------------------------------------------------- // Read until we have full header (\r\n\r\n) // --------------------------------------------------------- std::vector<char> raw; raw.reserve(8192); // immediate EOF after request if (recv <= 0) { throw netplus::NetException() [netplus::NetException::Error] << "Server closed connection before sending response."; } size_t header_end = std::string::npos; for (;;) { if (raw.size() >= 4) { for (size_t i = 0; i + 3 < raw.size(); ++i) { if (raw[i] == '\r' && raw[i+1] == '\n' && raw[i+2] == '\r' && raw[i+3] == '\n') { header_end = i + 4; break; } catch (netplus::NetException &re) { if (re.getErrorType() != netplus::NetException::Note) { throw re; } else { std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; } } } } catch (netplus::NetException &e) { libhttppp::HTTPException he; he[libhttppp::HTTPException::Error] << e.what(); throw he; if (header_end != std::string::npos) break; netplus::buffer buf(BLOCKSIZE); size_t n = recv_blocking(buf); if (n == 0) { netplus::NetException ne; ne[netplus::NetException::Error] << "HTTP POST: EOF while reading response header"; throw ne; } raw.insert(raw.end(), buf.data.buf, buf.data.buf + n); } // --------------------------------------------------------- // Parse header // --------------------------------------------------------- libhttppp::HttpResponse res; hsize = res.parse(data.data.buf, recv); // header length in bytes size_t parsed_hsize = res.parse(raw.data(), raw.size()); if (hsize > static_cast<size_t>(recv)) { libhttppp::HTTPException ee; ee[libhttppp::HTTPException::Error] << "nginxfiler: header size bigger than recv'd bytes"; throw ee; if (parsed_hsize == 0 || parsed_hsize > raw.size()) { libhttppp::HTTPException he; he[libhttppp::HTTPException::Error] << "HTTP POST: response header parse failed"; throw he; } // number of body bytes already read in this recv recv -= static_cast<int>(hsize); if (recv > 0) { std::memmove(data.data.buf, // dest data.data.buf + hsize, // src static_cast<size_t>(recv)); // count } size_t body_off = parsed_hsize; cpos = 0; // --------------------------------------------------------- // Determine transfer mode // --------------------------------------------------------- bool chunked = false; ptrdiff_t content_len = -1; // Determine if response is chunked or has Content-Length try { for (libhttppp::HttpHeader::HeaderData::Values *chdat = res.getTransferEncoding(); chdat; chdat = chdat->nextvalue()) for (libhttppp::HttpHeader::HeaderData::Values *v = res.getTransferEncoding(); v; v = v->nextvalue()) { std::string clstr = chdat->getvalue(); std::transform(clstr.begin(), clstr.end(), clstr.begin(), [](unsigned char c){ return static_cast<char>(std::tolower(c)); }); if (clstr == "chunked") std::string val = tolower_copy(v->getvalue()); if (val == "chunked") { chunked = true; break; } } if (!chunked) throw "e"; } catch (...) { rlen = res.getContentLength(); // ignore — no Transfer-Encoding header } // --- Non-Chunked Body Reading --- if (!chunked) { do { try { std::copy(data.data.buf + cpos, data.data.buf + recv, std::back_inserter(ret)); rlen -= recv; if (rlen > 0) { for (;;) { cpos = 0; try { recv = _cltsock->recvData(data); content_len = res.getContentLength(); if (content_len < 0) content_len = -1; } catch (...) { content_len = -1; } } if (recv <= 0) { throw netplus::NetException() [netplus::NetException::Error] << "Premature EOF while reading HTTP response body."; // --------------------------------------------------------- // Helper: append from raw buffer // --------------------------------------------------------- auto append_from_raw = [&](size_t& take_pos, size_t take_len) { if (take_len == 0) return; ret.insert(ret.end(), raw.begin() + (ptrdiff_t)take_pos, raw.begin() + (ptrdiff_t)(take_pos + take_len)); take_pos += take_len; }; // ========================================================= // BODY: Content-Length // ========================================================= if (!chunked && content_len >= 0) { size_t remaining = (size_t)content_len; size_t pos = body_off; size_t can = (std::min)(remaining, raw.size() - pos); append_from_raw(pos, can); remaining -= can; while (remaining > 0) { netplus::buffer buf(BLOCKSIZE); size_t n = recv_blocking(buf); if (n == 0) { netplus::NetException ne; ne[netplus::NetException::Error] << "HTTP POST: EOF before Content-Length complete"; throw ne; } size_t take = (std::min)(remaining, n); ret.insert(ret.end(), buf.data.buf, buf.data.buf + take); remaining -= take; } } break; } catch (netplus::NetException &e) { if (e.getErrorType() == netplus::NetException::Note) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; // ========================================================= // BODY: chunked // ========================================================= else if (chunked) { size_t pos = body_off; auto ensure_bytes = [&](size_t need) { while (raw.size() - pos < need) { netplus::buffer buf(BLOCKSIZE); size_t n = recv_blocking(buf); if (n == 0) { netplus::NetException ne; ne[netplus::NetException::Error] << "HTTP POST: EOF while reading chunked body"; throw ne; } e[netplus::NetException::Error] << "nginxfiler: can't reach nginx server !"; throw e; raw.insert(raw.end(), buf.data.buf, buf.data.buf + n); } }; auto read_line = [&]() -> std::string { for (;;) { for (size_t i = pos; i + 1 < raw.size(); ++i) { if (raw[i] == '\r' && raw[i+1] == '\n') { std::string line(raw.begin() + (ptrdiff_t)pos, raw.begin() + (ptrdiff_t)i); pos = i + 2; return line; } } } catch (netplus::NetException &e) { libhttppp::HTTPException ee; ee[libhttppp::HTTPException::Error] << e.what(); throw ee; netplus::buffer buf(BLOCKSIZE); size_t n = recv_blocking(buf); if (n == 0) { netplus::NetException ne; ne[netplus::NetException::Error] << "HTTP POST: EOF while reading chunk size line"; throw ne; } } while (rlen > 0); raw.insert(raw.end(), buf.data.buf, buf.data.buf + n); } }; // --- Chunked Body Reading (Transfer-Encoding: chunked) --- else { int readed = 0; for (;;) { std::string szline = read_line(); auto sem = szline.find(';'); if (sem != std::string::npos) szline.resize(sem); size_t chunk_size = 0; try { chunk_size = (size_t)std::stoul(szline, nullptr, 16); } catch (...) { libhttppp::HTTPException he; he[libhttppp::HTTPException::Error] << "HTTP POST: invalid chunk size line: " << szline; throw he; } if (chunk_size == 0) { for (;;) { if (recv - cpos > 0) { if (readed == chunklen) { chunklen = readchunk(data.data.buf, recv, cpos); if (chunklen == 0) { // final chunk: DO NOT copy 0\r\n\r\n into ret std::string tline = read_line(); if (tline.empty()) break; } readed = 0; break; } int len = (chunklen - readed < recv - cpos) ? (chunklen - readed) : (recv - cpos); ensure_bytes(chunk_size + 2); std::copy(data.data.buf + cpos, data.data.buf + cpos + len, std::back_inserter(ret)); cpos += len; readed += len; } else { cpos = 0; for (;;) { try { recv = _cltsock->recvData(data); ret.insert(ret.end(), raw.begin() + (ptrdiff_t)pos, raw.begin() + (ptrdiff_t)(pos + chunk_size)); pos += chunk_size; if (recv <= 0) { throw netplus::NetException() [netplus::NetException::Error] << "Premature EOF while reading chunked body."; if (raw[pos] != '\r' || raw[pos+1] != '\n') { libhttppp::HTTPException he; he[libhttppp::HTTPException::Error] << "HTTP POST: bad chunk terminator"; throw he; } break; } catch (netplus::NetException &e) { if (e.getErrorType() != netplus::NetException::Note) { libhttppp::HTTPException ee; ee[libhttppp::HTTPException::Error] << e.what(); throw ee; } else { std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; pos += 2; } } // ========================================================= // BODY: no length, not chunked -> read until EOF // ========================================================= else { size_t pos = body_off; if (raw.size() > pos) { ret.insert(ret.end(), raw.begin() + (ptrdiff_t)pos, raw.end()); } for (;;) { netplus::buffer buf(BLOCKSIZE); size_t n = 0; try { n = recv_blocking(buf); } catch (...) { throw; } if (n == 0) break; ret.insert(ret.end(), buf.data.buf, buf.data.buf + n); } } } catch (netplus::NetException &e) { Loading