Merge pull request #74 from an-tao/dev

Add configuration options that limit the number of keep-alive requests
This commit is contained in:
An Tao 2019-03-12 18:47:30 +08:00 committed by GitHub
commit 322e7672d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 437 additions and 320 deletions

View File

@ -1,143 +1,153 @@
/* This is a JSON format configuration file
*/
{
//ssl:The global ssl files setting
/*
"ssl": {
"cert": "../../trantor/trantor/tests/server.pem",
"key": "../../trantor/trantor/tests/server.pem"
},
"listeners": [
{
//address: Ip address,0.0.0.0 by default
"address": "0.0.0.0",
//port: Port number
"port": 80,
//https: If true, use https for security,false by default
"https": false
/*
//ssl:The global ssl files setting
"ssl": {
"cert": "../../trantor/trantor/tests/server.pem",
"key": "../../trantor/trantor/tests/server.pem"
},
{
"address": "0.0.0.0",
"port": 443,
"https": true,
//cert,key: Cert file path and key file path, empty by default,
//if empty, use global setting
"cert": "",
"key": ""
}
],
"db_clients": [
{
//name: Name of the client,'default' by default
//"name":"",
//rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
"rdbms": "postgresql",
//filename: Sqlite3 db file name
//"filename":"",
//host: Server address,localhost by default
"host": "127.0.0.1",
//port: Server port, 5432 by default
"port": 5432,
//dbname: Database name
"dbname": "test",
//user: 'postgres' by default
"user": "",
//passwd: '' by default
"passwd": "",
//connection_number: 1 by default
"connection_number": 1
}
],*/
"app": {
//threads_num: The number of IO threads,1 by default, if the value is set to 0, the number of threads
//is the number of processors.
"threads_num": 1,
//enable_session: False by default
"enable_session": true,
"session_timeout": 0,
//document_root: Root path of HTTP document,defaut path is ./
"document_root": "./",
//upload_path: The path to save the uploaded file. "uploads" by default.
//If the path isn't prefixed with /, ./ or ../,
//it is relative path of document_root path
"upload_path": "uploads",
/* file_types:
"listeners": [
{
//address: Ip address,0.0.0.0 by default
"address": "0.0.0.0",
//port: Port number
"port": 80,
//https: If true, use https for security,false by default
"https": false
},
{
"address": "0.0.0.0",
"port": 443,
"https": true,
//cert,key: Cert file path and key file path, empty by default,
//if empty, use global setting
"cert": "",
"key": ""
}
],
"db_clients": [
{
//name: Name of the client,'default' by default
//"name":"",
//rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
"rdbms": "postgresql",
//filename: Sqlite3 db file name
//"filename":"",
//host: Server address,localhost by default
"host": "127.0.0.1",
//port: Server port, 5432 by default
"port": 5432,
//dbname: Database name
"dbname": "test",
//user: 'postgres' by default
"user": "",
//passwd: '' by default
"passwd": "",
//connection_number: 1 by default
"connection_number": 1
}
],*/
"app": {
//threads_num: The number of IO threads,1 by default, if the value is set to 0, the number of threads
//is the number of processors.
"threads_num": 1,
//enable_session: False by default
"enable_session": true,
"session_timeout": 0,
//document_root: Root path of HTTP document,defaut path is ./
"document_root": "./",
//upload_path: The path to save the uploaded file. "uploads" by default.
//If the path isn't prefixed with /, ./ or ../,
//it is relative path of document_root path
"upload_path": "uploads",
/* file_types:
* HTTP download file types,The file types supported by drogon
* by default are "html", "js", "css", "xml", "xsl", "txt", "svg",
* "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg",
* "gif", "bmp", "ico", "icns", etc. */
"file_types": [
"gif",
"png",
"jpg",
"js",
"css",
"html",
"ico",
"swf",
"xap",
"apk",
"cur",
"xml"
],
//max_connections: Max connections number,100000 by default
"max_connections": 100000,
//max_connections_per_ip: Max connections number per clinet,0 by default which means no limit
"max_connections_per_ip": 0,
//Load_dynamic_views: False by default, when set to true, drogon
//compiles and loads dynamically "CSP View Files" in directories defined
//by "dynamic_views_path"
"load_dynamic_views":false,
//dynamic_views_path: If the path isn't prefixed with /, ./ or ../,
//it is relative path of document_root path
"dynamic_views_path":["./views"],
//log: Set log output, drogon output logs to stdout by default
"log": {
//log_path: Log file path,empty by default,in which case,logs are output to the stdout
//"log_path": "./",
//logfile_base_name: Log file base name,empty by default which means drogon names logfile as
//drogon.log ...
"logfile_base_name": "",
//log_size_limit: 100000000 bytes by default,
//When the log file size reaches "log_size_limit", the log file is switched.
"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"
},
//run_as_daemon: False by default
"run_as_daemon": false,
//relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
"relaunch_on_error": false,
//use_sendfile: True by default, if ture, the program
//uses sendfile() system-call to send static files to clients;
"use_sendfile": true,
//use_gzip: True by default, use gzip to compress the response body's content;
"use_gzip": true,
//static_files_cache_time: 5 (seconds) by default,the time in which static file response is cached,
//0 means cache forever, the negative value means no cache
"static_files_cache_time": 5,
//simple_controllers_map: Used to configure mapping from path to simple controller
"simple_controllers_map": [
{
"path": "/path/name",
"controller": "controllerClassName",
"http_methods": [
"get",
"post"
"file_types": [
"gif",
"png",
"jpg",
"js",
"css",
"html",
"ico",
"swf",
"xap",
"apk",
"cur",
"xml"
],
"filters": [
"FilterClassName"
]
}
],
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
//of the connection without read or write
"idle_connection_timeout": 60,
//enable_fast_db_client: Defaults to false
"enable_fast_db_client": false,
//server_header_field: Set the 'server' header field in each response sent by drogon,
//empty string by default with which the 'server' header field is set to "Server: drogon/version string\r\n"
"server_header_field": ""
}
}
//max_connections: Max connections number,100000 by default
"max_connections": 100000,
//max_connections_per_ip: Max connections number per clinet,0 by default which means no limit
"max_connections_per_ip": 0,
//Load_dynamic_views: False by default, when set to true, drogon
//compiles and loads dynamically "CSP View Files" in directories defined
//by "dynamic_views_path"
"load_dynamic_views": false,
//dynamic_views_path: If the path isn't prefixed with /, ./ or ../,
//it is relative path of document_root path
"dynamic_views_path": [
"./views"
],
//log: Set log output, drogon output logs to stdout by default
"log": {
//log_path: Log file path,empty by default,in which case,logs are output to the stdout
//"log_path": "./",
//logfile_base_name: Log file base name,empty by default which means drogon names logfile as
//drogon.log ...
"logfile_base_name": "",
//log_size_limit: 100000000 bytes by default,
//When the log file size reaches "log_size_limit", the log file is switched.
"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"
},
//run_as_daemon: False by default
"run_as_daemon": false,
//relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
"relaunch_on_error": false,
//use_sendfile: True by default, if ture, the program
//uses sendfile() system-call to send static files to clients;
"use_sendfile": true,
//use_gzip: True by default, use gzip to compress the response body's content;
"use_gzip": true,
//static_files_cache_time: 5 (seconds) by default,the time in which static file response is cached,
//0 means cache forever, the negative value means no cache
"static_files_cache_time": 5,
//simple_controllers_map: Used to configure mapping from path to simple controller
"simple_controllers_map": [
{
"path": "/path/name",
"controller": "controllerClassName",
"http_methods": [
"get",
"post"
],
"filters": [
"FilterClassName"
]
}
],
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
//of the connection without read or write
"idle_connection_timeout": 60,
//enable_fast_db_client: Defaults to false
"enable_fast_db_client": false,
//server_header_field: Set the 'server' header field in each response sent by drogon,
//empty string by default with which the 'server' header field is set to "Server: drogon/version string\r\n"
"server_header_field": "",
//keepalive_requests: Sets the maximum number of requests that can be served through one keep-alive connection.
//After the maximum number of requests are made, the connection is closed.
//The default value of 0 means no limit.
"keepalive_requests": 0,
//pipeline_requests: Sets the maximum number of unhandled requests that can be cached in pipeline buffer.
//After the maximum number of requests are made, the connection is closed.
//The default value of 0 means no limit.
"pipeline_requests": 0
}
}

