Merge branch 'master' into with_orm

This commit is contained in:
an-tao 2018-11-15 22:01:30 +08:00
commit e54aba423c
12 changed files with 646 additions and 558 deletions

View File

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

View File

@ -8,8 +8,8 @@ class Attachment : public drogon::HttpController<Attachment>
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,

View File

@ -28,7 +28,7 @@ enum class ReqResult
BadServerAddress,
Timeout
};
typedef std::function<void(ReqResult, const HttpResponse &response)> HttpReqCallback;
typedef std::function<void(ReqResult, const HttpResponsePtr &response)> HttpReqCallback;
class HttpClient;
typedef std::shared_ptr<HttpClient> HttpClientPtr;
class HttpClient : public trantor::NonCopyable

253
lib/src/HttpClientContext.cc Executable file
View File

@ -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 <trantor/utils/MsgBuffer.h>
#include <trantor/utils/Logger.h>
#include "HttpClientContext.h"
#include <iostream>
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:"<<request_->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;
}

96
lib/src/HttpClientContext.h Executable file
View File

@ -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 <trantor/utils/MsgBuffer.h>
#include <drogon/WebSocketConnection.h>
#include <list>
#include <mutex>
#include <trantor/net/TcpConnection.h>
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<trantor::TcpConnection> _conn;
};
} // namespace drogon

View File

@ -1,6 +1,6 @@
#include "HttpClientImpl.h"
#include "HttpRequestImpl.h"
#include "HttpContext.h"
#include "HttpClientContext.h"
#include "HttpAppFrameworkImpl.h"
#include <stdlib.h>
#include <algorithm>
@ -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<HttpContext>(connPtr->getMutableContext());
HttpClientContext *context = any_cast<HttpClientContext>(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();
}

View File

@ -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 <trantor/utils/MsgBuffer.h>
#include <trantor/utils/Logger.h>
#include "HttpContext.h"
#include <iostream>
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<std::mutex>()),
_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<<std::string(buf->peek(),buf->readableBytes())<<std::endl;
while (hasMore)
{
if (state_ == kExpectRequestLine)
{
const char *crlf = buf->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<HttpResponseImpl>(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<HttpResponseImpl>(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:"<<request_->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:"<<request_->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="<<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;
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<HttpRequestPtr, HttpResponsePtr> 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;
}

View File

@ -44,8 +44,7 @@ namespace drogon
class HttpRequestImpl : public HttpRequest
{
public:
friend class HttpContext;
friend class HttpServerContext;
HttpRequestImpl()
: _method(Invalid),
_version(kUnknown),

View File

@ -40,7 +40,7 @@ namespace drogon
{
class HttpResponseImpl : public HttpResponse
{
friend class HttpContext;
friend class HttpClientContext;
public:
explicit HttpResponseImpl()

View File

@ -25,7 +25,8 @@
#include "HttpServer.h"
#include <trantor/utils/Logger.h>
#include "HttpContext.h"
#include "HttpServerContext.h"
#include "HttpResponseImpl.h"
#include <drogon/HttpRequest.h>
#include <drogon/HttpResponse.h>
#include <drogon/utils/Utilities.h>
@ -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<HttpContext>(conn->getMutableContext());
HttpServerContext *context = any_cast<HttpServerContext>(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<HttpContext>(conn->getMutableContext());
HttpServerContext *context = any_cast<HttpServerContext>(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<HttpContext>(conn->getMutableContext());
HttpServerContext *context = any_cast<HttpServerContext>(conn->getMutableContext());
//request will be received in same thread,so we don't need mutex;
context->pushRquestToPipeLine(req);
httpAsyncCallback_(req, [=](const HttpResponsePtr &response) {

261
lib/src/HttpServerContext.cc Executable file
View File

@ -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 <trantor/utils/MsgBuffer.h>
#include <trantor/utils/Logger.h>
#include "HttpServerContext.h"
#include "HttpResponseImpl.h"
#include <iostream>
using namespace trantor;
using namespace drogon;
HttpServerContext::HttpServerContext(const trantor::TcpConnectionPtr &connPtr)
: state_(kExpectRequestLine),
request_(new HttpRequestImpl),
_pipeLineMutex(std::make_shared<std::mutex>()),
_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<<std::string(buf->peek(),buf->readableBytes())<<std::endl;
while (hasMore)
{
if (state_ == kExpectRequestLine)
{
const char *crlf = buf->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<HttpResponseImpl>(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<HttpResponseImpl>(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:"<<request_->content_;
LOG_TRACE << "content(END)";
hasMore = false;
}
}
}
return ok;
}
void HttpServerContext::pushRquestToPipeLine(const HttpRequestPtr &req)
{
std::pair<HttpRequestPtr, HttpResponsePtr> 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;
}

View File

@ -26,9 +26,9 @@
#pragma once
#include "HttpRequestImpl.h"
#include "HttpResponseImpl.h"
#include <trantor/utils/MsgBuffer.h>
#include <drogon/WebSocketConnection.h>
#include <drogon/HttpResponse.h>
#include <list>
#include <mutex>
#include <trantor/net/TcpConnection.h>
@ -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;