Add two configuration options: the client_max_body_size and the client_max_websocket_message_size

This commit is contained in:
antao 2019-05-07 14:04:37 +08:00
parent 0f6d2e31ac
commit a85c64ac69
8 changed files with 153 additions and 12 deletions

View File

@ -21,7 +21,7 @@
"port": 443,
"https": true,
//cert,key: Cert file path and key file path, empty by default,
//if empty, use global setting
//if empty, use the global setting
"cert": "",
"key": ""
}
@ -151,7 +151,13 @@
//gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
//file with the extension ".gz" in the same path and send the compressed file to the client.
//The default value of gzip_static is true.
"gzip_static": true
"gzip_static": true,
//client_max_body_size: Set the max body size of HTTP requests received by drogon. The default value is "1M".
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
"client_max_body_size": "1M",
//client_max_websocket_message_size: Set the max size of messages sent by WebSocket client. The default value is "128K".
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
"client_max_websocket_message_size": "128K"
},
//plugins: Define all plugins running in the application
"plugins": [{

View File

@ -21,7 +21,7 @@
"port": 443,
"https": true,
//cert,key: Cert file path and key file path, empty by default,
//if empty, use global setting
//if empty, use the global setting
"cert": "",
"key": ""
}
@ -151,7 +151,13 @@
//gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
//file with the extension ".gz" in the same path and send the compressed file to the client.
//The default value of gzip_static is true.
"gzip_static": true
"gzip_static": true,
//client_max_body_size: Set the max body size of HTTP requests received by drogon. The default value is "1M".
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
"client_max_body_size": "1M",
//client_max_websocket_message_size: Set the max size of messages sent by WebSocket client. The default value is "128K".
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
"client_max_websocket_message_size": "128K"
},
//plugins: Define all plugins running in the application
"plugins": [{

View File

@ -3,7 +3,7 @@
GIT_VER=$(git log|grep ^commit|wc -l|sed -e "s/^ *//")
MD5=$(git log|head -1|awk '{printf $2}')
TMP_FILE=/tmp/version
echo "#define VERSION \"0.9.33.$GIT_VER\"" > ${TMP_FILE}
echo "#define VERSION \"0.9.34.$GIT_VER\"" > ${TMP_FILE}
echo "#define VERSION_MD5 \"$MD5\"" >> ${TMP_FILE}
if [ ! -f $1 ];then
mv -f ${TMP_FILE} $1

View File

@ -652,6 +652,20 @@ public:
*/
virtual void setGzipStatic(bool useGzipStatic) = 0;
///Set the max body size of the requests received by drogon. The default value is 1M.
/**
* NOTE:
* This operation can be performed by an option in the configuration file.
*/
virtual void setClientMaxBodySize(size_t maxSize) = 0;
///Set the max size of messages sent by WebSocket client. The default value is 128K.
/**
* NOTE:
* This operation can be performed by an option in the configuration file.
*/
virtual void setClientMaxWebSocketMessageSize(size_t maxSize) = 0;
#if USE_ORM
///Get a database client by @param name
/**

View File

@ -20,9 +20,76 @@
#include <fstream>
#include <unistd.h>
#include <thread>
#include <sstream>
using namespace drogon;
static bool bytesSize(std::string &sizeStr, size_t &size)
{
if (sizeStr.empty())
{
size = -1;
return true;
}
else
{
size = 1;
switch (sizeStr[sizeStr.length() - 1])
{
case 'k':
case 'K':
size = 1024;
sizeStr.resize(sizeStr.length() - 1);
break;
case 'M':
case 'm':
size = (1024 * 1024);
sizeStr.resize(sizeStr.length() - 1);
break;
case 'g':
case 'G':
size = (1024 * 1024 * 1024);
sizeStr.resize(sizeStr.length() - 1);
break;
#if ((ULONG_MAX) != (UINT_MAX))
//64bit system
case 't':
case 'T':
size = (1024 * 1024 * 1024 * 1024);
sizeStr.resize(sizeStr.length() - 1);
break;
#endif
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '7':
case '8':
case '9':
break;
default:
std::cerr << "Invalid value of client_max_body_size: " << sizeStr << std::endl;
return false;
break;
}
std::istringstream iss(sizeStr);
size_t tmpSize;
iss >> tmpSize;
if (iss.fail())
{
std::cerr << "Invalid value of client_max_body_size: " << sizeStr << std::endl;
exit(-1);
}
if ((size_t(-1) / tmpSize) >= size)
size *= tmpSize;
else
{
size = -1;
}
return true;
}
}
ConfigLoader::ConfigLoader(const std::string &configFile)
{
if (access(configFile.c_str(), 0) != 0)
@ -237,6 +304,25 @@ static void loadApp(const Json::Value &app)
drogon::app().setPipeliningRequestsNumber(pipeliningReqs);
auto useGzipStatic = app.get("gzip_static", true).asBool();
drogon::app().setGzipStatic(useGzipStatic);
auto maxBodySize = app.get("client_max_body_size", "1M").asString();
size_t size;
if (bytesSize(maxBodySize, size))
{
drogon::app().setClientMaxBodySize(size);
}
else
{
exit(-1);
}
auto maxWsMsgSize = app.get("client_max_websocket_message_size", "128K").asString();
if (bytesSize(maxWsMsgSize, size))
{
drogon::app().setClientMaxWebSocketMessageSize(size);
}
else
{
exit(-1);
}
}
static void loadDbClients(const Json::Value &dbClients)
{

View File

@ -171,6 +171,10 @@ public:
virtual void setKeepaliveRequestsNumber(const size_t number) override { _keepaliveRequestsNumber = number; }
virtual void setPipeliningRequestsNumber(const size_t number) override { _pipeliningRequestsNumber = number; }
virtual void setGzipStatic(bool useGzipStatic) override { _gzipStaticFlag = useGzipStatic; }
virtual void setClientMaxBodySize(size_t maxSize) override { _clientMaxBodySize = maxSize; }
virtual void setClientMaxWebSocketMessageSize(size_t maxSize) override { _clientMaxWebSocketMessageSize = maxSize; }
size_t getClientMaxBodySize() { return _clientMaxBodySize; }
size_t getClientMaxWebSocketMessageSize() { return _clientMaxWebSocketMessageSize; }
virtual std::vector<std::tuple<std::string, HttpMethod, std::string>> getHandlersInfo() const override;
size_t keepaliveRequestsNumber() const { return _keepaliveRequestsNumber; }
@ -295,6 +299,8 @@ private:
bool _useSendfile = true;
bool _useGzip = true;
bool _gzipStaticFlag = true;
size_t _clientMaxBodySize = 1024 * 1024;
size_t _clientMaxWebSocketMessageSize = 128 * 1024;
int _staticFilesCacheTime = 5;
std::unordered_map<std::string, std::weak_ptr<HttpResponse>> _staticFilesCache;
std::mutex _staticFilesCacheMutex;

View File

@ -15,6 +15,7 @@
#include <drogon/HttpTypes.h>
#include <trantor/utils/MsgBuffer.h>
#include <trantor/utils/Logger.h>
#include "HttpAppFrameworkImpl.h"
#include "HttpRequestParser.h"
#include "HttpResponseImpl.h"
#include "HttpUtils.h"
@ -177,14 +178,23 @@ bool HttpRequestParser::parseRequest(MsgBuffer *buf)
return false;
}
//rfc2616-8.2.3
//TODO: here we can add content-length limitation
auto connPtr = _conn.lock();
if (connPtr)
{
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k100Continue);
auto httpString = std::dynamic_pointer_cast<HttpResponseImpl>(resp)->renderToString();
connPtr->send(httpString);
if (_request->_contentLen > HttpAppFrameworkImpl::instance().getClientMaxBodySize())
{
resp->setStatusCode(k413RequestEntityTooLarge);
auto httpString = std::dynamic_pointer_cast<HttpResponseImpl>(resp)->renderToString();
reset();
connPtr->send(httpString);
}
else
{
resp->setStatusCode(k100Continue);
auto httpString = std::dynamic_pointer_cast<HttpResponseImpl>(resp)->renderToString();
connPtr->send(httpString);
}
}
}
else if (!expect.empty())
@ -198,6 +208,12 @@ bool HttpRequestParser::parseRequest(MsgBuffer *buf)
return false;
}
}
else if (_request->_contentLen > HttpAppFrameworkImpl::instance().getClientMaxBodySize())
{
buf->retrieveAll();
shutdownConnection(k413RequestEntityTooLarge);
return false;
}
}
else
{
@ -314,7 +330,7 @@ void HttpRequestParser::popFirstRequest()
}
void HttpRequestParser::pushResponseToPipelining(const HttpRequestPtr &req,
const HttpResponsePtr &resp)
const HttpResponsePtr &resp)
{
#ifndef NDEBUG
auto conn = _conn.lock();

View File

@ -12,6 +12,7 @@
*
*/
#include "HttpAppFrameworkImpl.h"
#include "WebSocketConnectionImpl.h"
#include <trantor/net/TcpConnection.h>
#include <trantor/net/inner/TcpConnectionImpl.h>
@ -175,7 +176,6 @@ void WebSocketConnectionImpl::setPingMessage(const std::string &message, const s
});
}
bool WebSocketMessageParser::parse(trantor::MsgBuffer *buffer)
{
//According to the rfc6455
@ -271,6 +271,13 @@ bool WebSocketMessageParser::parse(trantor::MsgBuffer *buffer)
}
if (isMasked != 0)
{
//The message is sent by the client, check the length
if (length > HttpAppFrameworkImpl::instance().getClientMaxWebSocketMessageSize())
{
LOG_ERROR << "The size of the WebSocket message is too large!";
buffer->retrieveAll();
return false;
}
if (buffer->readableBytes() >= (indexFirstMask + 4 + length))
{
auto masks = buffer->peek() + indexFirstMask;