Commit b607c586 authored by jan.koester's avatar jan.koester
Browse files

test

parent f789acec
Loading
Loading
Loading
Loading
+117 −0
Original line number Diff line number Diff line
@@ -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;
}
+7 −0
Original line number Diff line number Diff line
@@ -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();

+30 −0
Original line number Diff line number Diff line
@@ -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);
@@ -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);