Loading src/hpack.cpp +50 −0 Original line number Diff line number Diff line Loading @@ -449,4 +449,54 @@ std::string Encoder::encodeResponseHeaders(uint16_t status_code, return out; } std::string Encoder::encodeRequestHeaders(const std::string &method, const std::string &path, const std::string &scheme, const std::string &authority, const std::vector<HeaderField> &extra) { std::string out; // :method — static table index 2 = GET, 3 = POST if (method == "GET") { out.push_back(static_cast<char>(0x82)); // indexed: 2 } else if (method == "POST") { out.push_back(static_cast<char>(0x83)); // indexed: 3 } else { // :method name is at index 2, literal with name index appendLiteralWithNameIndex(out, 0x02, method, false); } // :path — static table index 4 = "/", 5 = "/index.html" if (path == "/") { out.push_back(static_cast<char>(0x84)); // indexed: 4 } else if (path == "/index.html") { out.push_back(static_cast<char>(0x85)); // indexed: 5 } else { // :path name is at index 4, literal with name index appendLiteralWithNameIndex(out, 0x04, path, false); } // :scheme — static table index 6 = "http", 7 = "https" if (scheme == "http") { out.push_back(static_cast<char>(0x86)); // indexed: 6 } else if (scheme == "https") { out.push_back(static_cast<char>(0x87)); // indexed: 7 } else { appendLiteralWithNameIndex(out, 0x06, scheme, false); } // :authority — static table index 1 (name only, no value) if (!authority.empty()) { appendLiteralWithNameIndex(out, 0x01, authority, false); } // Extra headers for (const auto &h : extra) { if (h.name.empty()) continue; appendLiteral(out, h.name, h.value, h.incremental_index); } return out; } } // namespace libhttppp::hpack src/hpack.h +6 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,12 @@ public: size_t content_length, const std::vector<HeaderField> &extra = {}); static std::string encodeRequestHeaders(const std::string &method, const std::string &path, const std::string &scheme, const std::string &authority, const std::vector<HeaderField> &extra = {}); private: static void appendLiteral(std::string &out, const std::string &name, Loading src/http.cpp +285 −1 Original line number Diff line number Diff line Loading @@ -210,13 +210,29 @@ libhttppp::HttpClient::HttpClient(const HttpUrl& desturl) try { if (_url.getProtocol() == HttpUrl::HTTPS) { std::map<std::string, netplus::ssl::CertificateBundle> certs; _cltsock = std::make_unique<netplus::ssl>(certs,-1); auto sslsock = std::make_unique<netplus::ssl>(certs,-1); // Advertise h2 and http/1.1 via ALPN sslsock->getTls().client_alpn_protocols = std::string("\x02h2\x08http/1.1", 12); _cltsock = std::move(sslsock); } else { _cltsock = std::make_unique<netplus::tcp>(-1); } _cltsock->connect(_url.getHost(), _url.getPort(), false); // Check negotiated ALPN after TLS handshake if (auto *sslsock = dynamic_cast<netplus::ssl*>(_cltsock.get())) { const std::string &alpn = sslsock->getSelectedAlpn(); std::cerr << "[HttpClient] ALPN negotiated: '" << alpn << "'" << std::endl; _isH2 = (alpn == "h2"); std::cerr << "[HttpClient] _isH2 = " << _isH2 << std::endl; } else { std::cerr << "[HttpClient] Not an SSL socket" << std::endl; } } catch (netplus::NetException &e) { HTTPException ex; ex[HTTPException::Error] << "HttpClient ctor connect failed: " << e.what(); Loading @@ -241,8 +257,271 @@ static inline void sleep_note() { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } // --------------- HTTP/2 client frame helpers --------------- static constexpr uint8_t H2C_FRAME_DATA = 0x00; static constexpr uint8_t H2C_FRAME_HEADERS = 0x01; static constexpr uint8_t H2C_FRAME_SETTINGS = 0x04; static constexpr uint8_t H2C_FRAME_PING = 0x06; static constexpr uint8_t H2C_FRAME_GOAWAY = 0x07; static constexpr uint8_t H2C_FRAME_WINDOW_UPDATE = 0x08; static constexpr uint8_t H2C_FLAG_END_STREAM = 0x01; static constexpr uint8_t H2C_FLAG_ACK = 0x01; static constexpr uint8_t H2C_FLAG_END_HEADERS = 0x04; static constexpr size_t H2C_FRAME_HEADER_LEN = 9; static constexpr size_t H2C_MAX_FRAME_SIZE = 16384; static const char H2C_CLIENT_PREFACE[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; static constexpr size_t H2C_CLIENT_PREFACE_LEN = 24; static std::string h2cBuildFrame(uint8_t type, uint8_t flags, uint32_t stream_id, const std::string &payload) { std::string f(9 + payload.size(), '\0'); uint32_t len = static_cast<uint32_t>(payload.size()); f[0] = static_cast<char>((len >> 16) & 0xff); f[1] = static_cast<char>((len >> 8) & 0xff); f[2] = static_cast<char>(len & 0xff); f[3] = static_cast<char>(type); f[4] = static_cast<char>(flags); f[5] = static_cast<char>((stream_id >> 24) & 0x7f); f[6] = static_cast<char>((stream_id >> 16) & 0xff); f[7] = static_cast<char>((stream_id >> 8) & 0xff); f[8] = static_cast<char>(stream_id & 0xff); if (!payload.empty()) std::memcpy(&f[9], payload.data(), payload.size()); return f; } static std::string h2cBuildSettings() { // Send empty SETTINGS (accept defaults) return h2cBuildFrame(H2C_FRAME_SETTINGS, 0, 0, ""); } static std::string h2cBuildSettingsAck() { return h2cBuildFrame(H2C_FRAME_SETTINGS, H2C_FLAG_ACK, 0, ""); } static std::string h2cBuildWindowUpdate(uint32_t stream_id, uint32_t increment) { std::string p(4, '\0'); p[0] = static_cast<char>((increment >> 24) & 0x7f); p[1] = static_cast<char>((increment >> 16) & 0xff); p[2] = static_cast<char>((increment >> 8) & 0xff); p[3] = static_cast<char>(increment & 0xff); return h2cBuildFrame(H2C_FRAME_WINDOW_UPDATE, 0, stream_id, p); } // Generic HTTP/2 client request (GET or POST) const std::vector<char> libhttppp::HttpClient::_h2Request( const std::string &method, HttpRequest &nreq, const std::vector<char> *postBody) { std::vector<char> ret; try { // Helper: blocking recv auto recv_blocking = [&](netplus::buffer& b) -> size_t { for (;;) { try { return _cltsock->recvData(b, 0); } catch (netplus::NetException& e) { if (e.getErrorType() == netplus::NetException::Note) { sleep_note(); continue; } throw; } } }; auto send_all = [&](const std::string &data) { size_t off = 0; while (off < data.size()) { netplus::buffer buf(data.c_str() + off, data.size() - off); try { off += _cltsock->sendData(buf, 0); } catch (netplus::NetException& e) { if (e.getErrorType() == netplus::NetException::Note) { sleep_note(); continue; } throw; } } }; // 1) Send connection preface + SETTINGS std::string preface(H2C_CLIENT_PREFACE, H2C_CLIENT_PREFACE_LEN); preface += h2cBuildSettings(); send_all(preface); // 2) Build HEADERS frame for stream 1 std::string path = nreq.getRequestURL(); if (path.empty()) path = _url.getPath(); std::string scheme = (_url.getProtocol() == HttpUrl::HTTPS) ? "https" : "http"; std::stringstream auth; auth << _url.getHost() << ":" << _url.getPort(); // Collect extra headers from the request std::vector<hpack::HeaderField> extra; for (HttpHeader::HeaderData *hd = nreq.getfirstHeaderData(); hd; hd = hd->nextHeaderData()) { const std::string &key = hd->getkey(); if (key.empty() || key[0] == ':') continue; if (key == "host") continue; // :authority replaces host in H2 for (HttpHeader::HeaderData::Values *v = hd->getfirstValue(); v; v = v->nextvalue()) { extra.push_back({key, v->getvalue(), false}); } } std::string hpack_block = hpack::Encoder::encodeRequestHeaders( method, path, scheme, auth.str(), extra); uint32_t stream_id = 1; bool end_stream = (postBody == nullptr || postBody->empty()); uint8_t hdr_flags = H2C_FLAG_END_HEADERS; if (end_stream) hdr_flags |= H2C_FLAG_END_STREAM; std::string headers_frame = h2cBuildFrame(H2C_FRAME_HEADERS, hdr_flags, stream_id, hpack_block); send_all(headers_frame); // 3) Send DATA frames for POST body if present if (postBody && !postBody->empty()) { size_t offset = 0; while (offset < postBody->size()) { size_t chunk = std::min(postBody->size() - offset, H2C_MAX_FRAME_SIZE); bool last = (offset + chunk >= postBody->size()); uint8_t flags = last ? H2C_FLAG_END_STREAM : 0; std::string payload(postBody->data() + offset, chunk); std::string frame = h2cBuildFrame(H2C_FRAME_DATA, flags, stream_id, payload); send_all(frame); offset += chunk; } } // 4) Read response frames std::vector<uint8_t> raw; raw.reserve(16384); bool got_response_headers = false; bool got_end_stream = false; hpack::Decoder decoder; // Read enough data into raw buffer auto ensure_bytes = [&](size_t need) { while (raw.size() < need) { netplus::buffer buf(BLOCKSIZE); size_t n = recv_blocking(buf); if (n == 0) { HTTPException he; he[HTTPException::Error] << "HTTP/2: EOF while reading frames"; throw he; } raw.insert(raw.end(), reinterpret_cast<uint8_t*>(buf.data.buf), reinterpret_cast<uint8_t*>(buf.data.buf) + n); } }; while (!got_end_stream) { // Read frame header (9 bytes) ensure_bytes(H2C_FRAME_HEADER_LEN); uint32_t frame_len = (static_cast<uint32_t>(raw[0]) << 16) | (static_cast<uint32_t>(raw[1]) << 8) | static_cast<uint32_t>(raw[2]); uint8_t frame_type = raw[3]; uint8_t frame_flags = raw[4]; uint32_t frame_stream = ((static_cast<uint32_t>(raw[5]) & 0x7f) << 24) | (static_cast<uint32_t>(raw[6]) << 16) | (static_cast<uint32_t>(raw[7]) << 8) | static_cast<uint32_t>(raw[8]); // Read frame payload ensure_bytes(H2C_FRAME_HEADER_LEN + frame_len); const uint8_t *payload = raw.data() + H2C_FRAME_HEADER_LEN; switch (frame_type) { case H2C_FRAME_SETTINGS: { if (!(frame_flags & H2C_FLAG_ACK)) { // Server SETTINGS — acknowledge it send_all(h2cBuildSettingsAck()); } break; } case H2C_FRAME_HEADERS: { if (frame_stream == stream_id) { // Decode HPACK response headers auto headers = decoder.decode(payload, frame_len); (void)headers; // We don't need to inspect status for now got_response_headers = true; if (frame_flags & H2C_FLAG_END_STREAM) { got_end_stream = true; } } break; } case H2C_FRAME_DATA: { if (frame_stream == stream_id) { ret.insert(ret.end(), payload, payload + frame_len); if (frame_flags & H2C_FLAG_END_STREAM) { got_end_stream = true; } // Send WINDOW_UPDATE for connection and stream if (frame_len > 0) { send_all(h2cBuildWindowUpdate(0, frame_len)); send_all(h2cBuildWindowUpdate(stream_id, frame_len)); } } break; } case H2C_FRAME_WINDOW_UPDATE: case H2C_FRAME_PING: { if (frame_type == H2C_FRAME_PING && !(frame_flags & H2C_FLAG_ACK)) { // Respond to PING std::string ping_payload(reinterpret_cast<const char*>(payload), frame_len); send_all(h2cBuildFrame(H2C_FRAME_PING, H2C_FLAG_ACK, 0, ping_payload)); } break; } case H2C_FRAME_GOAWAY: { got_end_stream = true; break; } default: // Ignore unknown frame types break; } // Consume processed frame from buffer raw.erase(raw.begin(), raw.begin() + H2C_FRAME_HEADER_LEN + frame_len); } return ret; } catch (netplus::NetException &e) { HTTPException ee; ee[HTTPException::Error] << e.what(); throw ee; } } const std::vector<char> libhttppp::HttpClient::Get(libhttppp::HttpRequest &nreq) { // HTTP/2: delegate to binary framing path if (_isH2) { return _h2Request("GET", nreq); } std::vector<char> ret; try { Loading Loading @@ -534,6 +813,11 @@ const std::vector<char> libhttppp::HttpClient::Get(libhttppp::HttpRequest &nreq) const std::vector<char> libhttppp::HttpClient::Post(libhttppp::HttpRequest &nreq, const std::vector<char> &post) { // HTTP/2: delegate to binary framing path if (_isH2) { return _h2Request("POST", nreq, &post); } netplus::buffer data(BLOCKSIZE); std::vector<char> ret; Loading src/http.h +6 −0 Original line number Diff line number Diff line Loading @@ -91,6 +91,12 @@ namespace libhttppp { const std::vector<char> Post(HttpRequest &nreq,const std::vector<char> &post); private: int readchunk(const char *data,int datasize,int &pos); // HTTP/2 client helpers bool _isH2 = false; const std::vector<char> _h2Request(const std::string &method, HttpRequest &nreq, const std::vector<char> *postBody = nullptr); private: HttpUrl _url; std::unique_ptr<netplus::socket> _cltsock; Loading Loading
src/hpack.cpp +50 −0 Original line number Diff line number Diff line Loading @@ -449,4 +449,54 @@ std::string Encoder::encodeResponseHeaders(uint16_t status_code, return out; } std::string Encoder::encodeRequestHeaders(const std::string &method, const std::string &path, const std::string &scheme, const std::string &authority, const std::vector<HeaderField> &extra) { std::string out; // :method — static table index 2 = GET, 3 = POST if (method == "GET") { out.push_back(static_cast<char>(0x82)); // indexed: 2 } else if (method == "POST") { out.push_back(static_cast<char>(0x83)); // indexed: 3 } else { // :method name is at index 2, literal with name index appendLiteralWithNameIndex(out, 0x02, method, false); } // :path — static table index 4 = "/", 5 = "/index.html" if (path == "/") { out.push_back(static_cast<char>(0x84)); // indexed: 4 } else if (path == "/index.html") { out.push_back(static_cast<char>(0x85)); // indexed: 5 } else { // :path name is at index 4, literal with name index appendLiteralWithNameIndex(out, 0x04, path, false); } // :scheme — static table index 6 = "http", 7 = "https" if (scheme == "http") { out.push_back(static_cast<char>(0x86)); // indexed: 6 } else if (scheme == "https") { out.push_back(static_cast<char>(0x87)); // indexed: 7 } else { appendLiteralWithNameIndex(out, 0x06, scheme, false); } // :authority — static table index 1 (name only, no value) if (!authority.empty()) { appendLiteralWithNameIndex(out, 0x01, authority, false); } // Extra headers for (const auto &h : extra) { if (h.name.empty()) continue; appendLiteral(out, h.name, h.value, h.incremental_index); } return out; } } // namespace libhttppp::hpack
src/hpack.h +6 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,12 @@ public: size_t content_length, const std::vector<HeaderField> &extra = {}); static std::string encodeRequestHeaders(const std::string &method, const std::string &path, const std::string &scheme, const std::string &authority, const std::vector<HeaderField> &extra = {}); private: static void appendLiteral(std::string &out, const std::string &name, Loading
src/http.cpp +285 −1 Original line number Diff line number Diff line Loading @@ -210,13 +210,29 @@ libhttppp::HttpClient::HttpClient(const HttpUrl& desturl) try { if (_url.getProtocol() == HttpUrl::HTTPS) { std::map<std::string, netplus::ssl::CertificateBundle> certs; _cltsock = std::make_unique<netplus::ssl>(certs,-1); auto sslsock = std::make_unique<netplus::ssl>(certs,-1); // Advertise h2 and http/1.1 via ALPN sslsock->getTls().client_alpn_protocols = std::string("\x02h2\x08http/1.1", 12); _cltsock = std::move(sslsock); } else { _cltsock = std::make_unique<netplus::tcp>(-1); } _cltsock->connect(_url.getHost(), _url.getPort(), false); // Check negotiated ALPN after TLS handshake if (auto *sslsock = dynamic_cast<netplus::ssl*>(_cltsock.get())) { const std::string &alpn = sslsock->getSelectedAlpn(); std::cerr << "[HttpClient] ALPN negotiated: '" << alpn << "'" << std::endl; _isH2 = (alpn == "h2"); std::cerr << "[HttpClient] _isH2 = " << _isH2 << std::endl; } else { std::cerr << "[HttpClient] Not an SSL socket" << std::endl; } } catch (netplus::NetException &e) { HTTPException ex; ex[HTTPException::Error] << "HttpClient ctor connect failed: " << e.what(); Loading @@ -241,8 +257,271 @@ static inline void sleep_note() { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } // --------------- HTTP/2 client frame helpers --------------- static constexpr uint8_t H2C_FRAME_DATA = 0x00; static constexpr uint8_t H2C_FRAME_HEADERS = 0x01; static constexpr uint8_t H2C_FRAME_SETTINGS = 0x04; static constexpr uint8_t H2C_FRAME_PING = 0x06; static constexpr uint8_t H2C_FRAME_GOAWAY = 0x07; static constexpr uint8_t H2C_FRAME_WINDOW_UPDATE = 0x08; static constexpr uint8_t H2C_FLAG_END_STREAM = 0x01; static constexpr uint8_t H2C_FLAG_ACK = 0x01; static constexpr uint8_t H2C_FLAG_END_HEADERS = 0x04; static constexpr size_t H2C_FRAME_HEADER_LEN = 9; static constexpr size_t H2C_MAX_FRAME_SIZE = 16384; static const char H2C_CLIENT_PREFACE[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; static constexpr size_t H2C_CLIENT_PREFACE_LEN = 24; static std::string h2cBuildFrame(uint8_t type, uint8_t flags, uint32_t stream_id, const std::string &payload) { std::string f(9 + payload.size(), '\0'); uint32_t len = static_cast<uint32_t>(payload.size()); f[0] = static_cast<char>((len >> 16) & 0xff); f[1] = static_cast<char>((len >> 8) & 0xff); f[2] = static_cast<char>(len & 0xff); f[3] = static_cast<char>(type); f[4] = static_cast<char>(flags); f[5] = static_cast<char>((stream_id >> 24) & 0x7f); f[6] = static_cast<char>((stream_id >> 16) & 0xff); f[7] = static_cast<char>((stream_id >> 8) & 0xff); f[8] = static_cast<char>(stream_id & 0xff); if (!payload.empty()) std::memcpy(&f[9], payload.data(), payload.size()); return f; } static std::string h2cBuildSettings() { // Send empty SETTINGS (accept defaults) return h2cBuildFrame(H2C_FRAME_SETTINGS, 0, 0, ""); } static std::string h2cBuildSettingsAck() { return h2cBuildFrame(H2C_FRAME_SETTINGS, H2C_FLAG_ACK, 0, ""); } static std::string h2cBuildWindowUpdate(uint32_t stream_id, uint32_t increment) { std::string p(4, '\0'); p[0] = static_cast<char>((increment >> 24) & 0x7f); p[1] = static_cast<char>((increment >> 16) & 0xff); p[2] = static_cast<char>((increment >> 8) & 0xff); p[3] = static_cast<char>(increment & 0xff); return h2cBuildFrame(H2C_FRAME_WINDOW_UPDATE, 0, stream_id, p); } // Generic HTTP/2 client request (GET or POST) const std::vector<char> libhttppp::HttpClient::_h2Request( const std::string &method, HttpRequest &nreq, const std::vector<char> *postBody) { std::vector<char> ret; try { // Helper: blocking recv auto recv_blocking = [&](netplus::buffer& b) -> size_t { for (;;) { try { return _cltsock->recvData(b, 0); } catch (netplus::NetException& e) { if (e.getErrorType() == netplus::NetException::Note) { sleep_note(); continue; } throw; } } }; auto send_all = [&](const std::string &data) { size_t off = 0; while (off < data.size()) { netplus::buffer buf(data.c_str() + off, data.size() - off); try { off += _cltsock->sendData(buf, 0); } catch (netplus::NetException& e) { if (e.getErrorType() == netplus::NetException::Note) { sleep_note(); continue; } throw; } } }; // 1) Send connection preface + SETTINGS std::string preface(H2C_CLIENT_PREFACE, H2C_CLIENT_PREFACE_LEN); preface += h2cBuildSettings(); send_all(preface); // 2) Build HEADERS frame for stream 1 std::string path = nreq.getRequestURL(); if (path.empty()) path = _url.getPath(); std::string scheme = (_url.getProtocol() == HttpUrl::HTTPS) ? "https" : "http"; std::stringstream auth; auth << _url.getHost() << ":" << _url.getPort(); // Collect extra headers from the request std::vector<hpack::HeaderField> extra; for (HttpHeader::HeaderData *hd = nreq.getfirstHeaderData(); hd; hd = hd->nextHeaderData()) { const std::string &key = hd->getkey(); if (key.empty() || key[0] == ':') continue; if (key == "host") continue; // :authority replaces host in H2 for (HttpHeader::HeaderData::Values *v = hd->getfirstValue(); v; v = v->nextvalue()) { extra.push_back({key, v->getvalue(), false}); } } std::string hpack_block = hpack::Encoder::encodeRequestHeaders( method, path, scheme, auth.str(), extra); uint32_t stream_id = 1; bool end_stream = (postBody == nullptr || postBody->empty()); uint8_t hdr_flags = H2C_FLAG_END_HEADERS; if (end_stream) hdr_flags |= H2C_FLAG_END_STREAM; std::string headers_frame = h2cBuildFrame(H2C_FRAME_HEADERS, hdr_flags, stream_id, hpack_block); send_all(headers_frame); // 3) Send DATA frames for POST body if present if (postBody && !postBody->empty()) { size_t offset = 0; while (offset < postBody->size()) { size_t chunk = std::min(postBody->size() - offset, H2C_MAX_FRAME_SIZE); bool last = (offset + chunk >= postBody->size()); uint8_t flags = last ? H2C_FLAG_END_STREAM : 0; std::string payload(postBody->data() + offset, chunk); std::string frame = h2cBuildFrame(H2C_FRAME_DATA, flags, stream_id, payload); send_all(frame); offset += chunk; } } // 4) Read response frames std::vector<uint8_t> raw; raw.reserve(16384); bool got_response_headers = false; bool got_end_stream = false; hpack::Decoder decoder; // Read enough data into raw buffer auto ensure_bytes = [&](size_t need) { while (raw.size() < need) { netplus::buffer buf(BLOCKSIZE); size_t n = recv_blocking(buf); if (n == 0) { HTTPException he; he[HTTPException::Error] << "HTTP/2: EOF while reading frames"; throw he; } raw.insert(raw.end(), reinterpret_cast<uint8_t*>(buf.data.buf), reinterpret_cast<uint8_t*>(buf.data.buf) + n); } }; while (!got_end_stream) { // Read frame header (9 bytes) ensure_bytes(H2C_FRAME_HEADER_LEN); uint32_t frame_len = (static_cast<uint32_t>(raw[0]) << 16) | (static_cast<uint32_t>(raw[1]) << 8) | static_cast<uint32_t>(raw[2]); uint8_t frame_type = raw[3]; uint8_t frame_flags = raw[4]; uint32_t frame_stream = ((static_cast<uint32_t>(raw[5]) & 0x7f) << 24) | (static_cast<uint32_t>(raw[6]) << 16) | (static_cast<uint32_t>(raw[7]) << 8) | static_cast<uint32_t>(raw[8]); // Read frame payload ensure_bytes(H2C_FRAME_HEADER_LEN + frame_len); const uint8_t *payload = raw.data() + H2C_FRAME_HEADER_LEN; switch (frame_type) { case H2C_FRAME_SETTINGS: { if (!(frame_flags & H2C_FLAG_ACK)) { // Server SETTINGS — acknowledge it send_all(h2cBuildSettingsAck()); } break; } case H2C_FRAME_HEADERS: { if (frame_stream == stream_id) { // Decode HPACK response headers auto headers = decoder.decode(payload, frame_len); (void)headers; // We don't need to inspect status for now got_response_headers = true; if (frame_flags & H2C_FLAG_END_STREAM) { got_end_stream = true; } } break; } case H2C_FRAME_DATA: { if (frame_stream == stream_id) { ret.insert(ret.end(), payload, payload + frame_len); if (frame_flags & H2C_FLAG_END_STREAM) { got_end_stream = true; } // Send WINDOW_UPDATE for connection and stream if (frame_len > 0) { send_all(h2cBuildWindowUpdate(0, frame_len)); send_all(h2cBuildWindowUpdate(stream_id, frame_len)); } } break; } case H2C_FRAME_WINDOW_UPDATE: case H2C_FRAME_PING: { if (frame_type == H2C_FRAME_PING && !(frame_flags & H2C_FLAG_ACK)) { // Respond to PING std::string ping_payload(reinterpret_cast<const char*>(payload), frame_len); send_all(h2cBuildFrame(H2C_FRAME_PING, H2C_FLAG_ACK, 0, ping_payload)); } break; } case H2C_FRAME_GOAWAY: { got_end_stream = true; break; } default: // Ignore unknown frame types break; } // Consume processed frame from buffer raw.erase(raw.begin(), raw.begin() + H2C_FRAME_HEADER_LEN + frame_len); } return ret; } catch (netplus::NetException &e) { HTTPException ee; ee[HTTPException::Error] << e.what(); throw ee; } } const std::vector<char> libhttppp::HttpClient::Get(libhttppp::HttpRequest &nreq) { // HTTP/2: delegate to binary framing path if (_isH2) { return _h2Request("GET", nreq); } std::vector<char> ret; try { Loading Loading @@ -534,6 +813,11 @@ const std::vector<char> libhttppp::HttpClient::Get(libhttppp::HttpRequest &nreq) const std::vector<char> libhttppp::HttpClient::Post(libhttppp::HttpRequest &nreq, const std::vector<char> &post) { // HTTP/2: delegate to binary framing path if (_isH2) { return _h2Request("POST", nreq, &post); } netplus::buffer data(BLOCKSIZE); std::vector<char> ret; Loading
src/http.h +6 −0 Original line number Diff line number Diff line Loading @@ -91,6 +91,12 @@ namespace libhttppp { const std::vector<char> Post(HttpRequest &nreq,const std::vector<char> &post); private: int readchunk(const char *data,int datasize,int &pos); // HTTP/2 client helpers bool _isH2 = false; const std::vector<char> _h2Request(const std::string &method, HttpRequest &nreq, const std::vector<char> *postBody = nullptr); private: HttpUrl _url; std::unique_ptr<netplus::socket> _cltsock; Loading