mirror of
https://gitee.com/an-tao/drogon.git
synced 2024-12-04 20:57:50 +08:00
Merge pull request #74 from an-tao/dev
Add configuration options that limit the number of keep-alive requests
This commit is contained in:
commit
322e7672d2
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
|
16
examples/simple_example/PipelineTest.cc
Normal file
16
examples/simple_example/PipelineTest.cc
Normal 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);
|
||||
});
|
||||
}
|
12
examples/simple_example/PipelineTest.h
Normal file
12
examples/simple_example/PipelineTest.h
Normal 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
|
||||
};
|
36
examples/simple_example_test/HttpPipelineTest.cc
Executable file
36
examples/simple_example_test/HttpPipelineTest.cc
Executable 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();
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user