From e14bc9b41f9a30dceb3a267b299ecef3ee0cd186 Mon Sep 17 00:00:00 2001 From: an-tao <20741618@qq.com> Date: Thu, 15 Nov 2018 22:00:23 +0800 Subject: [PATCH] Split HttpContext class --- examples/client_example/main.cc | 4 +- examples/simple_example/api_Attachment.h | 4 +- lib/inc/drogon/HttpClient.h | 2 +- lib/src/HttpClientContext.cc | 253 ++++++++++ lib/src/HttpClientContext.h | 96 ++++ lib/src/HttpClientImpl.cc | 37 +- lib/src/HttpContext.cc | 477 ------------------ lib/src/HttpRequestImpl.h | 3 +- lib/src/HttpResponseImpl.h | 2 +- lib/src/HttpServer.cc | 11 +- lib/src/HttpServerContext.cc | 261 ++++++++++ .../{HttpContext.h => HttpServerContext.h} | 54 +- 12 files changed, 646 insertions(+), 558 deletions(-) create mode 100755 lib/src/HttpClientContext.cc create mode 100755 lib/src/HttpClientContext.h delete mode 100755 lib/src/HttpContext.cc create mode 100755 lib/src/HttpServerContext.cc rename lib/src/{HttpContext.h => HttpServerContext.h} (67%) diff --git a/examples/client_example/main.cc b/examples/client_example/main.cc index 35a97195..efb25749 100644 --- a/examples/client_example/main.cc +++ b/examples/client_example/main.cc @@ -14,11 +14,11 @@ int main() for (int i = 0; i < 10; i++) { - client->sendRequest(req, [&](ReqResult result, const HttpResponse &response) { + client->sendRequest(req, [&](ReqResult result, const HttpResponsePtr &response) { std::cout << "receive response!" << std::endl; //auto headers=response. count++; - std::cout << response.getBody() << std::endl; + std::cout << response->getBody() << std::endl; std::cout << "count=" << count << std::endl; }); } diff --git a/examples/simple_example/api_Attachment.h b/examples/simple_example/api_Attachment.h index d788178e..807e2504 100644 --- a/examples/simple_example/api_Attachment.h +++ b/examples/simple_example/api_Attachment.h @@ -8,8 +8,8 @@ class Attachment : public drogon::HttpController public: METHOD_LIST_BEGIN //use METHOD_ADD to add your custom processing function here; - METHOD_ADD(Attachment::get, "", Post); //Path will be '/api/attachment' - + METHOD_ADD(Attachment::get, "", Get); //Path will be '/api/attachment' + METHOD_ADD(Attachment::upload,"/upload",Post); METHOD_LIST_END //your declaration of processing function maybe like this: void get(const HttpRequestPtr &req, diff --git a/lib/inc/drogon/HttpClient.h b/lib/inc/drogon/HttpClient.h index c5adf579..687efed7 100644 --- a/lib/inc/drogon/HttpClient.h +++ b/lib/inc/drogon/HttpClient.h @@ -28,7 +28,7 @@ enum class ReqResult BadServerAddress, Timeout }; -typedef std::function HttpReqCallback; +typedef std::function HttpReqCallback; class HttpClient; typedef std::shared_ptr HttpClientPtr; class HttpClient : public trantor::NonCopyable diff --git a/lib/src/HttpClientContext.cc b/lib/src/HttpClientContext.cc new file mode 100755 index 00000000..eed81cf6 --- /dev/null +++ b/lib/src/HttpClientContext.cc @@ -0,0 +1,253 @@ +// Copyright 2010, Shuo Chen. All rights reserved. +// http://code.google.com/p/muduo/ +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Shuo Chen (chenshuo at chenshuo dot com) +// + +//taken from muduo and modified + +/** + * + * @file + * @author An Tao + * @section LICENSE + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a MIT license + * that can be found in the License file. + * + * @section DESCRIPTION + * + */ + +#include +#include +#include "HttpClientContext.h" +#include +using namespace trantor; +using namespace drogon; +HttpClientContext::HttpClientContext(const trantor::TcpConnectionPtr &connPtr) + : _state(HttpResponseParseState::kExpectResponseLine), + _response(new HttpResponseImpl), + _conn(connPtr) +{ +} + +bool HttpClientContext::processResponseLine(const char *begin, const char *end) +{ + const char *start = begin; + const char *space = std::find(start, end, ' '); + if (space != end) + { + LOG_TRACE << *(space - 1); + if (*(space - 1) == '1') + { + _response->setVersion(HttpResponse::kHttp11); + } + else if (*(space - 1) == '0') + { + _response->setVersion(HttpResponse::kHttp10); + } + else + { + return false; + } + } + + start = space + 1; + space = std::find(start, end, ' '); + if (space != end) + { + std::string status_code(start, space - start); + std::string status_message(space + 1, end - space - 1); + LOG_TRACE << status_code << " " << status_message; + auto code = atoi(status_code.c_str()); + _response->setStatusCode(HttpResponse::HttpStatusCode(code), status_message); + + return true; + } + return false; +} + +// return false if any error +bool HttpClientContext::parseResponse(MsgBuffer *buf) +{ + bool ok = true; + bool hasMore = true; + while (hasMore) + { + if (_state == HttpResponseParseState::kExpectResponseLine) + { + const char *crlf = buf->findCRLF(); + if (crlf) + { + ok = processResponseLine(buf->peek(), crlf); + if (ok) + { + //_response->setReceiveTime(receiveTime); + buf->retrieveUntil(crlf + 2); + _state = HttpResponseParseState::kExpectHeaders; + } + else + { + hasMore = false; + } + } + else + { + hasMore = false; + } + } + else if (_state == HttpResponseParseState::kExpectHeaders) + { + const char *crlf = buf->findCRLF(); + if (crlf) + { + const char *colon = std::find(buf->peek(), crlf, ':'); + if (colon != crlf) + { + _response->addHeader(buf->peek(), colon, crlf); + } + else + { + // empty line, end of header + // FIXME: + std::string len = _response->getHeader("Content-Length"); + LOG_INFO << "content len=" << len; + if (len != "") + { + _response->_left_body_length = atoi(len.c_str()); + _state = HttpResponseParseState::kExpectBody; + } + else + { + std::string encode = _response->getHeader("Transfer-Encoding"); + if (encode == "chunked") + { + _state = HttpResponseParseState::kExpectChunkLen; + hasMore = true; + } + else + { + _state = HttpResponseParseState::kExpectClose; + hasMore = true; + } + } + } + buf->retrieveUntil(crlf + 2); + } + else + { + hasMore = false; + } + } + else if (_state == HttpResponseParseState::kExpectBody) + { + //LOG_INFO << "expectBody:len=" << request_->contentLen; + //LOG_INFO << "expectBody:buf=" << buf; + if (buf->readableBytes() == 0) + { + if (_response->_left_body_length == 0) + { + _state = HttpResponseParseState::kGotAll; + } + break; + } + if (_response->_left_body_length >= buf->readableBytes()) + { + _response->_left_body_length -= buf->readableBytes(); + _response->_bodyPtr->append(std::string(buf->peek(), buf->readableBytes())); + buf->retrieveAll(); + } + else + { + _response->_bodyPtr->append(std::string(buf->peek(), _response->_left_body_length)); + buf->retrieve(_response->_left_body_length); + _response->_left_body_length = 0; + } + if (_response->_left_body_length == 0) + { + _state = HttpResponseParseState::kGotAll; + LOG_TRACE << "post got all:len=" << _response->_left_body_length; + //LOG_INFO<<"content:"<content_; + LOG_TRACE << "content(END)"; + hasMore = false; + } + } + else if (_state == HttpResponseParseState::kExpectClose) + { + _response->_bodyPtr->append(std::string(buf->peek(), buf->readableBytes())); + buf->retrieveAll(); + break; + } + else if (_state == HttpResponseParseState::kExpectChunkLen) + { + const char *crlf = buf->findCRLF(); + if (crlf) + { + //chunk length line + std::string len(buf->peek(), crlf - buf->peek()); + char *end; + _response->_current_chunk_length = strtol(len.c_str(), &end, 16); + //LOG_TRACE << "chun length : " << _response->_current_chunk_length; + if (_response->_current_chunk_length != 0) + { + _state = HttpResponseParseState::kExpectChunkBody; + } + else + { + _state = HttpResponseParseState::kExpectLastEmptyChunk; + } + buf->retrieveUntil(crlf + 2); + } + else + { + hasMore = false; + } + } + else if (_state == HttpResponseParseState::kExpectChunkBody) + { + //LOG_TRACE<<"expect chunk len="<<_response->_current_chunk_length; + if (buf->readableBytes() >= (_response->_current_chunk_length + 2)) + { + if (*(buf->peek() + _response->_current_chunk_length) == '\r' && + *(buf->peek() + _response->_current_chunk_length + 1) == '\n') + { + _response->_bodyPtr->append(std::string(buf->peek(), _response->_current_chunk_length)); + buf->retrieve(_response->_current_chunk_length + 2); + _response->_current_chunk_length = 0; + _state = HttpResponseParseState::kExpectChunkLen; + } + else + { + //error! + buf->retrieveAll(); + return false; + } + } + else + { + hasMore = false; + } + } + else if (_state == HttpResponseParseState::kExpectLastEmptyChunk) + { + //last empty chunk + const char *crlf = buf->findCRLF(); + if (crlf) + { + buf->retrieveUntil(crlf + 2); + _state = HttpResponseParseState::kGotAll; + break; + } + else + { + hasMore = false; + } + } + } + return ok; +} diff --git a/lib/src/HttpClientContext.h b/lib/src/HttpClientContext.h new file mode 100755 index 00000000..f6651aa3 --- /dev/null +++ b/lib/src/HttpClientContext.h @@ -0,0 +1,96 @@ +// Copyright 2010, Shuo Chen. All rights reserved. +// http://code.google.com/p/muduo/ +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Shuo Chen (chenshuo at chenshuo dot com) +// +// This is an internal header file, you should not include this. + +//taken from muduo and modified + +/** + * + * @file + * @author An Tao + * @section LICENSE + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a MIT license + * that can be found in the License file. + * + * @section DESCRIPTION + * + */ +#pragma once + +#include "HttpResponseImpl.h" +#include +#include +#include +#include +#include + +using namespace trantor; +namespace drogon +{ +class HttpClientContext +{ + public: + + enum class HttpResponseParseState + { + kExpectResponseLine, + kExpectHeaders, + kExpectBody, + kExpectChunkLen, + kExpectChunkBody, + kExpectLastEmptyChunk, + kExpectClose, + kGotAll, + }; + + HttpClientContext(const trantor::TcpConnectionPtr &connPtr); + + // default copy-ctor, dtor and assignment are fine + + // return false if any error + bool parseResponse(MsgBuffer *buf); + + bool gotAll() const + { + return _state == HttpResponseParseState::kGotAll; + } + + void reset() + { + _state = HttpResponseParseState::kExpectResponseLine; + _response.reset(new HttpResponseImpl); + } + + + const HttpResponsePtr response() const + { + return _response; + } + + HttpResponsePtr response() + { + return _response; + } + HttpResponseImplPtr responseImpl() + { + return _response; + } + + private: + bool processResponseLine(const char *begin, const char *end); + + HttpResponseParseState _state; + HttpResponseImplPtr _response; + + std::weak_ptr _conn; +}; + +} // namespace drogon diff --git a/lib/src/HttpClientImpl.cc b/lib/src/HttpClientImpl.cc index fec162c7..c9fcb1d2 100644 --- a/lib/src/HttpClientImpl.cc +++ b/lib/src/HttpClientImpl.cc @@ -1,6 +1,6 @@ #include "HttpClientImpl.h" #include "HttpRequestImpl.h" -#include "HttpContext.h" +#include "HttpClientContext.h" #include "HttpAppFrameworkImpl.h" #include #include @@ -95,7 +95,7 @@ void HttpClientImpl::sendRequestInLoop(const drogon::HttpRequestPtr &req, if (InetAddress::resolve(_domain, &_server) == false) { callback(ReqResult::BadServerAddress, - HttpResponseImpl()); + HttpResponse::newHttpResponse()); return; } LOG_TRACE << "dns:domain=" << _domain << ";ip=" << _server.toIp(); @@ -117,7 +117,7 @@ void HttpClientImpl::sendRequestInLoop(const drogon::HttpRequestPtr &req, _tcpClient->setConnectionCallback([=](const trantor::TcpConnectionPtr &connPtr) { if (connPtr->connected()) { - connPtr->setContext(HttpContext(connPtr)); + connPtr->setContext(HttpClientContext(connPtr)); //send request; LOG_TRACE << "Connection established!"; auto req = thisPtr->_reqAndCallbacks.front().first; @@ -130,7 +130,7 @@ void HttpClientImpl::sendRequestInLoop(const drogon::HttpRequestPtr &req, { auto reqCallback = _reqAndCallbacks.front().second; _reqAndCallbacks.pop(); - reqCallback(ReqResult::NetworkFailure, HttpResponseImpl()); + reqCallback(ReqResult::NetworkFailure, HttpResponse::newHttpResponse()); } thisPtr->_tcpClient.reset(); } @@ -141,7 +141,7 @@ void HttpClientImpl::sendRequestInLoop(const drogon::HttpRequestPtr &req, { auto reqCallback = _reqAndCallbacks.front().second; _reqAndCallbacks.pop(); - reqCallback(ReqResult::BadServerAddress, HttpResponseImpl()); + reqCallback(ReqResult::BadServerAddress, HttpResponse::newHttpResponse()); } thisPtr->_tcpClient.reset(); }); @@ -151,7 +151,7 @@ void HttpClientImpl::sendRequestInLoop(const drogon::HttpRequestPtr &req, else { callback(ReqResult::BadServerAddress, - HttpResponseImpl()); + HttpResponse::newHttpResponse()); return; } } @@ -183,33 +183,36 @@ void HttpClientImpl::sendReq(const trantor::TcpConnectionPtr &connPtr, const Htt void HttpClientImpl::onRecvMessage(const trantor::TcpConnectionPtr &connPtr, trantor::MsgBuffer *msg) { - HttpContext *context = any_cast(connPtr->getMutableContext()); + HttpClientContext *context = any_cast(connPtr->getMutableContext()); //LOG_TRACE << "###:" << msg->readableBytes(); if (!context->parseResponse(msg)) { assert(!_reqAndCallbacks.empty()); auto cb = _reqAndCallbacks.front().second; - cb(ReqResult::BadResponse, HttpResponseImpl()); + cb(ReqResult::BadResponse, HttpResponse::newHttpResponse()); _reqAndCallbacks.pop(); _tcpClient.reset(); return; } - if (context->resGotAll()) + if (context->gotAll()) { - auto &resp = context->responseImpl(); + auto resp = context->responseImpl(); + context->reset(); + assert(!_reqAndCallbacks.empty()); - auto cb = _reqAndCallbacks.front().second; - cb(ReqResult::Ok, resp); - _reqAndCallbacks.pop(); - auto type = resp.getHeader("Content-Type"); + + auto type = resp->getHeader("Content-Type"); if (type.find("application/json") != std::string::npos) { - resp.parseJson(); + resp->parseJson(); } - context->resetRes(); + auto &cb = _reqAndCallbacks.front().second; + cb(ReqResult::Ok, resp); + _reqAndCallbacks.pop(); + LOG_TRACE << "req buffer size=" << _reqAndCallbacks.size(); if (!_reqAndCallbacks.empty()) { @@ -218,7 +221,7 @@ void HttpClientImpl::onRecvMessage(const trantor::TcpConnectionPtr &connPtr, tra } else { - if (resp.closeConnection()) + if (resp->closeConnection()) { _tcpClient.reset(); } diff --git a/lib/src/HttpContext.cc b/lib/src/HttpContext.cc deleted file mode 100755 index fe0a68af..00000000 --- a/lib/src/HttpContext.cc +++ /dev/null @@ -1,477 +0,0 @@ -// Copyright 2010, Shuo Chen. All rights reserved. -// http://code.google.com/p/muduo/ -// -// Use of this source code is governed by a BSD-style license -// that can be found in the License file. - -// Author: Shuo Chen (chenshuo at chenshuo dot com) -// - -//taken from muduo and modified - -/** - * - * @file - * @author An Tao - * @section LICENSE - * - * Copyright 2018, An Tao. All rights reserved. - * Use of this source code is governed by a MIT license - * that can be found in the License file. - * - * @section DESCRIPTION - * - */ - -#include -#include -#include "HttpContext.h" -#include -using namespace trantor; -using namespace drogon; -HttpContext::HttpContext(const trantor::TcpConnectionPtr &connPtr) - : state_(kExpectRequestLine), - request_(new HttpRequestImpl), - res_state_(HttpResponseParseState::kExpectResponseLine), - _pipeLineMutex(std::make_shared()), - _conn(connPtr) -{ -} -bool HttpContext::processRequestLine(const char *begin, const char *end) -{ - bool succeed = false; - const char *start = begin; - const char *space = std::find(start, end, ' '); - if (space != end && request_->setMethod(start, space)) - { - start = space + 1; - space = std::find(start, end, ' '); - if (space != end) - { - const char *question = std::find(start, space, '?'); - if (question != space) - { - request_->setPath(start, question); - request_->setQuery(question + 1, space); - } - else - { - request_->setPath(start, space); - } - start = space + 1; - succeed = end - start == 8 && std::equal(start, end - 1, "HTTP/1."); - if (succeed) - { - if (*(end - 1) == '1') - { - request_->setVersion(HttpRequest::kHttp11); - } - else if (*(end - 1) == '0') - { - request_->setVersion(HttpRequest::kHttp10); - } - else - { - succeed = false; - } - } - } - } - return succeed; -} - -// return false if any error -bool HttpContext::parseRequest(MsgBuffer *buf) -{ - bool ok = true; - bool hasMore = true; - // std::cout<peek(),buf->readableBytes())<findCRLF(); - if (crlf) - { - ok = processRequestLine(buf->peek(), crlf); - if (ok) - { - //request_->setReceiveTime(receiveTime); - buf->retrieveUntil(crlf + 2); - state_ = kExpectHeaders; - } - else - { - hasMore = false; - } - } - else - { - hasMore = false; - } - } - else if (state_ == kExpectHeaders) - { - const char *crlf = buf->findCRLF(); - if (crlf) - { - const char *colon = std::find(buf->peek(), crlf, ':'); - if (colon != crlf) - { - request_->addHeader(buf->peek(), colon, crlf); - } - else - { - // empty line, end of header - std::string len = request_->getHeader("Content-Length"); - LOG_TRACE << "content len=" << len; - if (len != "") - { - request_->contentLen = atoi(len.c_str()); - state_ = kExpectBody; - auto expect = request_->getHeader("Expect"); - if (expect == "100-continue" && - request_->getVersion() >= HttpRequest::kHttp11) - { - //rfc2616-8.2.3 - //TODO:here we can add content-length limitation - auto connPtr = _conn.lock(); - if (connPtr) - { - auto resp = HttpResponse::newHttpResponse(); - resp->setStatusCode(HttpResponse::k100Continue); - MsgBuffer buffer; - std::dynamic_pointer_cast(resp) - ->appendToBuffer(&buffer); - connPtr->send(std::move(buffer)); - } - } - else if (!expect.empty()) - { - LOG_WARN << "417ExpectationFailed for \"" << expect << "\""; - auto connPtr = _conn.lock(); - if (connPtr) - { - auto resp = HttpResponse::newHttpResponse(); - resp->setStatusCode(HttpResponse::k417ExpectationFailed); - MsgBuffer buffer; - std::dynamic_pointer_cast(resp) - ->appendToBuffer(&buffer); - connPtr->send(std::move(buffer)); - buf->retrieveAll(); - connPtr->forceClose(); - - //return false; - } - } - } - else - { - state_ = kGotAll; - hasMore = false; - } - } - buf->retrieveUntil(crlf + 2); - } - else - { - hasMore = false; - } - } - else if (state_ == kExpectBody) - { - //LOG_INFO << "expectBody:len=" << request_->contentLen; - //LOG_INFO << "expectBody:buf=" << buf; - if (buf->readableBytes() == 0) - { - if (request_->contentLen == 0) - { - state_ = kGotAll; - } - break; - } - if (request_->contentLen >= buf->readableBytes()) - { - request_->contentLen -= buf->readableBytes(); - request_->content_ += std::string(buf->peek(), buf->readableBytes()); - buf->retrieveAll(); - } - else - { - request_->content_ += std::string(buf->peek(), request_->contentLen); - buf->retrieve(request_->contentLen); - request_->contentLen = 0; - } - if (request_->contentLen == 0) - { - state_ = kGotAll; - LOG_TRACE << "post got all:len=" << request_->content_.length(); - //LOG_INFO<<"content:"<content_; - LOG_TRACE << "content(END)"; - hasMore = false; - } - } - } - - return ok; -} - -bool HttpContext::processResponseLine(const char *begin, const char *end) -{ - const char *start = begin; - const char *space = std::find(start, end, ' '); - if (space != end) - { - LOG_TRACE << *(space - 1); - if (*(space - 1) == '1') - { - response_.setVersion(HttpResponse::kHttp11); - } - else if (*(space - 1) == '0') - { - response_.setVersion(HttpResponse::kHttp10); - } - else - { - return false; - } - } - - start = space + 1; - space = std::find(start, end, ' '); - if (space != end) - { - std::string status_code(start, space - start); - std::string status_message(space + 1, end - space - 1); - LOG_TRACE << status_code << " " << status_message; - auto code = atoi(status_code.c_str()); - response_.setStatusCode(HttpResponse::HttpStatusCode(code), status_message); - - return true; - } - return false; -} - -// return false if any error -bool HttpContext::parseResponse(MsgBuffer *buf) -{ - bool ok = true; - bool hasMore = true; - while (hasMore) - { - if (res_state_ == HttpResponseParseState::kExpectResponseLine) - { - const char *crlf = buf->findCRLF(); - if (crlf) - { - ok = processResponseLine(buf->peek(), crlf); - if (ok) - { - //response_.setReceiveTime(receiveTime); - buf->retrieveUntil(crlf + 2); - res_state_ = HttpResponseParseState::kExpectHeaders; - } - else - { - hasMore = false; - } - } - else - { - hasMore = false; - } - } - else if (res_state_ == HttpResponseParseState::kExpectHeaders) - { - const char *crlf = buf->findCRLF(); - if (crlf) - { - const char *colon = std::find(buf->peek(), crlf, ':'); - if (colon != crlf) - { - response_.addHeader(buf->peek(), colon, crlf); - } - else - { - // empty line, end of header - // FIXME: - std::string len = response_.getHeader("Content-Length"); - LOG_INFO << "content len=" << len; - if (len != "") - { - response_._left_body_length = atoi(len.c_str()); - res_state_ = HttpResponseParseState::kExpectBody; - } - else - { - std::string encode = response_.getHeader("Transfer-Encoding"); - if (encode == "chunked") - { - res_state_ = HttpResponseParseState::kExpectChunkLen; - hasMore = true; - } - else - { - res_state_ = HttpResponseParseState::kExpectClose; - hasMore = true; - } - } - } - buf->retrieveUntil(crlf + 2); - } - else - { - hasMore = false; - } - } - else if (res_state_ == HttpResponseParseState::kExpectBody) - { - //LOG_INFO << "expectBody:len=" << request_->contentLen; - //LOG_INFO << "expectBody:buf=" << buf; - if (buf->readableBytes() == 0) - { - if (response_._left_body_length == 0) - { - res_state_ = HttpResponseParseState::kGotAll; - } - break; - } - if (response_._left_body_length >= buf->readableBytes()) - { - response_._left_body_length -= buf->readableBytes(); - response_._bodyPtr->append(std::string(buf->peek(), buf->readableBytes())); - buf->retrieveAll(); - } - else - { - response_._bodyPtr->append(std::string(buf->peek(), response_._left_body_length)); - buf->retrieve(request_->contentLen); - response_._left_body_length = 0; - } - if (response_._left_body_length == 0) - { - res_state_ = HttpResponseParseState::kGotAll; - LOG_TRACE << "post got all:len=" << response_._left_body_length; - //LOG_INFO<<"content:"<content_; - LOG_TRACE << "content(END)"; - hasMore = false; - } - } - else if (res_state_ == HttpResponseParseState::kExpectClose) - { - response_._bodyPtr->append(std::string(buf->peek(), buf->readableBytes())); - buf->retrieveAll(); - break; - } - else if (res_state_ == HttpResponseParseState::kExpectChunkLen) - { - const char *crlf = buf->findCRLF(); - if (crlf) - { - //chunk length line - std::string len(buf->peek(), crlf - buf->peek()); - char *end; - response_._current_chunk_length = strtol(len.c_str(), &end, 16); - //LOG_TRACE << "chun length : " << response_._current_chunk_length; - if (response_._current_chunk_length != 0) - { - res_state_ = HttpResponseParseState::kExpectChunkBody; - } - else - { - res_state_ = HttpResponseParseState::kExpectLastEmptyChunk; - } - buf->retrieveUntil(crlf + 2); - } - else - { - hasMore = false; - } - } - else if (res_state_ == HttpResponseParseState::kExpectChunkBody) - { - //LOG_TRACE<<"expect chunk len="<readableBytes() >= (response_._current_chunk_length + 2)) - { - if (*(buf->peek() + response_._current_chunk_length) == '\r' && - *(buf->peek() + response_._current_chunk_length + 1) == '\n') - { - response_._bodyPtr->append(std::string(buf->peek(), response_._current_chunk_length)); - buf->retrieve(response_._current_chunk_length + 2); - response_._current_chunk_length = 0; - res_state_ = HttpResponseParseState::kExpectChunkLen; - } - else - { - //error! - buf->retrieveAll(); - return false; - } - } - else - { - hasMore = false; - } - } - else if (res_state_ == HttpResponseParseState::kExpectLastEmptyChunk) - { - //last empty chunk - const char *crlf = buf->findCRLF(); - if (crlf) - { - buf->retrieveUntil(crlf + 2); - res_state_ = HttpResponseParseState::kGotAll; - break; - } - else - { - hasMore = false; - } - } - } - return ok; -} - -void HttpContext::pushRquestToPipeLine(const HttpRequestPtr &req) -{ - std::pair reqPair(req, HttpResponseImplPtr()); - - _requestPipeLine.push_back(std::move(reqPair)); -} -HttpRequestPtr HttpContext::getFirstRequest() const -{ - if (_requestPipeLine.size() > 0) - { - return _requestPipeLine.front().first; - } - return HttpRequestImplPtr(); -} -HttpResponsePtr HttpContext::getFirstResponse() const -{ - if (_requestPipeLine.size() > 0) - { - return _requestPipeLine.front().second; - } - return HttpResponseImplPtr(); -} -void HttpContext::popFirstRequest() -{ - _requestPipeLine.pop_front(); -} -void HttpContext::pushResponseToPipeLine(const HttpRequestPtr &req, - const HttpResponsePtr &resp) -{ - for (auto &iter : _requestPipeLine) - { - if (iter.first == req) - { - iter.second = resp; - return; - } - } -} - -std::mutex &HttpContext::getPipeLineMutex() -{ - return *_pipeLineMutex; -} diff --git a/lib/src/HttpRequestImpl.h b/lib/src/HttpRequestImpl.h index b4b58599..b6370b35 100755 --- a/lib/src/HttpRequestImpl.h +++ b/lib/src/HttpRequestImpl.h @@ -44,8 +44,7 @@ namespace drogon class HttpRequestImpl : public HttpRequest { public: - friend class HttpContext; - + friend class HttpServerContext; HttpRequestImpl() : _method(Invalid), _version(kUnknown), diff --git a/lib/src/HttpResponseImpl.h b/lib/src/HttpResponseImpl.h index ff0a5c41..54046411 100755 --- a/lib/src/HttpResponseImpl.h +++ b/lib/src/HttpResponseImpl.h @@ -40,7 +40,7 @@ namespace drogon { class HttpResponseImpl : public HttpResponse { - friend class HttpContext; + friend class HttpClientContext; public: explicit HttpResponseImpl() diff --git a/lib/src/HttpServer.cc b/lib/src/HttpServer.cc index cc8db995..9864297d 100755 --- a/lib/src/HttpServer.cc +++ b/lib/src/HttpServer.cc @@ -25,7 +25,8 @@ #include "HttpServer.h" #include -#include "HttpContext.h" +#include "HttpServerContext.h" +#include "HttpResponseImpl.h" #include #include #include @@ -85,12 +86,12 @@ void HttpServer::onConnection(const TcpConnectionPtr &conn) { if (conn->connected()) { - conn->setContext(HttpContext(conn)); + conn->setContext(HttpServerContext(conn)); } else if (conn->disconnected()) { LOG_TRACE << "conn disconnected!"; - HttpContext *context = any_cast(conn->getMutableContext()); + HttpServerContext *context = any_cast(conn->getMutableContext()); // LOG_INFO << "###:" << string(buf->peek(), buf->readableBytes()); if (context->webSocketConn()) @@ -105,7 +106,7 @@ void HttpServer::onConnection(const TcpConnectionPtr &conn) void HttpServer::onMessage(const TcpConnectionPtr &conn, MsgBuffer *buf) { - HttpContext *context = any_cast(conn->getMutableContext()); + HttpServerContext *context = any_cast(conn->getMutableContext()); // LOG_INFO << "###:" << string(buf->peek(), buf->readableBytes()); if (context->webSocketConn()) @@ -167,7 +168,7 @@ void HttpServer::onRequest(const TcpConnectionPtr &conn, const HttpRequestPtr &r { req->setMethod(Get); } - HttpContext *context = any_cast(conn->getMutableContext()); + HttpServerContext *context = any_cast(conn->getMutableContext()); //request will be received in same thread,so we don't need mutex; context->pushRquestToPipeLine(req); httpAsyncCallback_(req, [=](const HttpResponsePtr &response) { diff --git a/lib/src/HttpServerContext.cc b/lib/src/HttpServerContext.cc new file mode 100755 index 00000000..0115e80d --- /dev/null +++ b/lib/src/HttpServerContext.cc @@ -0,0 +1,261 @@ +// Copyright 2010, Shuo Chen. All rights reserved. +// http://code.google.com/p/muduo/ +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Shuo Chen (chenshuo at chenshuo dot com) +// + +//taken from muduo and modified + +/** + * + * @file + * @author An Tao + * @section LICENSE + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a MIT license + * that can be found in the License file. + * + * @section DESCRIPTION + * + */ + +#include +#include +#include "HttpServerContext.h" +#include "HttpResponseImpl.h" +#include +using namespace trantor; +using namespace drogon; +HttpServerContext::HttpServerContext(const trantor::TcpConnectionPtr &connPtr) + : state_(kExpectRequestLine), + request_(new HttpRequestImpl), + _pipeLineMutex(std::make_shared()), + _conn(connPtr) +{ +} +bool HttpServerContext::processRequestLine(const char *begin, const char *end) +{ + bool succeed = false; + const char *start = begin; + const char *space = std::find(start, end, ' '); + if (space != end && request_->setMethod(start, space)) + { + start = space + 1; + space = std::find(start, end, ' '); + if (space != end) + { + const char *question = std::find(start, space, '?'); + if (question != space) + { + request_->setPath(start, question); + request_->setQuery(question + 1, space); + } + else + { + request_->setPath(start, space); + } + start = space + 1; + succeed = end - start == 8 && std::equal(start, end - 1, "HTTP/1."); + if (succeed) + { + if (*(end - 1) == '1') + { + request_->setVersion(HttpRequest::kHttp11); + } + else if (*(end - 1) == '0') + { + request_->setVersion(HttpRequest::kHttp10); + } + else + { + succeed = false; + } + } + } + } + return succeed; +} + +// return false if any error +bool HttpServerContext::parseRequest(MsgBuffer *buf) +{ + bool ok = true; + bool hasMore = true; + // std::cout<peek(),buf->readableBytes())<findCRLF(); + if (crlf) + { + ok = processRequestLine(buf->peek(), crlf); + if (ok) + { + //request_->setReceiveTime(receiveTime); + buf->retrieveUntil(crlf + 2); + state_ = kExpectHeaders; + } + else + { + hasMore = false; + } + } + else + { + hasMore = false; + } + } + else if (state_ == kExpectHeaders) + { + const char *crlf = buf->findCRLF(); + if (crlf) + { + const char *colon = std::find(buf->peek(), crlf, ':'); + if (colon != crlf) + { + request_->addHeader(buf->peek(), colon, crlf); + } + else + { + // empty line, end of header + std::string len = request_->getHeader("Content-Length"); + LOG_TRACE << "content len=" << len; + if (len != "") + { + request_->contentLen = atoi(len.c_str()); + state_ = kExpectBody; + auto expect = request_->getHeader("Expect"); + if (expect == "100-continue" && + request_->getVersion() >= HttpRequest::kHttp11) + { + //rfc2616-8.2.3 + //TODO:here we can add content-length limitation + auto connPtr = _conn.lock(); + if (connPtr) + { + auto resp = HttpResponse::newHttpResponse(); + resp->setStatusCode(HttpResponse::k100Continue); + MsgBuffer buffer; + std::dynamic_pointer_cast(resp) + ->appendToBuffer(&buffer); + connPtr->send(std::move(buffer)); + } + } + else if (!expect.empty()) + { + LOG_WARN << "417ExpectationFailed for \"" << expect << "\""; + auto connPtr = _conn.lock(); + if (connPtr) + { + auto resp = HttpResponse::newHttpResponse(); + resp->setStatusCode(HttpResponse::k417ExpectationFailed); + MsgBuffer buffer; + std::dynamic_pointer_cast(resp) + ->appendToBuffer(&buffer); + connPtr->send(std::move(buffer)); + buf->retrieveAll(); + connPtr->forceClose(); + + //return false; + } + } + } + else + { + state_ = kGotAll; + hasMore = false; + } + } + buf->retrieveUntil(crlf + 2); + } + else + { + hasMore = false; + } + } + else if (state_ == kExpectBody) + { + //LOG_INFO << "expectBody:len=" << request_->contentLen; + //LOG_INFO << "expectBody:buf=" << buf; + if (buf->readableBytes() == 0) + { + if (request_->contentLen == 0) + { + state_ = kGotAll; + } + break; + } + if (request_->contentLen >= buf->readableBytes()) + { + request_->contentLen -= buf->readableBytes(); + request_->content_ += std::string(buf->peek(), buf->readableBytes()); + buf->retrieveAll(); + } + else + { + request_->content_ += std::string(buf->peek(), request_->contentLen); + buf->retrieve(request_->contentLen); + request_->contentLen = 0; + } + if (request_->contentLen == 0) + { + state_ = kGotAll; + LOG_TRACE << "post got all:len=" << request_->content_.length(); + //LOG_INFO<<"content:"<content_; + LOG_TRACE << "content(END)"; + hasMore = false; + } + } + } + + return ok; +} + +void HttpServerContext::pushRquestToPipeLine(const HttpRequestPtr &req) +{ + std::pair reqPair(req, HttpResponseImplPtr()); + + _requestPipeLine.push_back(std::move(reqPair)); +} +HttpRequestPtr HttpServerContext::getFirstRequest() const +{ + if (_requestPipeLine.size() > 0) + { + return _requestPipeLine.front().first; + } + return HttpRequestImplPtr(); +} +HttpResponsePtr HttpServerContext::getFirstResponse() const +{ + if (_requestPipeLine.size() > 0) + { + return _requestPipeLine.front().second; + } + return HttpResponseImplPtr(); +} +void HttpServerContext::popFirstRequest() +{ + _requestPipeLine.pop_front(); +} +void HttpServerContext::pushResponseToPipeLine(const HttpRequestPtr &req, + const HttpResponsePtr &resp) +{ + for (auto &iter : _requestPipeLine) + { + if (iter.first == req) + { + iter.second = resp; + return; + } + } +} + +std::mutex &HttpServerContext::getPipeLineMutex() +{ + return *_pipeLineMutex; +} diff --git a/lib/src/HttpContext.h b/lib/src/HttpServerContext.h similarity index 67% rename from lib/src/HttpContext.h rename to lib/src/HttpServerContext.h index 11b2da09..500e9ff3 100755 --- a/lib/src/HttpContext.h +++ b/lib/src/HttpServerContext.h @@ -26,9 +26,9 @@ #pragma once #include "HttpRequestImpl.h" -#include "HttpResponseImpl.h" #include #include +#include #include #include #include @@ -36,7 +36,7 @@ using namespace trantor; namespace drogon { -class HttpContext +class HttpServerContext { public: enum HttpRequestParseState @@ -47,54 +47,22 @@ class HttpContext kGotAll, }; - enum class HttpResponseParseState - { - kExpectResponseLine, - kExpectHeaders, - kExpectBody, - kExpectChunkLen, - kExpectChunkBody, - kExpectLastEmptyChunk, - kExpectClose, - kGotAll, - }; - - HttpContext(const trantor::TcpConnectionPtr &connPtr); + HttpServerContext(const trantor::TcpConnectionPtr &connPtr); // default copy-ctor, dtor and assignment are fine // return false if any error bool parseRequest(MsgBuffer *buf); - bool parseResponse(MsgBuffer *buf); bool gotAll() const { return state_ == kGotAll; } - bool resGotAll() const - { - return res_state_ == HttpResponseParseState::kGotAll; - } - - bool resExpectResponseLine() const - { - return res_state_ == HttpResponseParseState::kExpectResponseLine; - } - void reset() { state_ = kExpectRequestLine; - res_state_ = HttpResponseParseState::kExpectResponseLine; request_.reset(new HttpRequestImpl); - // HttpResponseImpl dummy_res; - // response_.swap(dummy_res); - } - - void resetRes() - { - res_state_ = HttpResponseParseState::kExpectResponseLine; - response_.clear(); } const HttpRequestPtr request() const @@ -112,19 +80,6 @@ class HttpContext return request_; } - const HttpResponse &response() const - { - return response_; - } - - HttpResponse &response() - { - return response_; - } - HttpResponseImpl &responseImpl() - { - return response_; - } bool firstReq() { if (_firstRequest) @@ -152,13 +107,10 @@ class HttpContext private: bool processRequestLine(const char *begin, const char *end); - bool processResponseLine(const char *begin, const char *end); HttpRequestParseState state_; HttpRequestImplPtr request_; - HttpResponseParseState res_state_; - HttpResponseImpl response_; bool _firstRequest = true; WebSocketConnectionPtr _websockConnPtr;