View File

@ -1,143 +1,153 @@
/* This is a JSON format configuration file
*/
{
//ssl:The global ssl files setting
/*
"ssl": {
"cert": "../../trantor/trantor/tests/server.pem",
"key": "../../trantor/trantor/tests/server.pem"
},
"listeners": [
{
//address: Ip address,0.0.0.0 by default
"address": "0.0.0.0",
//port: Port number
"port": 80,
//https: If true, use https for security,false by default
"https": false
/*
//ssl:The global ssl files setting
"ssl": {
"cert": "../../trantor/trantor/tests/server.pem",
"key": "../../trantor/trantor/tests/server.pem"
},
{
"address": "0.0.0.0",
"port": 443,
"https": true,
//cert,key: Cert file path and key file path, empty by default,
//if empty, use global setting
"cert": "",
"key": ""
}
],
"db_clients": [
{
//name: Name of the client,'default' by default
//"name":"",
//rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
"rdbms": "postgresql",
//filename: Sqlite3 db file name
//"filename":"",
//host: Server address,localhost by default
"host": "127.0.0.1",
//port: Server port, 5432 by default
"port": 5432,
//dbname: Database name
"dbname": "test",
//user: 'postgres' by default
"user": "",
//passwd: '' by default
"passwd": "",
//connection_number: 1 by default
"connection_number": 1
}
],*/
"app": {
//threads_num: The number of IO threads,1 by default, if the value is set to 0, the number of threads
//is the number of processors.
"threads_num": 1,
//enable_session: False by default
"enable_session": true,
"session_timeout": 0,
//document_root: Root path of HTTP document,defaut path is ./
"document_root": "./",
//upload_path: The path to save the uploaded file. "uploads" by default.
//If the path isn't prefixed with /, ./ or ../,
//it is relative path of document_root path
"upload_path": "uploads",
/* file_types:
"listeners": [
{
//address: Ip address,0.0.0.0 by default
"address": "0.0.0.0",
//port: Port number
"port": 80,
//https: If true, use https for security,false by default
"https": false
},
{
"address": "0.0.0.0",
"port": 443,
"https": true,
//cert,key: Cert file path and key file path, empty by default,
//if empty, use global setting
"cert": "",
"key": ""
}
],
"db_clients": [
{
//name: Name of the client,'default' by default
//"name":"",
//rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
"rdbms": "postgresql",
//filename: Sqlite3 db file name
//"filename":"",
//host: Server address,localhost by default
"host": "127.0.0.1",
//port: Server port, 5432 by default
"port": 5432,
//dbname: Database name
"dbname": "test",
//user: 'postgres' by default
"user": "",
//passwd: '' by default
"passwd": "",
//connection_number: 1 by default
"connection_number": 1
}
],*/
"app": {
//threads_num: The number of IO threads,1 by default, if the value is set to 0, the number of threads
//is the number of processors.
"threads_num": 1,
//enable_session: False by default
"enable_session": true,
"session_timeout": 0,
//document_root: Root path of HTTP document,defaut path is ./
"document_root": "./",
//upload_path: The path to save the uploaded file. "uploads" by default.
//If the path isn't prefixed with /, ./ or ../,
//it is relative path of document_root path
"upload_path": "uploads",
/* file_types:
* HTTP download file types,The file types supported by drogon
* by default are "html", "js", "css", "xml", "xsl", "txt", "svg",
* "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg",
* "gif", "bmp", "ico", "icns", etc. */
"file_types": [
"gif",
"png",
"jpg",
"js",
"css",
"html",
"ico",
"swf",
"xap",
"apk",
"cur",
"xml"
],
//max_connections: Max connections number,100000 by default
"max_connections": 100000,
//max_connections_per_ip: Max connections number per clinet,0 by default which means no limit
"max_connections_per_ip": 0,
//Load_dynamic_views: False by default, when set to true, drogon
//compiles and loads dynamically "CSP View Files" in directories defined
//by "dynamic_views_path"
"load_dynamic_views":false,
//dynamic_views_path: If the path isn't prefixed with /, ./ or ../,
//it is relative path of document_root path
"dynamic_views_path":["./views"],
//log: Set log output, drogon output logs to stdout by default
"log": {
//log_path: Log file path,empty by default,in which case,logs are output to the stdout
//"log_path": "./",
//logfile_base_name: Log file base name,empty by default which means drogon names logfile as
//drogon.log ...
"logfile_base_name": "",
//log_size_limit: 100000000 bytes by default,
//When the log file size reaches "log_size_limit", the log file is switched.
"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"
},
//run_as_daemon: False by default
"run_as_daemon": false,
//relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
"relaunch_on_error": false,
//use_sendfile: True by default, if ture, the program
//uses sendfile() system-call to send static files to clients;
"use_sendfile": true,
//use_gzip: True by default, use gzip to compress the response body's content;
"use_gzip": true,
//static_files_cache_time: 5 (seconds) by default,the time in which static file response is cached,
//0 means cache forever, the negative value means no cache
"static_files_cache_time": 5,
//simple_controllers_map: Used to configure mapping from path to simple controller
"simple_controllers_map": [
{
"path": "/path/name",
"controller": "controllerClassName",
"http_methods": [
"get",
"post"
"file_types": [
"gif",
"png",
"jpg",
"js",
"css",
"html",
"ico",
"swf",
"xap",
"apk",
"cur",
"xml"
],
"filters": [
"FilterClassName"
]
}
],
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
//of the connection without read or write
"idle_connection_timeout": 60,
//enable_fast_db_client: Defaults to false
"enable_fast_db_client": false,
//server_header_field: Set the 'server' header field in each response sent by drogon,
//empty string by default with which the 'server' header field is set to "Server: drogon/version string\r\n"
"server_header_field": ""
}
}
//max_connections: Max connections number,100000 by default
"max_connections": 100000,
//max_connections_per_ip: Max connections number per clinet,0 by default which means no limit
"max_connections_per_ip": 0,
//Load_dynamic_views: False by default, when set to true, drogon
//compiles and loads dynamically "CSP View Files" in directories defined
//by "dynamic_views_path"
"load_dynamic_views": false,
//dynamic_views_path: If the path isn't prefixed with /, ./ or ../,
//it is relative path of document_root path
"dynamic_views_path": [
"./views"
],
//log: Set log output, drogon output logs to stdout by default
"log": {
//log_path: Log file path,empty by default,in which case,logs are output to the stdout
//"log_path": "./",
//logfile_base_name: Log file base name,empty by default which means drogon names logfile as
//drogon.log ...
"logfile_base_name": "",
//log_size_limit: 100000000 bytes by default,
//When the log file size reaches "log_size_limit", the log file is switched.
"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"
},
//run_as_daemon: False by default
"run_as_daemon": false,
//relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
"relaunch_on_error": false,
//use_sendfile: True by default, if ture, the program
//uses sendfile() system-call to send static files to clients;
"use_sendfile": true,
//use_gzip: True by default, use gzip to compress the response body's content;
"use_gzip": true,
//static_files_cache_time: 5 (seconds) by default,the time in which static file response is cached,
//0 means cache forever, the negative value means no cache
"static_files_cache_time": 5,
//simple_controllers_map: Used to configure mapping from path to simple controller
"simple_controllers_map": [
{
"path": "/path/name",
"controller": "controllerClassName",
"http_methods": [
"get",
"post"
],
"filters": [
"FilterClassName"
]
}
],
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
//of the connection without read or write
"idle_connection_timeout": 60,
//enable_fast_db_client: Defaults to false
"enable_fast_db_client": false,
//server_header_field: Set the 'server' header field in each response sent by drogon,
//empty string by default with which the 'server' header field is set to "Server: drogon/version string\r\n"
"server_header_field": "",
//keepalive_requests: Sets the maximum number of requests that can be served through one keep-alive connection.
//After the maximum number of requests are made, the connection is closed.
//The default value of 0 means no limit.
"keepalive_requests": 0,
//pipeline_requests: Sets the maximum number of unhandled requests that can be cached in pipeline buffer.
//After the maximum number of requests are made, the connection is closed.
//The default value of 0 means no limit.
"pipeline_requests": 0
}
}

