Commit 623f6e1d authored by jan.koester's avatar jan.koester
Browse files

put method added

parent 54cfbe86
Loading
Loading
Loading
Loading
+255 −2
Original line number Diff line number Diff line
@@ -1527,6 +1527,254 @@ const std::vector<char> libhttppp::HttpClient::Delete(libhttppp::HttpRequest &nr
    }
}

const std::vector<char> libhttppp::HttpClient::Put(libhttppp::HttpRequest &nreq,
                                                   const std::vector<char> &put)
{
    std::vector<char> ret;

    try {
    resetConnection();

    const int kMaxRedirects = 1;
    int redirects = 0;

    for (;;) {
        std::stringstream host;
        host << _url.getHost() << ":" << _url.getPort();

        nreq.setHeaderData("host")->push_back(host.str());
        nreq.setRequestType(PUTREQUEST);
        if (nreq.getRequestURL().empty())
          nreq.setRequestURL(_url.getPath());

        nreq.setHeaderData("content-length")->push_back(std::to_string(put.size()));

        if (_isH2) {
          return _h2Request("PUT", nreq, &put);
        }

        if (nreq.getRequestVersion().empty())
          nreq.setRequestVersion(HTTPVERSION(1.1));

        {
            std::string header;
            nreq.printHeader(header);

            if (!_cltsock || _cltsock->fd() < 0) {
                if (!_cltsock) {
                    if (_url.getProtocol() == HttpUrl::HTTPS) {
                        std::map<std::string, netplus::ssl::CertificateBundle> certs;
                        _cltsock = std::make_unique<netplus::ssl>(certs, -1);
                    } else {
                        _cltsock = std::make_unique<netplus::tcp>(-1);
                    }
                }
                _cltsock->connect(_url.getHost(), _url.getPort(), true);
            }

            std::vector<char> combined;
            combined.reserve(header.size() + put.size());
            combined.insert(combined.end(), header.begin(), header.end());
            combined.insert(combined.end(), put.begin(), put.end());

            try {
                size_t sended = 0;
                while (sended != combined.size()) {
                    netplus::buffer outbuf(combined.data() + sended,
                                           combined.size() - sended);
                    try {
                        size_t n = _cltsock->sendData(outbuf, 0);
                        if (n == 0) {
                            sleep_note();
                            continue;
                        }
                        sended += n;
                    } catch (netplus::NetException &se) {
                        if (se.getErrorType() == netplus::NetException::Note) {
                            sleep_note();
                            continue;
                        }
                        throw;
                    }
                }
            } catch (netplus::NetException &e) {
                throw HTTPException()[HTTPException::Error] << e.what();
            }
        }

        auto recv_blocking = [&](netplus::buffer& b) -> size_t {
            for (;;) {
                try {
                    size_t n = _cltsock->recvData(b, 0);
                    return n;
                } catch (netplus::NetException& e) {
                    if (e.getErrorType() == netplus::NetException::Note) {
                        sleep_note();
                        continue;
                    }
                    throw;
                }
            }
        };

        std::vector<char> raw;
        raw.reserve(8192);
        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;
                    }
                }
            }
            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 PUT: EOF while reading response header";
                throw ne;
            }
            raw.insert(raw.end(), buf.data.buf, buf.data.buf + n);
        }

        libhttppp::HttpResponse res;
        size_t parsed_hsize = res.parse(raw.data(), raw.size());
        if (parsed_hsize == 0 || parsed_hsize > raw.size()) {
            libhttppp::HTTPException he;
            he[libhttppp::HTTPException::Error] << "HTTP PUT: response header parse failed";
            throw he;
        }

        if (is_redirect_status(res.getStatusCode()) && redirects < kMaxRedirects) {
          std::string location = extract_location_from_response(res);
          if (!location.empty()) {
            ++redirects;
            if (location.rfind("http://", 0) == 0 || location.rfind("https://", 0) == 0) {
              _url = HttpUrl(location, false);
              nreq.setRequestURL(_url.getPath());
            } else {
              std::string base = nreq.getRequestURL().empty() ? _url.getPath() : nreq.getRequestURL();
              nreq.setRequestURL(normalize_path(location, base));
            }
            resetConnection();
            continue;
          }
        }

        size_t body_off = parsed_hsize;

        bool chunked = false;
        ptrdiff_t content_len = -1;
        try {
            for (libhttppp::HttpHeader::HeaderData::Values *v = res.getTransferEncoding();
                 v; v = v->nextvalue()) {
                std::string val = tolower_copy(v->getvalue());
                if (val == "chunked") { chunked = true; break; }
            }
        } catch (...) {}
        if (!chunked) {
            try { content_len = res.getContentLength(); if (content_len < 0) content_len = -1; } catch (...) { content_len = -1; }
        }

        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;
        };

        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 PUT: 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;
            }
            return ret;
        }

        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 PUT: EOF while reading chunked body"; throw ne; }
                    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;
                        }
                    }
                    netplus::buffer buf(BLOCKSIZE);
                    size_t n = recv_blocking(buf);
                    if (n == 0) { netplus::NetException ne; ne[netplus::NetException::Error] << "HTTP PUT: EOF while reading chunk size line"; throw ne; }
                    raw.insert(raw.end(), buf.data.buf, buf.data.buf + n);
                }
            };
            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 PUT: invalid chunk size line: " << szline; throw he;
                }
                if (chunk_size == 0) { for (;;) { std::string tline = read_line(); if (tline.empty()) break; } break; }
                ensure_bytes(chunk_size + 2);
                ret.insert(ret.end(), raw.begin() + (ptrdiff_t)pos, raw.begin() + (ptrdiff_t)(pos + chunk_size));
                pos += chunk_size;
                if (raw[pos] != '\r' || raw[pos+1] != '\n') { libhttppp::HTTPException he; he[libhttppp::HTTPException::Error] << "HTTP PUT: bad chunk terminator"; throw he; }
                pos += 2;
            }
            return ret;
        }

        {
            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);
            }
        }

        return ret;
    }

    } catch (netplus::NetException &e) {
        libhttppp::HTTPException ee;
        ee[libhttppp::HTTPException::Error] << e.what();
        throw ee;
    }
}

