Exemplo n.º 1
0
void HTTPTransaction::sendHeadersWithOptionalEOM(
    const HTTPMessage& headers,
    bool eom) {
  CHECK(HTTPTransactionEgressSM::transit(
          egressState_, HTTPTransactionEgressSM::Event::sendHeaders));
  DCHECK(!isEgressComplete());
  if (isDownstream() && !isPushed()) {
    lastResponseStatus_ = headers.getStatusCode();
  }
  if (headers.isRequest()) {
    headRequest_ = (headers.getMethod() == HTTPMethod::HEAD);
  }
  HTTPHeaderSize size;
  transport_.sendHeaders(this, headers, &size, eom);
  if (transportCallback_) {
    transportCallback_->headerBytesGenerated(size);
  }
  if (eom) {
    CHECK(HTTPTransactionEgressSM::transit(
          egressState_, HTTPTransactionEgressSM::Event::sendEOM));
    // trailers are supported in this case:
    // trailers are for chunked encoding-transfer of a body
    if (transportCallback_) {
      transportCallback_->bodyBytesGenerated(0);
    }
    CHECK(HTTPTransactionEgressSM::transit(
          egressState_, HTTPTransactionEgressSM::Event::eomFlushed));
  }
  flushWindowUpdate();
}
Exemplo n.º 2
0
TEST_F(EchoHandlerFixture, ReplaysBodyProperly) {
  EXPECT_CALL(stats, recordRequest()).WillOnce(Return());
  EXPECT_CALL(stats, getRequestCount()).WillOnce(Return(5));

  HTTPMessage response;
  folly::fbstring body;

  EXPECT_CALL(*responseHandler, sendHeaders(_)).WillOnce(
      DoAll(SaveArg<0>(&response), Return()));

  EXPECT_CALL(*responseHandler, sendBody(_)).WillRepeatedly(
      DoAll(
          Invoke([&] (std::shared_ptr<folly::IOBuf> b) {
            body += b->moveToFbString();
          }),
          Return()));

  EXPECT_CALL(*responseHandler, sendEOM()).WillOnce(Return());

  // Since we know we dont touch request, its ok to pass `nullptr` here.
  handler->onRequest(nullptr);
  handler->onBody(folly::IOBuf::copyBuffer("part1"));
  handler->onBody(folly::IOBuf::copyBuffer("part2"));
  handler->onEOM();
  handler->requestComplete();

  EXPECT_EQ("5", response.getHeaders().getSingleOrEmpty("Request-Number"));
  EXPECT_EQ(200, response.getStatusCode());
  EXPECT_EQ("part1part2", body);
}
Exemplo n.º 3
0
void
HTTPDownstreamSession::onHeadersSent(const HTTPMessage& headers,
                                     bool codecWasReusable) {
  if (!codec_->isReusable()) {
    // If the codec turned unreusable, some thing wrong must have happened.
    // Basically, the proxy decides the connection is not reusable.
    // e.g, an error message is being sent with Connection: close
    if (codecWasReusable) {
      uint32_t statusCode = headers.getStatusCode();
      if (statusCode >= 500) {
        setCloseReason(ConnectionCloseReason::REMOTE_ERROR);
      } else {
        if (statusCode >= 400) {
          setCloseReason(ConnectionCloseReason::ERR_RESP);
        } else {
          // should not be here
          setCloseReason(ConnectionCloseReason::UNKNOWN);
        }
      }
    } else {
      // shouldn't happen... this case is detected by REQ_NOTREUSABLE
      setCloseReason(ConnectionCloseReason::UNKNOWN);
    }
  }
}
Exemplo n.º 4
0
TEST_F(EchoHandlerFixture, OnProperRequestSendsResponse) {
  EXPECT_CALL(stats, recordRequest()).WillOnce(Return());
  EXPECT_CALL(stats, getRequestCount()).WillOnce(Return(5));

  HTTPMessage response;
  EXPECT_CALL(*responseHandler, sendHeaders(_)).WillOnce(
      DoAll(SaveArg<0>(&response), Return()));
  EXPECT_CALL(*responseHandler, sendEOM()).WillOnce(Return());

  // Since we know we dont touch request, its ok to pass `nullptr` here.
  handler->onRequest(nullptr);
  handler->onEOM();
  handler->requestComplete();

  EXPECT_EQ("5", response.getHeaders().getSingleOrEmpty("Request-Number"));
  EXPECT_EQ(200, response.getStatusCode());
}
Exemplo n.º 5
0
void ProxygenTransport::onHeadersComplete(
  unique_ptr<HTTPMessage> msg) noexcept {

  Timer::GetMonotonicTime(m_requestStart);
  m_request = std::move(msg);
  if (m_request->isSecure()) {
    setSSL();
  }
  m_request->dumpMessage(4);
  auto method = m_request->getMethod();
  const auto& methodStr = m_request->getMethodString();
  if (method == HTTPMethod::GET) {
    m_method = Transport::Method::GET;
  } else if (method == HTTPMethod::POST ||
             s_post_methods.find(methodStr) != s_post_methods.end()) {
    m_method = Transport::Method::POST;
  } else if (method == HTTPMethod::HEAD) {
    m_method = Transport::Method::HEAD;
  } else if (method == HTTPMethod::CONNECT) {
    sendErrorResponse(400 /* Bad Request */);
    return;
  } else {
    // looks like proxygen HTTP parser understands a few more methods
    // than libevent:
    //   TRACE, COPY, MOVE, MKACTIVITY, CHECKOUT, MERGE, MSEARCH, NOTIFY,
    //   SUBSCRIBE, UNSUBSCRIBE, PATCH
    sendErrorResponse(400 /* Bad Request */);
    return;
  }
  m_extended_method = methodStr.c_str();

  const auto& headers = m_request->getHeaders();
  headers.forEach([&] (const std::string &header, const std::string &val) {
      m_requestHeaders[header.c_str()].push_back(val.c_str());
    });

  if (m_method == Transport::Method::POST && m_request->isHTTP1_1()) {
    const std::string& expectation =
      headers.getSingleOrEmpty(HTTP_HEADER_EXPECT);
    if (!expectation.empty()) {
      bool sendEom = false;
      HTTPMessage response;
      if (!boost::iequals(expectation, "100-continue")) {
        response.setStatusCode(417);
        response.setStatusMessage(HTTPMessage::getDefaultReason(417));
        response.getHeaders().add(HTTP_HEADER_CONNECTION, "close");
        sendEom = true;
      } else {
        response.setStatusCode(100);
        response.setStatusMessage(HTTPMessage::getDefaultReason(100));
      }
      response.setHTTPVersion(1, 1);
      response.dumpMessage(4);
      m_clientTxn->sendHeaders(response);
      if (sendEom) {
        m_responseCode = response.getStatusCode();
        m_responseCodeInfo = response.getStatusMessage();
        m_server->onRequestError(this);
        m_clientTxn->sendEOM();
        // this object is no longer valid
        return;
      }
    }
  }

  if (!bufferRequest()) {
    m_enqueued = true;
    m_server->onRequest(shared_from_this());
  } // otherwise we wait for EOM
}