mirror of
https://gitee.com/an-tao/drogon.git
synced 2024-12-02 19:57:43 +08:00
Merge pull request #47 from an-tao/dev
1. Modify the HttpClientImpl class to fix some bugs; 2. Support attachments download;
This commit is contained in:
commit
8b61ce3bcb
@ -103,7 +103,7 @@
|
||||
"log_size_limit": 100000000,
|
||||
//log_level:"DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN"
|
||||
//The TRACE level is only valid when built in DEBUG mode.
|
||||
"log_level": "DEBUG"
|
||||
"log_level": "TRACE"
|
||||
},
|
||||
//run_as_daemon:false by default
|
||||
"run_as_daemon": false,
|
||||
|
@ -1,4 +1,6 @@
|
||||
#include "api_Attachment.h"
|
||||
#include <fstream>
|
||||
|
||||
using namespace api;
|
||||
//add definition of your processing function here
|
||||
void Attachment::get(const HttpRequestPtr &req,
|
||||
@ -12,7 +14,7 @@ void Attachment::upload(const HttpRequestPtr &req,
|
||||
const std::function<void(const HttpResponsePtr &)> &callback)
|
||||
{
|
||||
MultiPartParser fileUpload;
|
||||
if(fileUpload.parse(req)==0)
|
||||
if (fileUpload.parse(req) == 0)
|
||||
{
|
||||
auto files = fileUpload.getFiles();
|
||||
for (auto const &file : files)
|
||||
@ -43,3 +45,10 @@ void Attachment::upload(const HttpRequestPtr &req,
|
||||
auto resp = HttpResponse::newHttpJsonResponse(json);
|
||||
callback(resp);
|
||||
}
|
||||
|
||||
void Attachment::download(const HttpRequestPtr &req,
|
||||
const std::function<void(const HttpResponsePtr &)> &callback)
|
||||
{
|
||||
auto resp = HttpResponse::newFileResponse("./drogon.jpg");
|
||||
callback(resp);
|
||||
}
|
||||
|
@ -10,11 +10,14 @@ class Attachment : public drogon::HttpController<Attachment>
|
||||
//use METHOD_ADD to add your custom processing function here;
|
||||
METHOD_ADD(Attachment::get, "", Get); //Path will be '/api/attachment'
|
||||
METHOD_ADD(Attachment::upload, "/upload", Post);
|
||||
METHOD_ADD(Attachment::download, "/download", Get);
|
||||
METHOD_LIST_END
|
||||
//your declaration of processing function maybe like this:
|
||||
void get(const HttpRequestPtr &req,
|
||||
const std::function<void(const HttpResponsePtr &)> &callback);
|
||||
void upload(const HttpRequestPtr &req,
|
||||
const std::function<void(const HttpResponsePtr &)> &callback);
|
||||
void download(const HttpRequestPtr &req,
|
||||
const std::function<void(const HttpResponsePtr &)> &callback);
|
||||
};
|
||||
} // namespace api
|
||||
|
@ -104,7 +104,7 @@ int main()
|
||||
drogon::HttpAppFramework::instance().setSSLFiles("server.pem", "server.pem");
|
||||
drogon::HttpAppFramework::instance().addListener("0.0.0.0", 8849, true);
|
||||
#endif
|
||||
app().setThreadNum(4);
|
||||
//app().setThreadNum(4);
|
||||
// trantor::Logger::setLogLevel(trantor::Logger::TRACE);
|
||||
//class function
|
||||
app().registerHttpMethod("/api/v1/handle1/{1}/{2}/?p3={3}&p4={4}", &A::handle);
|
||||
|
@ -16,12 +16,18 @@
|
||||
|
||||
#include <drogon/drogon.h>
|
||||
#include <trantor/net/EventLoopThread.h>
|
||||
#include <trantor/net/TcpClient.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <future>
|
||||
#include <unistd.h>
|
||||
|
||||
#define RESET "\033[0m"
|
||||
#define RED "\033[31m" /* Red */
|
||||
#define GREEN "\033[32m" /* Green */
|
||||
|
||||
using namespace drogon;
|
||||
|
||||
void outputGood(const HttpRequestPtr &req)
|
||||
{
|
||||
static int i = 0;
|
||||
@ -33,7 +39,7 @@ void outputGood(const HttpRequestPtr &req)
|
||||
<< " " << req->path() << RESET << std::endl;
|
||||
}
|
||||
}
|
||||
void doTest(const HttpClientPtr &client)
|
||||
void doTest(const HttpClientPtr &client, std::promise<int> &pro)
|
||||
{
|
||||
/// Post json
|
||||
Json::Value json;
|
||||
@ -515,24 +521,61 @@ void doTest(const HttpClientPtr &client)
|
||||
exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
/// Test attachment download
|
||||
req = HttpRequest::newHttpRequest();
|
||||
req->setMethod(drogon::Get);
|
||||
req->setPath("/api/attachment/download");
|
||||
client->sendRequest(req, [=, &pro](ReqResult result, const HttpResponsePtr &resp) {
|
||||
if (result == ReqResult::Ok)
|
||||
{
|
||||
if (resp->getBody().length() == 51822)
|
||||
{
|
||||
outputGood(req);
|
||||
pro.set_value(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEBUG << resp->getBody().length();
|
||||
LOG_ERROR << "Error!";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR << "Error!";
|
||||
exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int main()
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
trantor::EventLoopThread loop[2];
|
||||
trantor::Logger::setLogLevel(trantor::Logger::LogLevel::DEBUG);
|
||||
bool ever = false;
|
||||
if (argc > 1 && std::string(argv[1]) == "-f")
|
||||
ever = true;
|
||||
loop[0].run();
|
||||
loop[1].run();
|
||||
// for (int i = 0; i < 100;i++)
|
||||
{
|
||||
auto client = HttpClient::newHttpClient("http://127.0.0.1:8848", loop[0].getLoop());
|
||||
doTest(client);
|
||||
#ifdef USE_OPENSSL
|
||||
auto sslClient = HttpClient::newHttpClient("https://127.0.0.1:8849", loop[1].getLoop());
|
||||
doTest(sslClient);
|
||||
#endif
|
||||
}
|
||||
|
||||
getchar();
|
||||
do
|
||||
{
|
||||
std::promise<int> pro1;
|
||||
auto client = HttpClient::newHttpClient("http://127.0.0.1:8848", loop[0].getLoop());
|
||||
doTest(client, pro1);
|
||||
#ifdef USE_OPENSSL
|
||||
std::promise<int> pro2;
|
||||
auto sslClient = HttpClient::newHttpClient("https://127.0.0.1:8849", loop[1].getLoop());
|
||||
doTest(sslClient, pro2);
|
||||
auto f2 = pro2.get_future();
|
||||
f2.get();
|
||||
#endif
|
||||
auto f1 = pro1.get_future();
|
||||
f1.get();
|
||||
//LOG_DEBUG << sslClient.use_count();
|
||||
} while (ever);
|
||||
//getchar();
|
||||
loop[0].getLoop()->quit();
|
||||
loop[1].getLoop()->quit();
|
||||
}
|
@ -58,9 +58,8 @@ class HttpClient : public trantor::NonCopyable
|
||||
* The response from the http server will be obtained
|
||||
* in the callback function.
|
||||
*/
|
||||
virtual void sendRequest(const HttpRequestPtr &req,
|
||||
const HttpReqCallback &callback) = 0;
|
||||
|
||||
virtual void sendRequest(const HttpRequestPtr &req, const HttpReqCallback &callback) = 0;
|
||||
virtual void sendRequest(const HttpRequestPtr &req, HttpReqCallback &&callback) = 0;
|
||||
/// Use ip and port to connect to server
|
||||
/**
|
||||
* If useSSL is set to true, the client will
|
||||
|
@ -92,6 +92,7 @@ class HttpResponse
|
||||
static HttpResponsePtr newHttpJsonResponse(const Json::Value &data);
|
||||
static HttpResponsePtr newHttpViewResponse(const std::string &viewName, const HttpViewData &data = HttpViewData());
|
||||
static HttpResponsePtr newLocationRedirectResponse(const std::string &path);
|
||||
static HttpResponsePtr newFileResponse(const std::string &fullPath, const std::string &attachmentFileName = "", bool asAttachment = true);
|
||||
|
||||
virtual ~HttpResponse() {}
|
||||
};
|
||||
|
@ -395,6 +395,7 @@ void HttpAppFrameworkImpl::onWebsockDisconnect(const WebSocketConnectionPtr &wsC
|
||||
void HttpAppFrameworkImpl::onConnection(const TcpConnectionPtr &conn)
|
||||
{
|
||||
static std::mutex mtx;
|
||||
LOG_TRACE << "connect!!!" << _maxConnectionNum << " num=" << _connectionNum.load();
|
||||
if (conn->connected())
|
||||
{
|
||||
if (_connectionNum.fetch_add(1) >= _maxConnectionNum)
|
||||
@ -422,7 +423,10 @@ void HttpAppFrameworkImpl::onConnection(const TcpConnectionPtr &conn)
|
||||
}
|
||||
else
|
||||
{
|
||||
_connectionNum--;
|
||||
if (_connectionNum-- == 0)
|
||||
{
|
||||
_connectionNum++;
|
||||
}
|
||||
if (_maxConnectionNumPerIP > 0)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
@ -611,7 +615,6 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, std::fu
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<HttpResponseImpl> resp = std::make_shared<HttpResponseImpl>();
|
||||
//find cached response
|
||||
HttpResponsePtr cachedResp;
|
||||
{
|
||||
@ -629,12 +632,14 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, std::fu
|
||||
//check last modified time,rfc2616-14.25
|
||||
//If-Modified-Since: Mon, 15 Oct 2018 06:26:33 GMT
|
||||
|
||||
std::string timeStr;
|
||||
if (_enableLastModify)
|
||||
{
|
||||
if (cachedResp)
|
||||
{
|
||||
if (std::dynamic_pointer_cast<HttpResponseImpl>(cachedResp)->getHeaderBy("last-modified") == req->getHeaderBy("if-modified-since"))
|
||||
{
|
||||
std::shared_ptr<HttpResponseImpl> resp = std::make_shared<HttpResponseImpl>();
|
||||
resp->setStatusCode(k304NotModified);
|
||||
if (needSetJsessionid)
|
||||
{
|
||||
@ -653,7 +658,6 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, std::fu
|
||||
LOG_TRACE << "last modify time:" << fileStat.st_mtime;
|
||||
struct tm tm1;
|
||||
gmtime_r(&fileStat.st_mtime, &tm1);
|
||||
std::string timeStr;
|
||||
timeStr.resize(64);
|
||||
auto len = strftime((char *)timeStr.data(), timeStr.size(), "%a, %d %b %Y %T GMT", &tm1);
|
||||
timeStr.resize(len);
|
||||
@ -661,6 +665,7 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, std::fu
|
||||
if (modiStr == timeStr && !modiStr.empty())
|
||||
{
|
||||
LOG_TRACE << "not Modified!";
|
||||
std::shared_ptr<HttpResponseImpl> resp = std::make_shared<HttpResponseImpl>();
|
||||
resp->setStatusCode(k304NotModified);
|
||||
if (needSetJsessionid)
|
||||
{
|
||||
@ -669,8 +674,6 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, std::fu
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
resp->addHeader("Last-Modified", timeStr);
|
||||
resp->addHeader("Expires", "Thu, 01 Jan 1970 00:00:00 GMT");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -689,50 +692,25 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, std::fu
|
||||
callback(cachedResp);
|
||||
return;
|
||||
}
|
||||
|
||||
// pick a Content-Type for the file
|
||||
if (filetype == "html")
|
||||
resp->setContentTypeCode(CT_TEXT_HTML);
|
||||
else if (filetype == "js")
|
||||
resp->setContentTypeCode(CT_APPLICATION_X_JAVASCRIPT);
|
||||
else if (filetype == "css")
|
||||
resp->setContentTypeCode(CT_TEXT_CSS);
|
||||
else if (filetype == "xml")
|
||||
resp->setContentTypeCode(CT_TEXT_XML);
|
||||
else if (filetype == "xsl")
|
||||
resp->setContentTypeCode(CT_TEXT_XSL);
|
||||
else if (filetype == "txt")
|
||||
resp->setContentTypeCode(CT_TEXT_PLAIN);
|
||||
else if (filetype == "svg")
|
||||
resp->setContentTypeCode(CT_IMAGE_SVG_XML);
|
||||
else if (filetype == "ttf")
|
||||
resp->setContentTypeCode(CT_APPLICATION_X_FONT_TRUETYPE);
|
||||
else if (filetype == "otf")
|
||||
resp->setContentTypeCode(CT_APPLICATION_X_FONT_OPENTYPE);
|
||||
else if (filetype == "woff2")
|
||||
resp->setContentTypeCode(CT_APPLICATION_FONT_WOFF2);
|
||||
else if (filetype == "woff")
|
||||
resp->setContentTypeCode(CT_APPLICATION_FONT_WOFF);
|
||||
else if (filetype == "eot")
|
||||
resp->setContentTypeCode(CT_APPLICATION_VND_MS_FONTOBJ);
|
||||
else if (filetype == "png")
|
||||
resp->setContentTypeCode(CT_IMAGE_PNG);
|
||||
else if (filetype == "jpg")
|
||||
resp->setContentTypeCode(CT_IMAGE_JPG);
|
||||
else if (filetype == "jpeg")
|
||||
resp->setContentTypeCode(CT_IMAGE_JPG);
|
||||
else if (filetype == "gif")
|
||||
resp->setContentTypeCode(CT_IMAGE_GIF);
|
||||
else if (filetype == "bmp")
|
||||
resp->setContentTypeCode(CT_IMAGE_BMP);
|
||||
else if (filetype == "ico")
|
||||
resp->setContentTypeCode(CT_IMAGE_XICON);
|
||||
else if (filetype == "icns")
|
||||
resp->setContentTypeCode(CT_IMAGE_ICNS);
|
||||
else
|
||||
resp->setContentTypeCode(CT_APPLICATION_OCTET_STREAM);
|
||||
|
||||
readSendFile(filePath, req, resp);
|
||||
auto resp = HttpResponse::newFileResponse(filePath, "", false);
|
||||
if (!timeStr.empty())
|
||||
{
|
||||
resp->addHeader("Last-Modified", timeStr);
|
||||
resp->addHeader("Expires", "Thu, 01 Jan 1970 00:00:00 GMT");
|
||||
}
|
||||
//cache the response for 5 seconds by default
|
||||
if (_staticFilesCacheTime >= 0)
|
||||
{
|
||||
resp->setExpiredTime(_staticFilesCacheTime);
|
||||
_responseCachingMap->insert(filePath, resp, resp->expiredTime(), [=]() {
|
||||
std::lock_guard<std::mutex> guard(_staticFilesCacheMutex);
|
||||
_staticFilesCache.erase(filePath);
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_staticFilesCacheMutex);
|
||||
_staticFilesCache[filePath] = resp;
|
||||
}
|
||||
}
|
||||
|
||||
if (needSetJsessionid)
|
||||
{
|
||||
@ -756,55 +734,6 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, std::fu
|
||||
_httpSimpleCtrlsRouter.route(req, std::move(callback), needSetJsessionid, std::move(sessionId));
|
||||
}
|
||||
|
||||
void HttpAppFrameworkImpl::readSendFile(const std::string &filePath, const HttpRequestImplPtr &req, const HttpResponsePtr &resp)
|
||||
{
|
||||
std::ifstream infile(filePath, std::ifstream::binary);
|
||||
LOG_TRACE << "send http file:" << filePath;
|
||||
if (!infile)
|
||||
{
|
||||
|
||||
resp->setStatusCode(k404NotFound);
|
||||
resp->setCloseConnection(true);
|
||||
return;
|
||||
}
|
||||
|
||||
std::streambuf *pbuf = infile.rdbuf();
|
||||
std::streamsize filesize = pbuf->pubseekoff(0, infile.end);
|
||||
pbuf->pubseekoff(0, infile.beg); // rewind
|
||||
|
||||
if (_useSendfile &&
|
||||
filesize > 1024 * 200)
|
||||
//TODO : Is 200k an appropriate value? Or set it to be configurable
|
||||
{
|
||||
//The advantages of sendfile() can only be reflected in sending large files.
|
||||
std::dynamic_pointer_cast<HttpResponseImpl>(resp)->setSendfile(filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string str;
|
||||
str.resize(filesize);
|
||||
pbuf->sgetn(&str[0], filesize);
|
||||
resp->setBody(std::move(str));
|
||||
}
|
||||
|
||||
resp->setStatusCode(k200OK);
|
||||
|
||||
//cache the response for 5 seconds by default
|
||||
|
||||
if (_staticFilesCacheTime >= 0)
|
||||
{
|
||||
resp->setExpiredTime(_staticFilesCacheTime);
|
||||
_responseCachingMap->insert(filePath, resp, resp->expiredTime(), [=]() {
|
||||
std::lock_guard<std::mutex> guard(_staticFilesCacheMutex);
|
||||
_staticFilesCache.erase(filePath);
|
||||
});
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_staticFilesCacheMutex);
|
||||
_staticFilesCache[filePath] = resp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trantor::EventLoop *HttpAppFrameworkImpl::loop()
|
||||
{
|
||||
static trantor::EventLoop loop;
|
||||
@ -813,8 +742,7 @@ trantor::EventLoop *HttpAppFrameworkImpl::loop()
|
||||
|
||||
HttpAppFramework &HttpAppFramework::instance()
|
||||
{
|
||||
static HttpAppFrameworkImpl _instance;
|
||||
return _instance;
|
||||
return HttpAppFrameworkImpl::instance();
|
||||
}
|
||||
|
||||
HttpAppFramework::~HttpAppFramework()
|
||||
|
@ -119,6 +119,13 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
const std::string &name = "default") override;
|
||||
#endif
|
||||
|
||||
inline static HttpAppFrameworkImpl &instance()
|
||||
{
|
||||
static HttpAppFrameworkImpl _instance;
|
||||
return _instance;
|
||||
}
|
||||
bool useSendfile() { return _useSendfile; }
|
||||
|
||||
private:
|
||||
virtual void registerHttpController(const std::string &pathPattern,
|
||||
const internal::HttpBinderBasePtr &binder,
|
||||
@ -131,7 +138,6 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
||||
void onWebsockMessage(const WebSocketConnectionPtr &wsConnPtr, trantor::MsgBuffer *buffer);
|
||||
void onWebsockDisconnect(const WebSocketConnectionPtr &wsConnPtr);
|
||||
void onConnection(const TcpConnectionPtr &conn);
|
||||
void readSendFile(const std::string &filePath, const HttpRequestImplPtr &req, const HttpResponsePtr &resp);
|
||||
void addHttpPath(const std::string &path,
|
||||
const internal::HttpBinderBasePtr &binder,
|
||||
const std::vector<HttpMethod> &validMethods,
|
||||
|
@ -95,7 +95,15 @@ HttpClientImpl::~HttpClientImpl()
|
||||
void HttpClientImpl::sendRequest(const drogon::HttpRequestPtr &req, const drogon::HttpReqCallback &callback)
|
||||
{
|
||||
auto thisPtr = shared_from_this();
|
||||
_loop->runInLoop([thisPtr,req,callback]() {
|
||||
_loop->runInLoop([thisPtr, req, callback]() {
|
||||
thisPtr->sendRequestInLoop(req, callback);
|
||||
});
|
||||
}
|
||||
|
||||
void HttpClientImpl::sendRequest(const drogon::HttpRequestPtr &req, drogon::HttpReqCallback &&callback)
|
||||
{
|
||||
auto thisPtr = shared_from_this();
|
||||
_loop->runInLoop([thisPtr, req, callback = std::move(callback)]() {
|
||||
thisPtr->sendRequestInLoop(req, callback);
|
||||
});
|
||||
}
|
||||
@ -139,10 +147,13 @@ void HttpClientImpl::sendRequestInLoop(const drogon::HttpRequestPtr &req,
|
||||
_tcpClient->enableSSL();
|
||||
}
|
||||
#endif
|
||||
auto thisPtr = shared_from_this();
|
||||
std::weak_ptr<HttpClientImpl> weakPtr = shared_from_this();
|
||||
assert(_reqAndCallbacks.empty());
|
||||
_reqAndCallbacks.push(std::make_pair(req, callback));
|
||||
_tcpClient->setConnectionCallback([=](const trantor::TcpConnectionPtr &connPtr) {
|
||||
_tcpClient->setConnectionCallback([weakPtr](const trantor::TcpConnectionPtr &connPtr) {
|
||||
auto thisPtr = weakPtr.lock();
|
||||
if (!thisPtr)
|
||||
return;
|
||||
if (connPtr->connected())
|
||||
{
|
||||
connPtr->setContext(HttpResponseParser(connPtr));
|
||||
@ -156,24 +167,33 @@ void HttpClientImpl::sendRequestInLoop(const drogon::HttpRequestPtr &req,
|
||||
LOG_TRACE << "connection disconnect";
|
||||
while (!(thisPtr->_reqAndCallbacks.empty()))
|
||||
{
|
||||
auto reqCallback = _reqAndCallbacks.front().second;
|
||||
_reqAndCallbacks.pop();
|
||||
auto reqCallback = thisPtr->_reqAndCallbacks.front().second;
|
||||
thisPtr->_reqAndCallbacks.pop();
|
||||
reqCallback(ReqResult::NetworkFailure, HttpResponse::newHttpResponse());
|
||||
}
|
||||
thisPtr->_tcpClient.reset();
|
||||
}
|
||||
});
|
||||
_tcpClient->setConnectionErrorCallback([=]() {
|
||||
_tcpClient->setConnectionErrorCallback([weakPtr]() {
|
||||
auto thisPtr = weakPtr.lock();
|
||||
if (!thisPtr)
|
||||
return;
|
||||
//can't connect to server
|
||||
while (!(thisPtr->_reqAndCallbacks.empty()))
|
||||
{
|
||||
auto reqCallback = _reqAndCallbacks.front().second;
|
||||
_reqAndCallbacks.pop();
|
||||
auto reqCallback = thisPtr->_reqAndCallbacks.front().second;
|
||||
thisPtr->_reqAndCallbacks.pop();
|
||||
reqCallback(ReqResult::BadServerAddress, HttpResponse::newHttpResponse());
|
||||
}
|
||||
thisPtr->_tcpClient.reset();
|
||||
});
|
||||
_tcpClient->setMessageCallback(std::bind(&HttpClientImpl::onRecvMessage, shared_from_this(), _1, _2));
|
||||
_tcpClient->setMessageCallback([weakPtr](const trantor::TcpConnectionPtr &connPtr, trantor::MsgBuffer *msg) {
|
||||
auto thisPtr = weakPtr.lock();
|
||||
if (thisPtr)
|
||||
{
|
||||
thisPtr->onRecvMessage(connPtr, msg);
|
||||
}
|
||||
});
|
||||
_tcpClient->connect();
|
||||
}
|
||||
else
|
||||
@ -194,7 +214,11 @@ void HttpClientImpl::sendRequestInLoop(const drogon::HttpRequestPtr &req,
|
||||
sendReq(connPtr, req);
|
||||
}
|
||||
}
|
||||
_reqAndCallbacks.push(std::make_pair(req, callback));
|
||||
auto thisPtr = shared_from_this();
|
||||
_reqAndCallbacks.push(std::make_pair(req, [thisPtr, callback](ReqResult result, const HttpResponsePtr &response) {
|
||||
//thisPtr.reset();
|
||||
callback(result, response);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ class HttpClientImpl : public HttpClient, public std::enable_shared_from_this<Ht
|
||||
HttpClientImpl(trantor::EventLoop *loop, const trantor::InetAddress &addr, bool useSSL = false);
|
||||
HttpClientImpl(trantor::EventLoop *loop, const std::string &hostString);
|
||||
virtual void sendRequest(const HttpRequestPtr &req, const HttpReqCallback &callback) override;
|
||||
virtual void sendRequest(const HttpRequestPtr &req, HttpReqCallback &&callback) override;
|
||||
~HttpClientImpl();
|
||||
|
||||
private:
|
||||
|
@ -12,15 +12,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "HttpAppFrameworkImpl.h"
|
||||
#include "HttpResponseImpl.h"
|
||||
|
||||
#include <drogon/HttpViewBase.h>
|
||||
#include <drogon/HttpViewData.h>
|
||||
#include <drogon/HttpAppFramework.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <memory>
|
||||
|
||||
using namespace trantor;
|
||||
using namespace drogon;
|
||||
@ -67,6 +68,114 @@ HttpResponsePtr HttpResponse::newHttpViewResponse(const std::string &viewName, c
|
||||
return HttpViewBase::genHttpResponse(viewName, data);
|
||||
}
|
||||
|
||||
HttpResponsePtr HttpResponse::newFileResponse(const std::string &fullPath, const std::string &fileNameForUser, bool asAttachment)
|
||||
{
|
||||
auto resp = std::make_shared<HttpResponseImpl>();
|
||||
std::ifstream infile(fullPath, std::ifstream::binary);
|
||||
LOG_TRACE << "send http file:" << fullPath;
|
||||
if (!infile)
|
||||
{
|
||||
resp->setStatusCode(k404NotFound);
|
||||
resp->setCloseConnection(true);
|
||||
return resp;
|
||||
}
|
||||
|
||||
std::streambuf *pbuf = infile.rdbuf();
|
||||
std::streamsize filesize = pbuf->pubseekoff(0, infile.end);
|
||||
pbuf->pubseekoff(0, infile.beg); // rewind
|
||||
|
||||
if (HttpAppFrameworkImpl::instance().useSendfile() &&
|
||||
filesize > 1024 * 200)
|
||||
//TODO : Is 200k an appropriate value? Or set it to be configurable
|
||||
{
|
||||
//The advantages of sendfile() can only be reflected in sending large files.
|
||||
std::dynamic_pointer_cast<HttpResponseImpl>(resp)->setSendfile(fullPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string str;
|
||||
str.resize(filesize);
|
||||
pbuf->sgetn(&str[0], filesize);
|
||||
resp->setBody(std::move(str));
|
||||
}
|
||||
|
||||
resp->setStatusCode(k200OK);
|
||||
std::string filename;
|
||||
std::string filetype;
|
||||
if (!fileNameForUser.empty())
|
||||
{
|
||||
filename = fileNameForUser;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto pos = fullPath.rfind("/");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
filename = fullPath.substr(pos + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = fullPath;
|
||||
}
|
||||
}
|
||||
auto pos = filename.rfind(".");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
filetype = filename.substr(pos + 1);
|
||||
transform(filetype.begin(), filetype.end(), filetype.begin(), tolower);
|
||||
}
|
||||
bool attachment = (asAttachment || (!fileNameForUser.empty()));
|
||||
// pick a Content-Type for the file
|
||||
if (filetype == "html")
|
||||
resp->setContentTypeCode(CT_TEXT_HTML);
|
||||
else if (filetype == "js")
|
||||
resp->setContentTypeCode(CT_APPLICATION_X_JAVASCRIPT);
|
||||
else if (filetype == "css")
|
||||
resp->setContentTypeCode(CT_TEXT_CSS);
|
||||
else if (filetype == "xml")
|
||||
resp->setContentTypeCode(CT_TEXT_XML);
|
||||
else if (filetype == "xsl")
|
||||
resp->setContentTypeCode(CT_TEXT_XSL);
|
||||
else if (filetype == "txt")
|
||||
resp->setContentTypeCode(CT_TEXT_PLAIN);
|
||||
else if (filetype == "svg")
|
||||
resp->setContentTypeCode(CT_IMAGE_SVG_XML);
|
||||
else if (filetype == "ttf")
|
||||
resp->setContentTypeCode(CT_APPLICATION_X_FONT_TRUETYPE);
|
||||
else if (filetype == "otf")
|
||||
resp->setContentTypeCode(CT_APPLICATION_X_FONT_OPENTYPE);
|
||||
else if (filetype == "woff2")
|
||||
resp->setContentTypeCode(CT_APPLICATION_FONT_WOFF2);
|
||||
else if (filetype == "woff")
|
||||
resp->setContentTypeCode(CT_APPLICATION_FONT_WOFF);
|
||||
else if (filetype == "eot")
|
||||
resp->setContentTypeCode(CT_APPLICATION_VND_MS_FONTOBJ);
|
||||
else if (filetype == "png")
|
||||
resp->setContentTypeCode(CT_IMAGE_PNG);
|
||||
else if (filetype == "jpg")
|
||||
resp->setContentTypeCode(CT_IMAGE_JPG);
|
||||
else if (filetype == "jpeg")
|
||||
resp->setContentTypeCode(CT_IMAGE_JPG);
|
||||
else if (filetype == "gif")
|
||||
resp->setContentTypeCode(CT_IMAGE_GIF);
|
||||
else if (filetype == "bmp")
|
||||
resp->setContentTypeCode(CT_IMAGE_BMP);
|
||||
else if (filetype == "ico")
|
||||
resp->setContentTypeCode(CT_IMAGE_XICON);
|
||||
else if (filetype == "icns")
|
||||
resp->setContentTypeCode(CT_IMAGE_ICNS);
|
||||
else
|
||||
{
|
||||
resp->setContentTypeCode(CT_APPLICATION_OCTET_STREAM);
|
||||
attachment = true;
|
||||
}
|
||||
if (attachment)
|
||||
{
|
||||
resp->addHeader("Content-Disposition", "attachment; filename=" + filename);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
std::string HttpResponseImpl::web_response_code_to_string(int code)
|
||||
{
|
||||
switch (code)
|
||||
|
@ -82,14 +82,15 @@ void HttpServer::onConnection(const TcpConnectionPtr &conn)
|
||||
{
|
||||
LOG_TRACE << "conn disconnected!";
|
||||
HttpRequestParser *requestParser = any_cast<HttpRequestParser>(conn->getMutableContext());
|
||||
|
||||
// LOG_INFO << "###:" << string(buf->peek(), buf->readableBytes());
|
||||
if (requestParser)
|
||||
{
|
||||
if (requestParser->webSocketConn())
|
||||
{
|
||||
_disconnectWebsocketCallback(requestParser->webSocketConn());
|
||||
}
|
||||
conn->getMutableContext()->reset();
|
||||
}
|
||||
}
|
||||
_connectionCallback(conn);
|
||||
}
|
||||
|
||||
|
2
trantor
2
trantor
@ -1 +1 @@
|
||||
Subproject commit 1173464eb3eb521232c84a76dc98ac91fa34b9e5
|
||||
Subproject commit 1904e558588ac67b8422d2c8a83166dff9506716
|
Loading…
Reference in New Issue
Block a user