int libhttppp::HttpClient::readchunk(const char *data, int datasize, int &pos)
{
    // 1) Skip CRLF left after previous chunk
@@ -2457,6 +2705,7 @@ size_t libhttppp::HttpRequest::parseH2(const std::vector<hpack::HeaderField> &he
  std::string method = (methodHd && methodHd->getfirstValue()) ? methodHd->getfirstValue()->getvalue() : "GET";
  if (method == "GET")          _RequestType = GETREQUEST;
  else if (method == "POST")    _RequestType = POSTREQUEST;
  else if (method == "PUT")     _RequestType = PUTREQUEST;
  else if (method == "DELETE")  _RequestType = DELETEREQUEST;
  else                          _RequestType = GETREQUEST;

@@ -2524,6 +2773,7 @@ size_t libhttppp::HttpRequest::parseH3(const std::vector<qpack::HeaderField> &he
  std::string method = (methodHd && methodHd->getfirstValue()) ? methodHd->getfirstValue()->getvalue() : "GET";
  if (method == "GET")          _RequestType = GETREQUEST;
  else if (method == "POST")    _RequestType = POSTREQUEST;
  else if (method == "PUT")     _RequestType = PUTREQUEST;
  else if (method == "DELETE")  _RequestType = DELETEREQUEST;
  else                          _RequestType = GETREQUEST;

@@ -2611,6 +2861,7 @@ size_t libhttppp::HttpRequest::parseH1() {
    // 5) Set request type
    if (method == "GET")          _RequestType = GETREQUEST;
    else if (method == "POST")    _RequestType = POSTREQUEST;
    else if (method == "PUT")     _RequestType = PUTREQUEST;
    else if (method == "DELETE")  _RequestType = DELETEREQUEST;
    else                          _RequestType = PARSEREQUEST; // or a dedicated enum for others

@@ -2739,6 +2990,8 @@ void libhttppp::HttpRequest::printHeader(std::string &buffer){
    buffer="GET ";
  else if(_RequestType==POSTREQUEST)
    buffer="POST ";
  else if(_RequestType==PUTREQUEST)
    buffer="PUT ";
  else if(_RequestType==DELETEREQUEST)
    buffer="DELETE ";

@@ -2873,7 +3126,7 @@ void libhttppp::HttpRequest::send(const HttpUrl &dest,
}

void libhttppp::HttpRequest::setRequestType(int req){
  if(req == POSTREQUEST || req == GETREQUEST || req == DELETEREQUEST){
  if(req == POSTREQUEST || req == GETREQUEST || req == PUTREQUEST || req == DELETEREQUEST){
    _RequestType=req;
    return;
  }
@@ -2897,7 +3150,7 @@ void libhttppp::HttpRequest::setRequestURL(const std::string &url){


void libhttppp::HttpForm::parse(libhttppp::HttpRequest &request){
  if(request.getRequestType()==POSTREQUEST){
  if(request.getRequestType()==POSTREQUEST || request.getRequestType()==PUTREQUEST){
    HttpHeader::HeaderData *ctype=request.getHeaderData("content-type");

    if(!ctype){
+1 −0
Original line number Diff line number Diff line
@@ -89,6 +89,7 @@ namespace libhttppp {
      void reconnect();
      const std::vector<char> Get(HttpRequest &nreq);
      const std::vector<char> Post(HttpRequest &nreq,const std::vector<char> &post);
      const std::vector<char> Put(HttpRequest &nreq,const std::vector<char> &put);
      const std::vector<char> Delete(HttpRequest &nreq);
  private:
      void resetConnection();
+13 −1
Original line number Diff line number Diff line
@@ -824,6 +824,18 @@ REQUESTHANDLING:
                RequestEvent(cureq,tid,args);
                cureq._RequestType=PARSEREQUEST;
                break;
            case PUTREQUEST: {
                size_t clen = cureq.getContentLength();
                if(clen == 0){
                    RequestEvent(cureq,tid,args);
                    cureq._RequestType=PARSEREQUEST;
                } else if(cureq.RecvData.size()>=clen){
                    RequestEvent(cureq,tid,args);
                    cureq.RecvData.erase(cureq.RecvData.begin(),cureq.RecvData.begin()+clen);
                    cureq._RequestType=PARSEREQUEST;
                }
                break;
            }
            case POSTREQUEST: {
                size_t clen = cureq.getContentLength();
                std::cerr << "[H1-POST] clen=" << clen
@@ -883,7 +895,7 @@ void libhttppp::HttpEvent::ResponseEvent(netplus::con &curcon,const int tid,ULON
        // destroy parsed headers — including Content-Length — causing the
        // partially received body to be re-parsed as HTTP headers, which
        // leads to an endless loop for large/multi-file uploads).
        if (cureq.getRequestType() == POSTREQUEST) {
        if (cureq.getRequestType() == POSTREQUEST || cureq.getRequestType() == PUTREQUEST) {
            return;
        }
        if (cureq.getRequestType() == PARSEREQUEST && cureq.getRequestURL().empty()) {
+1 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define GETREQUEST    1
#define POSTREQUEST   2
#define DELETEREQUEST 3
#define PUTREQUEST    4

//define http version