Commit 87df980e authored by jan.koester's avatar jan.koester
Browse files

est

parent 7012bec5
Loading
Loading
Loading
Loading
+193 −139
Original line number Diff line number Diff line
@@ -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();

@@ -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");

@@ -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) {