View File

@ -21,11 +21,12 @@ add_dependencies(webapp drogon_ctl)
AUX_SOURCE_DIRECTORY(client_example DIR_CLIENT)
AUX_SOURCE_DIRECTORY(benchmark DIR_BENCHMARK)
AUX_SOURCE_DIRECTORY(simple_example_test DIR_TEST)
#AUX_SOURCE_DIRECTORY(simple_example_test DIR_TEST)
add_executable(client ${DIR_CLIENT})
add_executable(benchmark ${DIR_BENCHMARK})
add_executable(webapp_test ${DIR_TEST})
add_executable(webapp_test simple_example_test/main.cc)
add_executable(pipeline_test simple_example_test/HttpPipelineTest.cc)
add_custom_command(TARGET webapp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different

View File

@ -0,0 +1,16 @@
#include "PipelineTest.h"
#include <trantor/net/EventLoop.h>
#include <atomic>
void PipelineTest::asyncHandleHttpRequest(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback)
{
static std::atomic<int> counter = 0;
int c = counter.fetch_add(1);
double delay = ((double)(10 - (c % 10))) / 10.0;
trantor::EventLoop::getEventLoopOfCurrentThread()->runAfter(delay, [c, callback]() {
auto resp = HttpResponse::newHttpResponse();
auto str = formattedString("<P>the %dth response</P>", c);
resp->setBody(std::move(str));
callback(resp);
});
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <drogon/HttpSimpleController.h>
using namespace drogon;
class PipelineTest : public drogon::HttpSimpleController<PipelineTest>
{
public:
virtual void asyncHandleHttpRequest(const HttpRequestPtr &req, const std::function<void(const HttpResponsePtr &)> &callback) override;
PATH_LIST_BEGIN
//list path definitions here;
PATH_ADD("/pipe", Get);
PATH_LIST_END
};

View File

@ -0,0 +1,36 @@
#include <trantor/net/TcpClient.h>
#include <trantor/utils/Logger.h>
#include <trantor/net/EventLoopThread.h>
#include <string>
#include <iostream>
#include <atomic>
using namespace trantor;
int main()
{
trantor::Logger::setLogLevel(trantor::Logger::TRACE);
LOG_DEBUG << "TcpClient class test!";
EventLoop loop;
InetAddress serverAddr("127.0.0.1", 8848);
trantor::TcpClient client(&loop, serverAddr, "tcpclienttest");
client.setConnectionCallback([=, &loop](const TcpConnectionPtr &conn) {
if (conn->connected())
{
std::string str = "GET /pipe HTTP/1.1\r\n"
"\r\n";
for (int i = 0; i < 32; i++)
conn->send(str);
}
else
{
loop.quit();
}
});
client.setMessageCallback([=](const TcpConnectionPtr &conn,
MsgBuffer *buf) {
LOG_DEBUG << string_view(buf->peek(), buf->readableBytes());
buf->retrieveAll();
});
client.connect();
loop.loop();
}

View File

@ -28,7 +28,7 @@
using namespace drogon;
void outputGood(const HttpRequestPtr &req)
void outputGood(const HttpRequestPtr &req, bool isHttps)
{
static int i = 0;
// auto start = req->creationDate();
@ -42,10 +42,13 @@ void outputGood(const HttpRequestPtr &req)
std::lock_guard<std::mutex> lock(mtx);
i++;
std::cout << i << GREEN << '\t' << "Good" << '\t' << RED << req->methodString()
<< " " << req->path() << RESET << std::endl;
<< " " << req->path();
if (isHttps)
std::cout << "\t(https)";
std::cout << RESET << std::endl;
}
}
void doTest(const HttpClientPtr &client, std::promise<int> &pro)
void doTest(const HttpClientPtr &client, std::promise<int> &pro, bool isHttps = false)
{
/// Post json
Json::Value json;
@ -59,7 +62,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
auto ret = resp->getJsonObject();
if (ret && (*ret)["result"].asString() == "ok")
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -84,7 +87,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
//LOG_DEBUG << resp->getBody();
if (resp->getBody() == "<p>Hello, world!</p>")
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -108,7 +111,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
//LOG_DEBUG << resp->getBody();
if (resp->getBody() == "<p>Hello, world!</p>")
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -132,7 +135,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
//LOG_DEBUG << resp->getBody();
if (resp->statusCode() == k405MethodNotAllowed)
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -155,7 +158,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
{
if (resp->getBody() == "<p>Hello, world!</p>")
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -179,7 +182,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
{
if (resp->getBody() == "ROOT Post!!!")
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -203,7 +206,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
{
if (resp->getBody() == "ROOT Get!!!")
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -228,7 +231,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
if (resp->getBody().find("<td>p1</td>\n <td>123</td>") != std::string::npos &&
resp->getBody().find("<td>p2</td>\n <td>abc</td>") != std::string::npos)
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -253,7 +256,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
if (resp->getBody().find("<td>p1</td>\n <td>3.140000</td>") != std::string::npos &&
resp->getBody().find("<td>p2</td>\n <td>1234</td>") != std::string::npos)
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -277,7 +280,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
{
if (resp->getBody() == "staticApi,hello!!")
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -303,7 +306,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
{
if (resp->getBody() == "staticApi,hello!!")
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -327,7 +330,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
{
if (resp->getBody().length() == 5123)
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -353,7 +356,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
{
if (resp->getBody().length() == 5123)
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -381,7 +384,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
resp->getBody().find("<td>string p2</td>\n <td>22</td>") != std::string::npos &&
resp->getBody().find("<td>string p3</td>\n <td>33</td>") != std::string::npos)
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -407,7 +410,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
if (resp->getBody().find("<td>a</td>\n <td>111</td>") != std::string::npos &&
resp->getBody().find("<td>b</td>\n <td>222.000000</td>") != std::string::npos)
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -435,7 +438,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
resp->getBody().find("<td>string p3</td>\n <td>333</td>") != std::string::npos &&
resp->getBody().find("<td>string p2</td>\n <td></td>") != std::string::npos)
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -460,7 +463,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
{
if (resp->getBody().length() == 52594)
{
outputGood(req);
outputGood(req, isHttps);
auto lastModified = resp->getHeader("last-modified");
//LOG_DEBUG << lastModified;
// Test 'Not Modified'
@ -473,7 +476,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
{
if (resp->statusCode() == k304NotModified)
{
outputGood(req);
outputGood(req, isHttps);
pro.set_value(1);
}
else
@ -485,6 +488,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
}
else
{
LOG_ERROR << "Error!";
exit(1);
}
@ -513,7 +517,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
{
if (resp->statusCode() == k403Forbidden)
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -540,7 +544,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
auto ret = resp->getJsonObject();
if (ret && (*ret)["result"].asString() == "ok")
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -565,7 +569,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
{
if (resp->getBody().length() == 52594)
{
outputGood(req);
outputGood(req, isHttps);
}
else
{
@ -594,7 +598,7 @@ void doTest(const HttpClientPtr &client, std::promise<int> &pro)
auto json = resp->getJsonObject();
if (json && (*json)["result"].asString() == "ok" && (*json)["P1"] == "upload" && (*json)["P2"] == "test")
{
outputGood(req);
outputGood(req, isHttps);
//std::cout << (*json) << std::endl;
}
else
@ -630,7 +634,7 @@ int main(int argc, char *argv[])
#ifdef USE_OPENSSL
std::promise<int> pro2;
auto sslClient = HttpClient::newHttpClient("https://127.0.0.1:8849", loop[1].getLoop());
doTest(sslClient, pro2);
doTest(sslClient, pro2, true);
auto f2 = pro2.get_future();
f2.get();
#endif

View File

@ -184,6 +184,8 @@ class HttpAppFramework : public trantor::NonCopyable
virtual int staticFilesCacheTime() const = 0;
virtual void setIdleConnectionTimeout(size_t timeout) = 0;
virtual void setServerHeaderField(const std::string &server) = 0;
virtual void setKeepaliveRequestsNumber(const size_t number) = 0;
virtual void setPipelineRequestsNumber(const size_t number) = 0;
#if USE_ORM
virtual orm::DbClientPtr getDbClient(const std::string &name = "default") = 0;
virtual void enableFastDbClient() = 0;

View File

@ -237,6 +237,10 @@ static void loadApp(const Json::Value &app)
auto server = app.get("server_header_field", "").asString();
if(!server.empty())
drogon::app().setServerHeaderField(server);
auto keepaliveReqs = app.get("keepalive_requests", 0).asUInt64();
drogon::app().setKeepaliveRequestsNumber(keepaliveReqs);
auto pipelineReqs = app.get("pipeline_requests", 0).asUInt64();
drogon::app().setPipelineRequestsNumber(pipelineReqs);
}
static void loadDbClients(const Json::Value &dbClients)
{

View File

@ -90,6 +90,11 @@ class HttpAppFrameworkImpl : public HttpAppFramework
virtual void setStaticFilesCacheTime(int cacheTime) override { _staticFilesCacheTime = cacheTime; }
virtual int staticFilesCacheTime() const override { return _staticFilesCacheTime; }
virtual void setIdleConnectionTimeout(size_t timeout) override { _idleConnectionTimeout = timeout; }
virtual void setKeepaliveRequestsNumber(const size_t number) override { _keepaliveRequestsNumber = number; }
virtual void setPipelineRequestsNumber(const size_t number) override { _pipelineRequestsNumber = number; }
size_t keepaliveRequestsNumber() const { return _keepaliveRequestsNumber; }
size_t pipelineRequestsNumber() const { return _pipelineRequestsNumber; }
virtual ~HttpAppFrameworkImpl() noexcept
{
//Destroy the following objects before _loop destruction
@ -203,6 +208,8 @@ class HttpAppFrameworkImpl : public HttpAppFramework
std::string _logPath = "";
std::string _logfileBaseName = "";
size_t _logfileSize = 100000000;
size_t _keepaliveRequestsNumber = 0;
size_t _pipelineRequestsNumber = 0;
bool _useSendfile = true;
bool _useGzip = true;
bool _enableFastDbClient = false;

View File

@ -158,6 +158,7 @@ bool HttpRequestParser::parseRequest(MsgBuffer *buf)
else
{
_state = kGotAll;
_requestsCounter++;
hasMore = false;
}
}
@ -175,6 +176,7 @@ bool HttpRequestParser::parseRequest(MsgBuffer *buf)
if (_request->_contentLen == 0)
{
_state = kGotAll;
_requestsCounter++;
}
break;
}
@ -193,11 +195,11 @@ bool HttpRequestParser::parseRequest(MsgBuffer *buf)
if (_request->_contentLen == 0)
{
_state = kGotAll;
_requestsCounter++;
hasMore = false;
}
}
}
return ok;
}

View File

@ -18,7 +18,7 @@
#include <trantor/utils/MsgBuffer.h>
#include <drogon/WebSocketConnection.h>
#include <drogon/HttpResponse.h>
#include <list>
#include <deque>
#include <mutex>
#include <trantor/net/TcpConnection.h>
@ -86,22 +86,22 @@ class HttpRequestParser
HttpResponsePtr getFirstResponse() const;
void popFirstRequest();
void pushResponseToPipeLine(const HttpRequestPtr &req, const HttpResponsePtr &resp);
size_t numberOfRequestsInPipeLine() const { return _requestPipeLine.size(); }
bool isStop() const { return _stopWorking; }
void stop() { _stopWorking = true; }
size_t numberOfRequestsParsed() const { return _requestsCounter; }
private:
bool processRequestLine(const char *begin, const char *end);
HttpRequestParseState _state;
trantor::EventLoop *_loop;
HttpRequestImplPtr _request;
bool _firstRequest = true;
WebSocketConnectionPtr _websockConnPtr;
std::list<std::pair<HttpRequestPtr, HttpResponsePtr>> _requestPipeLine;
//std::shared_ptr<std::mutex> _pipeLineMutex;
std::deque<std::pair<HttpRequestPtr, HttpResponsePtr>> _requestPipeLine;
size_t _requestsCounter = 0;
std::weak_ptr<trantor::TcpConnection> _conn;
bool _stopWorking = false;
};
} // namespace drogon

View File

@ -17,6 +17,7 @@
#include <trantor/utils/Logger.h>
#include "HttpRequestParser.h"
#include "HttpResponseImpl.h"
#include "HttpAppFrameworkImpl.h"
#include <drogon/HttpRequest.h>
#include <drogon/HttpResponse.h>
#include <drogon/utils/Utilities.h>
@ -219,6 +220,12 @@ void HttpServer::onMessage(const TcpConnectionPtr &conn,
}
return;
}
if (requestParser->isStop())
{
//The number of requests has reached the limit.
buf->retrieveAll();
return;
}
if (!requestParser->parseRequest(buf))
{
conn->send("HTTP/1.1 400 Bad Request\r\n\r\n");
@ -272,23 +279,22 @@ void HttpServer::onRequest(const TcpConnectionPtr &conn, const HttpRequestImplPt
}
HttpRequestParser *requestParser = any_cast<HttpRequestParser>(conn->getMutableContext());
requestParser->pushRquestToPipeLine(req);
if (HttpAppFrameworkImpl::instance().keepaliveRequestsNumber() > 0 &&
requestParser->numberOfRequestsParsed() >= HttpAppFrameworkImpl::instance().keepaliveRequestsNumber())
{
requestParser->stop();
}
if (HttpAppFrameworkImpl::instance().pipelineRequestsNumber() > 0 &&
requestParser->numberOfRequestsInPipeLine() >= HttpAppFrameworkImpl::instance().pipelineRequestsNumber())
{
requestParser->stop();
}
_httpAsyncCallback(req, [=](const HttpResponsePtr &response) {
if (!response)
return;
response->setCloseConnection(_close);
//if the request method is HEAD,remove the body of response(rfc2616-9.4)
//
// if (isHeadMethod)
// {
// if (newResp->expiredTime() >= 0)
// {
// //Cached response,make a copy
// newResp = std::make_shared<HttpResponseImpl>(*std::dynamic_pointer_cast<HttpResponseImpl>(response));
// newResp->setExpiredTime(-1);
// }
// newResp->setBody(std::string());
// }
auto newResp = response;
auto &sendfileName = std::dynamic_pointer_cast<HttpResponseImpl>(newResp)->sendfileName();
@ -351,7 +357,11 @@ void HttpServer::onRequest(const TcpConnectionPtr &conn, const HttpRequestImplPt
sendResponse(conn, resp, isHeadMethod);
}
else
return;
break;
}
if (requestParser->isStop() && requestParser->numberOfRequestsInPipeLine() == 0)
{
conn->shutdown();
}
}
else
@ -379,7 +389,11 @@ void HttpServer::onRequest(const TcpConnectionPtr &conn, const HttpRequestImplPt
sendResponse(conn, resp, isHeadMethod);
}
else
return;
break;
}
if (requestParser->isStop() && requestParser->numberOfRequestsInPipeLine() == 0)
{
conn->shutdown();
}
}
else
@ -420,4 +434,3 @@ void HttpServer::sendResponse(const TcpConnectionPtr &conn,
conn->shutdown();
}
}