mirror of
https://github.com/jellyfin/jellyfin-media-player.git
synced 2024-11-27 00:00:38 +00:00
WIP: qhttpserver->qhttp
This commit is contained in:
parent
7e4212d1fc
commit
4638151bfd
@ -26,7 +26,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules/")
|
||||
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Core)
|
||||
|
||||
add_definitions(-DQS_LOG_LINE_NUMBERS -DQHTTPSERVER_EXPORT)
|
||||
add_definitions(-DQS_LOG_LINE_NUMBERS)
|
||||
|
||||
include(DependencyConfiguration)
|
||||
include(QtConfiguration)
|
||||
|
4
external/CMakeLists.txt
vendored
4
external/CMakeLists.txt
vendored
@ -1,7 +1,7 @@
|
||||
add_subdirectory(qhttpserver)
|
||||
add_subdirectory(qslog)
|
||||
add_subdirectory(qhttp)
|
||||
|
||||
if(APPLE)
|
||||
add_subdirectory(plistparser)
|
||||
add_subdirectory(SPMediaKeyTap)
|
||||
endif(APPLE)
|
||||
endif(APPLE)
|
||||
|
1
external/qhttpserver/http-parser/AUTHORS → external/qhttp/3rdparty/http-parser/AUTHORS
vendored
Executable file → Normal file
1
external/qhttpserver/http-parser/AUTHORS → external/qhttp/3rdparty/http-parser/AUTHORS
vendored
Executable file → Normal file
@ -65,3 +65,4 @@ Romain Giraud <giraud.romain@gmail.com>
|
||||
Jay Satiro <raysatiro@yahoo.com>
|
||||
Arne Steen <Arne.Steen@gmx.de>
|
||||
Kjell Schubert <kjell.schubert@gmail.com>
|
||||
Olivier Mengué <dolmen@cpan.org>
|
0
external/qhttpserver/http-parser/LICENSE-MIT → external/qhttp/3rdparty/http-parser/LICENSE-MIT
vendored
Executable file → Normal file
0
external/qhttpserver/http-parser/LICENSE-MIT → external/qhttp/3rdparty/http-parser/LICENSE-MIT
vendored
Executable file → Normal file
71
external/qhttpserver/http-parser/README.md → external/qhttp/3rdparty/http-parser/README.md
vendored
Executable file → Normal file
71
external/qhttpserver/http-parser/README.md → external/qhttp/3rdparty/http-parser/README.md
vendored
Executable file → Normal file
@ -1,7 +1,7 @@
|
||||
HTTP Parser
|
||||
===========
|
||||
|
||||
[![Build Status](https://travis-ci.org/joyent/http-parser.png?branch=master)](https://travis-ci.org/joyent/http-parser)
|
||||
[![Build Status](https://api.travis-ci.org/nodejs/http-parser.svg?branch=master)](https://travis-ci.org/nodejs/http-parser)
|
||||
|
||||
This is a parser for HTTP messages written in C. It parses both requests and
|
||||
responses. The parser is designed to be used in performance HTTP
|
||||
@ -94,7 +94,7 @@ The Special Problem of Upgrade
|
||||
------------------------------
|
||||
|
||||
HTTP supports upgrading the connection to a different protocol. An
|
||||
increasingly common example of this is the Web Socket protocol which sends
|
||||
increasingly common example of this is the WebSocket protocol which sends
|
||||
a request like
|
||||
|
||||
GET /demo HTTP/1.1
|
||||
@ -106,8 +106,8 @@ a request like
|
||||
|
||||
followed by non-HTTP data.
|
||||
|
||||
(See http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more
|
||||
information the Web Socket protocol.)
|
||||
(See [RFC6455](https://tools.ietf.org/html/rfc6455) for more information the
|
||||
WebSocket protocol.)
|
||||
|
||||
To support this, the parser will treat this as a normal HTTP message without a
|
||||
body, issuing both on_headers_complete and on_message_complete callbacks. However
|
||||
@ -137,6 +137,69 @@ There are two types of callbacks:
|
||||
Callbacks must return 0 on success. Returning a non-zero value indicates
|
||||
error to the parser, making it exit immediately.
|
||||
|
||||
For cases where it is necessary to pass local information to/from a callback,
|
||||
the `http_parser` object's `data` field can be used.
|
||||
An example of such a case is when using threads to handle a socket connection,
|
||||
parse a request, and then give a response over that socket. By instantiation
|
||||
of a thread-local struct containing relevant data (e.g. accepted socket,
|
||||
allocated memory for callbacks to write into, etc), a parser's callbacks are
|
||||
able to communicate data between the scope of the thread and the scope of the
|
||||
callback in a threadsafe manner. This allows http-parser to be used in
|
||||
multi-threaded contexts.
|
||||
|
||||
Example:
|
||||
```
|
||||
typedef struct {
|
||||
socket_t sock;
|
||||
void* buffer;
|
||||
int buf_len;
|
||||
} custom_data_t;
|
||||
|
||||
|
||||
int my_url_callback(http_parser* parser, const char *at, size_t length) {
|
||||
/* access to thread local custom_data_t struct.
|
||||
Use this access save parsed data for later use into thread local
|
||||
buffer, or communicate over socket
|
||||
*/
|
||||
parser->data;
|
||||
...
|
||||
return 0;
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
void http_parser_thread(socket_t sock) {
|
||||
int nparsed = 0;
|
||||
/* allocate memory for user data */
|
||||
custom_data_t *my_data = malloc(sizeof(custom_data_t));
|
||||
|
||||
/* some information for use by callbacks.
|
||||
* achieves thread -> callback information flow */
|
||||
my_data->sock = sock;
|
||||
|
||||
/* instantiate a thread-local parser */
|
||||
http_parser *parser = malloc(sizeof(http_parser));
|
||||
http_parser_init(parser, HTTP_REQUEST); /* initialise parser */
|
||||
/* this custom data reference is accessible through the reference to the
|
||||
parser supplied to callback functions */
|
||||
parser->data = my_data;
|
||||
|
||||
http_parser_settings settings; / * set up callbacks */
|
||||
settings.on_url = my_url_callback;
|
||||
|
||||
/* execute parser */
|
||||
nparsed = http_parser_execute(parser, &settings, buf, recved);
|
||||
|
||||
...
|
||||
/* parsed information copied from callback.
|
||||
can now perform action on data copied into thread-local memory from callbacks.
|
||||
achieves callback -> thread information flow */
|
||||
my_data->buffer;
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
In case you parse HTTP message in chunks (i.e. `read()` request line
|
||||
from socket, parse, read half headers, parse, etc) your data callbacks
|
||||
may be called more than once. Http-parser guarantees that data pointer is only
|
@ -35,6 +35,7 @@ int main(int argc, char ** argv) {
|
||||
connect = strcmp("connect", argv[1]) == 0 ? 1 : 0;
|
||||
printf("Parsing %s, connect %d\n", argv[2], connect);
|
||||
|
||||
http_parser_url_init(&u);
|
||||
result = http_parser_parse_url(argv[2], len, connect, &u);
|
||||
if (result != 0) {
|
||||
printf("Parse error : %d\n", result);
|
||||
@ -43,4 +44,4 @@ int main(int argc, char ** argv) {
|
||||
printf("Parse ok, result : \n");
|
||||
dump_url(argv[2], &u);
|
||||
return 0;
|
||||
}
|
||||
}
|
99
external/qhttpserver/http-parser/http_parser.c → external/qhttp/3rdparty/http-parser/http_parser.c
vendored
Executable file → Normal file
99
external/qhttpserver/http-parser/http_parser.c → external/qhttp/3rdparty/http-parser/http_parser.c
vendored
Executable file → Normal file
@ -400,6 +400,8 @@ enum http_host_state
|
||||
, s_http_host
|
||||
, s_http_host_v6
|
||||
, s_http_host_v6_end
|
||||
, s_http_host_v6_zone_start
|
||||
, s_http_host_v6_zone
|
||||
, s_http_host_port_start
|
||||
, s_http_host_port
|
||||
};
|
||||
@ -957,21 +959,23 @@ reexecute:
|
||||
parser->method = (enum http_method) 0;
|
||||
parser->index = 1;
|
||||
switch (ch) {
|
||||
case 'A': parser->method = HTTP_ACL; break;
|
||||
case 'B': parser->method = HTTP_BIND; break;
|
||||
case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
|
||||
case 'D': parser->method = HTTP_DELETE; break;
|
||||
case 'G': parser->method = HTTP_GET; break;
|
||||
case 'H': parser->method = HTTP_HEAD; break;
|
||||
case 'L': parser->method = HTTP_LOCK; break;
|
||||
case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
|
||||
case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
|
||||
case 'N': parser->method = HTTP_NOTIFY; break;
|
||||
case 'O': parser->method = HTTP_OPTIONS; break;
|
||||
case 'P': parser->method = HTTP_POST;
|
||||
/* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
|
||||
break;
|
||||
case 'R': parser->method = HTTP_REPORT; break;
|
||||
case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
|
||||
case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
|
||||
case 'T': parser->method = HTTP_TRACE; break;
|
||||
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
|
||||
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
|
||||
default:
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
@ -1027,16 +1031,32 @@ reexecute:
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
}
|
||||
} else if (parser->index == 1 && parser->method == HTTP_POST) {
|
||||
if (ch == 'R') {
|
||||
parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
|
||||
} else if (ch == 'U') {
|
||||
parser->method = HTTP_PUT; /* or HTTP_PURGE */
|
||||
} else if (ch == 'A') {
|
||||
parser->method = HTTP_PATCH;
|
||||
} else {
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
} else if (parser->method == HTTP_REPORT) {
|
||||
if (parser->index == 2 && ch == 'B') {
|
||||
parser->method = HTTP_REBIND;
|
||||
} else {
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
}
|
||||
} else if (parser->index == 1) {
|
||||
if (parser->method == HTTP_POST) {
|
||||
if (ch == 'R') {
|
||||
parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
|
||||
} else if (ch == 'U') {
|
||||
parser->method = HTTP_PUT; /* or HTTP_PURGE */
|
||||
} else if (ch == 'A') {
|
||||
parser->method = HTTP_PATCH;
|
||||
} else {
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
}
|
||||
} else if (parser->method == HTTP_LOCK) {
|
||||
if (ch == 'I') {
|
||||
parser->method = HTTP_LINK;
|
||||
} else {
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
} else if (parser->index == 2) {
|
||||
if (parser->method == HTTP_PUT) {
|
||||
@ -1049,6 +1069,8 @@ reexecute:
|
||||
} else if (parser->method == HTTP_UNLOCK) {
|
||||
if (ch == 'S') {
|
||||
parser->method = HTTP_UNSUBSCRIBE;
|
||||
} else if(ch == 'B') {
|
||||
parser->method = HTTP_UNBIND;
|
||||
} else {
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
@ -1059,6 +1081,8 @@ reexecute:
|
||||
}
|
||||
} else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
|
||||
parser->method = HTTP_PROPPATCH;
|
||||
} else if (parser->index == 3 && parser->method == HTTP_UNLOCK && ch == 'I') {
|
||||
parser->method = HTTP_UNLINK;
|
||||
} else {
|
||||
SET_ERRNO(HPE_INVALID_METHOD);
|
||||
goto error;
|
||||
@ -1828,11 +1852,12 @@ reexecute:
|
||||
|
||||
case s_headers_done:
|
||||
{
|
||||
int hasBody;
|
||||
STRICT_CHECK(ch != LF);
|
||||
|
||||
parser->nread = 0;
|
||||
|
||||
int hasBody = parser->flags & F_CHUNKED ||
|
||||
hasBody = parser->flags & F_CHUNKED ||
|
||||
(parser->content_length > 0 && parser->content_length != ULLONG_MAX);
|
||||
if (parser->upgrade && (parser->method == HTTP_CONNECT ||
|
||||
(parser->flags & F_SKIPBODY) || !hasBody)) {
|
||||
@ -1857,8 +1882,7 @@ reexecute:
|
||||
/* Content-Length header given and non-zero */
|
||||
UPDATE_STATE(s_body_identity);
|
||||
} else {
|
||||
if (parser->type == HTTP_REQUEST ||
|
||||
!http_message_needs_eof(parser)) {
|
||||
if (!http_message_needs_eof(parser)) {
|
||||
/* Assume content-length 0 - read the next */
|
||||
UPDATE_STATE(NEW_MESSAGE());
|
||||
CALLBACK_NOTIFY(message_complete);
|
||||
@ -2153,15 +2177,13 @@ http_parser_settings_init(http_parser_settings *settings)
|
||||
|
||||
const char *
|
||||
http_errno_name(enum http_errno err) {
|
||||
assert(((size_t) err) <
|
||||
(sizeof(http_strerror_tab) / sizeof(http_strerror_tab[0])));
|
||||
assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
|
||||
return http_strerror_tab[err].name;
|
||||
}
|
||||
|
||||
const char *
|
||||
http_errno_description(enum http_errno err) {
|
||||
assert(((size_t) err) <
|
||||
(sizeof(http_strerror_tab) / sizeof(http_strerror_tab[0])));
|
||||
assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
|
||||
return http_strerror_tab[err].description;
|
||||
}
|
||||
|
||||
@ -2214,6 +2236,23 @@ http_parse_host_char(enum http_host_state s, const char ch) {
|
||||
return s_http_host_v6;
|
||||
}
|
||||
|
||||
if (s == s_http_host_v6 && ch == '%') {
|
||||
return s_http_host_v6_zone_start;
|
||||
}
|
||||
break;
|
||||
|
||||
case s_http_host_v6_zone:
|
||||
if (ch == ']') {
|
||||
return s_http_host_v6_end;
|
||||
}
|
||||
|
||||
/* FALLTHROUGH */
|
||||
case s_http_host_v6_zone_start:
|
||||
/* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
|
||||
if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
|
||||
ch == '~') {
|
||||
return s_http_host_v6_zone;
|
||||
}
|
||||
break;
|
||||
|
||||
case s_http_host_port:
|
||||
@ -2232,6 +2271,7 @@ http_parse_host_char(enum http_host_state s, const char ch) {
|
||||
|
||||
static int
|
||||
http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
|
||||
assert(u->field_set & (1 << UF_HOST));
|
||||
enum http_host_state s;
|
||||
|
||||
const char *p;
|
||||
@ -2263,6 +2303,11 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
|
||||
u->field_data[UF_HOST].len++;
|
||||
break;
|
||||
|
||||
case s_http_host_v6_zone_start:
|
||||
case s_http_host_v6_zone:
|
||||
u->field_data[UF_HOST].len++;
|
||||
break;
|
||||
|
||||
case s_http_host_port:
|
||||
if (s != s_http_host_port) {
|
||||
u->field_data[UF_PORT].off = p - buf;
|
||||
@ -2292,6 +2337,8 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
|
||||
case s_http_host_start:
|
||||
case s_http_host_v6_start:
|
||||
case s_http_host_v6:
|
||||
case s_http_host_v6_zone_start:
|
||||
case s_http_host_v6_zone:
|
||||
case s_http_host_port_start:
|
||||
case s_http_userinfo:
|
||||
case s_http_userinfo_start:
|
||||
@ -2303,6 +2350,11 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
http_parser_url_init(struct http_parser_url *u) {
|
||||
memset(u, 0, sizeof(*u));
|
||||
}
|
||||
|
||||
int
|
||||
http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
|
||||
struct http_parser_url *u)
|
||||
@ -2376,7 +2428,12 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
|
||||
|
||||
/* host must be present if there is a schema */
|
||||
/* parsing http:///toto will fail */
|
||||
if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) {
|
||||
if ((u->field_set & (1 << UF_SCHEMA)) &&
|
||||
(u->field_set & (1 << UF_HOST)) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (u->field_set & (1 << UF_HOST)) {
|
||||
if (http_parse_host(buf, u, found_at) != 0) {
|
||||
return 1;
|
||||
}
|
0
external/qhttpserver/http-parser/http_parser.gyp → external/qhttp/3rdparty/http-parser/http_parser.gyp
vendored
Executable file → Normal file
0
external/qhttpserver/http-parser/http_parser.gyp → external/qhttp/3rdparty/http-parser/http_parser.gyp
vendored
Executable file → Normal file
41
external/qhttpserver/http-parser/http_parser.h → external/qhttp/3rdparty/http-parser/http_parser.h
vendored
Executable file → Normal file
41
external/qhttpserver/http-parser/http_parser.h → external/qhttp/3rdparty/http-parser/http_parser.h
vendored
Executable file → Normal file
@ -26,11 +26,12 @@ extern "C" {
|
||||
|
||||
/* Also update SONAME in the Makefile whenever you change these. */
|
||||
#define HTTP_PARSER_VERSION_MAJOR 2
|
||||
#define HTTP_PARSER_VERSION_MINOR 5
|
||||
#define HTTP_PARSER_VERSION_MINOR 6
|
||||
#define HTTP_PARSER_VERSION_PATCH 0
|
||||
|
||||
#include <sys/types.h>
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && \
|
||||
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
|
||||
#include <BaseTsd.h>
|
||||
#include <stddef.h>
|
||||
typedef __int8 int8_t;
|
||||
@ -95,7 +96,7 @@ typedef int (*http_cb) (http_parser*);
|
||||
XX(5, CONNECT, CONNECT) \
|
||||
XX(6, OPTIONS, OPTIONS) \
|
||||
XX(7, TRACE, TRACE) \
|
||||
/* webdav */ \
|
||||
/* WebDAV */ \
|
||||
XX(8, COPY, COPY) \
|
||||
XX(9, LOCK, LOCK) \
|
||||
XX(10, MKCOL, MKCOL) \
|
||||
@ -104,21 +105,28 @@ typedef int (*http_cb) (http_parser*);
|
||||
XX(13, PROPPATCH, PROPPATCH) \
|
||||
XX(14, SEARCH, SEARCH) \
|
||||
XX(15, UNLOCK, UNLOCK) \
|
||||
XX(16, BIND, BIND) \
|
||||
XX(17, REBIND, REBIND) \
|
||||
XX(18, UNBIND, UNBIND) \
|
||||
XX(19, ACL, ACL) \
|
||||
/* subversion */ \
|
||||
XX(16, REPORT, REPORT) \
|
||||
XX(17, MKACTIVITY, MKACTIVITY) \
|
||||
XX(18, CHECKOUT, CHECKOUT) \
|
||||
XX(19, MERGE, MERGE) \
|
||||
XX(20, REPORT, REPORT) \
|
||||
XX(21, MKACTIVITY, MKACTIVITY) \
|
||||
XX(22, CHECKOUT, CHECKOUT) \
|
||||
XX(23, MERGE, MERGE) \
|
||||
/* upnp */ \
|
||||
XX(20, MSEARCH, M-SEARCH) \
|
||||
XX(21, NOTIFY, NOTIFY) \
|
||||
XX(22, SUBSCRIBE, SUBSCRIBE) \
|
||||
XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||
XX(24, MSEARCH, M-SEARCH) \
|
||||
XX(25, NOTIFY, NOTIFY) \
|
||||
XX(26, SUBSCRIBE, SUBSCRIBE) \
|
||||
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||
/* RFC-5789 */ \
|
||||
XX(24, PATCH, PATCH) \
|
||||
XX(25, PURGE, PURGE) \
|
||||
XX(28, PATCH, PATCH) \
|
||||
XX(29, PURGE, PURGE) \
|
||||
/* CalDAV */ \
|
||||
XX(26, MKCALENDAR, MKCALENDAR) \
|
||||
XX(30, MKCALENDAR, MKCALENDAR) \
|
||||
/* RFC-2068, section 19.6.1.2 */ \
|
||||
XX(31, LINK, LINK) \
|
||||
XX(32, UNLINK, UNLINK) \
|
||||
|
||||
enum http_method
|
||||
{
|
||||
@ -144,7 +152,7 @@ enum flags
|
||||
|
||||
|
||||
/* Map for errno-related constants
|
||||
*
|
||||
*
|
||||
* The provided argument should be a macro that takes 2 arguments.
|
||||
*/
|
||||
#define HTTP_ERRNO_MAP(XX) \
|
||||
@ -325,6 +333,9 @@ const char *http_errno_name(enum http_errno err);
|
||||
/* Return a string description of the given error */
|
||||
const char *http_errno_description(enum http_errno err);
|
||||
|
||||
/* Initialize all http_parser_url members to 0 */
|
||||
void http_parser_url_init(struct http_parser_url *u);
|
||||
|
||||
/* Parse a URL; return nonzero on failure */
|
||||
int http_parser_parse_url(const char *buf, size_t buflen,
|
||||
int is_connect,
|
114
external/qhttpserver/http-parser/test.c → external/qhttp/3rdparty/http-parser/test.c
vendored
Executable file → Normal file
114
external/qhttpserver/http-parser/test.c → external/qhttp/3rdparty/http-parser/test.c
vendored
Executable file → Normal file
@ -1101,6 +1101,58 @@ const struct message requests[] =
|
||||
,.body= ""
|
||||
}
|
||||
|
||||
/* Examples from the Internet draft for LINK/UNLINK methods:
|
||||
* https://tools.ietf.org/id/draft-snell-link-method-01.html#rfc.section.5
|
||||
*/
|
||||
|
||||
#define LINK_REQUEST 40
|
||||
, {.name = "link request"
|
||||
,.type= HTTP_REQUEST
|
||||
,.raw= "LINK /images/my_dog.jpg HTTP/1.1\r\n"
|
||||
"Host: example.com\r\n"
|
||||
"Link: <http://example.com/profiles/joe>; rel=\"tag\"\r\n"
|
||||
"Link: <http://example.com/profiles/sally>; rel=\"tag\"\r\n"
|
||||
"\r\n"
|
||||
,.should_keep_alive= TRUE
|
||||
,.message_complete_on_eof= FALSE
|
||||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_LINK
|
||||
,.request_path= "/images/my_dog.jpg"
|
||||
,.request_url= "/images/my_dog.jpg"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.num_headers= 3
|
||||
,.headers= { { "Host", "example.com" }
|
||||
, { "Link", "<http://example.com/profiles/joe>; rel=\"tag\"" }
|
||||
, { "Link", "<http://example.com/profiles/sally>; rel=\"tag\"" }
|
||||
}
|
||||
,.body= ""
|
||||
}
|
||||
|
||||
#define UNLINK_REQUEST 41
|
||||
, {.name = "link request"
|
||||
,.type= HTTP_REQUEST
|
||||
,.raw= "UNLINK /images/my_dog.jpg HTTP/1.1\r\n"
|
||||
"Host: example.com\r\n"
|
||||
"Link: <http://example.com/profiles/sally>; rel=\"tag\"\r\n"
|
||||
"\r\n"
|
||||
,.should_keep_alive= TRUE
|
||||
,.message_complete_on_eof= FALSE
|
||||
,.http_major= 1
|
||||
,.http_minor= 1
|
||||
,.method= HTTP_UNLINK
|
||||
,.request_path= "/images/my_dog.jpg"
|
||||
,.request_url= "/images/my_dog.jpg"
|
||||
,.query_string= ""
|
||||
,.fragment= ""
|
||||
,.num_headers= 2
|
||||
,.headers= { { "Host", "example.com" }
|
||||
, { "Link", "<http://example.com/profiles/sally>; rel=\"tag\"" }
|
||||
}
|
||||
,.body= ""
|
||||
}
|
||||
|
||||
, {.name= NULL } /* sentinel */
|
||||
};
|
||||
|
||||
@ -2918,6 +2970,59 @@ const struct url_test url_tests[] =
|
||||
,.rv=1 /* s_dead */
|
||||
}
|
||||
|
||||
, {.name="ipv6 address with Zone ID"
|
||||
,.url="http://[fe80::a%25eth0]/"
|
||||
,.is_connect=0
|
||||
,.u=
|
||||
{.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
|
||||
,.port=0
|
||||
,.field_data=
|
||||
{{ 0, 4 } /* UF_SCHEMA */
|
||||
,{ 8, 14 } /* UF_HOST */
|
||||
,{ 0, 0 } /* UF_PORT */
|
||||
,{ 23, 1 } /* UF_PATH */
|
||||
,{ 0, 0 } /* UF_QUERY */
|
||||
,{ 0, 0 } /* UF_FRAGMENT */
|
||||
,{ 0, 0 } /* UF_USERINFO */
|
||||
}
|
||||
}
|
||||
,.rv=0
|
||||
}
|
||||
|
||||
, {.name="ipv6 address with Zone ID, but '%' is not percent-encoded"
|
||||
,.url="http://[fe80::a%eth0]/"
|
||||
,.is_connect=0
|
||||
,.u=
|
||||
{.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
|
||||
,.port=0
|
||||
,.field_data=
|
||||
{{ 0, 4 } /* UF_SCHEMA */
|
||||
,{ 8, 12 } /* UF_HOST */
|
||||
,{ 0, 0 } /* UF_PORT */
|
||||
,{ 21, 1 } /* UF_PATH */
|
||||
,{ 0, 0 } /* UF_QUERY */
|
||||
,{ 0, 0 } /* UF_FRAGMENT */
|
||||
,{ 0, 0 } /* UF_USERINFO */
|
||||
}
|
||||
}
|
||||
,.rv=0
|
||||
}
|
||||
|
||||
, {.name="ipv6 address ending with '%'"
|
||||
,.url="http://[fe80::a%]/"
|
||||
,.rv=1 /* s_dead */
|
||||
}
|
||||
|
||||
, {.name="ipv6 address with Zone ID including bad character"
|
||||
,.url="http://[fe80::a%$HOME]/"
|
||||
,.rv=1 /* s_dead */
|
||||
}
|
||||
|
||||
, {.name="just ipv6 Zone ID"
|
||||
,.url="http://[%eth0]/"
|
||||
,.rv=1 /* s_dead */
|
||||
}
|
||||
|
||||
#if HTTP_PARSER_STRICT
|
||||
|
||||
, {.name="tab in URL"
|
||||
@ -3690,7 +3795,12 @@ main (void)
|
||||
"MOVE",
|
||||
"PROPFIND",
|
||||
"PROPPATCH",
|
||||
"SEARCH",
|
||||
"UNLOCK",
|
||||
"BIND",
|
||||
"REBIND",
|
||||
"UNBIND",
|
||||
"ACL",
|
||||
"REPORT",
|
||||
"MKACTIVITY",
|
||||
"CHECKOUT",
|
||||
@ -3700,6 +3810,10 @@ main (void)
|
||||
"SUBSCRIBE",
|
||||
"UNSUBSCRIBE",
|
||||
"PATCH",
|
||||
"PURGE",
|
||||
"MKCALENDAR",
|
||||
"LINK",
|
||||
"UNLINK",
|
||||
0 };
|
||||
const char **this_method;
|
||||
for (this_method = all_methods; *this_method; this_method++) {
|
8
external/qhttp/CMakeLists.txt
vendored
Normal file
8
external/qhttp/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
aux_source_directory(src HTTP_SRC)
|
||||
|
||||
include_directories(src 3rdparty)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
add_library(qhttp STATIC ${HTTP_SRC} 3rdparty/http-parser/http_parser.c)
|
||||
target_link_libraries(qhttp)
|
||||
set_target_properties(qhttp PROPERTIES CXX_STANDARD 11)
|
22
external/qhttp/LICENSE
vendored
Normal file
22
external/qhttp/LICENSE
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Amir Zamani
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
229
external/qhttp/README.md
vendored
Normal file
229
external/qhttp/README.md
vendored
Normal file
@ -0,0 +1,229 @@
|
||||
# QHttp
|
||||
|
||||
|
||||
### Table of contents
|
||||
- [About](#about)
|
||||
- [Features](#features)
|
||||
- [Sample codes](#sample-codes)
|
||||
- [Setup](#setup)
|
||||
- [Multi-threading](#multi-threading)
|
||||
- [Source tree](#source-tree)
|
||||
- [Disclaimer](#disclaimer)
|
||||
- [License](#license)
|
||||
|
||||
## About
|
||||
[TOC](#table-of-contents)
|
||||
|
||||
`QHttp` is a lightweight, asynchronous and fast HTTP library, containing both server and client side classes for managing connections, parsing and building HTTP requests and responses.
|
||||
|
||||
* this project is inspired by [nikhilm/qhttpserver](https://github.com/nikhilm/qhttpserver) effort to implement a Qt HTTP server. `QHttp` pushes the idea further by implementing client classes and better memory management, a lot more Node.js-like API, ...
|
||||
|
||||
* the fantastic [joyent/http-parser](https://github.com/joyent/http-parser) is the core parser of HTTP requests (server mode) and responses (client mode).
|
||||
|
||||
* By using `std::function` and `c++11 lambda`, the API is intentionally similar to the [Node.js' http module](http://nodejs.org/api/http.html). Asynchronous and non-blocking HTTP programming is quite easy with `QHttp`. have a look at [sample codes](#sample-codes).
|
||||
|
||||
* the objective of `QHttp` is being light weight with a simple API for Qt developers to implement RESTful web services in private (internal) zones. for a fast c++ Json parser / builder, have a look at [azadkuh/gason++](https://github.com/azadkuh/gason--)
|
||||
|
||||
|
||||
|
||||
## Features
|
||||
[TOC](#table-of-contents)
|
||||
|
||||
* the only dependencies are: [Qt 5](http://qt-project.org/downloads), [c++11](http://en.wikipedia.org/wiki/C%2B%2B11) and [joyent/http-parser](https://github.com/joyent/http-parser)
|
||||
|
||||
* both TCP and UNIX (local) sockets are supported as backend.
|
||||
|
||||
* separate `namespace`s for server and client classes.
|
||||
|
||||
* HTTP server classes: [QHttpServer](./src/qhttpserver.hpp), [QHttpConnection](./src/qhttpserverconnection.hpp), [QHttpRequest](./src/qhttpserverrequest.hpp) and [QHttpResponse](./src/qhttpserverresponse.hpp).
|
||||
|
||||
* HTTP client classes: [QHttpClient](./src/qhttpclient.hpp), [QHttpRequest](./src/qhttpclientrequest.hpp) and [QHttpResponse](./src/qhttpclientresponse.hpp).
|
||||
|
||||
* **automatic memory management** of objects. Instances of connections, requests and replies will be deleted automatically when socket drops or disconnected.
|
||||
|
||||
* **PIMPL** (Private classes) to achieve better ABI compatibility and cleaner API.
|
||||
|
||||
* **Asynchronous** and **non-blocking**. You can handle thousands of concurrent HTTP connections efficiently by a single thread, although a multi-threaded HTTP server is easy to implement.
|
||||
|
||||
* **high throughput**, I have tried the `QHttp` and [gason++](https://github.com/azadkuh/gason--) to implement a REST/Json web service on an Ubuntu VPS (dual core + 512MB ram) with more than **5800** connections per second (stress test). On a MacBook Pro (i5 4258U 4cores with HT + 8096MB ram), `QHttp` easily reaches to more than **11700** connections / second. Generally `QHttp` is **1.5x ~ 3x** faster than `Node.js` depending on your machine / OS. check [benchmark app](./example/benchmard/README.md) to measure your system.
|
||||
|
||||
* Tested under **Linux** (Ubuntu 12.04 LTS, 14.04 LTS, g++) and **OS X** (10.9/10.10, clang) and Windows7 (msvc 2013). Easily portable where ever Qt 5 works. I have no *Windows* machine (nor time, nor interest), but this lib should work just fine under *Windows*, although I've not tested it by myself.
|
||||
|
||||
|
||||
## Sample codes
|
||||
[TOC](#table-of-contents)
|
||||
|
||||
a HelloWorld **HTTP server** by `QHttp` looks like:
|
||||
``` cpp
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
using namespace qhttp::server;
|
||||
|
||||
QHttpServer server(&app);
|
||||
// listening on 0.0.0.0:8080
|
||||
server.listen(QHostAddress::Any, 8080, [](QHttpRequest* req, QHttpResponse* res) {
|
||||
|
||||
res->setStatusCode(qhttp::ESTATUS_OK); // status 200
|
||||
res->addHeader("connection", "close"); // it's the default header, this line can be omitted.
|
||||
res->end("Hello World!\n"); // response body data
|
||||
|
||||
// when "connection: close", the req and res will be deleted automatically.
|
||||
});
|
||||
|
||||
|
||||
if ( !server.isListening() ) {
|
||||
fprintf(stderr, "failed. can not listen at port 8080!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// application's main event loop
|
||||
return app.exec();
|
||||
}
|
||||
```
|
||||
|
||||
to request weather information by **HTTP client**:
|
||||
```cpp
|
||||
int main(int argc, char** argv) {
|
||||
QCoreApplication app(argc, argv);
|
||||
using namespace qhttp::client;
|
||||
|
||||
QHttpClient client(&app);
|
||||
QByteArray httpBody;
|
||||
|
||||
QUrl weatherUrl("http://api.openweathermap.org/data/2.5/weather?q=tehran,ir&units=metric&mode=xml");
|
||||
|
||||
client.request(qhttp::EHTTP_GET, weatherUrl, [&httpBody](QHttpResponse* res) {
|
||||
// response handler, called when the HTTP headers of the response are ready
|
||||
|
||||
// gather HTTP response data
|
||||
res->onData([&httpBody](const QByteArray& chunk) {
|
||||
httpBody.append(chunk);
|
||||
});
|
||||
|
||||
// called when all data in HTTP response have been read.
|
||||
res->onEnd([&httpBody]() {
|
||||
// print the XML body of the response
|
||||
puts("\n[incoming response:]");
|
||||
puts(httpBody.constData());
|
||||
puts("\n\n");
|
||||
|
||||
QCoreApplication::instance()->quit();
|
||||
});
|
||||
|
||||
// just for fun! print headers:
|
||||
puts("\n[Headers:]");
|
||||
const qhttp::THeaderHash& hs = res->headers();
|
||||
for ( auto cit = hs.constBegin(); cit != hs.constEnd(); cit++) {
|
||||
printf("%s : %s\n", cit.key().constData(), cit.value().constData());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
```
|
||||
|
||||
## Setup
|
||||
[TOC](#table-of-contents)
|
||||
|
||||
instructions:
|
||||
```bash
|
||||
# first clone this repository:
|
||||
$> git clone --depth=1 https://github.com/azadkuh/qhttp.git -b master
|
||||
$> cd qhttp
|
||||
|
||||
# prepare dependencies:
|
||||
$> ./update-dependencies.sh
|
||||
|
||||
# now build the library and the examples
|
||||
$> qmake qhttp.pro
|
||||
$> make -j 8
|
||||
```
|
||||
|
||||
## Multi-threading
|
||||
[TOC](#table-of-contents)
|
||||
|
||||
As `QHttp` is **asynchrounous** and **non-bloking**, your app can handle thousands of concurrent HTTP connections by a single thread.
|
||||
|
||||
in some rare scenarios you may want to use multiple handler threads (although it's not the best solution):
|
||||
|
||||
* there are some blocking APIs (QSql, system calls, ...) in your connection handler (adopting asynchronous layer over the blocking API is a better approach).
|
||||
|
||||
* the hardware has lots of free cores and the measurement shows that the load on the main `QHttp` thread is close to highest limit. There you can spawn some other handler threads.
|
||||
|
||||
|
||||
[benchmark example](./example/benchmark/README.md) shows how to implement a single or multi threaded HTTP app (both server and client). This example uses worker `QThread` and `QObject::moveToThread()` for worker objects. see aslo: [Subclassing no longer recommended way of using QThread](http://qt-project.org/doc/note_revisions/5/8/view).
|
||||
|
||||
**Note**:
|
||||
> moving objects between threads is an expensive job, more ever the locking/unlocking mechanism, creating or stopping threads, ... cost even more! so using multiple threads in an application is not guaranteed to get better performance, but it's guaranteed to add more complexity, nasty bugs and headache!
|
||||
|
||||
see why other top performer networking libraries as ZeroMQ are concurrent but not multi-threaded by default:
|
||||
|
||||
* [ZeroMQ : Multithreading Magic](http://zeromq.org/blog:multithreading-magic)
|
||||
* [Node.js : about](http://nodejs.org/about/)
|
||||
|
||||
|
||||
## Source tree
|
||||
[TOC](#table-of-contents)
|
||||
|
||||
|
||||
* **`3rdparty/`**:
|
||||
will contain `http-parser` source tree as the only dependency.
|
||||
this directory is created by setup. see also: [setup](#setup).
|
||||
|
||||
* **`example/`**:
|
||||
contains some sample applications representing the `QHttp` usage:
|
||||
* **`helloworld/`**:
|
||||
the HelloWorld example of `QHttp`, both server + client are represented.
|
||||
see: [README@helloworld](./example/helloworld/README.md)
|
||||
|
||||
* **`basic-server/`**:
|
||||
a basic HTTP server shows how to collect the request body, and respond to the clients.
|
||||
see: [README@basic-server](./example/basic-server/README.md)
|
||||
|
||||
|
||||
* **`benchmark/`**:
|
||||
a simple utility to measure the throughput (requests per second) of `QHttp` as a REST/Json server. this app provides both the server and attacking clinets.
|
||||
see: [README@benchmark](./example/benchmark/README.md)
|
||||
|
||||
* **`nodejs/`**:
|
||||
Node.js implementation of `benchmark/` in server mode. Provided for benchmarking `QHttp` with `Node.js` as a RESTFul service app.
|
||||
see: [README@nodejs](./example/nodejs/README.md)
|
||||
|
||||
|
||||
* **`src/`**:
|
||||
holds the source code of `QHttp`. server classes are prefixed by `qhttpserver*` and client classes by `qhttpclient*`.
|
||||
* **`private/`**:
|
||||
Private classes of the library. see: [d-pointers](https://qt-project.org/wiki/Dpointer).
|
||||
|
||||
* **`tmp/`**:
|
||||
a temporary directory which is created while `make`ing the library and holds all the `.o`, `moc files`, etc.
|
||||
|
||||
* **`xbin/`**:
|
||||
all the executable and binaries will be placed on this folder by `make`.
|
||||
|
||||
|
||||
|
||||
|
||||
## Disclaimer
|
||||
[TOC](#table-of-contents)
|
||||
|
||||
* Implementing a lightweight and simple HTTP server/client in Qt with Node.js like API, is the main purpose of `QHttp`.
|
||||
|
||||
* There are lots of features in a full blown HTTP server which are out of scope of this small library, although those can be added on top of `QHttp`.
|
||||
|
||||
* The client classes are by no mean designed as a `QNetworkAccessManager` replacement. `QHttpClient` is simpler and lighter, for serious scenarios just use `QNetworkAccessManager`.
|
||||
|
||||
* I'm a busy person.
|
||||
|
||||
|
||||
> If you have any ideas, critiques, suggestions or whatever you want to call it, please open an issue. I'll be happy to hear from you what you'd see in this lib. I think about all suggestions, and I try to add those that make sense.
|
||||
|
||||
|
||||
## License
|
||||
[TOC](#table-of-contents)
|
||||
|
||||
Distributed under the MIT license. Copyright (c) 2014, Amir Zamani.
|
||||
|
470
external/qhttp/src/private/qhttpbase.hpp
vendored
Normal file
470
external/qhttp/src/private/qhttpbase.hpp
vendored
Normal file
@ -0,0 +1,470 @@
|
||||
/** base classes for private implementations.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPBASE_HPP
|
||||
#define QHTTPBASE_HPP
|
||||
|
||||
#include "qhttpfwd.hpp"
|
||||
|
||||
#include <QTcpSocket>
|
||||
#include <QLocalSocket>
|
||||
#include <QHostAddress>
|
||||
#include <QUrl>
|
||||
#include <QBasicTimer>
|
||||
|
||||
#include "http-parser/http_parser.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class QSocket
|
||||
{
|
||||
public:
|
||||
void close() {
|
||||
if ( itcpSocket )
|
||||
itcpSocket->close();
|
||||
if ( ilocalSocket )
|
||||
ilocalSocket->close();
|
||||
}
|
||||
|
||||
void release() {
|
||||
close();
|
||||
if ( itcpSocket )
|
||||
itcpSocket->deleteLater();
|
||||
if ( ilocalSocket )
|
||||
ilocalSocket->deleteLater();
|
||||
|
||||
itcpSocket = nullptr;
|
||||
ilocalSocket = nullptr;
|
||||
}
|
||||
|
||||
void flush() {
|
||||
if ( itcpSocket )
|
||||
itcpSocket->flush();
|
||||
else if ( ilocalSocket )
|
||||
ilocalSocket->flush();
|
||||
}
|
||||
|
||||
bool isOpen() const {
|
||||
if ( ibackendType == ETcpSocket && itcpSocket )
|
||||
return itcpSocket->isOpen() && itcpSocket->state() == QTcpSocket::ConnectedState;
|
||||
|
||||
else if ( ibackendType == ELocalSocket && ilocalSocket )
|
||||
return ilocalSocket->isOpen() && ilocalSocket->state() == QLocalSocket::ConnectedState;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void connectTo(const QUrl& url) {
|
||||
if ( ilocalSocket )
|
||||
ilocalSocket->connectToServer(url.path());
|
||||
}
|
||||
|
||||
void connectTo(const QString& host, quint16 port) {
|
||||
if ( itcpSocket )
|
||||
itcpSocket->connectToHost(host, port);
|
||||
}
|
||||
|
||||
qint64 readRaw(char* buffer, int maxlen) {
|
||||
if ( itcpSocket )
|
||||
return itcpSocket->read(buffer, maxlen);
|
||||
else if ( ilocalSocket )
|
||||
return ilocalSocket->read(buffer, maxlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void writeRaw(const QByteArray& data) {
|
||||
if ( itcpSocket )
|
||||
itcpSocket->write(data);
|
||||
else if ( ilocalSocket )
|
||||
ilocalSocket->write(data);
|
||||
}
|
||||
|
||||
qint64 bytesAvailable() {
|
||||
if ( itcpSocket )
|
||||
return itcpSocket->bytesAvailable();
|
||||
else if ( ilocalSocket )
|
||||
return ilocalSocket->bytesAvailable();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void disconnectAllQtConnections() {
|
||||
if ( itcpSocket )
|
||||
QObject::disconnect(itcpSocket, 0, 0, 0);
|
||||
if ( ilocalSocket )
|
||||
QObject::disconnect(ilocalSocket, 0, 0, 0);
|
||||
}
|
||||
|
||||
public:
|
||||
TBackend ibackendType = ETcpSocket;
|
||||
QTcpSocket* itcpSocket = nullptr;
|
||||
QLocalSocket* ilocalSocket = nullptr;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class HttpBase
|
||||
{
|
||||
public:
|
||||
THeaderHash iheaders;
|
||||
QString iversion;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class HttpRequestBase : public HttpBase
|
||||
{
|
||||
public:
|
||||
QUrl iurl;
|
||||
THttpMethod imethod;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class HttpResponseBase : public HttpBase
|
||||
{
|
||||
public:
|
||||
HttpResponseBase() {
|
||||
iversion = "1.1";
|
||||
}
|
||||
|
||||
public:
|
||||
TStatusCode istatus = ESTATUS_BAD_REQUEST;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// usage in client::QHttpResponse, server::QHttpRequest
|
||||
template<class TBase>
|
||||
class HttpReader : public TBase
|
||||
{
|
||||
public:
|
||||
enum TReadState {
|
||||
EEmpty,
|
||||
EPartial,
|
||||
EComplete,
|
||||
ESent
|
||||
};
|
||||
|
||||
public:
|
||||
void collectData(int atMost) {
|
||||
icollectCapacity = atMost;
|
||||
icollectedData.clear();
|
||||
icollectedData.reserve(atMost);
|
||||
}
|
||||
|
||||
bool shouldCollect() const {
|
||||
return icollectCapacity > 0;
|
||||
}
|
||||
|
||||
bool append(const char* data, size_t length) {
|
||||
int currentLength = icollectedData.length();
|
||||
|
||||
if ( (currentLength + (int)length) >= icollectCapacity )
|
||||
return false; // capacity if full
|
||||
|
||||
icollectedData.append(data, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
TReadState ireadState = EEmpty;
|
||||
bool isuccessful = false;
|
||||
|
||||
int icollectCapacity = 0;
|
||||
QByteArray icollectedData;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// usage in client::QHttpRequest, server::QHttpResponse
|
||||
template<class TBase, class TImpl>
|
||||
class HttpWriter : public TBase
|
||||
{
|
||||
public:
|
||||
bool addHeader(const QByteArray &field, const QByteArray &value) {
|
||||
if ( ifinished )
|
||||
return false;
|
||||
|
||||
TBase::iheaders.insert(field.toLower(), value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeHeader(const QByteArray& field, const QByteArray& value) {
|
||||
if ( ifinished )
|
||||
return false;
|
||||
|
||||
QByteArray buffer = QByteArray(field)
|
||||
.append(": ")
|
||||
.append(value)
|
||||
.append("\r\n");
|
||||
|
||||
isocket.writeRaw(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeData(const QByteArray& data) {
|
||||
if ( ifinished )
|
||||
return false;
|
||||
|
||||
ensureWritingHeaders();
|
||||
isocket.writeRaw(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool endPacket(const QByteArray& data) {
|
||||
if ( !writeData(data) )
|
||||
return false;
|
||||
|
||||
isocket.flush();
|
||||
ifinished = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ensureWritingHeaders() {
|
||||
if ( ifinished || iheaderWritten )
|
||||
return;
|
||||
|
||||
TImpl* me = static_cast<TImpl*>(this);
|
||||
isocket.writeRaw(me->makeTitle());
|
||||
writeHeaders();
|
||||
|
||||
iheaderWritten = true;
|
||||
}
|
||||
|
||||
void writeHeaders(bool doFlush = false) {
|
||||
if ( ifinished || iheaderWritten )
|
||||
return;
|
||||
|
||||
if ( TBase::iheaders.keyHasValue("connection", "keep-alive") )
|
||||
ikeepAlive = true;
|
||||
else
|
||||
TBase::iheaders.insert("connection", "close");
|
||||
|
||||
TImpl* me = static_cast<TImpl*>(this);
|
||||
me->prepareHeadersToWrite();
|
||||
|
||||
|
||||
for ( auto cit = TBase::iheaders.constBegin(); cit != TBase::iheaders.constEnd(); cit++ ) {
|
||||
const QByteArray& field = cit.key();
|
||||
const QByteArray& value = cit.value();
|
||||
|
||||
writeHeader(field, value);
|
||||
}
|
||||
|
||||
isocket.writeRaw("\r\n");
|
||||
if ( doFlush )
|
||||
isocket.flush();
|
||||
}
|
||||
|
||||
public:
|
||||
QSocket isocket;
|
||||
|
||||
bool ifinished = false;
|
||||
bool iheaderWritten = false;
|
||||
bool ikeepAlive = false;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// usage in client::QHttpClient, server::QHttpConnection
|
||||
template<class TImpl>
|
||||
class HttpParser
|
||||
{
|
||||
public:
|
||||
explicit HttpParser(http_parser_type type) {
|
||||
// create http_parser object
|
||||
iparser.data = static_cast<TImpl*>(this);
|
||||
http_parser_init(&iparser, type);
|
||||
|
||||
memset(&iparserSettings, 0, sizeof(http_parser_settings));
|
||||
iparserSettings.on_message_begin = onMessageBegin;
|
||||
iparserSettings.on_url = onUrl;
|
||||
iparserSettings.on_status = onStatus;
|
||||
iparserSettings.on_header_field = onHeaderField;
|
||||
iparserSettings.on_header_value = onHeaderValue;
|
||||
iparserSettings.on_headers_complete = onHeadersComplete;
|
||||
iparserSettings.on_body = onBody;
|
||||
iparserSettings.on_message_complete = onMessageComplete;
|
||||
}
|
||||
|
||||
size_t parse(const char* data, size_t length) {
|
||||
return http_parser_execute(&iparser,
|
||||
&iparserSettings,
|
||||
data,
|
||||
length);
|
||||
}
|
||||
|
||||
public: // callback functions for http_parser_settings
|
||||
static int onMessageBegin(http_parser* parser) {
|
||||
TImpl *me = static_cast<TImpl*>(parser->data);
|
||||
return me->messageBegin(parser);
|
||||
}
|
||||
|
||||
static int onUrl(http_parser* parser, const char* at, size_t length) {
|
||||
TImpl *me = static_cast<TImpl*>(parser->data);
|
||||
return me->url(parser, at, length);
|
||||
}
|
||||
|
||||
static int onStatus(http_parser* parser, const char* at, size_t length) {
|
||||
TImpl *me = static_cast<TImpl*>(parser->data);
|
||||
return me->status(parser, at, length);
|
||||
}
|
||||
|
||||
static int onHeaderField(http_parser* parser, const char* at, size_t length) {
|
||||
TImpl *me = static_cast<TImpl*>(parser->data);
|
||||
return me->headerField(parser, at, length);
|
||||
}
|
||||
|
||||
static int onHeaderValue(http_parser* parser, const char* at, size_t length) {
|
||||
TImpl *me = static_cast<TImpl*>(parser->data);
|
||||
return me->headerValue(parser, at, length);
|
||||
}
|
||||
|
||||
static int onHeadersComplete(http_parser* parser) {
|
||||
TImpl *me = static_cast<TImpl*>(parser->data);
|
||||
return me->headersComplete(parser);
|
||||
}
|
||||
|
||||
static int onBody(http_parser* parser, const char* at, size_t length) {
|
||||
TImpl *me = static_cast<TImpl*>(parser->data);
|
||||
return me->body(parser, at, length);
|
||||
}
|
||||
|
||||
static int onMessageComplete(http_parser* parser) {
|
||||
TImpl *me = static_cast<TImpl*>(parser->data);
|
||||
return me->messageComplete(parser);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
// The ones we are reading in from the parser
|
||||
QByteArray itempHeaderField;
|
||||
QByteArray itempHeaderValue;
|
||||
// if connection has a timeout, these fields will be used
|
||||
quint32 itimeOut = 0;
|
||||
QBasicTimer itimer;
|
||||
// uniform socket object
|
||||
QSocket isocket;
|
||||
// if connection should persist
|
||||
bool ikeepAlive = false;
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
http_parser iparser;
|
||||
http_parser_settings iparserSettings;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#if 0
|
||||
template<class T>
|
||||
class HttpWriterBase
|
||||
{
|
||||
public:
|
||||
explicit HttpWriterBase() {
|
||||
}
|
||||
|
||||
virtual ~HttpWriterBase() {
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
reset();
|
||||
|
||||
if ( itcpSocket ) {
|
||||
// first disconnects previous dangling lambdas
|
||||
QObject::disconnect(itcpSocket, &QTcpSocket::bytesWritten, 0, 0);
|
||||
|
||||
QObject::connect(itcpSocket, &QTcpSocket::bytesWritten, [this](qint64 ){
|
||||
if ( itcpSocket->bytesToWrite() == 0 )
|
||||
static_cast<T*>(this)->allBytesWritten();
|
||||
|
||||
});
|
||||
|
||||
} else if ( ilocalSocket ) {
|
||||
// first disconnects previous dangling lambdas
|
||||
QObject::disconnect(ilocalSocket, &QLocalSocket::bytesWritten, 0, 0);
|
||||
|
||||
QObject::connect(ilocalSocket, &QLocalSocket::bytesWritten, [this](qint64 ){
|
||||
if ( ilocalSocket->bytesToWrite() == 0 )
|
||||
static_cast<T*>(this)->allBytesWritten();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
iheaderWritten = false;
|
||||
ifinished = false;
|
||||
}
|
||||
|
||||
public:
|
||||
bool addHeader(const QByteArray &field, const QByteArray &value) {
|
||||
if ( ifinished )
|
||||
return false;
|
||||
|
||||
static_cast<T*>(this)->iheaders.insert(field.toLower(), value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeHeader(const QByteArray& field, const QByteArray& value) {
|
||||
if ( ifinished )
|
||||
return false;
|
||||
|
||||
QByteArray buffer = QByteArray(field)
|
||||
.append(": ")
|
||||
.append(value)
|
||||
.append("\r\n");
|
||||
writeRaw(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeData(const QByteArray& data) {
|
||||
if ( ifinished )
|
||||
return false;
|
||||
|
||||
static_cast<T*>(this)->ensureWritingHeaders();
|
||||
writeRaw(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool endPacket(const QByteArray& data) {
|
||||
if ( !writeData(data) )
|
||||
return false;
|
||||
|
||||
if ( itcpSocket )
|
||||
itcpSocket->flush();
|
||||
else if ( ilocalSocket )
|
||||
ilocalSocket->flush();
|
||||
|
||||
ifinished = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
void writeRaw(const QByteArray &data) {
|
||||
if ( itcpSocket )
|
||||
itcpSocket->write(data);
|
||||
else if ( ilocalSocket )
|
||||
ilocalSocket->write(data);
|
||||
}
|
||||
|
||||
public:
|
||||
QTcpSocket* itcpSocket = nullptr;
|
||||
QLocalSocket* ilocalSocket = nullptr;
|
||||
|
||||
bool iheaderWritten;
|
||||
bool ifinished;
|
||||
};
|
||||
#endif
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // QHTTPBASE_HPP
|
167
external/qhttp/src/private/qhttpclient_private.hpp
vendored
Normal file
167
external/qhttp/src/private/qhttpclient_private.hpp
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
/** private imeplementation.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPCLIENT_PRIVATE_HPP
|
||||
#define QHTTPCLIENT_PRIVATE_HPP
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "qhttpclient.hpp"
|
||||
#include "qhttpclientrequest_private.hpp"
|
||||
#include "qhttpclientresponse_private.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace client {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class QHttpClientPrivate : public HttpParser<QHttpClientPrivate>
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QHttpClient)
|
||||
|
||||
public:
|
||||
explicit QHttpClientPrivate(QHttpClient* q) : HttpParser(HTTP_RESPONSE), q_ptr(q) {
|
||||
QObject::connect(q_func(), &QHttpClient::disconnected, [this](){
|
||||
release();
|
||||
});
|
||||
|
||||
QHTTP_LINE_DEEPLOG
|
||||
}
|
||||
|
||||
virtual ~QHttpClientPrivate() {
|
||||
QHTTP_LINE_DEEPLOG
|
||||
}
|
||||
|
||||
void release() {
|
||||
// if socket drops and http_parser can not call messageComplete, dispatch the ilastResponse
|
||||
onDispatchResponse();
|
||||
|
||||
isocket.disconnectAllQtConnections();
|
||||
isocket.close();
|
||||
isocket.release();
|
||||
|
||||
if ( ilastRequest ) {
|
||||
ilastRequest->deleteLater();
|
||||
ilastRequest = nullptr;
|
||||
}
|
||||
if ( ilastResponse ) {
|
||||
ilastResponse->deleteLater();
|
||||
ilastResponse = nullptr;
|
||||
}
|
||||
|
||||
// must be called! or the later http_parser_execute() may fail
|
||||
http_parser_init(&iparser, HTTP_RESPONSE);
|
||||
}
|
||||
|
||||
void initializeSocket() {
|
||||
if ( isocket.isOpen() ) {
|
||||
if ( ikeepAlive ) // no need to reconnect. do nothing and simply return
|
||||
return;
|
||||
|
||||
// close previous connection
|
||||
release(); // release now! instead being called by emitted disconnected signal
|
||||
}
|
||||
|
||||
ikeepAlive = false;
|
||||
|
||||
// create a tcp connection
|
||||
if ( isocket.ibackendType == ETcpSocket ) {
|
||||
|
||||
QTcpSocket* sok = new QTcpSocket(q_func());
|
||||
isocket.itcpSocket = sok;
|
||||
|
||||
QObject::connect(sok, &QTcpSocket::connected, [this](){
|
||||
onConnected();
|
||||
});
|
||||
QObject::connect(sok, &QTcpSocket::readyRead, [this](){
|
||||
onReadyRead();
|
||||
});
|
||||
QObject::connect(sok, &QTcpSocket::bytesWritten, [this](qint64){
|
||||
if ( isocket.itcpSocket->bytesToWrite() == 0 && ilastRequest )
|
||||
emit ilastRequest->allBytesWritten();
|
||||
});
|
||||
QObject::connect(sok, &QTcpSocket::disconnected,
|
||||
q_func(), &QHttpClient::disconnected);
|
||||
|
||||
} else if ( isocket.ibackendType == ELocalSocket ) {
|
||||
|
||||
QLocalSocket* sok = new QLocalSocket(q_func());
|
||||
isocket.ilocalSocket = sok;
|
||||
|
||||
QObject::connect(sok, &QLocalSocket::connected, [this](){
|
||||
onConnected();
|
||||
});
|
||||
QObject::connect(sok, &QLocalSocket::readyRead, [this](){
|
||||
onReadyRead();
|
||||
});
|
||||
QObject::connect(sok, &QLocalSocket::bytesWritten, [this](qint64){
|
||||
if ( isocket.ilocalSocket->bytesToWrite() == 0 && ilastRequest )
|
||||
emit ilastRequest->allBytesWritten();
|
||||
});
|
||||
QObject::connect(sok, &QLocalSocket::disconnected,
|
||||
q_func(), &QHttpClient::disconnected);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
int messageBegin(http_parser* parser);
|
||||
int url(http_parser*, const char*, size_t) {
|
||||
return 0; // not used in parsing incoming respone.
|
||||
}
|
||||
int status(http_parser* parser, const char* at, size_t length) ;
|
||||
int headerField(http_parser* parser, const char* at, size_t length);
|
||||
int headerValue(http_parser* parser, const char* at, size_t length);
|
||||
int headersComplete(http_parser* parser);
|
||||
int body(http_parser* parser, const char* at, size_t length);
|
||||
int messageComplete(http_parser* parser);
|
||||
|
||||
protected:
|
||||
void onConnected() {
|
||||
if ( itimeOut > 0 )
|
||||
itimer.start(itimeOut, Qt::CoarseTimer, q_func());
|
||||
|
||||
if ( ireqHandler )
|
||||
ireqHandler(ilastRequest);
|
||||
else
|
||||
q_func()->onRequestReady(ilastRequest);
|
||||
}
|
||||
|
||||
void onReadyRead() {
|
||||
while ( isocket.bytesAvailable() > 0 ) {
|
||||
char buffer[4097] = {0};
|
||||
size_t readLength = (size_t) isocket.readRaw(buffer, 4096);
|
||||
|
||||
parse(buffer, readLength);
|
||||
}
|
||||
|
||||
onDispatchResponse();
|
||||
}
|
||||
|
||||
void onDispatchResponse() {
|
||||
// if ilastResponse has been sent previously, just return
|
||||
if ( ilastResponse->d_func()->ireadState == QHttpResponsePrivate::ESent )
|
||||
return;
|
||||
|
||||
ilastResponse->d_func()->ireadState = QHttpResponsePrivate::ESent;
|
||||
emit ilastResponse->end();
|
||||
}
|
||||
|
||||
protected:
|
||||
QHttpClient* const q_ptr;
|
||||
|
||||
QHttpRequest* ilastRequest = nullptr;
|
||||
QHttpResponse* ilastResponse = nullptr;
|
||||
TRequstHandler ireqHandler;
|
||||
TResponseHandler irespHandler;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace client
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // QHTTPCLIENT_PRIVATE_HPP
|
56
external/qhttp/src/private/qhttpclientrequest_private.hpp
vendored
Normal file
56
external/qhttp/src/private/qhttpclientrequest_private.hpp
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
/** private imeplementation.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPCLIENT_REQUEST_PRIVATE_HPP
|
||||
#define QHTTPCLIENT_REQUEST_PRIVATE_HPP
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#include "qhttpbase.hpp"
|
||||
#include "qhttpclient.hpp"
|
||||
#include "qhttpclientrequest.hpp"
|
||||
|
||||
#include <QTcpSocket>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace client {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class QHttpRequestPrivate : public HttpWriter<HttpRequestBase, QHttpRequestPrivate>
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QHttpRequest)
|
||||
|
||||
public:
|
||||
explicit QHttpRequestPrivate(QHttpClient* cli, QHttpRequest* q) : q_ptr(q), iclient(cli) {
|
||||
QHTTP_LINE_DEEPLOG
|
||||
}
|
||||
|
||||
virtual ~QHttpRequestPrivate() {
|
||||
QHTTP_LINE_DEEPLOG
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
iversion = "1.1";
|
||||
|
||||
isocket.ibackendType = iclient->backendType();
|
||||
isocket.itcpSocket = iclient->tcpSocket();
|
||||
isocket.ilocalSocket = iclient->localSocket();
|
||||
}
|
||||
|
||||
QByteArray makeTitle();
|
||||
|
||||
void prepareHeadersToWrite();
|
||||
|
||||
protected:
|
||||
QHttpRequest* const q_ptr;
|
||||
QHttpClient* const iclient;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace client
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // QHTTPCLIENT_REQUEST_PRIVATE_HPP
|
50
external/qhttp/src/private/qhttpclientresponse_private.hpp
vendored
Normal file
50
external/qhttp/src/private/qhttpclientresponse_private.hpp
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
/** private imeplementation.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPCLIENT_RESPONSE_PRIVATE_HPP
|
||||
#define QHTTPCLIENT_RESPONSE_PRIVATE_HPP
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "qhttpbase.hpp"
|
||||
#include "qhttpclient.hpp"
|
||||
#include "qhttpclientresponse.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace client {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class QHttpResponsePrivate : public HttpReader<HttpResponseBase>
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QHttpResponse)
|
||||
QHttpResponse* const q_ptr;
|
||||
|
||||
public:
|
||||
explicit QHttpResponsePrivate(QHttpClient* cli, QHttpResponse* q)
|
||||
: q_ptr(q), iclient(cli) {
|
||||
QHTTP_LINE_DEEPLOG
|
||||
}
|
||||
|
||||
virtual ~QHttpResponsePrivate() {
|
||||
QHTTP_LINE_DEEPLOG
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
}
|
||||
|
||||
public:
|
||||
QString icustomStatusMessage;
|
||||
|
||||
protected:
|
||||
QHttpClient* const iclient;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace client
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // QHTTPCLIENT_RESPONSE_PRIVATE_HPP
|
90
external/qhttp/src/private/qhttpserver_private.hpp
vendored
Normal file
90
external/qhttp/src/private/qhttpserver_private.hpp
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
/** private imeplementation.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPSERVER_PRIVATE_HPP
|
||||
#define QHTTPSERVER_PRIVATE_HPP
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "qhttpserver.hpp"
|
||||
#include "qhttpserverconnection.hpp"
|
||||
#include "qhttpserverrequest.hpp"
|
||||
#include "qhttpserverresponse.hpp"
|
||||
|
||||
#include <QTcpServer>
|
||||
#include <QLocalServer>
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace server {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class QHttpServerPrivate
|
||||
{
|
||||
public:
|
||||
template<class TServer>
|
||||
class BackendServer : public TServer
|
||||
{
|
||||
public:
|
||||
QHttpServer* iserver;
|
||||
|
||||
explicit BackendServer(QHttpServer* s) : TServer(s), iserver(s) {
|
||||
}
|
||||
|
||||
protected:
|
||||
// if it's a QTcpServer
|
||||
virtual void incomingConnection(qintptr socketDescriptor) {
|
||||
iserver->incomingConnection(socketDescriptor);
|
||||
}
|
||||
|
||||
// if it's a QLocalServer
|
||||
virtual void incomingConnection(quintptr socketDescriptor) {
|
||||
iserver->incomingConnection((qintptr) socketDescriptor);
|
||||
}
|
||||
};
|
||||
|
||||
typedef QScopedPointer<BackendServer<QTcpServer>> TTcpServer;
|
||||
typedef QScopedPointer<BackendServer<QLocalServer>> TLocalServer;
|
||||
|
||||
public:
|
||||
quint32 itimeOut = 0;
|
||||
TServerHandler ihandler = nullptr;
|
||||
|
||||
TBackend ibackend = ETcpSocket;
|
||||
|
||||
TTcpServer itcpServer;
|
||||
TLocalServer ilocalServer;
|
||||
|
||||
public:
|
||||
explicit QHttpServerPrivate() {
|
||||
QHTTP_LINE_DEEPLOG
|
||||
}
|
||||
|
||||
virtual ~QHttpServerPrivate() {
|
||||
QHTTP_LINE_DEEPLOG
|
||||
}
|
||||
|
||||
void initialize(TBackend backend, QHttpServer* parent) {
|
||||
ibackend = backend;
|
||||
|
||||
if ( ibackend == ETcpSocket ) {
|
||||
itcpServer.reset( new BackendServer<QTcpServer>(parent) );
|
||||
ilocalServer.reset( nullptr );
|
||||
|
||||
} else if ( ibackend == ELocalSocket ) {
|
||||
itcpServer.reset( nullptr );
|
||||
ilocalServer.reset( new BackendServer<QLocalServer>(parent) );
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace server
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // QHTTPSERVER_PRIVATE_HPP
|
148
external/qhttp/src/private/qhttpserverconnection_private.hpp
vendored
Normal file
148
external/qhttp/src/private/qhttpserverconnection_private.hpp
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
/** private imeplementation.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPSERVER_CONNECTION_PRIVATE_HPP
|
||||
#define QHTTPSERVER_CONNECTION_PRIVATE_HPP
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "qhttpserverconnection.hpp"
|
||||
|
||||
#include "qhttpserverrequest.hpp"
|
||||
#include "qhttpserverresponse.hpp"
|
||||
|
||||
#include "private/qhttpserverrequest_private.hpp"
|
||||
#include "private/qhttpserverresponse_private.hpp"
|
||||
|
||||
#include <QBasicTimer>
|
||||
#include <QFile>
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace server {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class QHttpConnectionPrivate : public HttpParser<QHttpConnectionPrivate>
|
||||
{
|
||||
protected:
|
||||
Q_DECLARE_PUBLIC(QHttpConnection)
|
||||
QHttpConnection* const q_ptr;
|
||||
|
||||
public:
|
||||
QByteArray itempUrl;
|
||||
|
||||
// Since there can only be one request/response pair per connection at any time even with pipelining.
|
||||
QHttpRequest* ilastRequest = nullptr;
|
||||
QHttpResponse* ilastResponse = nullptr;
|
||||
|
||||
|
||||
TServerHandler ihandler = nullptr;
|
||||
|
||||
public:
|
||||
explicit QHttpConnectionPrivate(QHttpConnection* q) : HttpParser(HTTP_REQUEST), q_ptr(q) {
|
||||
|
||||
QObject::connect(q_func(), &QHttpConnection::disconnected, [this](){
|
||||
// if socket drops and http_parser can not call messageComplete, dispatch the ilastRequest
|
||||
onDispatchRequest();
|
||||
isocket.release();
|
||||
|
||||
if ( ilastRequest )
|
||||
ilastRequest->deleteLater();
|
||||
if ( ilastResponse )
|
||||
ilastResponse->deleteLater();
|
||||
|
||||
q_func()->deleteLater();
|
||||
});
|
||||
|
||||
QHTTP_LINE_DEEPLOG
|
||||
}
|
||||
|
||||
virtual ~QHttpConnectionPrivate() {
|
||||
QHTTP_LINE_DEEPLOG
|
||||
}
|
||||
|
||||
void createSocket(qintptr sokDesc, TBackend bend) {
|
||||
isocket.ibackendType = bend;
|
||||
|
||||
if ( bend == ETcpSocket ) {
|
||||
QTcpSocket* sok = new QTcpSocket( q_func() );
|
||||
isocket.itcpSocket = sok;
|
||||
sok->setSocketDescriptor(sokDesc);
|
||||
|
||||
QObject::connect(sok, &QTcpSocket::readyRead, [this](){
|
||||
onReadyRead();
|
||||
});
|
||||
QObject::connect(sok, &QTcpSocket::bytesWritten, [this](){
|
||||
if ( isocket.itcpSocket->bytesToWrite() == 0 && ilastResponse )
|
||||
emit ilastResponse->allBytesWritten();
|
||||
});
|
||||
QObject::connect(sok, &QTcpSocket::disconnected,
|
||||
q_func(), &QHttpConnection::disconnected,
|
||||
Qt::QueuedConnection);
|
||||
|
||||
} else if ( bend == ELocalSocket ) {
|
||||
QLocalSocket* sok = new QLocalSocket( q_func() );
|
||||
isocket.ilocalSocket = sok;
|
||||
sok->setSocketDescriptor(sokDesc);
|
||||
|
||||
QObject::connect(sok, &QLocalSocket::readyRead, [this](){
|
||||
onReadyRead();
|
||||
});
|
||||
QObject::connect(sok, &QLocalSocket::bytesWritten, [this](){
|
||||
if ( isocket.ilocalSocket->bytesToWrite() == 0 && ilastResponse )
|
||||
emit ilastResponse->allBytesWritten();
|
||||
});
|
||||
QObject::connect(sok, &QLocalSocket::disconnected,
|
||||
q_func(), &QHttpConnection::disconnected,
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
void onReadyRead() {
|
||||
while ( isocket.bytesAvailable() > 0 ) {
|
||||
char buffer[4097] = {0};
|
||||
size_t readLength = (size_t) isocket.readRaw(buffer, 4096);
|
||||
|
||||
parse(buffer, readLength);
|
||||
}
|
||||
|
||||
onDispatchRequest();
|
||||
}
|
||||
|
||||
void onDispatchRequest() {
|
||||
// if ilastRequest has been sent previously, just return
|
||||
if ( ilastRequest->d_func()->ireadState == QHttpRequestPrivate::ESent )
|
||||
return;
|
||||
|
||||
ilastRequest->d_func()->ireadState = QHttpRequestPrivate::ESent;
|
||||
emit ilastRequest->end();
|
||||
}
|
||||
|
||||
public:
|
||||
int messageBegin(http_parser* parser);
|
||||
int url(http_parser* parser, const char* at, size_t length);
|
||||
int status(http_parser*, const char*, size_t) {
|
||||
return 0; // not used in parsing incoming request.
|
||||
}
|
||||
int headerField(http_parser* parser, const char* at, size_t length);
|
||||
int headerValue(http_parser* parser, const char* at, size_t length);
|
||||
int headersComplete(http_parser* parser);
|
||||
int body(http_parser* parser, const char* at, size_t length);
|
||||
int messageComplete(http_parser* parser);
|
||||
|
||||
#ifdef USE_CUSTOM_URL_CREATOR
|
||||
public:
|
||||
static QUrl createUrl(const char *urlData, const http_parser_url &urlInfo);
|
||||
#endif // USE_CUSTOM_URL_CREATOR
|
||||
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace server
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // QHTTPSERVER_CONNECTION_PRIVATE_HPP
|
50
external/qhttp/src/private/qhttpserverrequest_private.hpp
vendored
Normal file
50
external/qhttp/src/private/qhttpserverrequest_private.hpp
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
/** private imeplementation.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPSERVER_REQUEST_PRIVATE_HPP
|
||||
#define QHTTPSERVER_REQUEST_PRIVATE_HPP
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "qhttpbase.hpp"
|
||||
#include "qhttpserverrequest.hpp"
|
||||
#include "qhttpserverconnection.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace server {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class QHttpRequestPrivate : public HttpReader<HttpRequestBase>
|
||||
{
|
||||
protected:
|
||||
Q_DECLARE_PUBLIC(QHttpRequest)
|
||||
QHttpRequest* const q_ptr;
|
||||
|
||||
public:
|
||||
explicit QHttpRequestPrivate(QHttpConnection* conn, QHttpRequest* q) : q_ptr(q), iconnection(conn) {
|
||||
QHTTP_LINE_DEEPLOG
|
||||
}
|
||||
|
||||
virtual ~QHttpRequestPrivate() {
|
||||
QHTTP_LINE_DEEPLOG
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
}
|
||||
|
||||
public:
|
||||
QString iremoteAddress;
|
||||
quint16 iremotePort = 0;
|
||||
|
||||
QHttpConnection* const iconnection = nullptr;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace server
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // QHTTPSERVER_REQUEST_PRIVATE_HPP
|
61
external/qhttp/src/private/qhttpserverresponse_private.hpp
vendored
Normal file
61
external/qhttp/src/private/qhttpserverresponse_private.hpp
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
/** private imeplementation.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPSERVER_RESPONSE_PRIVATE_HPP
|
||||
#define QHTTPSERVER_RESPONSE_PRIVATE_HPP
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#include "qhttpbase.hpp"
|
||||
#include "qhttpserverresponse.hpp"
|
||||
#include "qhttpserver.hpp"
|
||||
#include "qhttpserverconnection.hpp"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QLocale>
|
||||
#include <QTcpSocket>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace server {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class QHttpResponsePrivate : public HttpWriter<HttpResponseBase, QHttpResponsePrivate>
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QHttpResponse)
|
||||
|
||||
public:
|
||||
explicit QHttpResponsePrivate(QHttpConnection* conn, QHttpResponse* q)
|
||||
: q_ptr(q), iconnection(conn) {
|
||||
QHTTP_LINE_DEEPLOG
|
||||
}
|
||||
|
||||
virtual ~QHttpResponsePrivate() {
|
||||
QHTTP_LINE_DEEPLOG
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
isocket.ibackendType = iconnection->backendType();
|
||||
isocket.ilocalSocket = iconnection->localSocket();
|
||||
isocket.itcpSocket = iconnection->tcpSocket();
|
||||
|
||||
QObject::connect(iconnection, &QHttpConnection::disconnected,
|
||||
q_func(), &QHttpResponse::deleteLater);
|
||||
}
|
||||
|
||||
QByteArray makeTitle();
|
||||
|
||||
void prepareHeadersToWrite();
|
||||
|
||||
protected:
|
||||
QHttpResponse* const q_ptr;
|
||||
QHttpConnection* const iconnection;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace server
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // QHTTPSERVER_RESPONSE_PRIVATE_HPP
|
114
external/qhttp/src/qhttpabstracts.cpp
vendored
Normal file
114
external/qhttp/src/qhttpabstracts.cpp
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
#include "qhttpabstracts.hpp"
|
||||
#include "http-parser/http_parser.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
# error "to compile QHttp classes, Qt 5.0 or later is needed."
|
||||
#endif
|
||||
|
||||
#define HTTP_STATUS_MAP(XX) \
|
||||
XX(100, "Continue") \
|
||||
XX(101, "Switching Protocols") \
|
||||
/* RFC 2518) obsoleted by RFC 4918 */ \
|
||||
XX(102, "Processing") \
|
||||
XX(200, "OK") \
|
||||
XX(201, "Created") \
|
||||
XX(202, "Accepted") \
|
||||
XX(203, "Non-Authoritative Information") \
|
||||
XX(204, "No Content") \
|
||||
XX(205, "Reset Content") \
|
||||
XX(206, "Partial Content") \
|
||||
/* RFC 4918 */ \
|
||||
XX(207, "Multi-Status") \
|
||||
XX(300, "Multiple Choices") \
|
||||
XX(301, "Moved Permanently") \
|
||||
XX(302, "Moved Temporarily") \
|
||||
XX(303, "See Other") \
|
||||
XX(304, "Not Modified") \
|
||||
XX(305, "Use Proxy") \
|
||||
XX(307, "Temporary Redirect") \
|
||||
XX(400, "Bad Request") \
|
||||
XX(401, "Unauthorized") \
|
||||
XX(402, "Payment Required") \
|
||||
XX(403, "Forbidden") \
|
||||
XX(404, "Not Found") \
|
||||
XX(405, "Method Not Allowed") \
|
||||
XX(406, "Not Acceptable") \
|
||||
XX(407, "Proxy Authentication Required") \
|
||||
XX(408, "Request Time-out") \
|
||||
XX(409, "Conflict") \
|
||||
XX(410, "Gone") \
|
||||
XX(411, "Length Required") \
|
||||
XX(412, "Precondition Failed") \
|
||||
XX(413, "Request Entity Too Large") \
|
||||
XX(414, "Request-URI Too Large") \
|
||||
XX(415, "Unsupported Media Type") \
|
||||
XX(416, "Requested Range Not Satisfiable") \
|
||||
XX(417, "Expectation Failed") \
|
||||
/* RFC 2324 */ \
|
||||
XX(418, "I\"m a teapot") \
|
||||
/* RFC 4918 */ \
|
||||
XX(422, "Unprocessable Entity") \
|
||||
/* RFC 4918 */ \
|
||||
XX(423, "Locked") \
|
||||
/* RFC 4918 */ \
|
||||
XX(424, "Failed Dependency") \
|
||||
/* RFC 4918 */ \
|
||||
XX(425, "Unordered Collection") \
|
||||
/* RFC 2817 */ \
|
||||
XX(426, "Upgrade Required") \
|
||||
XX(500, "Internal Server Error") \
|
||||
XX(501, "Not Implemented") \
|
||||
XX(502, "Bad Gateway") \
|
||||
XX(503, "Service Unavailable") \
|
||||
XX(504, "Gateway Time-out") \
|
||||
XX(505, "HTTP Version not supported") \
|
||||
/* RFC 2295 */ \
|
||||
XX(506, "Variant Also Negotiates") \
|
||||
/* RFC 4918 */ \
|
||||
XX(507, "Insufficient Storage") \
|
||||
XX(509, "Bandwidth Limit Exceeded") \
|
||||
/* RFC 2774 */ \
|
||||
XX(510, "Not Extended")
|
||||
|
||||
#define PATCH_STATUS_CODES(n,s) {n, s},
|
||||
static struct {
|
||||
int code;
|
||||
const char* message;
|
||||
} g_status_codes[] {
|
||||
HTTP_STATUS_MAP(PATCH_STATUS_CODES)
|
||||
};
|
||||
#undef PATCH_STATUS_CODES
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const char*
|
||||
Stringify::toString(TStatusCode code) {
|
||||
size_t count = sizeof(g_status_codes) / sizeof(g_status_codes[0]);
|
||||
for ( size_t i = 0; i < count; i++ ) {
|
||||
if ( g_status_codes[i].code == code )
|
||||
return g_status_codes[i].message;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char*
|
||||
Stringify::toString(THttpMethod method) {
|
||||
return http_method_str(static_cast<http_method>(method));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
QHttpAbstractInput::QHttpAbstractInput(QObject* parent) : QObject(parent) {
|
||||
}
|
||||
|
||||
QHttpAbstractOutput::QHttpAbstractOutput(QObject *parent) : QObject(parent) {
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
193
external/qhttp/src/qhttpabstracts.hpp
vendored
Normal file
193
external/qhttp/src/qhttpabstracts.hpp
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
/** interfaces of QHttp' incomming and outgoing classes.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPABSTRACTS_HPP
|
||||
#define QHTTPABSTRACTS_HPP
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#include "qhttpfwd.hpp"
|
||||
|
||||
#include <QObject>
|
||||
#include <functional>
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** a utility class to give the string representation of qhttp types. */
|
||||
class QHTTP_API Stringify
|
||||
{
|
||||
public:
|
||||
/** returns the standard message for an HTTP status code. */
|
||||
static const char* toString(TStatusCode);
|
||||
|
||||
/** returns the standars name of an HTTP method. */
|
||||
static const char* toString(THttpMethod);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef std::function<void (QByteArray)> TDataHandler;
|
||||
typedef std::function<void (void)> TEndHandler;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/** an interface for input (incoming) HTTP packets.
|
||||
* server::QHttpRequest or client::QHttpResponse inherit from this class. */
|
||||
class QHTTP_API QHttpAbstractInput : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/** Return all the headers in the incomming packet.
|
||||
* This returns a reference. If you want to store headers
|
||||
* somewhere else, where the request may be deleted,
|
||||
* make sure you store them as a copy.
|
||||
* @note All header names are <b>lowercase</b> . */
|
||||
virtual const THeaderHash& headers() const=0;
|
||||
|
||||
/** The HTTP version of the packet.
|
||||
* @return A string in the form of "x.x" */
|
||||
virtual const QString& httpVersion() const=0;
|
||||
|
||||
/** If this packet was successfully received.
|
||||
* Set before end() has been emitted, stating whether
|
||||
* the message was properly received. This is false
|
||||
* until the receiving the full request has completed. */
|
||||
virtual bool isSuccessful() const=0;
|
||||
|
||||
signals:
|
||||
/** Emitted when new body data has been received.
|
||||
* @param data Received data.
|
||||
* @note This may be emitted zero or more times depending on the transfer type.
|
||||
* @see onData();
|
||||
*/
|
||||
void data(QByteArray data);
|
||||
|
||||
/** Emitted when the incomming packet has been fully received.
|
||||
* @note The no more data() signals will be emitted after this.
|
||||
* @see onEnd();
|
||||
*/
|
||||
void end();
|
||||
|
||||
public:
|
||||
/** optionally set a handler for data() signal.
|
||||
* @param dataHandler a std::function or lambda handler to receive incoming data.
|
||||
* @note if you set this handler, the data() signal won't be emitted anymore.
|
||||
*/
|
||||
void onData(const TDataHandler& dataHandler) {
|
||||
QObject::connect(this, &QHttpAbstractInput::data, [dataHandler](QByteArray data){
|
||||
dataHandler(data);
|
||||
});
|
||||
}
|
||||
|
||||
/** optionally set a handler for end() signal.
|
||||
* @param endHandler a std::function or lambda handler to receive end notification.
|
||||
* @note if you set this handler, the end() signal won't be emitted anymore.
|
||||
*/
|
||||
void onEnd(const TEndHandler& endHandler) {
|
||||
QObject::connect(this, &QHttpAbstractInput::end, [endHandler](){
|
||||
endHandler();
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
/** tries to collect all the incoming data internally.
|
||||
* @note if you call this method, data() signal won't be emitted and
|
||||
* onData() will have no effect.
|
||||
*
|
||||
* @param atMost maximum acceptable incoming data. if the incoming data
|
||||
* exceeds this value, the connection won't read any more data and
|
||||
* end() signal will be emitted. -1 means unlimited.
|
||||
*/
|
||||
virtual void collectData(int atMost = -1) =0;
|
||||
|
||||
/** returns the collected data requested by collectData(). */
|
||||
virtual const QByteArray& collectedData()const =0;
|
||||
|
||||
|
||||
public:
|
||||
virtual ~QHttpAbstractInput() = default;
|
||||
|
||||
explicit QHttpAbstractInput(QObject* parent);
|
||||
|
||||
Q_DISABLE_COPY(QHttpAbstractInput)
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** an interface for output (outgoing) HTTP packets.
|
||||
* server::QHttpResponse or client::QHttpRequest inherit from this class. */
|
||||
class QHTTP_API QHttpAbstractOutput : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/** changes the HTTP version string ex: "1.1" or "1.0".
|
||||
* version is "1.1" set by default. */
|
||||
virtual void setVersion(const QString& versionString)=0;
|
||||
|
||||
/** helper function. @sa addHeader */
|
||||
template<typename T>
|
||||
void addHeaderValue(const QByteArray &field, T value);
|
||||
|
||||
/** adds an HTTP header to the packet.
|
||||
* @note this method does not actually write anything to socket, just prepares the headers(). */
|
||||
virtual void addHeader(const QByteArray& field, const QByteArray& value)=0;
|
||||
|
||||
/** returns all the headers that already been set. */
|
||||
virtual THeaderHash& headers()=0;
|
||||
|
||||
/** writes a block of data into the HTTP packet.
|
||||
* @note headers are written (flushed) before any data.
|
||||
* @warning after calling this method add a new header, set staus code, set Url have no effect! */
|
||||
virtual void write(const QByteArray &data)=0;
|
||||
|
||||
/** ends (finishes) the outgoing packet by calling write().
|
||||
* headers and data will be flushed to the underlying socket.
|
||||
*
|
||||
* @sa write() */
|
||||
virtual void end(const QByteArray &data = QByteArray())=0;
|
||||
|
||||
signals:
|
||||
/** Emitted when all the data has been sent.
|
||||
* this signal indicates that the underlaying socket has transmitted all
|
||||
* of it's buffered data. */
|
||||
void allBytesWritten();
|
||||
|
||||
/** Emitted when the packet is finished and reports if it was the last packet.
|
||||
* if it was the last packet (google for "Connection: keep-alive / close")
|
||||
* the http connection (socket) will be closed automatically. */
|
||||
void done(bool wasTheLastPacket);
|
||||
|
||||
public:
|
||||
virtual ~QHttpAbstractOutput() = default;
|
||||
|
||||
protected:
|
||||
explicit QHttpAbstractOutput(QObject* parent);
|
||||
|
||||
Q_DISABLE_COPY(QHttpAbstractOutput)
|
||||
};
|
||||
|
||||
template<> inline void
|
||||
QHttpAbstractOutput::addHeaderValue<int>(const QByteArray &field, int value) {
|
||||
addHeader(field, QString::number(value).toLatin1());
|
||||
}
|
||||
|
||||
template<> inline void
|
||||
QHttpAbstractOutput::addHeaderValue<size_t>(const QByteArray &field, size_t value) {
|
||||
addHeader(field, QString::number(value).toLatin1());
|
||||
}
|
||||
|
||||
template<> inline void
|
||||
QHttpAbstractOutput::addHeaderValue<QString>(const QByteArray &field, QString value) {
|
||||
addHeader(field, value.toUtf8());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // QHTTPABSTRACTS_HPP
|
256
external/qhttp/src/qhttpclient.cpp
vendored
Normal file
256
external/qhttp/src/qhttpclient.cpp
vendored
Normal file
@ -0,0 +1,256 @@
|
||||
#include "private/qhttpclient_private.hpp"
|
||||
|
||||
#include <QTimerEvent>
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace client {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
QHttpClient::QHttpClient(QObject *parent)
|
||||
: QObject(parent), d_ptr(new QHttpClientPrivate(this)) {
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
QHttpClient::QHttpClient(QHttpClientPrivate &dd, QObject *parent)
|
||||
: QObject(parent), d_ptr(&dd) {
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
QHttpClient::~QHttpClient() {
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
quint32
|
||||
QHttpClient::timeOut() const {
|
||||
return d_func()->itimeOut;
|
||||
}
|
||||
|
||||
void
|
||||
QHttpClient::setTimeOut(quint32 t) {
|
||||
d_func()->itimeOut = t;
|
||||
}
|
||||
|
||||
bool
|
||||
QHttpClient::isOpen() const {
|
||||
return d_func()->isocket.isOpen();
|
||||
}
|
||||
|
||||
void
|
||||
QHttpClient::killConnection() {
|
||||
d_func()->isocket.close();
|
||||
}
|
||||
|
||||
TBackend
|
||||
QHttpClient::backendType() const {
|
||||
return d_func()->isocket.ibackendType;
|
||||
}
|
||||
|
||||
QTcpSocket*
|
||||
QHttpClient::tcpSocket() const {
|
||||
return d_func()->isocket.itcpSocket;
|
||||
}
|
||||
|
||||
QLocalSocket*
|
||||
QHttpClient::localSocket() const {
|
||||
return d_func()->isocket.ilocalSocket;
|
||||
}
|
||||
|
||||
bool
|
||||
QHttpClient::request(THttpMethod method, QUrl url,
|
||||
const TRequstHandler &reqHandler,
|
||||
const TResponseHandler &resHandler) {
|
||||
Q_D(QHttpClient);
|
||||
|
||||
d->ireqHandler = nullptr;
|
||||
d->irespHandler = nullptr;
|
||||
|
||||
// if url is a local file (UNIX socket) the host could be empty!
|
||||
if ( !url.isValid() || url.isEmpty() /*|| url.host().isEmpty()*/ )
|
||||
return false;
|
||||
|
||||
// process handlers
|
||||
if ( resHandler ) {
|
||||
d->irespHandler = resHandler;
|
||||
|
||||
if ( reqHandler )
|
||||
d->ireqHandler = reqHandler;
|
||||
else
|
||||
d->ireqHandler = [](QHttpRequest* req) ->void {
|
||||
req->addHeader("connection", "close");
|
||||
req->end();
|
||||
};
|
||||
}
|
||||
|
||||
auto requestCreator = [this, method, url]() {
|
||||
// create request object
|
||||
if ( d_ptr->ilastRequest )
|
||||
d_ptr->ilastRequest->deleteLater();
|
||||
|
||||
d_ptr->ilastRequest = new QHttpRequest(this);
|
||||
QObject::connect(d_ptr->ilastRequest, &QHttpRequest::done, [this](bool wasTheLastPacket){
|
||||
d_ptr->ikeepAlive = !wasTheLastPacket;
|
||||
});
|
||||
|
||||
d_ptr->ilastRequest->d_ptr->imethod = method;
|
||||
d_ptr->ilastRequest->d_ptr->iurl = url;
|
||||
};
|
||||
|
||||
// connecting to host/server must be the last thing. (after all function handlers and ...)
|
||||
// check for type
|
||||
if ( url.scheme().toLower() == QLatin1String("file") ) {
|
||||
d->isocket.ibackendType = ELocalSocket;
|
||||
d->initializeSocket();
|
||||
|
||||
requestCreator();
|
||||
|
||||
if ( d->isocket.isOpen() )
|
||||
d->onConnected();
|
||||
else
|
||||
d->isocket.connectTo(url);
|
||||
|
||||
} else {
|
||||
d->isocket.ibackendType = ETcpSocket;
|
||||
d->initializeSocket();
|
||||
|
||||
requestCreator();
|
||||
|
||||
if ( d->isocket.isOpen() )
|
||||
d->onConnected();
|
||||
else
|
||||
d->isocket.connectTo(url.host(), url.port(80));
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
QHttpClient::timerEvent(QTimerEvent *e) {
|
||||
Q_D(QHttpClient);
|
||||
|
||||
if ( e->timerId() == d->itimer.timerId() ) {
|
||||
killConnection();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
QHttpClient::onRequestReady(QHttpRequest *req) {
|
||||
emit httpConnected(req);
|
||||
}
|
||||
|
||||
void
|
||||
QHttpClient::onResponseReady(QHttpResponse *res) {
|
||||
emit newResponse(res);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// if user closes the connection, ends the response or by any other reason
|
||||
// the socket be disconnected, then the iresponse instance may has been deleted.
|
||||
// In these situations reading more http body or emitting end() for incoming response
|
||||
// is not possible.
|
||||
#define CHECK_FOR_DISCONNECTED if ( ilastResponse == nullptr ) \
|
||||
return 0;
|
||||
|
||||
|
||||
int
|
||||
QHttpClientPrivate::messageBegin(http_parser*) {
|
||||
itempHeaderField.clear();
|
||||
itempHeaderValue.clear();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
QHttpClientPrivate::status(http_parser* parser, const char* at, size_t length) {
|
||||
if ( ilastResponse )
|
||||
ilastResponse->deleteLater();
|
||||
|
||||
ilastResponse = new QHttpResponse(q_func());
|
||||
ilastResponse->d_func()->istatus = static_cast<TStatusCode>(parser->status_code);
|
||||
ilastResponse->d_func()->iversion = QString("%1.%2")
|
||||
.arg(parser->http_major)
|
||||
.arg(parser->http_minor);
|
||||
ilastResponse->d_func()->icustomStatusMessage = QString::fromUtf8(at, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
QHttpClientPrivate::headerField(http_parser*, const char* at, size_t length) {
|
||||
CHECK_FOR_DISCONNECTED
|
||||
|
||||
// insert the header we parsed previously
|
||||
// into the header map
|
||||
if ( !itempHeaderField.isEmpty() && !itempHeaderValue.isEmpty() ) {
|
||||
// header names are always lower-cased
|
||||
ilastResponse->d_func()->iheaders.insert(
|
||||
itempHeaderField.toLower(),
|
||||
itempHeaderValue.toLower()
|
||||
);
|
||||
// clear header value. this sets up a nice
|
||||
// feedback loop where the next time
|
||||
// HeaderValue is called, it can simply append
|
||||
itempHeaderField.clear();
|
||||
itempHeaderValue.clear();
|
||||
}
|
||||
|
||||
itempHeaderField.append(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
QHttpClientPrivate::headerValue(http_parser*, const char* at, size_t length) {
|
||||
|
||||
itempHeaderValue.append(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
QHttpClientPrivate::headersComplete(http_parser*) {
|
||||
CHECK_FOR_DISCONNECTED
|
||||
|
||||
// Insert last remaining header
|
||||
ilastResponse->d_func()->iheaders.insert(
|
||||
itempHeaderField.toLower(),
|
||||
itempHeaderValue.toLower()
|
||||
);
|
||||
|
||||
if ( irespHandler )
|
||||
irespHandler(ilastResponse);
|
||||
else
|
||||
q_func()->onResponseReady(ilastResponse);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
QHttpClientPrivate::body(http_parser*, const char* at, size_t length) {
|
||||
CHECK_FOR_DISCONNECTED
|
||||
|
||||
ilastResponse->d_func()->ireadState = QHttpResponsePrivate::EPartial;
|
||||
|
||||
if ( ilastResponse->d_func()->shouldCollect() ) {
|
||||
if ( !ilastResponse->d_func()->append(at, length) )
|
||||
onDispatchResponse(); // forcefully dispatch the ilastResponse
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
emit ilastResponse->data(QByteArray(at, length));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
QHttpClientPrivate::messageComplete(http_parser*) {
|
||||
CHECK_FOR_DISCONNECTED
|
||||
|
||||
// response is ready to be dispatched
|
||||
ilastResponse->d_func()->isuccessful = true;
|
||||
ilastResponse->d_func()->ireadState = QHttpResponsePrivate::EComplete;
|
||||
return 0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace client
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
160
external/qhttp/src/qhttpclient.hpp
vendored
Normal file
160
external/qhttp/src/qhttpclient.hpp
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
/** HTTP client class.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPCLIENT_HPP
|
||||
#define QHTTPCLIENT_HPP
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#include "qhttpfwd.hpp"
|
||||
|
||||
#include <QTcpSocket>
|
||||
#include <QUrl>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace client {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
typedef std::function<void (QHttpRequest*)> TRequstHandler;
|
||||
typedef std::function<void (QHttpResponse*)> TResponseHandler;
|
||||
|
||||
/** a simple and async HTTP client class which sends a request to an HTTP server and parses the
|
||||
* corresponding response.
|
||||
* This class internally handles the memory management and life cycle of QHttpRequest and
|
||||
* QHttpResponse instances. you do not have to manually delete or keep their pointers.
|
||||
* in fact the QHttpRequest and QHttpResponse object will be deleted when the internal socket
|
||||
* disconnects.
|
||||
*/
|
||||
class QHTTP_API QHttpClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(quint32 timeOut READ timeOut WRITE setTimeOut)
|
||||
|
||||
public:
|
||||
explicit QHttpClient(QObject *parent = nullptr);
|
||||
|
||||
virtual ~QHttpClient();
|
||||
|
||||
/** tries to connect to a HTTP server.
|
||||
* when the connection is made, the reqHandler will be called
|
||||
* and when the response is ready, resHandler will be called.
|
||||
* @note httpConnected() and newResponse() won't be emitted.
|
||||
*
|
||||
* @param method an HTTP method, ex: GET, POST, ...
|
||||
* @param url specifies server's address, port and optional path and query strings.
|
||||
* if url starts with socket:// the request will be made on QLocalSocket, otherwise
|
||||
* normal QTcpSocket will be used.
|
||||
* @param resHandler response handler (a lambda, std::function object, ...)
|
||||
* @return true if the url is valid or false (no connection will be made).
|
||||
*/
|
||||
bool request(THttpMethod method, QUrl url,
|
||||
const TRequstHandler& reqHandler,
|
||||
const TResponseHandler& resHandler);
|
||||
|
||||
/** tries to connect to a HTTP server.
|
||||
* when the connection is made, a default request handler is called automatically (
|
||||
* simply calls req->end()) and when the response is ready, resHandler will be called.
|
||||
* @note httpConnected() and newResponse() won't be emitted.
|
||||
*
|
||||
* @param method an HTTP method, ex: GET, POST, ...
|
||||
* @param url specifies server's address, port and optional path and query strings.
|
||||
* @param resHandler response handler (a lambda, std::function object, ...)
|
||||
* @return true if the url is valid or false (no connection will be made).
|
||||
*/
|
||||
inline bool request(THttpMethod method, QUrl url, const TResponseHandler& resHandler) {
|
||||
return request(method, url, nullptr, resHandler);
|
||||
}
|
||||
|
||||
/** tries to connect to a HTTP server.
|
||||
* when the connection is made, creates and emits a QHttpRequest instance
|
||||
* by @sa httpConnected(QHttpRequest*).
|
||||
* @note both httpConnected() and newResponse() may be emitted.
|
||||
*
|
||||
* @param method an HTTP method, ex: GET, POST, ...
|
||||
* @param url specifies server's address, port and optional path and query strings.
|
||||
* @return true if the url is valid or false (no connection will be made).
|
||||
*/
|
||||
inline bool request(THttpMethod method, QUrl url) {
|
||||
return request(method, url, nullptr, nullptr);
|
||||
}
|
||||
|
||||
/** checks if the connetion to the server is open. */
|
||||
bool isOpen() const;
|
||||
|
||||
/** forcefully close the connection. */
|
||||
void killConnection();
|
||||
|
||||
|
||||
/** returns time-out value [mSec] for open connections (sockets).
|
||||
* @sa setTimeOut(). */
|
||||
quint32 timeOut()const;
|
||||
|
||||
/** set time-out for new open connections in miliseconds [mSec].
|
||||
* each connection will be forcefully closed after this timeout.
|
||||
* a zero (0) value disables timer for new connections. */
|
||||
void setTimeOut(quint32);
|
||||
|
||||
/** returns the backend type of this client. */
|
||||
TBackend backendType() const;
|
||||
|
||||
/** returns tcp socket of the connection if backend() == ETcpSocket. */
|
||||
QTcpSocket* tcpSocket() const;
|
||||
|
||||
/** returns local socket of the connection if backend() == ELocalSocket. */
|
||||
QLocalSocket* localSocket() const;
|
||||
|
||||
signals:
|
||||
/** emitted when a new HTTP connection to the server is established.
|
||||
* if you overload onRequestReady this signal won't be emitted.
|
||||
* @sa onRequestReady
|
||||
* @sa QHttpRequest
|
||||
*/
|
||||
void httpConnected(QHttpRequest* req);
|
||||
|
||||
/** emitted when a new response is received from the server.
|
||||
* if you overload onResponseReady this signal won't be emitted.
|
||||
* @sa onResponseReady
|
||||
* @sa QHttpResponse
|
||||
*/
|
||||
void newResponse(QHttpResponse* res);
|
||||
|
||||
/** emitted when the HTTP connection drops or being disconnected. */
|
||||
void disconnected();
|
||||
|
||||
|
||||
protected:
|
||||
/** called when a new HTTP connection is established.
|
||||
* you can overload this method, the default implementaion only emits connected().
|
||||
* @param req use this request instance for assinging the
|
||||
* request headers and sending optional body.
|
||||
* @see httpConnected(QHttpRequest*)
|
||||
*/
|
||||
virtual void onRequestReady(QHttpRequest* req);
|
||||
|
||||
/** called when a new response is received from the server.
|
||||
* you can overload this method, the default implementaion only emits newResponse().
|
||||
* @param res use this instance for reading incoming response.
|
||||
* @see newResponse(QHttpResponse*)
|
||||
*/
|
||||
virtual void onResponseReady(QHttpResponse* res);
|
||||
|
||||
protected:
|
||||
explicit QHttpClient(QHttpClientPrivate&, QObject*);
|
||||
|
||||
void timerEvent(QTimerEvent*) override;
|
||||
|
||||
Q_DECLARE_PRIVATE(QHttpClient)
|
||||
Q_DISABLE_COPY(QHttpClient)
|
||||
QScopedPointer<QHttpClientPrivate> d_ptr;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace client
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // define QHTTPCLIENT_HPP
|
98
external/qhttp/src/qhttpclientrequest.cpp
vendored
Normal file
98
external/qhttp/src/qhttpclientrequest.cpp
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
#include "private/qhttpclientrequest_private.hpp"
|
||||
#include "qhttpclient.hpp"
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace client {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
QHttpRequest::QHttpRequest(QHttpClient* cli)
|
||||
: QHttpAbstractOutput(cli) , d_ptr(new QHttpRequestPrivate(cli, this)) {
|
||||
d_ptr->initialize();
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
QHttpRequest::QHttpRequest(QHttpRequestPrivate& dd, QHttpClient* cli)
|
||||
: QHttpAbstractOutput(cli) , d_ptr(&dd) {
|
||||
d_ptr->initialize();
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
QHttpRequest::~QHttpRequest() {
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
void
|
||||
QHttpRequest::setVersion(const QString &versionString) {
|
||||
d_func()->iversion = versionString;
|
||||
}
|
||||
|
||||
void
|
||||
QHttpRequest::addHeader(const QByteArray &field, const QByteArray &value) {
|
||||
d_func()->addHeader(field, value);
|
||||
}
|
||||
|
||||
THeaderHash&
|
||||
QHttpRequest::headers() {
|
||||
return d_func()->iheaders;
|
||||
}
|
||||
|
||||
void
|
||||
QHttpRequest::write(const QByteArray &data) {
|
||||
d_func()->writeData(data);
|
||||
}
|
||||
|
||||
void
|
||||
QHttpRequest::end(const QByteArray &data) {
|
||||
Q_D(QHttpRequest);
|
||||
|
||||
if ( d->endPacket(data) )
|
||||
emit done(!d->ikeepAlive);
|
||||
}
|
||||
|
||||
QHttpClient*
|
||||
QHttpRequest::connection() const {
|
||||
return d_func()->iclient;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
QByteArray
|
||||
QHttpRequestPrivate::makeTitle() {
|
||||
|
||||
QByteArray title;
|
||||
title.reserve(512);
|
||||
title.append(qhttp::Stringify::toString(imethod))
|
||||
.append(" ");
|
||||
|
||||
QByteArray path = iurl.path(QUrl::FullyEncoded).toLatin1();
|
||||
if ( path.size() == 0 )
|
||||
path = "/";
|
||||
title.append(path);
|
||||
|
||||
if ( iurl.hasQuery() )
|
||||
title.append("?").append(iurl.query(QUrl::FullyEncoded).toLatin1());
|
||||
|
||||
|
||||
title.append(" HTTP/")
|
||||
.append(iversion.toLatin1())
|
||||
.append("\r\n");
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
void
|
||||
QHttpRequestPrivate::prepareHeadersToWrite() {
|
||||
|
||||
if ( !iheaders.contains("host") ) {
|
||||
quint16 port = iurl.port();
|
||||
if ( port == 0 )
|
||||
port = 80;
|
||||
|
||||
iheaders.insert("host",
|
||||
QString("%1:%2").arg(iurl.host()).arg(port).toLatin1()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace client
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
63
external/qhttp/src/qhttpclientrequest.hpp
vendored
Normal file
63
external/qhttp/src/qhttpclientrequest.hpp
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
/** HTTP request from a client.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPCLIENT_REQUEST_HPP
|
||||
#define QHTTPCLIENT_REQUEST_HPP
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#include "qhttpabstracts.hpp"
|
||||
#include <QUrl>
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace client {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/** a class for building a new HTTP request.
|
||||
* the life cycle of this class and the memory management is handled by QHttpClient.
|
||||
* @sa QHttpClient
|
||||
*/
|
||||
class QHTTP_API QHttpRequest : public QHttpAbstractOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
virtual ~QHttpRequest();
|
||||
|
||||
public: // QHttpAbstractOutput methods:
|
||||
/** @see QHttpAbstractOutput::setVersion(). */
|
||||
void setVersion(const QString& versionString) override;
|
||||
|
||||
/** @see QHttpAbstractOutput::addHeader(). */
|
||||
void addHeader(const QByteArray& field, const QByteArray& value) override;
|
||||
|
||||
/** @see QHttpAbstractOutput::headers(). */
|
||||
THeaderHash& headers() override;
|
||||
|
||||
/** @see QHttpAbstractOutput::write(). */
|
||||
void write(const QByteArray &data) override;
|
||||
|
||||
/** @see QHttpAbstractOutput::end(). */
|
||||
void end(const QByteArray &data = QByteArray()) override;
|
||||
|
||||
public:
|
||||
/** returns parent QHttpClient object. */
|
||||
QHttpClient* connection() const;
|
||||
|
||||
protected:
|
||||
explicit QHttpRequest(QHttpClient*);
|
||||
explicit QHttpRequest(QHttpRequestPrivate&, QHttpClient*);
|
||||
friend class QHttpClient;
|
||||
|
||||
Q_DECLARE_PRIVATE(QHttpRequest)
|
||||
QScopedPointer<QHttpRequestPrivate> d_ptr;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace client
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // define QHTTPCLIENT_REQUEST_HPP
|
66
external/qhttp/src/qhttpclientresponse.cpp
vendored
Normal file
66
external/qhttp/src/qhttpclientresponse.cpp
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
#include "private/qhttpclientresponse_private.hpp"
|
||||
#include "qhttpclient.hpp"
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace client {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
QHttpResponse::QHttpResponse(QHttpClient *cli)
|
||||
: QHttpAbstractInput(cli), d_ptr(new QHttpResponsePrivate(cli, this)) {
|
||||
d_ptr->initialize();
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
QHttpResponse::QHttpResponse(QHttpResponsePrivate &dd, QHttpClient *cli)
|
||||
: QHttpAbstractInput(cli), d_ptr(&dd) {
|
||||
d_ptr->initialize();
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
QHttpResponse::~QHttpResponse() {
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
TStatusCode
|
||||
QHttpResponse::status() const {
|
||||
return d_func()->istatus;
|
||||
}
|
||||
|
||||
const QString&
|
||||
QHttpResponse::statusString() const {
|
||||
return d_func()->icustomStatusMessage;
|
||||
}
|
||||
|
||||
const QString&
|
||||
QHttpResponse::httpVersion() const {
|
||||
return d_func()->iversion;
|
||||
}
|
||||
|
||||
const THeaderHash&
|
||||
QHttpResponse::headers() const {
|
||||
return d_func()->iheaders;
|
||||
}
|
||||
|
||||
bool
|
||||
QHttpResponse::isSuccessful() const {
|
||||
return d_func()->isuccessful;
|
||||
}
|
||||
|
||||
void
|
||||
QHttpResponse::collectData(int atMost) {
|
||||
d_func()->collectData(atMost);
|
||||
}
|
||||
|
||||
const QByteArray&
|
||||
QHttpResponse::collectedData() const {
|
||||
return d_func()->icollectedData;
|
||||
}
|
||||
|
||||
QHttpClient*
|
||||
QHttpResponse::connection() const {
|
||||
return d_func()->iclient;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace client
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
73
external/qhttp/src/qhttpclientresponse.hpp
vendored
Normal file
73
external/qhttp/src/qhttpclientresponse.hpp
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
/** HTTP response received by client.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPCLIENT_RESPONSE_HPP
|
||||
#define QHTTPCLIENT_RESPONSE_HPP
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "qhttpabstracts.hpp"
|
||||
|
||||
#include <QUrl>
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace client {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/** a class for reading incoming HTTP response from a server.
|
||||
* the life cycle of this class and the memory management is handled by QHttpClient.
|
||||
* @sa QHttpClient
|
||||
*/
|
||||
class QHTTP_API QHttpResponse : public QHttpAbstractInput
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
virtual ~QHttpResponse();
|
||||
|
||||
public: // QHttpAbstractInput methods:
|
||||
/** @see QHttpAbstractInput::headers(). */
|
||||
const THeaderHash& headers() const override;
|
||||
|
||||
/** @see QHttpAbstractInput::httpVersion(). */
|
||||
const QString& httpVersion() const override;
|
||||
|
||||
/** @see QHttpAbstractInput::isSuccessful(). */
|
||||
bool isSuccessful() const override;
|
||||
|
||||
/** @see QHttpAbstractInput::collectData(). */
|
||||
void collectData(int atMost = -1) override;
|
||||
|
||||
/** @see QHttpAbstractInput::collectedData(). */
|
||||
const QByteArray& collectedData()const override;
|
||||
|
||||
|
||||
public:
|
||||
/** The status code of this response. */
|
||||
TStatusCode status() const ;
|
||||
|
||||
/** The server status message as string.
|
||||
* may be slightly different than: @code qhttp::Stringify::toString(status()); @endcode
|
||||
* depending on implementation of HTTP server. */
|
||||
const QString& statusString() const;
|
||||
|
||||
/** returns parent QHttpClient object. */
|
||||
QHttpClient* connection() const;
|
||||
|
||||
protected:
|
||||
explicit QHttpResponse(QHttpClient*);
|
||||
explicit QHttpResponse(QHttpResponsePrivate&, QHttpClient*);
|
||||
friend class QHttpClientPrivate;
|
||||
|
||||
Q_DECLARE_PRIVATE(QHttpResponse)
|
||||
QScopedPointer<QHttpResponsePrivate> d_ptr;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace client
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // define QHTTPCLIENT_RESPONSE_HPP
|
197
external/qhttp/src/qhttpfwd.hpp
vendored
Normal file
197
external/qhttp/src/qhttpfwd.hpp
vendored
Normal file
@ -0,0 +1,197 @@
|
||||
/** forward declarations and general definitions of the QHttp.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPFWD_HPP
|
||||
#define QHTTPFWD_HPP
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <functional>
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Qt
|
||||
class QTcpServer;
|
||||
class QTcpSocket;
|
||||
class QLocalServer;
|
||||
class QLocalSocket;
|
||||
|
||||
// http_parser
|
||||
struct http_parser_settings;
|
||||
struct http_parser;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** A map of request or response headers. */
|
||||
class THeaderHash : public QHash<QByteArray, QByteArray>
|
||||
{
|
||||
public:
|
||||
/** checks for a header item, regardless of the case of the characters. */
|
||||
inline bool has(const QByteArray& key) const {
|
||||
return contains(key.toLower());
|
||||
}
|
||||
|
||||
/** checks if a header has the specified value ignoring the case of the characters. */
|
||||
inline bool keyHasValue(const QByteArray& key, const QByteArray& value) const {
|
||||
if ( !contains(key) )
|
||||
return false;
|
||||
|
||||
const QByteArray& v = QHash<QByteArray, QByteArray>::value(key);
|
||||
return qstrnicmp(value.constData(), v.constData(), v.size()) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
/** Request method enumeration.
|
||||
* @note Taken from http_parser.h */
|
||||
enum THttpMethod {
|
||||
EHTTP_DELETE = 0, ///< DELETE
|
||||
EHTTP_GET = 1, ///< GET
|
||||
EHTTP_HEAD = 2, ///< HEAD
|
||||
EHTTP_POST = 3, ///< POST
|
||||
EHTTP_PUT = 4, ///< PUT
|
||||
/* pathological */
|
||||
EHTTP_CONNECT = 5, ///< CONNECT
|
||||
EHTTP_OPTIONS = 6, ///< OPTIONS
|
||||
EHTTP_TRACE = 7, ///< TRACE
|
||||
/* webdav */
|
||||
EHTTP_COPY = 8, ///< COPY
|
||||
EHTTP_LOCK = 9, ///< LOCK
|
||||
EHTTP_MKCOL = 10, ///< MKCOL
|
||||
EHTTP_MOVE = 11, ///< MOVE
|
||||
EHTTP_PROPFIND = 12, ///< PROPFIND
|
||||
EHTTP_PROPPATCH = 13, ///< PROPPATCH
|
||||
EHTTP_SEARCH = 14, ///< SEARCH
|
||||
EHTTP_UNLOCK = 15, ///< UNLOCK
|
||||
/* subversion */
|
||||
EHTTP_REPORT = 16, ///< REPORT
|
||||
EHTTP_MKACTIVITY = 17, ///< MKACTIVITY
|
||||
EHTTP_CHECKOUT = 18, ///< CHECKOUT
|
||||
EHTTP_MERGE = 19, ///< MERGE
|
||||
/* upnp */
|
||||
EHTTP_MSEARCH = 20, ///< M-SEARCH
|
||||
EHTTP_NOTIFY = 21, ///< NOTIFY
|
||||
EHTTP_SUBSCRIBE = 22, ///< SUBSCRIBE
|
||||
EHTTP_UNSUBSCRIBE = 23, ///< UNSUBSCRIBE
|
||||
/* RFC-5789 */
|
||||
EHTTP_PATCH = 24, ///< PATCH
|
||||
EHTTP_PURGE = 25, ///< PURGE
|
||||
};
|
||||
|
||||
/** HTTP status codes. */
|
||||
enum TStatusCode {
|
||||
ESTATUS_CONTINUE = 100,
|
||||
ESTATUS_SWITCH_PROTOCOLS = 101,
|
||||
ESTATUS_OK = 200,
|
||||
ESTATUS_CREATED = 201,
|
||||
ESTATUS_ACCEPTED = 202,
|
||||
ESTATUS_NON_AUTHORITATIVE_INFORMATION = 203,
|
||||
ESTATUS_NO_CONTENT = 204,
|
||||
ESTATUS_RESET_CONTENT = 205,
|
||||
ESTATUS_PARTIAL_CONTENT = 206,
|
||||
ESTATUS_MULTI_STATUS = 207,
|
||||
ESTATUS_MULTIPLE_CHOICES = 300,
|
||||
ESTATUS_MOVED_PERMANENTLY = 301,
|
||||
ESTATUS_FOUND = 302,
|
||||
ESTATUS_SEE_OTHER = 303,
|
||||
ESTATUS_NOT_MODIFIED = 304,
|
||||
ESTATUS_USE_PROXY = 305,
|
||||
ESTATUS_TEMPORARY_REDIRECT = 307,
|
||||
ESTATUS_BAD_REQUEST = 400,
|
||||
ESTATUS_UNAUTHORIZED = 401,
|
||||
ESTATUS_PAYMENT_REQUIRED = 402,
|
||||
ESTATUS_FORBIDDEN = 403,
|
||||
ESTATUS_NOT_FOUND = 404,
|
||||
ESTATUS_METHOD_NOT_ALLOWED = 405,
|
||||
ESTATUS_NOT_ACCEPTABLE = 406,
|
||||
ESTATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||
ESTATUS_REQUEST_TIMEOUT = 408,
|
||||
ESTATUS_CONFLICT = 409,
|
||||
ESTATUS_GONE = 410,
|
||||
ESTATUS_LENGTH_REQUIRED = 411,
|
||||
ESTATUS_PRECONDITION_FAILED = 412,
|
||||
ESTATUS_REQUEST_ENTITY_TOO_LARGE = 413,
|
||||
ESTATUS_REQUEST_URI_TOO_LONG = 414,
|
||||
ESTATUS_REQUEST_UNSUPPORTED_MEDIA_TYPE = 415,
|
||||
ESTATUS_REQUESTED_RANGE_NOT_SATISFIABLE = 416,
|
||||
ESTATUS_EXPECTATION_FAILED = 417,
|
||||
ESTATUS_INTERNAL_SERVER_ERROR = 500,
|
||||
ESTATUS_NOT_IMPLEMENTED = 501,
|
||||
ESTATUS_BAD_GATEWAY = 502,
|
||||
ESTATUS_SERVICE_UNAVAILABLE = 503,
|
||||
ESTATUS_GATEWAY_TIMEOUT = 504,
|
||||
ESTATUS_HTTP_VERSION_NOT_SUPPORTED = 505
|
||||
};
|
||||
|
||||
/** The backend of QHttp library. */
|
||||
enum TBackend {
|
||||
ETcpSocket = 0, ///< client / server work on top of TCP/IP stack. (default)
|
||||
ESslSocket = 1, ///< client / server work on SSL/TLS tcp stack. (not implemented yet)
|
||||
ELocalSocket = 2 ///< client / server work on local socket (unix socket).
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace server {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class QHttpServer;
|
||||
class QHttpConnection;
|
||||
class QHttpRequest;
|
||||
class QHttpResponse;
|
||||
|
||||
// Privte classes
|
||||
class QHttpServerPrivate;
|
||||
class QHttpConnectionPrivate;
|
||||
class QHttpRequestPrivate;
|
||||
class QHttpResponsePrivate;
|
||||
|
||||
typedef std::function<void (QHttpRequest*, QHttpResponse*)> TServerHandler;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace server
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace client {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class QHttpClient;
|
||||
class QHttpRequest;
|
||||
class QHttpResponse;
|
||||
|
||||
// Private classes
|
||||
class QHttpClientPrivate;
|
||||
class QHttpRequestPrivate;
|
||||
class QHttpResponsePrivate;
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace client
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef Q_OS_WIN
|
||||
# if defined(QHTTP_EXPORT)
|
||||
# define QHTTP_API __declspec(dllexport)
|
||||
# else
|
||||
# define QHTTP_API __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define QHTTP_API
|
||||
#endif
|
||||
|
||||
|
||||
#if QHTTP_MEMORY_LOG > 0
|
||||
# define QHTTP_LINE_LOG fprintf(stderr, "%s(): obj = %p @ %s[%d]\n",\
|
||||
__FUNCTION__, this, __FILE__, __LINE__);
|
||||
#else
|
||||
# define QHTTP_LINE_LOG
|
||||
#endif
|
||||
|
||||
#if QHTTP_MEMORY_LOG > 1
|
||||
# define QHTTP_LINE_DEEPLOG QHTTP_LINE_LOG
|
||||
#else
|
||||
# define QHTTP_LINE_DEEPLOG
|
||||
#endif
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // define QHTTPFWD_HPP
|
118
external/qhttp/src/qhttpserver.cpp
vendored
Normal file
118
external/qhttp/src/qhttpserver.cpp
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
#include "private/qhttpserver_private.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace server {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
QHttpServer::QHttpServer(QObject *parent)
|
||||
: QObject(parent), d_ptr(new QHttpServerPrivate) {
|
||||
}
|
||||
|
||||
QHttpServer::QHttpServer(QHttpServerPrivate &dd, QObject *parent)
|
||||
: QObject(parent), d_ptr(&dd) {
|
||||
}
|
||||
|
||||
QHttpServer::~QHttpServer() {
|
||||
stopListening();
|
||||
}
|
||||
|
||||
bool
|
||||
QHttpServer::listen(const QString &socketOrPort, const TServerHandler &handler) {
|
||||
Q_D(QHttpServer);
|
||||
|
||||
bool isNumber = false;
|
||||
quint16 tcpPort = socketOrPort.toUShort(&isNumber);
|
||||
if ( isNumber && tcpPort > 0 )
|
||||
return listen(QHostAddress::Any, tcpPort, handler);
|
||||
|
||||
d->initialize(ELocalSocket, this);
|
||||
d->ihandler = handler;
|
||||
return d->ilocalServer->listen(socketOrPort);
|
||||
}
|
||||
|
||||
bool
|
||||
QHttpServer::listen(const QHostAddress& address, quint16 port, const qhttp::server::TServerHandler& handler) {
|
||||
Q_D(QHttpServer);
|
||||
|
||||
d->initialize(ETcpSocket, this);
|
||||
d->ihandler = handler;
|
||||
return d->itcpServer->listen(address, port);
|
||||
}
|
||||
|
||||
bool
|
||||
QHttpServer::isListening() const {
|
||||
const Q_D(QHttpServer);
|
||||
|
||||
if ( d->ibackend == ETcpSocket && d->itcpServer )
|
||||
return d->itcpServer->isListening();
|
||||
|
||||
else if ( d->ibackend == ELocalSocket && d->ilocalServer )
|
||||
return d->ilocalServer->isListening();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
QHttpServer::stopListening() {
|
||||
Q_D(QHttpServer);
|
||||
|
||||
if ( d->itcpServer )
|
||||
d->itcpServer->close();
|
||||
|
||||
if ( d->ilocalServer ) {
|
||||
d->ilocalServer->close();
|
||||
QLocalServer::removeServer( d->ilocalServer->fullServerName() );
|
||||
}
|
||||
}
|
||||
|
||||
quint32
|
||||
QHttpServer::timeOut() const {
|
||||
return d_func()->itimeOut;
|
||||
}
|
||||
|
||||
void
|
||||
QHttpServer::setTimeOut(quint32 newValue) {
|
||||
d_func()->itimeOut = newValue;
|
||||
}
|
||||
|
||||
TBackend
|
||||
QHttpServer::backendType() const {
|
||||
return d_func()->ibackend;
|
||||
}
|
||||
|
||||
QTcpServer*
|
||||
QHttpServer::tcpServer() const {
|
||||
return d_func()->itcpServer.data();
|
||||
}
|
||||
|
||||
QLocalServer*
|
||||
QHttpServer::localServer() const {
|
||||
return d_func()->ilocalServer.data();
|
||||
}
|
||||
|
||||
void
|
||||
QHttpServer::incomingConnection(qintptr handle) {
|
||||
QHttpConnection* conn = new QHttpConnection(this);
|
||||
conn->setSocketDescriptor(handle, backendType());
|
||||
conn->setTimeOut(d_func()->itimeOut);
|
||||
|
||||
emit newConnection(conn);
|
||||
|
||||
Q_D(QHttpServer);
|
||||
if ( d->ihandler )
|
||||
QObject::connect(conn, &QHttpConnection::newRequest, d->ihandler);
|
||||
else
|
||||
incomingConnection(conn);
|
||||
}
|
||||
|
||||
void
|
||||
QHttpServer::incomingConnection(QHttpConnection *connection) {
|
||||
QObject::connect(connection, &QHttpConnection::newRequest,
|
||||
this, &QHttpServer::newRequest);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace server
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
131
external/qhttp/src/qhttpserver.hpp
vendored
Normal file
131
external/qhttp/src/qhttpserver.hpp
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
/** HTTP server class.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPSERVER_HPP
|
||||
#define QHTTPSERVER_HPP
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#include "qhttpfwd.hpp"
|
||||
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace server {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** The QHttpServer class is a fast, async (non-blocking) HTTP server. */
|
||||
class QHTTP_API QHttpServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(quint32 timeOut READ timeOut WRITE setTimeOut)
|
||||
|
||||
public:
|
||||
/** construct a new HTTP Server. */
|
||||
explicit QHttpServer(QObject *parent = nullptr);
|
||||
|
||||
virtual ~QHttpServer();
|
||||
|
||||
/** starts a TCP or Local (unix domain socket) server.
|
||||
* if you provide a server handler, the newRequest() signal won't be emitted.
|
||||
*
|
||||
* @param socketOrPort could be a tcp port number as "8080" or a unix socket name as
|
||||
* "/tmp/sample.socket" or "sample.socket".
|
||||
* @param handler optional server handler (a lambda, std::function, ...)
|
||||
* @return false if listening fails.
|
||||
*/
|
||||
bool listen(const QString& socketOrPort,
|
||||
const TServerHandler& handler = nullptr);
|
||||
|
||||
/** starts a TCP server on specified address and port.
|
||||
* if you provide a server handler, the newRequest() signal won't be emitted.
|
||||
*
|
||||
* @param address listening address as QHostAddress::Any.
|
||||
* @param port listening port.
|
||||
* @param handler optional server handler (a lambda, std::function, ...)
|
||||
* @return false if listening fails.
|
||||
*/
|
||||
bool listen(const QHostAddress& address, quint16 port,
|
||||
const TServerHandler& handler = nullptr);
|
||||
|
||||
/** @overload listen() */
|
||||
bool listen(quint16 port) {
|
||||
return listen(QHostAddress::Any, port);
|
||||
}
|
||||
|
||||
/** returns true if server successfully listens. @sa listen() */
|
||||
bool isListening() const;
|
||||
|
||||
/** closes the server and stops from listening. */
|
||||
void stopListening();
|
||||
|
||||
/** returns timeout value [mSec] for open connections (sockets).
|
||||
* @sa setTimeOut(). */
|
||||
quint32 timeOut()const;
|
||||
|
||||
/** set time-out for new open connections in miliseconds [mSec].
|
||||
* new incoming connections will be forcefully closed after this time out.
|
||||
* a zero (0) value disables timer for new connections. */
|
||||
void setTimeOut(quint32);
|
||||
|
||||
/** returns the QHttpServer's backend type. */
|
||||
TBackend backendType() const;
|
||||
|
||||
signals:
|
||||
/** emitted when a client makes a new request to the server if you do not override
|
||||
* incomingConnection(QHttpConnection *connection);
|
||||
* @sa incommingConnection(). */
|
||||
void newRequest(QHttpRequest *request, QHttpResponse *response);
|
||||
|
||||
/** emitted when a new connection comes to the server if you do not override
|
||||
* incomingConnection(QHttpConnection *connection);
|
||||
* @sa incomingConnection(); */
|
||||
void newConnection(QHttpConnection* connection);
|
||||
|
||||
protected:
|
||||
/** returns the tcp server instance if the backend() == ETcpSocket. */
|
||||
QTcpServer* tcpServer() const;
|
||||
|
||||
/** returns the local server instance if the backend() == ELocalSocket. */
|
||||
QLocalServer* localServer() const;
|
||||
|
||||
|
||||
/** is called when server accepts a new connection.
|
||||
* you can override this function for using a thread-pool or ... some other reasons.
|
||||
*
|
||||
* the default implementation just connects QHttpConnection::newRequest signal.
|
||||
* @note if you override this method, the signal won't be emitted by QHttpServer.
|
||||
* (perhaps, you do not need it anymore).
|
||||
*
|
||||
* @param connection New incoming connection. */
|
||||
virtual void incomingConnection(QHttpConnection* connection);
|
||||
|
||||
/** overrides QTcpServer::incomingConnection() to make a new QHttpConnection.
|
||||
* override this function if you like to create your derived QHttpConnection instances.
|
||||
*
|
||||
* @note if you override this method, incomingConnection(QHttpConnection*) or
|
||||
* newRequest(QHttpRequest *, QHttpResponse *) signal won't be called.
|
||||
*
|
||||
* @see example/benchmark/server.cpp to see how to override.
|
||||
*/
|
||||
virtual void incomingConnection(qintptr handle);
|
||||
|
||||
private:
|
||||
explicit QHttpServer(QHttpServerPrivate&, QObject *parent);
|
||||
|
||||
Q_DECLARE_PRIVATE(QHttpServer)
|
||||
Q_DISABLE_COPY(QHttpServer)
|
||||
QScopedPointer<QHttpServerPrivate> d_ptr;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace server
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // define QHTTPSERVER_HPP
|
271
external/qhttp/src/qhttpserverconnection.cpp
vendored
Normal file
271
external/qhttp/src/qhttpserverconnection.cpp
vendored
Normal file
@ -0,0 +1,271 @@
|
||||
#include "private/qhttpserverconnection_private.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace server {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
QHttpConnection::QHttpConnection(QObject *parent)
|
||||
: QObject(parent), d_ptr(new QHttpConnectionPrivate(this)) {
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
QHttpConnection::QHttpConnection(QHttpConnectionPrivate& dd, QObject* parent)
|
||||
: QObject(parent), d_ptr(&dd) {
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
void
|
||||
QHttpConnection::setSocketDescriptor(qintptr sokDescriptor, TBackend backendType) {
|
||||
d_ptr->createSocket(sokDescriptor, backendType);
|
||||
}
|
||||
|
||||
QHttpConnection::~QHttpConnection() {
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
void
|
||||
QHttpConnection::setTimeOut(quint32 miliSeconds) {
|
||||
if ( miliSeconds != 0 ) {
|
||||
d_func()->itimeOut = miliSeconds;
|
||||
d_func()->itimer.start(miliSeconds, Qt::CoarseTimer, this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
QHttpConnection::killConnection() {
|
||||
d_func()->isocket.close();
|
||||
}
|
||||
|
||||
TBackend
|
||||
QHttpConnection::backendType() const {
|
||||
return d_func()->isocket.ibackendType;
|
||||
}
|
||||
|
||||
QTcpSocket*
|
||||
QHttpConnection::tcpSocket() const {
|
||||
return d_func()->isocket.itcpSocket;
|
||||
}
|
||||
|
||||
QLocalSocket*
|
||||
QHttpConnection::localSocket() const {
|
||||
return d_func()->isocket.ilocalSocket;
|
||||
}
|
||||
|
||||
void
|
||||
QHttpConnection::onHandler(const TServerHandler &handler) {
|
||||
d_func()->ihandler = handler;
|
||||
}
|
||||
|
||||
void
|
||||
QHttpConnection::timerEvent(QTimerEvent *) {
|
||||
killConnection();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// if user closes the connection, ends the response or by any other reason
|
||||
// the socket be disconnected, then the irequest and iresponse instances may bhave been deleted.
|
||||
// In these situations reading more http body or emitting end() for incoming request
|
||||
// are not possible.
|
||||
#define CHECK_FOR_DISCONNECTED if ( ilastRequest == nullptr ) \
|
||||
return 0;
|
||||
|
||||
|
||||
int
|
||||
QHttpConnectionPrivate::messageBegin(http_parser*) {
|
||||
itempUrl.clear();
|
||||
itempUrl.reserve(128);
|
||||
|
||||
if ( ilastRequest )
|
||||
ilastRequest->deleteLater();
|
||||
|
||||
ilastRequest = new QHttpRequest(q_func());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
QHttpConnectionPrivate::url(http_parser*, const char* at, size_t length) {
|
||||
Q_ASSERT(ilastRequest);
|
||||
|
||||
itempUrl.append(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
QHttpConnectionPrivate::headerField(http_parser*, const char* at, size_t length) {
|
||||
CHECK_FOR_DISCONNECTED
|
||||
|
||||
// insert the header we parsed previously
|
||||
// into the header map
|
||||
if ( !itempHeaderField.isEmpty() && !itempHeaderValue.isEmpty() ) {
|
||||
// header names are always lower-cased
|
||||
ilastRequest->d_func()->iheaders.insert(
|
||||
itempHeaderField.toLower(),
|
||||
itempHeaderValue.toLower()
|
||||
);
|
||||
// clear header value. this sets up a nice
|
||||
// feedback loop where the next time
|
||||
// HeaderValue is called, it can simply append
|
||||
itempHeaderField.clear();
|
||||
itempHeaderValue.clear();
|
||||
}
|
||||
|
||||
itempHeaderField.append(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
QHttpConnectionPrivate::headerValue(http_parser*, const char* at, size_t length) {
|
||||
CHECK_FOR_DISCONNECTED
|
||||
|
||||
itempHeaderValue.append(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
QHttpConnectionPrivate::headersComplete(http_parser* parser) {
|
||||
CHECK_FOR_DISCONNECTED
|
||||
|
||||
#if defined(USE_CUSTOM_URL_CREATOR)
|
||||
// get parsed url
|
||||
struct http_parser_url urlInfo;
|
||||
int r = http_parser_parse_url(itempUrl.constData(),
|
||||
itempUrl.size(),
|
||||
parser->method == HTTP_CONNECT,
|
||||
&urlInfo);
|
||||
Q_ASSERT(r == 0);
|
||||
Q_UNUSED(r);
|
||||
|
||||
ilastRequest->d_func()->iurl = createUrl(
|
||||
itempUrl.constData(),
|
||||
urlInfo
|
||||
);
|
||||
#else
|
||||
ilastRequest->d_func()->iurl = QUrl(itempUrl);
|
||||
#endif // defined(USE_CUSTOM_URL_CREATOR)
|
||||
|
||||
// set method
|
||||
ilastRequest->d_func()->imethod =
|
||||
static_cast<THttpMethod>(parser->method);
|
||||
|
||||
// set version
|
||||
ilastRequest->d_func()->iversion = QString("%1.%2")
|
||||
.arg(parser->http_major)
|
||||
.arg(parser->http_minor);
|
||||
|
||||
// Insert last remaining header
|
||||
ilastRequest->d_func()->iheaders.insert(
|
||||
itempHeaderField.toLower(),
|
||||
itempHeaderValue.toLower()
|
||||
);
|
||||
|
||||
// set client information
|
||||
if ( isocket.ibackendType == ETcpSocket ) {
|
||||
ilastRequest->d_func()->iremoteAddress = isocket.itcpSocket->peerAddress().toString();
|
||||
ilastRequest->d_func()->iremotePort = isocket.itcpSocket->peerPort();
|
||||
|
||||
} else if ( isocket.ibackendType == ELocalSocket ) {
|
||||
ilastRequest->d_func()->iremoteAddress = isocket.ilocalSocket->fullServerName();
|
||||
ilastRequest->d_func()->iremotePort = 0; // not used in local sockets
|
||||
}
|
||||
|
||||
if ( ilastResponse )
|
||||
ilastResponse->deleteLater();
|
||||
ilastResponse = new QHttpResponse(q_func());
|
||||
|
||||
if ( parser->http_major < 1 || parser->http_minor < 1 )
|
||||
ilastResponse->d_func()->ikeepAlive = false;
|
||||
|
||||
// close the connection if response was the last packet
|
||||
QObject::connect(ilastResponse, &QHttpResponse::done, [this](bool wasTheLastPacket){
|
||||
ikeepAlive = !wasTheLastPacket;
|
||||
if ( wasTheLastPacket ) {
|
||||
isocket.flush();
|
||||
isocket.close();
|
||||
}
|
||||
});
|
||||
|
||||
// we are good to go!
|
||||
if ( ihandler )
|
||||
ihandler(ilastRequest, ilastResponse);
|
||||
else
|
||||
emit q_ptr->newRequest(ilastRequest, ilastResponse);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
QHttpConnectionPrivate::body(http_parser*, const char* at, size_t length) {
|
||||
CHECK_FOR_DISCONNECTED
|
||||
|
||||
ilastRequest->d_func()->ireadState = QHttpRequestPrivate::EPartial;
|
||||
|
||||
if ( ilastRequest->d_func()->shouldCollect() ) {
|
||||
if ( !ilastRequest->d_func()->append(at, length) )
|
||||
onDispatchRequest(); // forcefully dispatch the ilastRequest
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
emit ilastRequest->data(QByteArray(at, length));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
QHttpConnectionPrivate::messageComplete(http_parser*) {
|
||||
CHECK_FOR_DISCONNECTED
|
||||
|
||||
// request is ready to be dispatched
|
||||
ilastRequest->d_func()->isuccessful = true;
|
||||
ilastRequest->d_func()->ireadState = QHttpRequestPrivate::EComplete;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#if defined(USE_CUSTOM_URL_CREATOR)
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/* URL Utilities */
|
||||
#define HAS_URL_FIELD(info, field) (info.field_set &(1 << (field)))
|
||||
|
||||
#define GET_FIELD(data, info, field) \
|
||||
QString::fromLatin1(data + info.field_data[field].off, info.field_data[field].len)
|
||||
|
||||
#define CHECK_AND_GET_FIELD(data, info, field) \
|
||||
(HAS_URL_FIELD(info, field) ? GET_FIELD(data, info, field) : QString())
|
||||
|
||||
QUrl
|
||||
QHttpConnectionPrivate::createUrl(const char *urlData, const http_parser_url &urlInfo) {
|
||||
QUrl url;
|
||||
url.setScheme(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_SCHEMA));
|
||||
url.setHost(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_HOST));
|
||||
// Port is dealt with separately since it is available as an integer.
|
||||
url.setPath(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_PATH));
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
url.setQuery(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_QUERY));
|
||||
#else
|
||||
if (HAS_URL_FIELD(urlInfo, UF_QUERY)) {
|
||||
url.setEncodedQuery(QByteArray(urlData + urlInfo.field_data[UF_QUERY].off,
|
||||
urlInfo.field_data[UF_QUERY].len));
|
||||
}
|
||||
#endif
|
||||
url.setFragment(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_FRAGMENT));
|
||||
url.setUserInfo(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_USERINFO));
|
||||
|
||||
if (HAS_URL_FIELD(urlInfo, UF_PORT))
|
||||
url.setPort(urlInfo.port);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
#undef CHECK_AND_SET_FIELD
|
||||
#undef GET_FIELD
|
||||
#undef HAS_URL_FIELD
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // defined(USE_CUSTOM_URL_CREATOR)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace server
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
87
external/qhttp/src/qhttpserverconnection.hpp
vendored
Normal file
87
external/qhttp/src/qhttpserverconnection.hpp
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
/** HTTP connection class.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPSERVER_CONNECTION_HPP
|
||||
#define QHTTPSERVER_CONNECTION_HPP
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#include "qhttpfwd.hpp"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace server {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/** a HTTP connection in the server.
|
||||
* this class controls the HTTP connetion and handles life cycle and the memory management
|
||||
* of QHttpRequest and QHttpResponse instances autoamtically.
|
||||
*/
|
||||
class QHTTP_API QHttpConnection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
virtual ~QHttpConnection();
|
||||
|
||||
/** set an optional timer event to close the connection. */
|
||||
void setTimeOut(quint32 miliSeconds);
|
||||
|
||||
/** forcefully kills (closes) a connection. */
|
||||
void killConnection();
|
||||
|
||||
/** optionally set a handler for connection class.
|
||||
* @note if you set this handler, the newRequest() signal won't be emitted.
|
||||
*/
|
||||
void onHandler(const TServerHandler& handler);
|
||||
|
||||
/** returns the backend type of the connection. */
|
||||
TBackend backendType() const;
|
||||
|
||||
/** returns connected socket if the backend() == ETcpSocket. */
|
||||
QTcpSocket* tcpSocket() const;
|
||||
|
||||
/** returns connected socket if the backend() == ELocalSocket. */
|
||||
QLocalSocket* localSocket() const;
|
||||
|
||||
/** creates a new QHttpConnection based on arguments. */
|
||||
static
|
||||
QHttpConnection* create(qintptr sokDescriptor, TBackend backendType, QObject* parent) {
|
||||
QHttpConnection* conn = new QHttpConnection(parent);
|
||||
conn->setSocketDescriptor(sokDescriptor, backendType);
|
||||
return conn;
|
||||
}
|
||||
|
||||
signals:
|
||||
/** emitted when a pair of HTTP request and response are ready to interact.
|
||||
* @param req incoming request by the client.
|
||||
* @param res outgoing response to the client.
|
||||
*/
|
||||
void newRequest(QHttpRequest* req, QHttpResponse* res);
|
||||
|
||||
/** emitted when the tcp/local socket, disconnects. */
|
||||
void disconnected();
|
||||
|
||||
protected:
|
||||
explicit QHttpConnection(QObject *parent);
|
||||
explicit QHttpConnection(QHttpConnectionPrivate&, QObject *);
|
||||
|
||||
void setSocketDescriptor(qintptr sokDescriptor, TBackend backendType);
|
||||
void timerEvent(QTimerEvent*) override;
|
||||
|
||||
Q_DISABLE_COPY(QHttpConnection)
|
||||
Q_DECLARE_PRIVATE(QHttpConnection)
|
||||
QScopedPointer<QHttpConnectionPrivate> d_ptr;
|
||||
|
||||
friend class QHttpServer;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace server
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // #define QHTTPSERVER_CONNECTION_HPP
|
81
external/qhttp/src/qhttpserverrequest.cpp
vendored
Normal file
81
external/qhttp/src/qhttpserverrequest.cpp
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
#include "private/qhttpserverrequest_private.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace server {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
QHttpRequest::QHttpRequest(QHttpConnection *conn)
|
||||
: QHttpAbstractInput(conn), d_ptr(new QHttpRequestPrivate(conn, this)) {
|
||||
d_ptr->initialize();
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
QHttpRequest::QHttpRequest(QHttpRequestPrivate &dd, QHttpConnection *conn)
|
||||
: QHttpAbstractInput(conn), d_ptr(&dd) {
|
||||
d_ptr->initialize();
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
QHttpRequest::~QHttpRequest() {
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
THttpMethod
|
||||
QHttpRequest::method() const {
|
||||
return d_func()->imethod;
|
||||
}
|
||||
|
||||
const QString
|
||||
QHttpRequest::methodString() const {
|
||||
return http_method_str(static_cast<http_method>(d_func()->imethod));
|
||||
}
|
||||
|
||||
const QUrl&
|
||||
QHttpRequest::url() const {
|
||||
return d_func()->iurl;
|
||||
}
|
||||
|
||||
const QString&
|
||||
QHttpRequest::httpVersion() const {
|
||||
return d_func()->iversion;
|
||||
}
|
||||
|
||||
const THeaderHash&
|
||||
QHttpRequest::headers() const {
|
||||
return d_func()->iheaders;
|
||||
}
|
||||
|
||||
const QString&
|
||||
QHttpRequest::remoteAddress() const {
|
||||
return d_func()->iremoteAddress;
|
||||
}
|
||||
|
||||
quint16
|
||||
QHttpRequest::remotePort() const {
|
||||
return d_func()->iremotePort;
|
||||
}
|
||||
|
||||
bool
|
||||
QHttpRequest::isSuccessful() const {
|
||||
return d_func()->isuccessful;
|
||||
}
|
||||
|
||||
void
|
||||
QHttpRequest::collectData(int atMost) {
|
||||
d_func()->collectData(atMost);
|
||||
}
|
||||
|
||||
const QByteArray&
|
||||
QHttpRequest::collectedData() const {
|
||||
return d_func()->icollectedData;
|
||||
}
|
||||
|
||||
QHttpConnection*
|
||||
QHttpRequest::connection() const {
|
||||
return d_ptr->iconnection;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace server
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
82
external/qhttp/src/qhttpserverrequest.hpp
vendored
Normal file
82
external/qhttp/src/qhttpserverrequest.hpp
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
/** HTTP request which is received by the server.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPSERVER_REQUEST_HPP
|
||||
#define QHTTPSERVER_REQUEST_HPP
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "qhttpabstracts.hpp"
|
||||
|
||||
#include <QUrl>
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace server {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/** The QHttpRequest class represents the header and body data sent by the client.
|
||||
* The class is <b>read-only</b>.
|
||||
* @sa QHttpConnection
|
||||
*/
|
||||
class QHTTP_API QHttpRequest : public QHttpAbstractInput
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
virtual ~QHttpRequest();
|
||||
|
||||
public: // QHttpAbstractInput methods:
|
||||
/** @see QHttpAbstractInput::headers(). */
|
||||
const THeaderHash& headers() const override;
|
||||
|
||||
/** @see QHttpAbstractInput::httpVersion(). */
|
||||
const QString& httpVersion() const override;
|
||||
|
||||
/** @see QHttpAbstractInput::isSuccessful(). */
|
||||
bool isSuccessful() const override;
|
||||
|
||||
/** @see QHttpAbstractInput::collectData(). */
|
||||
void collectData(int atMost = -1) override;
|
||||
|
||||
/** @see QHttpAbstractInput::collectedData(). */
|
||||
const QByteArray& collectedData()const override;
|
||||
|
||||
|
||||
public:
|
||||
/** The method used for the request. */
|
||||
THttpMethod method() const ;
|
||||
|
||||
/** Returns the method string for the request.
|
||||
* @note This will plainly transform the enum into a string HTTP_GET -> "HTTP_GET". */
|
||||
const QString methodString() const;
|
||||
|
||||
/** The complete URL for the request.
|
||||
* This includes the path and query string. @sa path(). */
|
||||
const QUrl& url() const;
|
||||
|
||||
/** IP Address of the client in dotted decimal format. */
|
||||
const QString& remoteAddress() const;
|
||||
|
||||
/** Outbound connection port for the client. */
|
||||
quint16 remotePort() const;
|
||||
|
||||
/** returns the parent QHttpConnection object. */
|
||||
QHttpConnection* connection() const;
|
||||
|
||||
protected:
|
||||
explicit QHttpRequest(QHttpConnection*);
|
||||
explicit QHttpRequest(QHttpRequestPrivate&, QHttpConnection*);
|
||||
friend class QHttpConnectionPrivate;
|
||||
|
||||
Q_DECLARE_PRIVATE(QHttpRequest)
|
||||
QScopedPointer<QHttpRequestPrivate> d_ptr;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace server
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // define QHTTPSERVER_REQUEST_HPP
|
90
external/qhttp/src/qhttpserverresponse.cpp
vendored
Normal file
90
external/qhttp/src/qhttpserverresponse.cpp
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
#include "private/qhttpserverresponse_private.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace server {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
QHttpResponse::QHttpResponse(QHttpConnection* conn)
|
||||
: QHttpAbstractOutput(conn) , d_ptr(new QHttpResponsePrivate(conn, this)) {
|
||||
d_ptr->initialize();
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
QHttpResponse::QHttpResponse(QHttpResponsePrivate& dd, QHttpConnection* conn)
|
||||
: QHttpAbstractOutput(conn) , d_ptr(&dd) {
|
||||
d_ptr->initialize();
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
QHttpResponse::~QHttpResponse() {
|
||||
QHTTP_LINE_LOG
|
||||
}
|
||||
|
||||
void
|
||||
QHttpResponse::setStatusCode(TStatusCode code) {
|
||||
d_func()->istatus = code;
|
||||
}
|
||||
|
||||
void
|
||||
QHttpResponse::setVersion(const QString &versionString) {
|
||||
d_func()->iversion = versionString;
|
||||
}
|
||||
|
||||
void
|
||||
QHttpResponse::addHeader(const QByteArray &field, const QByteArray &value) {
|
||||
d_func()->addHeader(field, value);
|
||||
}
|
||||
|
||||
THeaderHash&
|
||||
QHttpResponse::headers() {
|
||||
return d_func()->iheaders;
|
||||
}
|
||||
|
||||
void
|
||||
QHttpResponse::write(const QByteArray &data) {
|
||||
d_func()->writeData(data);
|
||||
}
|
||||
|
||||
void
|
||||
QHttpResponse::end(const QByteArray &data) {
|
||||
Q_D(QHttpResponse);
|
||||
|
||||
if ( d->endPacket(data) )
|
||||
emit done(!d->ikeepAlive);
|
||||
}
|
||||
|
||||
QHttpConnection*
|
||||
QHttpResponse::connection() const {
|
||||
return d_func()->iconnection;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
QByteArray
|
||||
QHttpResponsePrivate::makeTitle() {
|
||||
|
||||
QString title = QString("HTTP/%1 %2 %3\r\n")
|
||||
.arg(iversion)
|
||||
.arg(istatus)
|
||||
.arg(Stringify::toString(istatus));
|
||||
|
||||
return title.toLatin1();
|
||||
}
|
||||
|
||||
void
|
||||
QHttpResponsePrivate::prepareHeadersToWrite() {
|
||||
|
||||
if ( !iheaders.contains("date") ) {
|
||||
// Sun, 06 Nov 1994 08:49:37 GMT - RFC 822. Use QLocale::c() so english is used for month and
|
||||
// day.
|
||||
QString dateString = QLocale::c().
|
||||
toString(QDateTime::currentDateTimeUtc(),
|
||||
"ddd, dd MMM yyyy hh:mm:ss")
|
||||
.append(" GMT");
|
||||
addHeader("date", dateString.toLatin1());
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace server
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
70
external/qhttp/src/qhttpserverresponse.hpp
vendored
Normal file
70
external/qhttp/src/qhttpserverresponse.hpp
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
/** HTTP response from a server.
|
||||
* https://github.com/azadkuh/qhttp
|
||||
*
|
||||
* @author amir zamani
|
||||
* @version 2.0.0
|
||||
* @date 2014-07-11
|
||||
*/
|
||||
|
||||
#ifndef QHTTPSERVER_RESPONSE_HPP
|
||||
#define QHTTPSERVER_RESPONSE_HPP
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "qhttpabstracts.hpp"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace qhttp {
|
||||
namespace server {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/** The QHttpResponse class handles sending data back to the client as a response to a request.
|
||||
* @sa QHttpConnection
|
||||
*/
|
||||
class QHTTP_API QHttpResponse : public QHttpAbstractOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
virtual ~QHttpResponse();
|
||||
|
||||
public:
|
||||
/** set the response HTTP status code. @sa TStatusCode.
|
||||
* default value is ESTATUS_BAD_REQUEST.
|
||||
* @sa write()
|
||||
*/
|
||||
void setStatusCode(TStatusCode code);
|
||||
|
||||
public: // QHttpAbstractOutput methods:
|
||||
/** @see QHttpAbstractOutput::setVersion(). */
|
||||
void setVersion(const QString& versionString) override;
|
||||
|
||||
/** @see QHttpAbstractOutput::addHeader(). */
|
||||
void addHeader(const QByteArray& field, const QByteArray& value) override;
|
||||
|
||||
/** @see QHttpAbstractOutput::headers(). */
|
||||
THeaderHash& headers() override;
|
||||
|
||||
/** @see QHttpAbstractOutput::write(). */
|
||||
void write(const QByteArray &data) override;
|
||||
|
||||
/** @see QHttpAbstractOutput::end(). */
|
||||
void end(const QByteArray &data = QByteArray()) override;
|
||||
|
||||
public:
|
||||
/** returns the parent QHttpConnection object. */
|
||||
QHttpConnection* connection() const;
|
||||
|
||||
protected:
|
||||
explicit QHttpResponse(QHttpConnection*);
|
||||
explicit QHttpResponse(QHttpResponsePrivate&, QHttpConnection*);
|
||||
friend class QHttpConnectionPrivate;
|
||||
|
||||
Q_DECLARE_PRIVATE(QHttpResponse)
|
||||
QScopedPointer<QHttpResponsePrivate> d_ptr;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace server
|
||||
} // namespace qhttp
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#endif // define QHTTPSERVER_RESPONSE_HPP
|
30
external/qhttpserver/.gitignore
vendored
30
external/qhttpserver/.gitignore
vendored
@ -1,30 +0,0 @@
|
||||
# Folders
|
||||
build
|
||||
lib
|
||||
|
||||
# Generated
|
||||
Makefile
|
||||
*.o
|
||||
moc_*
|
||||
|
||||
# Docs
|
||||
docs/html
|
||||
|
||||
# Build folders
|
||||
*/debug
|
||||
*/release
|
||||
*/*/debug
|
||||
*/*/release
|
||||
|
||||
# Visual studio
|
||||
*.suo
|
||||
*.ncb
|
||||
*.user
|
||||
*.pdb
|
||||
*.idb
|
||||
*.vcproj
|
||||
*.vcxproj
|
||||
*.vcxproj.filters
|
||||
*.lib
|
||||
*.sln
|
||||
*.rc
|
8
external/qhttpserver/CMakeLists.txt
vendored
8
external/qhttpserver/CMakeLists.txt
vendored
@ -1,8 +0,0 @@
|
||||
aux_source_directory(src HTTP_SRC)
|
||||
|
||||
include_directories(http-parser)
|
||||
add_library(http-parser STATIC http-parser/http_parser.c)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
add_library(qhttpserver STATIC ${HTTP_SRC} ${PARSER_SRC})
|
||||
target_link_libraries(qhttpserver http-parser)
|
19
external/qhttpserver/LICENSE
vendored
19
external/qhttpserver/LICENSE
vendored
@ -1,19 +0,0 @@
|
||||
Copyright (C) 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
72
external/qhttpserver/README.md
vendored
72
external/qhttpserver/README.md
vendored
@ -1,72 +0,0 @@
|
||||
QHttpServer
|
||||
===========
|
||||
|
||||
A Qt HTTP Server - because hard-core programmers write web-apps in C++ :)
|
||||
|
||||
It uses Joyent's [HTTP Parser](http://github.com/joyent/http-parser) and is asynchronous and does not require any inheritance.
|
||||
|
||||
QHttpServer is available under the MIT License.
|
||||
|
||||
**NOTE: QHttpServer is NOT fully HTTP compliant right now! DO NOT use it for
|
||||
anything complex**
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Requires Qt 4 or Qt 5.
|
||||
|
||||
qmake && make && su -c 'make install'
|
||||
|
||||
To link to your projects put this in your project's qmake project file
|
||||
|
||||
LIBS += -lqhttpserver
|
||||
|
||||
By default, the installation prefix is /usr/local. To change that to /usr,
|
||||
for example, run:
|
||||
|
||||
qmake -r PREFIX=/usr
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Include the headers
|
||||
|
||||
#include <qhttpserver.h>
|
||||
#include <qhttprequest.h>
|
||||
#include <qhttpresponse.h>
|
||||
|
||||
Create a server, and connect to the signal for new requests
|
||||
|
||||
QHttpServer *server = new QHttpServer;
|
||||
connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),
|
||||
handler, SLOT(handle(QHttpRequest*, QHttpResponse*)));
|
||||
|
||||
// let's go
|
||||
server->listen(8080);
|
||||
|
||||
In the handler, you may dispatch on routes or do whatever other things
|
||||
you want. See the API documentation for what information
|
||||
is provided about the request via the QHttpRequest object.
|
||||
|
||||
To send data back to the browser and end the request:
|
||||
|
||||
void Handler::handle(QHttpRequest *req, QHttpResponse *resp)
|
||||
{
|
||||
resp->setHeader("Content-Length", 11);
|
||||
resp->writeHead(200); // everything is OK
|
||||
resp->write("Hello World");
|
||||
resp->end();
|
||||
}
|
||||
|
||||
The server and request/response objects emit various signals
|
||||
and have guarantees about memory management. See the API documentation for
|
||||
these.
|
||||
|
||||
Contribute
|
||||
----------
|
||||
|
||||
Feel free to file issues, branch and send pull requests. If you plan to work on a major feature (say WebSocket support), please run it by me first by filing an issue! qhttpserver has a narrow scope and API and I'd like to keep it that way, so a thousand line patch that implements the kitchen sink is unlikely to be accepted.
|
||||
|
||||
- Nikhil Marathe (maintainer)
|
||||
|
||||
Everybody who has ever contributed shows up in [Contributors](https://github.com/nikhilm/qhttpserver/graphs/contributors).
|
7
external/qhttpserver/TODO
vendored
7
external/qhttpserver/TODO
vendored
@ -1,7 +0,0 @@
|
||||
* Expect & Continue stuff
|
||||
* Chunked encoding support
|
||||
* Only copy over public headers etc.
|
||||
* connection object should connect to QHttpResponse::destroyed()
|
||||
and stop pushing data into it or whatever if the object is destroyed.
|
||||
* response object should keep track of emitting done() and not accept writes after that
|
||||
* handle encoding in response write and end
|
2314
external/qhttpserver/docs/Doxyfile
vendored
2314
external/qhttpserver/docs/Doxyfile
vendored
File diff suppressed because it is too large
Load Diff
10
external/qhttpserver/docs/pages/examples.dox
vendored
10
external/qhttpserver/docs/pages/examples.dox
vendored
@ -1,10 +0,0 @@
|
||||
/**
|
||||
|
||||
@example helloworld/helloworld.cpp
|
||||
@example helloworld/helloworld.h
|
||||
@example greeting/greeting.cpp
|
||||
@example greeting/greeting.h
|
||||
@example bodydata/bodydata.cpp
|
||||
@example bodydata/bodydata.h
|
||||
|
||||
*/
|
105
external/qhttpserver/docs/pages/main-page.dox
vendored
105
external/qhttpserver/docs/pages/main-page.dox
vendored
@ -1,105 +0,0 @@
|
||||
/**
|
||||
|
||||
@mainpage %QHttpServer Documentation
|
||||
|
||||
@section introduction Introduction
|
||||
|
||||
%QHttpServer is a easy to use, fast and light-weight
|
||||
HTTP Server suitable for C++ web applications backed
|
||||
by Qt. Since C++ web applications are pretty uncommon
|
||||
the market for this project is pretty low.
|
||||
|
||||
But integrating this with a module like QtScript and using it to write
|
||||
JavaScript web applications is a tempting possibility, and something that
|
||||
I demonstrated at <a href="http://conf.kde.in">conf.kde.in 2011</a>. The slides
|
||||
can be found on <a href="https://github.com/nikhilm/confkdein11">Github</a>.
|
||||
|
||||
%QHttpServer uses a signal-slots based mechanism
|
||||
for all communication, so no inheritance is required.
|
||||
It tries to be as asynchronous as possible, to the
|
||||
extent that request body data is also delivered as and
|
||||
when it is received over the socket via signals. This
|
||||
kind of programming may take some getting used to.
|
||||
|
||||
%QHttpServer is backed by <a href="http://github.com/ry/http-parser">Ryan
|
||||
Dahl's secure and fast http parser</a> which makes it streaming
|
||||
till the lowest level.
|
||||
|
||||
@section usage Usage
|
||||
|
||||
Using %QHttpServer is very simple. Simply create a QHttpServer,
|
||||
connect a slot to the newRequest() signal and use the request and
|
||||
response objects.
|
||||
See the QHttpServer class documentation for an example.
|
||||
|
||||
@section memorymanagement Memory Management
|
||||
|
||||
The QHttpRequest and QHttpResponse deletion policies
|
||||
are such.
|
||||
|
||||
QHttpRequest is <b>never</b> deleted by %QHttpServer.
|
||||
Since it is not possible to determine till what point the application
|
||||
may want access to its data, it is up to the application to delete it.
|
||||
A recommended way to handle this is to create a new responder object for
|
||||
every request and to delete the request in that object's destructor. The
|
||||
object itself can be deleted by connecting to QHttpResponse's done()
|
||||
slot as explained below.
|
||||
|
||||
You should <b>not</b> delete the QHttpRequest object until it
|
||||
has emitted an QHttpRequest::end() signal.
|
||||
|
||||
QHttpResponse queues itself up for auto-deletion once the application
|
||||
calls its end() method. Once the data has been flushed to the underlying
|
||||
socket, the object will emit a QHttpResponse::done() signal before queueing itself up
|
||||
for deletion. You should <b>not</b> interact with the response
|
||||
object once it has emitted QHttpResponse::done() although actual deletion does not
|
||||
happen until QHttpResponse::destroyed() is emitted.
|
||||
QHttpResponse::done() serves as a useful way to handle memory management of the
|
||||
application itself. For example:
|
||||
|
||||
@code
|
||||
|
||||
MyApp::MyApp() : QObject()
|
||||
{
|
||||
QHttpServer *server = new QHttpServer(this);
|
||||
connect(server, SIGNAL(newRequest(...)), this, SLOT(handle(...)));
|
||||
s.listen(8080);
|
||||
}
|
||||
|
||||
void MyApp::handle(QHttpRequest *request, QHttpResponse *response)
|
||||
{
|
||||
if (request->path() == x) // Match a route
|
||||
new Responder(request, response);
|
||||
else
|
||||
new PageNotFound(request, response);
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
Responder::Responder(QHttpRequest *request, QHttpResponse *response)
|
||||
{
|
||||
m_request = request;
|
||||
|
||||
connect(request, SIGNAL(end()), response, SLOT(end()));
|
||||
|
||||
// Once the request is complete, the response is sent.
|
||||
// When the response ends, it deletes itself
|
||||
// the Responder object connects to done()
|
||||
// which will lead to it being deleted
|
||||
// and this will delete the request.
|
||||
// So all 3 are properly deleted.
|
||||
connect(response, SIGNAL(done()), this, SLOT(deleteLater()));
|
||||
|
||||
response->writeHead(200);
|
||||
response->write("Quitting soon");
|
||||
}
|
||||
|
||||
Responder::~Responder()
|
||||
{
|
||||
delete m_request;
|
||||
m_request = 0;
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
*/
|
@ -1,76 +0,0 @@
|
||||
#include "bodydata.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QRegExp>
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
|
||||
#include <qhttpserver.h>
|
||||
#include <qhttprequest.h>
|
||||
#include <qhttpresponse.h>
|
||||
|
||||
/// BodyData
|
||||
|
||||
BodyData::BodyData()
|
||||
{
|
||||
QHttpServer *server = new QHttpServer(this);
|
||||
connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),
|
||||
this, SLOT(handleRequest(QHttpRequest*, QHttpResponse*)));
|
||||
|
||||
server->listen(QHostAddress::Any, 8080);
|
||||
}
|
||||
|
||||
void BodyData::handleRequest(QHttpRequest *req, QHttpResponse *resp)
|
||||
{
|
||||
new Responder(req, resp);
|
||||
}
|
||||
|
||||
/// Responder
|
||||
|
||||
Responder::Responder(QHttpRequest *req, QHttpResponse *resp)
|
||||
: m_req(req)
|
||||
, m_resp(resp)
|
||||
{
|
||||
QRegExp exp("^/user/([a-z]+$)");
|
||||
if (exp.indexIn(req->path()) == -1)
|
||||
{
|
||||
resp->writeHead(403);
|
||||
resp->end(QByteArray("You aren't allowed here!"));
|
||||
/// @todo There should be a way to tell request to stop streaming data
|
||||
return;
|
||||
}
|
||||
|
||||
resp->setHeader("Content-Type", "text/html");
|
||||
resp->writeHead(200);
|
||||
|
||||
QString name = exp.capturedTexts()[1];
|
||||
QString bodyStart = tr("<html><head><title>BodyData App</title></head><body><h1>Hello %1!</h1><p>").arg(name);
|
||||
resp->write(bodyStart.toUtf8());
|
||||
|
||||
connect(req, SIGNAL(data(const QByteArray&)), this, SLOT(accumulate(const QByteArray&)));
|
||||
connect(req, SIGNAL(end()), this, SLOT(reply()));
|
||||
connect(m_resp, SIGNAL(done()), this, SLOT(deleteLater()));
|
||||
}
|
||||
|
||||
Responder::~Responder()
|
||||
{
|
||||
}
|
||||
|
||||
void Responder::accumulate(const QByteArray &data)
|
||||
{
|
||||
m_resp->write(data);
|
||||
}
|
||||
|
||||
void Responder::reply()
|
||||
{
|
||||
m_resp->end(QByteArray("</p></body></html>"));
|
||||
}
|
||||
|
||||
/// main
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
BodyData bodydata;
|
||||
app.exec();
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
#include "qhttpserverfwd.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QScopedPointer>
|
||||
|
||||
/// BodyData
|
||||
|
||||
class BodyData : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BodyData();
|
||||
|
||||
private slots:
|
||||
void handleRequest(QHttpRequest *req, QHttpResponse *resp);
|
||||
};
|
||||
|
||||
/// Responder
|
||||
|
||||
class Responder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Responder(QHttpRequest *req, QHttpResponse *resp);
|
||||
~Responder();
|
||||
|
||||
signals:
|
||||
void done();
|
||||
|
||||
private slots:
|
||||
void accumulate(const QByteArray &data);
|
||||
void reply();
|
||||
|
||||
private:
|
||||
QScopedPointer<QHttpRequest> m_req;
|
||||
QHttpResponse *m_resp;
|
||||
};
|
@ -1,19 +0,0 @@
|
||||
TARGET = bodydata
|
||||
|
||||
QT += network
|
||||
QT -= gui
|
||||
|
||||
CONFIG += debug
|
||||
|
||||
INCLUDEPATH += ../../src
|
||||
LIBS += -L../../lib
|
||||
|
||||
win32 {
|
||||
debug: LIBS += -lqhttpserverd
|
||||
else: LIBS += -lqhttpserver
|
||||
} else {
|
||||
LIBS += -lqhttpserver
|
||||
}
|
||||
|
||||
SOURCES = bodydata.cpp
|
||||
HEADERS = bodydata.h
|
5
external/qhttpserver/examples/examples.pro
vendored
5
external/qhttpserver/examples/examples.pro
vendored
@ -1,5 +0,0 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS += \
|
||||
helloworld\
|
||||
greeting\
|
||||
bodydata\
|
@ -1,48 +0,0 @@
|
||||
#include "greeting.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QRegExp>
|
||||
#include <QStringList>
|
||||
|
||||
#include <qhttpserver.h>
|
||||
#include <qhttprequest.h>
|
||||
#include <qhttpresponse.h>
|
||||
|
||||
/// Greeting
|
||||
|
||||
Greeting::Greeting()
|
||||
{
|
||||
QHttpServer *server = new QHttpServer(this);
|
||||
connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),
|
||||
this, SLOT(handleRequest(QHttpRequest*, QHttpResponse*)));
|
||||
|
||||
server->listen(QHostAddress::Any, 8080);
|
||||
}
|
||||
|
||||
void Greeting::handleRequest(QHttpRequest *req, QHttpResponse *resp)
|
||||
{
|
||||
QRegExp exp("^/user/([a-z]+)$");
|
||||
if( exp.indexIn(req->path()) != -1 )
|
||||
{
|
||||
resp->setHeader("Content-Type", "text/html");
|
||||
resp->writeHead(200);
|
||||
|
||||
QString name = exp.capturedTexts()[1];
|
||||
QString body = tr("<html><head><title>Greeting App</title></head><body><h1>Hello %1!</h1></body></html>");
|
||||
resp->end(body.arg(name).toUtf8());
|
||||
}
|
||||
else
|
||||
{
|
||||
resp->writeHead(403);
|
||||
resp->end(QByteArray("You aren't allowed here!"));
|
||||
}
|
||||
}
|
||||
|
||||
/// main
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
Greeting greeting;
|
||||
app.exec();
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#include "qhttpserverfwd.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
/// Greeting
|
||||
|
||||
class Greeting : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Greeting();
|
||||
|
||||
private slots:
|
||||
void handleRequest(QHttpRequest *req, QHttpResponse *resp);
|
||||
};
|
@ -1,19 +0,0 @@
|
||||
TARGET = greeting
|
||||
|
||||
QT += network
|
||||
QT -= gui
|
||||
|
||||
CONFIG += debug
|
||||
|
||||
INCLUDEPATH += ../../src
|
||||
LIBS += -L../../lib
|
||||
|
||||
win32 {
|
||||
debug: LIBS += -lqhttpserverd
|
||||
else: LIBS += -lqhttpserver
|
||||
} else {
|
||||
LIBS += -lqhttpserver
|
||||
}
|
||||
|
||||
SOURCES = greeting.cpp
|
||||
HEADERS = greeting.h
|
@ -1,37 +0,0 @@
|
||||
#include "helloworld.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <qhttpserver.h>
|
||||
#include <qhttprequest.h>
|
||||
#include <qhttpresponse.h>
|
||||
|
||||
/// HelloWorld
|
||||
|
||||
HelloWorld::HelloWorld()
|
||||
{
|
||||
QHttpServer *server = new QHttpServer(this);
|
||||
connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),
|
||||
this, SLOT(handleRequest(QHttpRequest*, QHttpResponse*)));
|
||||
|
||||
server->listen(QHostAddress::Any, 8080);
|
||||
}
|
||||
|
||||
void HelloWorld::handleRequest(QHttpRequest *req, QHttpResponse *resp)
|
||||
{
|
||||
Q_UNUSED(req);
|
||||
|
||||
QByteArray body = "Hello World";
|
||||
resp->setHeader("Content-Length", QString::number(body.size()));
|
||||
resp->writeHead(200);
|
||||
resp->end(body);
|
||||
}
|
||||
|
||||
/// main
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
HelloWorld hello;
|
||||
app.exec();
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#include "qhttpserverfwd.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
/// HelloWorld
|
||||
|
||||
class HelloWorld : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HelloWorld();
|
||||
|
||||
private slots:
|
||||
void handleRequest(QHttpRequest *req, QHttpResponse *resp);
|
||||
};
|
@ -1,19 +0,0 @@
|
||||
TARGET = helloworld
|
||||
|
||||
QT += network
|
||||
QT -= gui
|
||||
|
||||
CONFIG += debug
|
||||
|
||||
INCLUDEPATH += ../../src
|
||||
LIBS += -L../../lib
|
||||
|
||||
win32 {
|
||||
debug: LIBS += -lqhttpserverd
|
||||
else: LIBS += -lqhttpserver
|
||||
} else {
|
||||
LIBS += -lqhttpserver
|
||||
}
|
||||
|
||||
SOURCES = helloworld.cpp
|
||||
HEADERS = helloworld.h
|
28
external/qhttpserver/http-parser/.gitignore
vendored
28
external/qhttpserver/http-parser/.gitignore
vendored
@ -1,28 +0,0 @@
|
||||
/out/
|
||||
core
|
||||
tags
|
||||
*.o
|
||||
test
|
||||
test_g
|
||||
test_fast
|
||||
bench
|
||||
url_parser
|
||||
parsertrace
|
||||
parsertrace_g
|
||||
*.mk
|
||||
*.Makefile
|
||||
*.so.*
|
||||
*.a
|
||||
|
||||
|
||||
# Visual Studio uglies
|
||||
*.suo
|
||||
*.sln
|
||||
*.vcxproj
|
||||
*.vcxproj.filters
|
||||
*.vcxproj.user
|
||||
*.opensdf
|
||||
*.ncrunchsolution*
|
||||
*.sdf
|
||||
*.vsp
|
||||
*.psess
|
@ -1,4 +0,0 @@
|
||||
Contributors must agree to the Contributor License Agreement before patches
|
||||
can be accepted.
|
||||
|
||||
http://spreadsheets2.google.com/viewform?hl=en&formkey=dDJXOGUwbzlYaWM4cHN1MERwQS1CSnc6MQ
|
44
external/qhttpserver/http-parser/url_parser.c
vendored
44
external/qhttpserver/http-parser/url_parser.c
vendored
@ -1,44 +0,0 @@
|
||||
#include "http_parser.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
dump_url (const char *url, const struct http_parser_url *u)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
|
||||
for (i = 0; i < UF_MAX; i++) {
|
||||
if ((u->field_set & (1 << i)) == 0) {
|
||||
printf("\tfield_data[%u]: unset\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n",
|
||||
i,
|
||||
u->field_data[i].off,
|
||||
u->field_data[i].len,
|
||||
u->field_data[i].len,
|
||||
url + u->field_data[i].off);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
if (argc != 3) {
|
||||
printf("Syntax : %s connect|get url\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
struct http_parser_url u;
|
||||
int len = strlen(argv[2]);
|
||||
int connect = strcmp("connect", argv[1]) == 0 ? 1 : 0;
|
||||
printf("Parsing %s, connect %d\n", argv[2], connect);
|
||||
|
||||
int result = http_parser_parse_url(argv[2], len, connect, &u);
|
||||
if (result != 0) {
|
||||
printf("Parse error : %d\n", result);
|
||||
return result;
|
||||
}
|
||||
printf("Parse ok, result : \n");
|
||||
dump_url(argv[2], &u);
|
||||
return 0;
|
||||
}
|
46
external/qhttpserver/konvergo-error.patch
vendored
46
external/qhttpserver/konvergo-error.patch
vendored
@ -1,46 +0,0 @@
|
||||
diff --git a/external/qhttpserver/src/qhttpserver.cpp b/external/qhttpserver/src/qhttpserver.cpp
|
||||
index 07e4a85..507645d 100755
|
||||
--- a/external/qhttpserver/src/qhttpserver.cpp
|
||||
+++ b/external/qhttpserver/src/qhttpserver.cpp
|
||||
@@ -115,6 +115,7 @@ bool QHttpServer::listen(const QHostAddress &address, quint16 port)
|
||||
if (couldBindToPort) {
|
||||
connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection()));
|
||||
} else {
|
||||
+ m_errorString = m_tcpServer->errorString();
|
||||
delete m_tcpServer;
|
||||
m_tcpServer = NULL;
|
||||
}
|
||||
@@ -126,6 +127,11 @@ bool QHttpServer::listen(quint16 port)
|
||||
return listen(QHostAddress::Any, port);
|
||||
}
|
||||
|
||||
+QString QHttpServer::errorString() const
|
||||
+{
|
||||
+ return m_errorString;
|
||||
+}
|
||||
+
|
||||
void QHttpServer::close()
|
||||
{
|
||||
if (m_tcpServer)
|
||||
diff --git a/external/qhttpserver/src/qhttpserver.h b/external/qhttpserver/src/qhttpserver.h
|
||||
index a3cb74b..4adb391 100755
|
||||
--- a/external/qhttpserver/src/qhttpserver.h
|
||||
+++ b/external/qhttpserver/src/qhttpserver.h
|
||||
@@ -79,6 +79,9 @@ public:
|
||||
@sa listen(const QHostAddress&, quint16) */
|
||||
bool listen(quint16 port);
|
||||
|
||||
+ /// Return the last error encountered.
|
||||
+ QString errorString() const;
|
||||
+
|
||||
/// Stop the server and listening for new connections.
|
||||
void close();
|
||||
signals:
|
||||
@@ -94,6 +97,7 @@ private slots:
|
||||
|
||||
private:
|
||||
QTcpServer *m_tcpServer;
|
||||
+ QString m_errorString;
|
||||
};
|
||||
|
||||
#endif
|
3
external/qhttpserver/qhttpserver.pri
vendored
3
external/qhttpserver/qhttpserver.pri
vendored
@ -1,3 +0,0 @@
|
||||
isEmpty(PREFIX):PREFIX = /usr/local
|
||||
isEmpty(LIBDIR):LIBDIR = $${PREFIX}/lib
|
||||
isEmpty(INCLUDEDIR):INCLUDEDIR = $${PREFIX}/include
|
8
external/qhttpserver/qhttpserver.pro
vendored
8
external/qhttpserver/qhttpserver.pro
vendored
@ -1,8 +0,0 @@
|
||||
CONFIG += ordered
|
||||
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += src \
|
||||
examples
|
||||
|
||||
examples.depends = src
|
293
external/qhttpserver/src/qhttpconnection.cpp
vendored
293
external/qhttpserver/src/qhttpconnection.cpp
vendored
@ -1,293 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qhttpconnection.h"
|
||||
|
||||
#include <QTcpSocket>
|
||||
#include <QHostAddress>
|
||||
|
||||
#include "http_parser.h"
|
||||
#include "qhttprequest.h"
|
||||
#include "qhttpresponse.h"
|
||||
|
||||
/// @cond nodoc
|
||||
|
||||
QHttpConnection::QHttpConnection(QTcpSocket *socket, QObject *parent)
|
||||
: QObject(parent),
|
||||
m_socket(socket),
|
||||
m_parser(0),
|
||||
m_parserSettings(0),
|
||||
m_request(0),
|
||||
m_transmitLen(0),
|
||||
m_transmitPos(0)
|
||||
{
|
||||
m_parser = (http_parser *)malloc(sizeof(http_parser));
|
||||
http_parser_init(m_parser, HTTP_REQUEST);
|
||||
|
||||
m_parserSettings = new http_parser_settings();
|
||||
m_parserSettings->on_message_begin = MessageBegin;
|
||||
m_parserSettings->on_url = Url;
|
||||
m_parserSettings->on_header_field = HeaderField;
|
||||
m_parserSettings->on_header_value = HeaderValue;
|
||||
m_parserSettings->on_headers_complete = HeadersComplete;
|
||||
m_parserSettings->on_body = Body;
|
||||
m_parserSettings->on_message_complete = MessageComplete;
|
||||
|
||||
m_parser->data = this;
|
||||
|
||||
connect(socket, SIGNAL(readyRead()), this, SLOT(parseRequest()));
|
||||
connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
|
||||
connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(updateWriteCount(qint64)));
|
||||
}
|
||||
|
||||
QHttpConnection::~QHttpConnection()
|
||||
{
|
||||
m_socket = 0;
|
||||
|
||||
free(m_parser);
|
||||
m_parser = 0;
|
||||
|
||||
delete m_parserSettings;
|
||||
m_parserSettings = 0;
|
||||
}
|
||||
|
||||
void QHttpConnection::socketDisconnected()
|
||||
{
|
||||
deleteLater();
|
||||
|
||||
if (m_request) {
|
||||
if (m_request->successful())
|
||||
return;
|
||||
|
||||
m_request->setSuccessful(false);
|
||||
Q_EMIT m_request->end();
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpConnection::updateWriteCount(qint64 count)
|
||||
{
|
||||
Q_ASSERT(m_transmitPos + count <= m_transmitLen);
|
||||
|
||||
m_transmitPos += count;
|
||||
|
||||
if (m_transmitPos == m_transmitLen)
|
||||
{
|
||||
m_transmitLen = 0;
|
||||
m_transmitPos = 0;
|
||||
Q_EMIT allBytesWritten();
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpConnection::parseRequest()
|
||||
{
|
||||
Q_ASSERT(m_parser);
|
||||
|
||||
while (m_socket->bytesAvailable()) {
|
||||
QByteArray arr = m_socket->readAll();
|
||||
http_parser_execute(m_parser, m_parserSettings, arr.constData(), arr.size());
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpConnection::write(const QByteArray &data)
|
||||
{
|
||||
m_socket->write(data);
|
||||
m_transmitLen += data.size();
|
||||
}
|
||||
|
||||
void QHttpConnection::flush()
|
||||
{
|
||||
m_socket->flush();
|
||||
}
|
||||
|
||||
void QHttpConnection::waitForBytesWritten()
|
||||
{
|
||||
m_socket->waitForBytesWritten();
|
||||
}
|
||||
|
||||
void QHttpConnection::responseDone()
|
||||
{
|
||||
QHttpResponse *response = qobject_cast<QHttpResponse *>(QObject::sender());
|
||||
if (response->m_last)
|
||||
m_socket->disconnectFromHost();
|
||||
}
|
||||
|
||||
/* URL Utilities */
|
||||
#define HAS_URL_FIELD(info, field) (info.field_set &(1 << (field)))
|
||||
|
||||
#define GET_FIELD(data, info, field) \
|
||||
QString::fromLatin1(data + info.field_data[field].off, info.field_data[field].len)
|
||||
|
||||
#define CHECK_AND_GET_FIELD(data, info, field) \
|
||||
(HAS_URL_FIELD(info, field) ? GET_FIELD(data, info, field) : QString())
|
||||
|
||||
QUrl createUrl(const char *urlData, const http_parser_url &urlInfo)
|
||||
{
|
||||
QUrl url;
|
||||
url.setScheme(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_SCHEMA));
|
||||
url.setHost(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_HOST));
|
||||
// Port is dealt with separately since it is available as an integer.
|
||||
url.setPath(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_PATH));
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
url.setQuery(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_QUERY));
|
||||
#else
|
||||
if (HAS_URL_FIELD(urlInfo, UF_QUERY)) {
|
||||
url.setEncodedQuery(QByteArray(urlData + urlInfo.field_data[UF_QUERY].off,
|
||||
urlInfo.field_data[UF_QUERY].len));
|
||||
}
|
||||
#endif
|
||||
url.setFragment(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_FRAGMENT));
|
||||
url.setUserInfo(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_USERINFO));
|
||||
|
||||
if (HAS_URL_FIELD(urlInfo, UF_PORT))
|
||||
url.setPort(urlInfo.port);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
#undef CHECK_AND_SET_FIELD
|
||||
#undef GET_FIELD
|
||||
#undef HAS_URL_FIELD
|
||||
|
||||
/********************
|
||||
* Static Callbacks *
|
||||
*******************/
|
||||
|
||||
int QHttpConnection::MessageBegin(http_parser *parser)
|
||||
{
|
||||
QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
|
||||
theConnection->m_currentHeaders.clear();
|
||||
theConnection->m_currentUrl.clear();
|
||||
theConnection->m_currentUrl.reserve(128);
|
||||
|
||||
// The QHttpRequest should not be parented to this, since it's memory
|
||||
// management is the responsibility of the user of the library.
|
||||
theConnection->m_request = new QHttpRequest(theConnection);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpConnection::HeadersComplete(http_parser *parser)
|
||||
{
|
||||
QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
|
||||
Q_ASSERT(theConnection->m_request);
|
||||
|
||||
/** set method **/
|
||||
theConnection->m_request->setMethod(static_cast<QHttpRequest::HttpMethod>(parser->method));
|
||||
|
||||
/** set version **/
|
||||
theConnection->m_request->setVersion(
|
||||
QString("%1.%2").arg(parser->http_major).arg(parser->http_minor));
|
||||
|
||||
/** get parsed url **/
|
||||
struct http_parser_url urlInfo;
|
||||
int r = http_parser_parse_url(theConnection->m_currentUrl.constData(),
|
||||
theConnection->m_currentUrl.size(),
|
||||
parser->method == HTTP_CONNECT, &urlInfo);
|
||||
Q_ASSERT(r == 0);
|
||||
Q_UNUSED(r);
|
||||
|
||||
theConnection->m_request->setUrl(createUrl(theConnection->m_currentUrl.constData(), urlInfo));
|
||||
|
||||
// Insert last remaining header
|
||||
theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] =
|
||||
theConnection->m_currentHeaderValue;
|
||||
theConnection->m_request->setHeaders(theConnection->m_currentHeaders);
|
||||
|
||||
/** set client information **/
|
||||
theConnection->m_request->m_remoteAddress = theConnection->m_socket->peerAddress().toString();
|
||||
theConnection->m_request->m_remotePort = theConnection->m_socket->peerPort();
|
||||
|
||||
QHttpResponse *response = new QHttpResponse(theConnection);
|
||||
if (parser->http_major < 1 || parser->http_minor < 1)
|
||||
response->m_keepAlive = false;
|
||||
|
||||
connect(theConnection, SIGNAL(destroyed()), response, SLOT(connectionClosed()));
|
||||
connect(response, SIGNAL(done()), theConnection, SLOT(responseDone()));
|
||||
|
||||
// we are good to go!
|
||||
Q_EMIT theConnection->newRequest(theConnection->m_request, response);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpConnection::MessageComplete(http_parser *parser)
|
||||
{
|
||||
// TODO: do cleanup and prepare for next request
|
||||
QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
|
||||
Q_ASSERT(theConnection->m_request);
|
||||
|
||||
theConnection->m_request->setSuccessful(true);
|
||||
Q_EMIT theConnection->m_request->end();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpConnection::Url(http_parser *parser, const char *at, size_t length)
|
||||
{
|
||||
QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
|
||||
Q_ASSERT(theConnection->m_request);
|
||||
|
||||
theConnection->m_currentUrl.append(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpConnection::HeaderField(http_parser *parser, const char *at, size_t length)
|
||||
{
|
||||
QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
|
||||
Q_ASSERT(theConnection->m_request);
|
||||
|
||||
// insert the header we parsed previously
|
||||
// into the header map
|
||||
if (!theConnection->m_currentHeaderField.isEmpty() &&
|
||||
!theConnection->m_currentHeaderValue.isEmpty()) {
|
||||
// header names are always lower-cased
|
||||
theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] =
|
||||
theConnection->m_currentHeaderValue;
|
||||
// clear header value. this sets up a nice
|
||||
// feedback loop where the next time
|
||||
// HeaderValue is called, it can simply append
|
||||
theConnection->m_currentHeaderField = QString();
|
||||
theConnection->m_currentHeaderValue = QString();
|
||||
}
|
||||
|
||||
QString fieldSuffix = QString::fromLatin1(at, length);
|
||||
theConnection->m_currentHeaderField += fieldSuffix;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpConnection::HeaderValue(http_parser *parser, const char *at, size_t length)
|
||||
{
|
||||
QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
|
||||
Q_ASSERT(theConnection->m_request);
|
||||
|
||||
QString valueSuffix = QString::fromLatin1(at, length);
|
||||
theConnection->m_currentHeaderValue += valueSuffix;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpConnection::Body(http_parser *parser, const char *at, size_t length)
|
||||
{
|
||||
QHttpConnection *theConnection = static_cast<QHttpConnection *>(parser->data);
|
||||
Q_ASSERT(theConnection->m_request);
|
||||
|
||||
Q_EMIT theConnection->m_request->data(QByteArray(at, length));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// @endcond
|
85
external/qhttpserver/src/qhttpconnection.h
vendored
85
external/qhttpserver/src/qhttpconnection.h
vendored
@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef Q_HTTP_CONNECTION
|
||||
#define Q_HTTP_CONNECTION
|
||||
|
||||
#include "qhttpserverapi.h"
|
||||
#include "qhttpserverfwd.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
/// @cond nodoc
|
||||
|
||||
class QHTTPSERVER_API QHttpConnection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QHttpConnection(QTcpSocket *socket, QObject *parent = 0);
|
||||
virtual ~QHttpConnection();
|
||||
|
||||
void write(const QByteArray &data);
|
||||
void flush();
|
||||
void waitForBytesWritten();
|
||||
|
||||
Q_SIGNALS:
|
||||
void newRequest(QHttpRequest *, QHttpResponse *);
|
||||
void allBytesWritten();
|
||||
|
||||
private Q_SLOTS:
|
||||
void parseRequest();
|
||||
void responseDone();
|
||||
void socketDisconnected();
|
||||
void updateWriteCount(qint64);
|
||||
|
||||
private:
|
||||
static int MessageBegin(http_parser *parser);
|
||||
static int Url(http_parser *parser, const char *at, size_t length);
|
||||
static int HeaderField(http_parser *parser, const char *at, size_t length);
|
||||
static int HeaderValue(http_parser *parser, const char *at, size_t length);
|
||||
static int HeadersComplete(http_parser *parser);
|
||||
static int Body(http_parser *parser, const char *at, size_t length);
|
||||
static int MessageComplete(http_parser *parser);
|
||||
|
||||
private:
|
||||
QTcpSocket *m_socket;
|
||||
http_parser *m_parser;
|
||||
http_parser_settings *m_parserSettings;
|
||||
|
||||
// Since there can only be one request at any time even with pipelining.
|
||||
QHttpRequest *m_request;
|
||||
|
||||
QByteArray m_currentUrl;
|
||||
// The ones we are reading in from the parser
|
||||
HeaderHash m_currentHeaders;
|
||||
QString m_currentHeaderField;
|
||||
QString m_currentHeaderValue;
|
||||
|
||||
// Keep track of transmit buffer status
|
||||
qint64 m_transmitLen;
|
||||
qint64 m_transmitPos;
|
||||
};
|
||||
|
||||
/// @endcond
|
||||
|
||||
#endif
|
96
external/qhttpserver/src/qhttprequest.cpp
vendored
96
external/qhttpserver/src/qhttprequest.cpp
vendored
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qhttprequest.h"
|
||||
|
||||
#include "qhttpconnection.h"
|
||||
|
||||
QHttpRequest::QHttpRequest(QHttpConnection *connection, QObject *parent)
|
||||
: QObject(parent), m_connection(connection), m_url("http://localhost/"), m_success(false)
|
||||
{
|
||||
}
|
||||
|
||||
QHttpRequest::~QHttpRequest()
|
||||
{
|
||||
}
|
||||
|
||||
QString QHttpRequest::header(const QString &field)
|
||||
{
|
||||
return m_headers.value(field.toLower(), "");
|
||||
}
|
||||
|
||||
const HeaderHash &QHttpRequest::headers() const
|
||||
{
|
||||
return m_headers;
|
||||
}
|
||||
|
||||
const QString &QHttpRequest::httpVersion() const
|
||||
{
|
||||
return m_version;
|
||||
}
|
||||
|
||||
const QUrl &QHttpRequest::url() const
|
||||
{
|
||||
return m_url;
|
||||
}
|
||||
|
||||
const QString QHttpRequest::path() const
|
||||
{
|
||||
return m_url.path();
|
||||
}
|
||||
|
||||
const QString QHttpRequest::methodString() const
|
||||
{
|
||||
return MethodToString(method());
|
||||
}
|
||||
|
||||
QHttpRequest::HttpMethod QHttpRequest::method() const
|
||||
{
|
||||
return m_method;
|
||||
}
|
||||
|
||||
const QString &QHttpRequest::remoteAddress() const
|
||||
{
|
||||
return m_remoteAddress;
|
||||
}
|
||||
|
||||
quint16 QHttpRequest::remotePort() const
|
||||
{
|
||||
return m_remotePort;
|
||||
}
|
||||
|
||||
void QHttpRequest::storeBody()
|
||||
{
|
||||
connect(this, SIGNAL(data(const QByteArray &)), this, SLOT(appendBody(const QByteArray &)),
|
||||
Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
QString QHttpRequest::MethodToString(HttpMethod method)
|
||||
{
|
||||
int index = staticMetaObject.indexOfEnumerator("HttpMethod");
|
||||
return staticMetaObject.enumerator(index).valueToKey(method);
|
||||
}
|
||||
|
||||
void QHttpRequest::appendBody(const QByteArray &body)
|
||||
{
|
||||
m_body.append(body);
|
||||
}
|
202
external/qhttpserver/src/qhttprequest.h
vendored
202
external/qhttpserver/src/qhttprequest.h
vendored
@ -1,202 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef Q_HTTP_REQUEST
|
||||
#define Q_HTTP_REQUEST
|
||||
|
||||
#include "qhttpserverapi.h"
|
||||
#include "qhttpserverfwd.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QMetaEnum>
|
||||
#include <QMetaType>
|
||||
#include <QUrl>
|
||||
|
||||
/// The QHttpRequest class represents the header and body data sent by the client.
|
||||
/** The requests header data is available immediately. Body data is streamed as
|
||||
it comes in via the data() signal. As a consequence the application's request
|
||||
callback should ensure that it connects to the data() signal before control
|
||||
returns back to the event loop. Otherwise there is a risk of some data never
|
||||
being received by the application.
|
||||
|
||||
The class is <b>read-only</b>. */
|
||||
class QHTTPSERVER_API QHttpRequest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(HeaderHash headers READ headers)
|
||||
Q_PROPERTY(QString remoteAddress READ remoteAddress)
|
||||
Q_PROPERTY(quint16 remotePort READ remotePort)
|
||||
Q_PROPERTY(QString method READ method)
|
||||
Q_PROPERTY(QUrl url READ url)
|
||||
Q_PROPERTY(QString path READ path)
|
||||
Q_PROPERTY(QString httpVersion READ httpVersion)
|
||||
|
||||
Q_ENUMS(HttpMethod)
|
||||
|
||||
/// @cond nodoc
|
||||
friend class QHttpConnection;
|
||||
/// @endcond
|
||||
|
||||
public:
|
||||
virtual ~QHttpRequest();
|
||||
|
||||
/// Request method enumeration.
|
||||
/** @note Taken from http_parser.h -- make sure to keep synced */
|
||||
enum HttpMethod {
|
||||
HTTP_DELETE = 0,
|
||||
HTTP_GET,
|
||||
HTTP_HEAD,
|
||||
HTTP_POST,
|
||||
HTTP_PUT,
|
||||
// pathological
|
||||
HTTP_CONNECT,
|
||||
HTTP_OPTIONS,
|
||||
HTTP_TRACE,
|
||||
// webdav
|
||||
HTTP_COPY,
|
||||
HTTP_LOCK,
|
||||
HTTP_MKCOL,
|
||||
HTTP_MOVE,
|
||||
HTTP_PROPFIND,
|
||||
HTTP_PROPPATCH,
|
||||
HTTP_SEARCH,
|
||||
HTTP_UNLOCK,
|
||||
// subversion
|
||||
HTTP_REPORT,
|
||||
HTTP_MKACTIVITY,
|
||||
HTTP_CHECKOUT,
|
||||
HTTP_MERGE,
|
||||
// upnp
|
||||
HTTP_MSEARCH,
|
||||
HTTP_NOTIFY,
|
||||
HTTP_SUBSCRIBE,
|
||||
HTTP_UNSUBSCRIBE,
|
||||
// RFC-5789
|
||||
HTTP_PATCH,
|
||||
HTTP_PURGE
|
||||
};
|
||||
|
||||
/// The method used for the request.
|
||||
HttpMethod method() const;
|
||||
|
||||
/// Returns the method string for the request.
|
||||
/** @note This will plainly transform the enum into a string HTTP_GET -> "HTTP_GET". */
|
||||
const QString methodString() const;
|
||||
|
||||
/// The complete URL for the request.
|
||||
/** This includes the path and query string.
|
||||
@sa path() */
|
||||
const QUrl &url() const;
|
||||
|
||||
/// The path portion of the query URL.
|
||||
/** @sa url() */
|
||||
const QString path() const;
|
||||
|
||||
/// The HTTP version of the request.
|
||||
/** @return A string in the form of "x.x" */
|
||||
const QString &httpVersion() const;
|
||||
|
||||
/// Return all the headers sent by the client.
|
||||
/** This returns a reference. If you want to store headers
|
||||
somewhere else, where the request may be deleted,
|
||||
make sure you store them as a copy.
|
||||
@note All header names are <b>lowercase</b>
|
||||
so that Content-Length becomes content-length etc. */
|
||||
const HeaderHash &headers() const;
|
||||
|
||||
/// Get the value of a header.
|
||||
/** Headers are stored as lowercase so the input @c field will be lowercased.
|
||||
@param field Name of the header field
|
||||
@return Value of the header or empty string if not found. */
|
||||
QString header(const QString &field);
|
||||
|
||||
/// IP Address of the client in dotted decimal format.
|
||||
const QString &remoteAddress() const;
|
||||
|
||||
/// Outbound connection port for the client.
|
||||
quint16 remotePort() const;
|
||||
|
||||
/// Request body data, empty for non POST/PUT requests.
|
||||
/** @sa storeBody() */
|
||||
const QByteArray &body() const
|
||||
{
|
||||
return m_body;
|
||||
}
|
||||
|
||||
/// If this request was successfully received.
|
||||
/** Set before end() has been emitted, stating whether
|
||||
the message was properly received. This is false
|
||||
until the receiving the full request has completed. */
|
||||
bool successful() const
|
||||
{
|
||||
return m_success;
|
||||
}
|
||||
|
||||
/// Utility function to make this request store all body data internally.
|
||||
/** If you call this when the request is received via QHttpServer::newRequest()
|
||||
the request will take care of storing the body data for you.
|
||||
Once the end() signal is emitted you can access the body data with
|
||||
the body() function.
|
||||
|
||||
If you wish to handle incoming data yourself don't call this function
|
||||
and see the data() signal.
|
||||
@sa data() body() */
|
||||
void storeBody();
|
||||
|
||||
Q_SIGNALS:
|
||||
/// Emitted when new body data has been received.
|
||||
/** @note This may be emitted zero or more times
|
||||
depending on the request type.
|
||||
@param data Received data. */
|
||||
void data(const QByteArray &data);
|
||||
|
||||
/// Emitted when the request has been fully received.
|
||||
/** @note The no more data() signals will be emitted after this. */
|
||||
void end();
|
||||
|
||||
private Q_SLOTS:
|
||||
void appendBody(const QByteArray &body);
|
||||
|
||||
private:
|
||||
QHttpRequest(QHttpConnection *connection, QObject *parent = 0);
|
||||
|
||||
static QString MethodToString(HttpMethod method);
|
||||
|
||||
void setMethod(HttpMethod method) { m_method = method; }
|
||||
void setVersion(const QString &version) { m_version = version; }
|
||||
void setUrl(const QUrl &url) { m_url = url; }
|
||||
void setHeaders(const HeaderHash headers) { m_headers = headers; }
|
||||
void setSuccessful(bool success) { m_success = success; }
|
||||
|
||||
QHttpConnection *m_connection;
|
||||
HeaderHash m_headers;
|
||||
HttpMethod m_method;
|
||||
QUrl m_url;
|
||||
QString m_version;
|
||||
QString m_remoteAddress;
|
||||
quint16 m_remotePort;
|
||||
QByteArray m_body;
|
||||
bool m_success;
|
||||
};
|
||||
|
||||
#endif
|
196
external/qhttpserver/src/qhttpresponse.cpp
vendored
196
external/qhttpserver/src/qhttpresponse.cpp
vendored
@ -1,196 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qhttpresponse.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QLocale>
|
||||
|
||||
#include "qhttpserver.h"
|
||||
#include "qhttpconnection.h"
|
||||
|
||||
QHttpResponse::QHttpResponse(QHttpConnection *connection)
|
||||
// TODO: parent child relation
|
||||
: QObject(0),
|
||||
m_connection(connection),
|
||||
m_headerWritten(false),
|
||||
m_sentConnectionHeader(false),
|
||||
m_sentContentLengthHeader(false),
|
||||
m_sentTransferEncodingHeader(false),
|
||||
m_sentDate(false),
|
||||
m_keepAlive(true),
|
||||
m_last(false),
|
||||
m_useChunkedEncoding(false),
|
||||
m_finished(false)
|
||||
{
|
||||
connect(m_connection, SIGNAL(allBytesWritten()), this, SIGNAL(allBytesWritten()));
|
||||
}
|
||||
|
||||
QHttpResponse::~QHttpResponse()
|
||||
{
|
||||
}
|
||||
|
||||
void QHttpResponse::setHeader(const QString &field, const QString &value)
|
||||
{
|
||||
if (!m_finished)
|
||||
m_headers[field] = value;
|
||||
else
|
||||
qWarning() << "QHttpResponse::setHeader() Cannot set headers after response has finished.";
|
||||
}
|
||||
|
||||
void QHttpResponse::writeHeader(const char *field, const QString &value)
|
||||
{
|
||||
if (!m_finished) {
|
||||
m_connection->write(field);
|
||||
m_connection->write(": ");
|
||||
m_connection->write(value.toUtf8());
|
||||
m_connection->write("\r\n");
|
||||
} else
|
||||
qWarning()
|
||||
<< "QHttpResponse::writeHeader() Cannot write headers after response has finished.";
|
||||
}
|
||||
|
||||
void QHttpResponse::writeHeaders()
|
||||
{
|
||||
if (m_finished)
|
||||
return;
|
||||
|
||||
foreach(const QString & name, m_headers.keys()) {
|
||||
QString value = m_headers[name];
|
||||
if (name.compare("connection", Qt::CaseInsensitive) == 0) {
|
||||
m_sentConnectionHeader = true;
|
||||
if (value.compare("close", Qt::CaseInsensitive) == 0)
|
||||
m_last = true;
|
||||
else
|
||||
m_keepAlive = true;
|
||||
} else if (name.compare("transfer-encoding", Qt::CaseInsensitive) == 0) {
|
||||
m_sentTransferEncodingHeader = true;
|
||||
if (value.compare("chunked", Qt::CaseInsensitive) == 0)
|
||||
m_useChunkedEncoding = true;
|
||||
} else if (name.compare("content-length", Qt::CaseInsensitive) == 0)
|
||||
m_sentContentLengthHeader = true;
|
||||
else if (name.compare("date", Qt::CaseInsensitive) == 0)
|
||||
m_sentDate = true;
|
||||
|
||||
/// @todo Expect case (??)
|
||||
|
||||
writeHeader(name.toLatin1(), value.toLatin1());
|
||||
}
|
||||
|
||||
if (!m_sentConnectionHeader) {
|
||||
if (m_keepAlive && (m_sentContentLengthHeader || m_useChunkedEncoding)) {
|
||||
writeHeader("Connection", "keep-alive");
|
||||
} else {
|
||||
m_last = true;
|
||||
writeHeader("Connection", "close");
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_sentContentLengthHeader && !m_sentTransferEncodingHeader) {
|
||||
if (m_useChunkedEncoding)
|
||||
writeHeader("Transfer-Encoding", "chunked");
|
||||
else
|
||||
m_last = true;
|
||||
}
|
||||
|
||||
// Sun, 06 Nov 1994 08:49:37 GMT - RFC 822. Use QLocale::c() so english is used for month and
|
||||
// day.
|
||||
if (!m_sentDate)
|
||||
writeHeader("Date",
|
||||
QLocale::c().toString(QDateTime::currentDateTimeUtc(),
|
||||
"ddd, dd MMM yyyy hh:mm:ss") + " GMT");
|
||||
}
|
||||
|
||||
void QHttpResponse::writeHead(int status)
|
||||
{
|
||||
if (m_finished) {
|
||||
qWarning()
|
||||
<< "QHttpResponse::writeHead() Cannot write headers after response has finished.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_headerWritten) {
|
||||
qWarning() << "QHttpResponse::writeHead() Already called once for this response.";
|
||||
return;
|
||||
}
|
||||
|
||||
m_connection->write(
|
||||
QString("HTTP/1.1 %1 %2\r\n").arg(status).arg(STATUS_CODES[status]).toLatin1());
|
||||
writeHeaders();
|
||||
m_connection->write("\r\n");
|
||||
|
||||
m_headerWritten = true;
|
||||
}
|
||||
|
||||
void QHttpResponse::writeHead(StatusCode statusCode)
|
||||
{
|
||||
writeHead(static_cast<int>(statusCode));
|
||||
}
|
||||
|
||||
void QHttpResponse::write(const QByteArray &data)
|
||||
{
|
||||
if (m_finished) {
|
||||
qWarning() << "QHttpResponse::write() Cannot write body after response has finished.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_headerWritten) {
|
||||
qWarning() << "QHttpResponse::write() You must call writeHead() before writing body data.";
|
||||
return;
|
||||
}
|
||||
|
||||
m_connection->write(data);
|
||||
}
|
||||
|
||||
void QHttpResponse::flush()
|
||||
{
|
||||
m_connection->flush();
|
||||
}
|
||||
|
||||
void QHttpResponse::waitForBytesWritten()
|
||||
{
|
||||
m_connection->waitForBytesWritten();
|
||||
}
|
||||
|
||||
void QHttpResponse::end(const QByteArray &data)
|
||||
{
|
||||
if (m_finished) {
|
||||
qWarning() << "QHttpResponse::end() Cannot write end after response has finished.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.size() > 0)
|
||||
write(data);
|
||||
m_finished = true;
|
||||
|
||||
Q_EMIT done();
|
||||
|
||||
/// @todo End connection and delete ourselves. Is this a still valid note?
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void QHttpResponse::connectionClosed()
|
||||
{
|
||||
m_finished = true;
|
||||
Q_EMIT done();
|
||||
deleteLater();
|
||||
}
|
172
external/qhttpserver/src/qhttpresponse.h
vendored
172
external/qhttpserver/src/qhttpresponse.h
vendored
@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef Q_HTTP_RESPONSE
|
||||
#define Q_HTTP_RESPONSE
|
||||
|
||||
#include "qhttpserverapi.h"
|
||||
#include "qhttpserverfwd.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
/// The QHttpResponse class handles sending data back to the client as a response to a request.
|
||||
/** The steps to respond correctly are
|
||||
<ol>
|
||||
<li>Call setHeader() to set headers [optional]</li>
|
||||
<li>Call writeHead() with the HTTP status code</li>
|
||||
<li>Call write() zero or more times for body data.</li>
|
||||
<li>Call end() when the resonse can be sent back</li>
|
||||
</ol> */
|
||||
class QHTTPSERVER_API QHttpResponse : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/// HTTP status code.
|
||||
enum StatusCode {
|
||||
STATUS_CONTINUE = 100,
|
||||
STATUS_SWITCH_PROTOCOLS = 101,
|
||||
STATUS_OK = 200,
|
||||
STATUS_CREATED = 201,
|
||||
STATUS_ACCEPTED = 202,
|
||||
STATUS_NON_AUTHORITATIVE_INFORMATION = 203,
|
||||
STATUS_NO_CONTENT = 204,
|
||||
STATUS_RESET_CONTENT = 205,
|
||||
STATUS_PARTIAL_CONTENT = 206,
|
||||
STATUS_MULTIPLE_CHOICES = 300,
|
||||
STATUS_MOVED_PERMANENTLY = 301,
|
||||
STATUS_FOUND = 302,
|
||||
STATUS_SEE_OTHER = 303,
|
||||
STATUS_NOT_MODIFIED = 304,
|
||||
STATUS_USE_PROXY = 305,
|
||||
STATUS_TEMPORARY_REDIRECT = 307,
|
||||
STATUS_BAD_REQUEST = 400,
|
||||
STATUS_UNAUTHORIZED = 401,
|
||||
STATUS_PAYMENT_REQUIRED = 402,
|
||||
STATUS_FORBIDDEN = 403,
|
||||
STATUS_NOT_FOUND = 404,
|
||||
STATUS_METHOD_NOT_ALLOWED = 405,
|
||||
STATUS_NOT_ACCEPTABLE = 406,
|
||||
STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||
STATUS_REQUEST_TIMEOUT = 408,
|
||||
STATUS_CONFLICT = 409,
|
||||
STATUS_GONE = 410,
|
||||
STATUS_LENGTH_REQUIRED = 411,
|
||||
STATUS_PRECONDITION_FAILED = 412,
|
||||
STATUS_REQUEST_ENTITY_TOO_LARGE = 413,
|
||||
STATUS_REQUEST_URI_TOO_LONG = 414,
|
||||
STATUS_REQUEST_UNSUPPORTED_MEDIA_TYPE = 415,
|
||||
STATUS_REQUESTED_RANGE_NOT_SATISFIABLE = 416,
|
||||
STATUS_EXPECTATION_FAILED = 417,
|
||||
STATUS_INTERNAL_SERVER_ERROR = 500,
|
||||
STATUS_NOT_IMPLEMENTED = 501,
|
||||
STATUS_BAD_GATEWAY = 502,
|
||||
STATUS_SERVICE_UNAVAILABLE = 503,
|
||||
STATUS_GATEWAY_TIMEOUT = 504,
|
||||
STATUS_HTTP_VERSION_NOT_SUPPORTED = 505
|
||||
};
|
||||
|
||||
virtual ~QHttpResponse();
|
||||
|
||||
/// @cond nodoc
|
||||
friend class QHttpConnection;
|
||||
/// @endcond
|
||||
|
||||
public Q_SLOTS:
|
||||
/// Sets a response header @c field to @c value.
|
||||
/** @note You must call this with all your custom headers
|
||||
before calling writeHead(), write() or end().
|
||||
@param field Header field to be set.
|
||||
@param value Header value to be set. */
|
||||
void setHeader(const QString &field, const QString &value);
|
||||
|
||||
/// Writes the header section of the response
|
||||
/// using @c status as the response status code.
|
||||
/** @param statusCode Status code for the response.
|
||||
@note Any headers should be set before
|
||||
invoking this function with setHeader(). */
|
||||
void writeHead(int statusCode);
|
||||
|
||||
/** @overload */
|
||||
void writeHead(StatusCode statusCode);
|
||||
|
||||
/// Writes a block of @c data to the client.
|
||||
/** @note writeHead() must be called before this function. */
|
||||
void write(const QByteArray &data);
|
||||
|
||||
/// Flushes the written data to the client.
|
||||
/** @note writeHead() must be called before this function. */
|
||||
void flush();
|
||||
|
||||
/// Waiting for bytes to be written. See QAbstractSocket::waitForBytesWritten in the Qt documentation
|
||||
/** @note writeHead() must be called before this function. */
|
||||
void waitForBytesWritten();
|
||||
|
||||
/// End/finish the response.
|
||||
/** Data will be flushed to the underlying socket
|
||||
and the connection itself will be closed if
|
||||
this is the last response.
|
||||
|
||||
This will emit done() and queue this object
|
||||
for deletion. For details see \ref memorymanagement.
|
||||
@param data Optional data to be written before finishing. */
|
||||
void end(const QByteArray &data = "");
|
||||
|
||||
Q_SIGNALS:
|
||||
/// Emitted when all the data has been sent
|
||||
/** This signal indicates that the underlaying socket has transmitted all
|
||||
of it's buffered data. It is possible to implement memory-efficient
|
||||
file transfers by calling \ref write() for a block of data only after
|
||||
receiving this signal. */
|
||||
void allBytesWritten();
|
||||
|
||||
/// Emitted when the response is finished.
|
||||
/** You should <b>not</b> interact with this object
|
||||
after done() has been emitted as the object
|
||||
has already been scheduled for deletion. */
|
||||
void done();
|
||||
|
||||
private:
|
||||
QHttpResponse(QHttpConnection *connection);
|
||||
|
||||
void writeHeaders();
|
||||
void writeHeader(const char *field, const QString &value);
|
||||
|
||||
QHttpConnection *m_connection;
|
||||
|
||||
HeaderHash m_headers;
|
||||
|
||||
bool m_headerWritten;
|
||||
bool m_sentConnectionHeader;
|
||||
bool m_sentContentLengthHeader;
|
||||
bool m_sentTransferEncodingHeader;
|
||||
bool m_sentDate;
|
||||
bool m_keepAlive;
|
||||
bool m_last;
|
||||
bool m_useChunkedEncoding;
|
||||
bool m_finished;
|
||||
|
||||
private Q_SLOTS:
|
||||
void connectionClosed();
|
||||
};
|
||||
|
||||
#endif
|
139
external/qhttpserver/src/qhttpserver.cpp
vendored
139
external/qhttpserver/src/qhttpserver.cpp
vendored
@ -1,139 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qhttpserver.h"
|
||||
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QVariant>
|
||||
#include <QDebug>
|
||||
|
||||
#include "qhttpconnection.h"
|
||||
|
||||
QHash<int, QString> STATUS_CODES;
|
||||
|
||||
QHttpServer::QHttpServer(QObject *parent) : QObject(parent), m_tcpServer(0)
|
||||
{
|
||||
#define STATUS_CODE(num, reason) STATUS_CODES.insert(num, reason);
|
||||
// {{{
|
||||
STATUS_CODE(100, "Continue")
|
||||
STATUS_CODE(101, "Switching Protocols")
|
||||
STATUS_CODE(102, "Processing") // RFC 2518) obsoleted by RFC 4918
|
||||
STATUS_CODE(200, "OK")
|
||||
STATUS_CODE(201, "Created")
|
||||
STATUS_CODE(202, "Accepted")
|
||||
STATUS_CODE(203, "Non-Authoritative Information")
|
||||
STATUS_CODE(204, "No Content")
|
||||
STATUS_CODE(205, "Reset Content")
|
||||
STATUS_CODE(206, "Partial Content")
|
||||
STATUS_CODE(207, "Multi-Status") // RFC 4918
|
||||
STATUS_CODE(300, "Multiple Choices")
|
||||
STATUS_CODE(301, "Moved Permanently")
|
||||
STATUS_CODE(302, "Moved Temporarily")
|
||||
STATUS_CODE(303, "See Other")
|
||||
STATUS_CODE(304, "Not Modified")
|
||||
STATUS_CODE(305, "Use Proxy")
|
||||
STATUS_CODE(307, "Temporary Redirect")
|
||||
STATUS_CODE(400, "Bad Request")
|
||||
STATUS_CODE(401, "Unauthorized")
|
||||
STATUS_CODE(402, "Payment Required")
|
||||
STATUS_CODE(403, "Forbidden")
|
||||
STATUS_CODE(404, "Not Found")
|
||||
STATUS_CODE(405, "Method Not Allowed")
|
||||
STATUS_CODE(406, "Not Acceptable")
|
||||
STATUS_CODE(407, "Proxy Authentication Required")
|
||||
STATUS_CODE(408, "Request Time-out")
|
||||
STATUS_CODE(409, "Conflict")
|
||||
STATUS_CODE(410, "Gone")
|
||||
STATUS_CODE(411, "Length Required")
|
||||
STATUS_CODE(412, "Precondition Failed")
|
||||
STATUS_CODE(413, "Request Entity Too Large")
|
||||
STATUS_CODE(414, "Request-URI Too Large")
|
||||
STATUS_CODE(415, "Unsupported Media Type")
|
||||
STATUS_CODE(416, "Requested Range Not Satisfiable")
|
||||
STATUS_CODE(417, "Expectation Failed")
|
||||
STATUS_CODE(418, "I\"m a teapot") // RFC 2324
|
||||
STATUS_CODE(422, "Unprocessable Entity") // RFC 4918
|
||||
STATUS_CODE(423, "Locked") // RFC 4918
|
||||
STATUS_CODE(424, "Failed Dependency") // RFC 4918
|
||||
STATUS_CODE(425, "Unordered Collection") // RFC 4918
|
||||
STATUS_CODE(426, "Upgrade Required") // RFC 2817
|
||||
STATUS_CODE(500, "Internal Server Error")
|
||||
STATUS_CODE(501, "Not Implemented")
|
||||
STATUS_CODE(502, "Bad Gateway")
|
||||
STATUS_CODE(503, "Service Unavailable")
|
||||
STATUS_CODE(504, "Gateway Time-out")
|
||||
STATUS_CODE(505, "HTTP Version not supported")
|
||||
STATUS_CODE(506, "Variant Also Negotiates") // RFC 2295
|
||||
STATUS_CODE(507, "Insufficient Storage") // RFC 4918
|
||||
STATUS_CODE(509, "Bandwidth Limit Exceeded")
|
||||
STATUS_CODE(510, "Not Extended") // RFC 2774
|
||||
// }}}
|
||||
}
|
||||
|
||||
QHttpServer::~QHttpServer()
|
||||
{
|
||||
}
|
||||
|
||||
void QHttpServer::newConnection()
|
||||
{
|
||||
Q_ASSERT(m_tcpServer);
|
||||
|
||||
while (m_tcpServer->hasPendingConnections()) {
|
||||
QHttpConnection *connection =
|
||||
new QHttpConnection(m_tcpServer->nextPendingConnection(), this);
|
||||
connect(connection, SIGNAL(newRequest(QHttpRequest *, QHttpResponse *)), this,
|
||||
SIGNAL(newRequest(QHttpRequest *, QHttpResponse *)));
|
||||
}
|
||||
}
|
||||
|
||||
bool QHttpServer::listen(const QHostAddress &address, quint16 port)
|
||||
{
|
||||
Q_ASSERT(!m_tcpServer);
|
||||
m_tcpServer = new QTcpServer(this);
|
||||
|
||||
bool couldBindToPort = m_tcpServer->listen(address, port);
|
||||
if (couldBindToPort) {
|
||||
connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection()));
|
||||
} else {
|
||||
m_errorString = m_tcpServer->errorString();
|
||||
delete m_tcpServer;
|
||||
m_tcpServer = NULL;
|
||||
}
|
||||
return couldBindToPort;
|
||||
}
|
||||
|
||||
bool QHttpServer::listen(quint16 port)
|
||||
{
|
||||
return listen(QHostAddress::Any, port);
|
||||
}
|
||||
|
||||
QString QHttpServer::errorString() const
|
||||
{
|
||||
return m_errorString;
|
||||
}
|
||||
|
||||
void QHttpServer::close()
|
||||
{
|
||||
if (m_tcpServer)
|
||||
m_tcpServer->close();
|
||||
}
|
103
external/qhttpserver/src/qhttpserver.h
vendored
103
external/qhttpserver/src/qhttpserver.h
vendored
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef Q_HTTP_SERVER
|
||||
#define Q_HTTP_SERVER
|
||||
|
||||
#define QHTTPSERVER_VERSION_MAJOR 0
|
||||
#define QHTTPSERVER_VERSION_MINOR 1
|
||||
#define QHTTPSERVER_VERSION_PATCH 0
|
||||
|
||||
#include "qhttpserverapi.h"
|
||||
#include "qhttpserverfwd.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
|
||||
/// Maps status codes to string reason phrases
|
||||
extern QHash<int, QString> STATUS_CODES;
|
||||
|
||||
/// The QHttpServer class forms the basis of the %QHttpServer
|
||||
/// project. It is a fast, non-blocking HTTP server.
|
||||
/** These are the steps to create a server, handle and respond to requests:
|
||||
<ol>
|
||||
<li>Create an instance of QHttpServer.</li>
|
||||
<li>Connect a slot to the newRequest() signal.</li>
|
||||
<li>Create a QCoreApplication to drive the server event loop.</li>
|
||||
<li>Respond to clients by writing out to the QHttpResponse object.</li>
|
||||
</ol>
|
||||
|
||||
<b>Here is a simple sample application on how to use this library</b>
|
||||
|
||||
helloworld.cpp
|
||||
@include helloworld/helloworld.cpp
|
||||
|
||||
helloworld.h
|
||||
@include helloworld/helloworld.h */
|
||||
class QHTTPSERVER_API QHttpServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/// Construct a new HTTP Server.
|
||||
/** @param parent Parent QObject for the server. */
|
||||
QHttpServer(QObject *parent = 0);
|
||||
|
||||
virtual ~QHttpServer();
|
||||
|
||||
/// Start the server by bounding to the given @c address and @c port.
|
||||
/** @note This function returns immediately, it does not block.
|
||||
@param address Address on which to listen to. Default is to listen on
|
||||
all interfaces which means the server can be accessed from anywhere.
|
||||
@param port Port number on which the server should run.
|
||||
@return True if the server was started successfully, false otherwise.
|
||||
@sa listen(quint16) */
|
||||
bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 80);
|
||||
|
||||
/// Starts the server on @c port listening on all interfaces.
|
||||
/** @param port Port number on which the server should run.
|
||||
@return True if the server was started successfully, false otherwise.
|
||||
@sa listen(const QHostAddress&, quint16) */
|
||||
bool listen(quint16 port);
|
||||
|
||||
/// Return the last error encountered.
|
||||
QString errorString() const;
|
||||
|
||||
/// Stop the server and listening for new connections.
|
||||
void close();
|
||||
Q_SIGNALS:
|
||||
/// Emitted when a client makes a new request to the server.
|
||||
/** The slot should use the given @c request and @c response
|
||||
objects to communicate with the client.
|
||||
@param request New incoming request.
|
||||
@param response Response object to the request. */
|
||||
void newRequest(QHttpRequest *request, QHttpResponse *response);
|
||||
|
||||
private Q_SLOTS:
|
||||
void newConnection();
|
||||
|
||||
private:
|
||||
QTcpServer *m_tcpServer;
|
||||
QString m_errorString;
|
||||
};
|
||||
|
||||
#endif
|
99
external/qhttpserver/src/qhttpserver.h.orig
vendored
99
external/qhttpserver/src/qhttpserver.h.orig
vendored
@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef Q_HTTP_SERVER
|
||||
#define Q_HTTP_SERVER
|
||||
|
||||
#define QHTTPSERVER_VERSION_MAJOR 0
|
||||
#define QHTTPSERVER_VERSION_MINOR 1
|
||||
#define QHTTPSERVER_VERSION_PATCH 0
|
||||
|
||||
#include "qhttpserverapi.h"
|
||||
#include "qhttpserverfwd.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
|
||||
/// Maps status codes to string reason phrases
|
||||
extern QHash<int, QString> STATUS_CODES;
|
||||
|
||||
/// The QHttpServer class forms the basis of the %QHttpServer
|
||||
/// project. It is a fast, non-blocking HTTP server.
|
||||
/** These are the steps to create a server, handle and respond to requests:
|
||||
<ol>
|
||||
<li>Create an instance of QHttpServer.</li>
|
||||
<li>Connect a slot to the newRequest() signal.</li>
|
||||
<li>Create a QCoreApplication to drive the server event loop.</li>
|
||||
<li>Respond to clients by writing out to the QHttpResponse object.</li>
|
||||
</ol>
|
||||
|
||||
<b>Here is a simple sample application on how to use this library</b>
|
||||
|
||||
helloworld.cpp
|
||||
@include helloworld/helloworld.cpp
|
||||
|
||||
helloworld.h
|
||||
@include helloworld/helloworld.h */
|
||||
class QHTTPSERVER_API QHttpServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/// Construct a new HTTP Server.
|
||||
/** @param parent Parent QObject for the server. */
|
||||
QHttpServer(QObject *parent = 0);
|
||||
|
||||
virtual ~QHttpServer();
|
||||
|
||||
/// Start the server by bounding to the given @c address and @c port.
|
||||
/** @note This function returns immediately, it does not block.
|
||||
@param address Address on which to listen to. Default is to listen on
|
||||
all interfaces which means the server can be accessed from anywhere.
|
||||
@param port Port number on which the server should run.
|
||||
@return True if the server was started successfully, false otherwise.
|
||||
@sa listen(quint16) */
|
||||
bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 80);
|
||||
|
||||
/// Starts the server on @c port listening on all interfaces.
|
||||
/** @param port Port number on which the server should run.
|
||||
@return True if the server was started successfully, false otherwise.
|
||||
@sa listen(const QHostAddress&, quint16) */
|
||||
bool listen(quint16 port);
|
||||
|
||||
/// Stop the server and listening for new connections.
|
||||
void close();
|
||||
Q_SIGNALS:
|
||||
/// Emitted when a client makes a new request to the server.
|
||||
/** The slot should use the given @c request and @c response
|
||||
objects to communicate with the client.
|
||||
@param request New incoming request.
|
||||
@param response Response object to the request. */
|
||||
void newRequest(QHttpRequest *request, QHttpResponse *response);
|
||||
|
||||
private Q_SLOTS:
|
||||
void newConnection();
|
||||
|
||||
private:
|
||||
QTcpServer *m_tcpServer;
|
||||
};
|
||||
|
||||
#endif
|
56
external/qhttpserver/src/qhttpserverapi.h
vendored
56
external/qhttpserver/src/qhttpserverapi.h
vendored
@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef Q_HTTP_SERVER_API
|
||||
#define Q_HTTP_SERVER_API
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
#ifdef Q_OS_WIN
|
||||
// Define to export or import depending if we are building or using the library.
|
||||
// QHTTPSERVER_EXPORT should only be defined when building.
|
||||
#if defined(QHTTPSERVER_EXPORT)
|
||||
#define QHTTPSERVER_API __declspec(dllexport)
|
||||
#else
|
||||
#define QHTTPSERVER_API __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
// Define empty for other platforms
|
||||
#define QHTTPSERVER_API
|
||||
#endif
|
||||
#else
|
||||
#ifdef Q_WS_WIN
|
||||
// Define to export or import depending if we are building or using the library.
|
||||
// QHTTPSERVER_EXPORT should only be defined when building.
|
||||
#if defined(QHTTPSERVER_EXPORT)
|
||||
#define QHTTPSERVER_API __declspec(dllexport)
|
||||
#else
|
||||
#define QHTTPSERVER_API __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
// Define empty for other platforms
|
||||
#define QHTTPSERVER_API
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
48
external/qhttpserver/src/qhttpserverfwd.h
vendored
48
external/qhttpserver/src/qhttpserverfwd.h
vendored
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011-2014 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef Q_HTTP_SERVER_FWD
|
||||
#define Q_HTTP_SERVER_FWD
|
||||
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
|
||||
/*!
|
||||
* A map of request or response headers
|
||||
*/
|
||||
typedef QHash<QString, QString> HeaderHash;
|
||||
|
||||
// QHttpServer
|
||||
class QHttpServer;
|
||||
class QHttpConnection;
|
||||
class QHttpRequest;
|
||||
class QHttpResponse;
|
||||
|
||||
// Qt
|
||||
class QTcpServer;
|
||||
class QTcpSocket;
|
||||
|
||||
// http_parser
|
||||
struct http_parser_settings;
|
||||
struct http_parser;
|
||||
|
||||
#endif
|
37
external/qhttpserver/src/src.pro
vendored
37
external/qhttpserver/src/src.pro
vendored
@ -1,37 +0,0 @@
|
||||
include(../qhttpserver.pri)
|
||||
|
||||
QHTTPSERVER_BASE = ..
|
||||
TEMPLATE = lib
|
||||
|
||||
TARGET = qhttpserver
|
||||
|
||||
!win32:VERSION = 0.1.0
|
||||
|
||||
QT += network
|
||||
QT -= gui
|
||||
|
||||
CONFIG += dll debug_and_release
|
||||
|
||||
CONFIG(debug, debug|release) {
|
||||
win32: TARGET = $$join(TARGET,,,d)
|
||||
}
|
||||
|
||||
DEFINES += QHTTPSERVER_EXPORT
|
||||
|
||||
INCLUDEPATH += $$QHTTPSERVER_BASE/http-parser
|
||||
|
||||
PRIVATE_HEADERS += $$QHTTPSERVER_BASE/http-parser/http_parser.h qhttpconnection.h
|
||||
|
||||
PUBLIC_HEADERS += qhttpserver.h qhttprequest.h qhttpresponse.h qhttpserverapi.h qhttpserverfwd.h
|
||||
|
||||
HEADERS = $$PRIVATE_HEADERS $$PUBLIC_HEADERS
|
||||
SOURCES = *.cpp $$QHTTPSERVER_BASE/http-parser/http_parser.c
|
||||
|
||||
OBJECTS_DIR = $$QHTTPSERVER_BASE/build
|
||||
MOC_DIR = $$QHTTPSERVER_BASE/build
|
||||
DESTDIR = $$QHTTPSERVER_BASE/lib
|
||||
|
||||
target.path = $$LIBDIR
|
||||
headers.path = $$INCLUDEDIR
|
||||
headers.files = $$PUBLIC_HEADERS
|
||||
INSTALLS += target headers
|
@ -1,5 +1,5 @@
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/external/qhttpserver/src
|
||||
${CMAKE_SOURCE_DIR}/external/qhttp/src
|
||||
${CMAKE_SOURCE_DIR}/external/qslog
|
||||
${CMAKE_SOURCE_DIR}/external/SPMediaKeyTap
|
||||
${CMAKE_SOURCE_DIR}/external/plistparser
|
||||
@ -146,7 +146,7 @@ endif(APPLE)
|
||||
|
||||
target_link_libraries(${MAIN_TARGET}
|
||||
shared
|
||||
qhttpserver
|
||||
qhttp
|
||||
qslog
|
||||
${PLISTPARSER}
|
||||
${MPV_LIBRARY}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "system/UpdaterComponent.h"
|
||||
#include "settings/SettingsComponent.h"
|
||||
#include "remote/RemoteComponent.h"
|
||||
#include "remote/RokuRemoteComponent.h"
|
||||
|
||||
#if KONVERGO_OPENELEC
|
||||
#include "system/openelec/OESystemComponent.h"
|
||||
@ -62,6 +63,7 @@ void ComponentManager::initialize()
|
||||
registerComponent(&RemoteComponent::Get());
|
||||
registerComponent(&PlayerComponent::Get());
|
||||
registerComponent(&PowerComponent::Get());
|
||||
registerComponent(&RokuRemoteComponent::Get());
|
||||
|
||||
#if KONVERGO_OPENELEC
|
||||
registerComponent(&OESystemComponent::Get());
|
||||
|
@ -113,7 +113,7 @@ QVariantMap RemoteComponent::GDMInformation()
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
void RemoteComponent::handleResource(QHttpRequest* request, QHttpResponse* response)
|
||||
{
|
||||
if (request->method() == QHttpRequest::HTTP_GET)
|
||||
if (request->method() == qhttp::EHTTP_GET)
|
||||
{
|
||||
QVariantMap headers = ResourceInformation();
|
||||
|
||||
@ -130,13 +130,13 @@ void RemoteComponent::handleResource(QHttpRequest* request, QHttpResponse* respo
|
||||
output.writeEndElement();
|
||||
output.writeEndDocument();
|
||||
|
||||
response->writeHead(QHttpResponse::STATUS_OK);
|
||||
response->setStatusCode(qhttp::ESTATUS_OK);
|
||||
response->write(outputData);
|
||||
response->end();
|
||||
}
|
||||
else
|
||||
{
|
||||
response->writeHead(QHttpResponse::STATUS_METHOD_NOT_ALLOWED);
|
||||
response->setStatusCode(qhttp::ESTATUS_METHOD_NOT_ALLOWED);
|
||||
response->end();
|
||||
}
|
||||
}
|
||||
@ -171,11 +171,11 @@ QVariantMap RemoteComponent::QueryToMap(const QUrl& url)
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
QVariantMap RemoteComponent::HeaderToMap(const HeaderHash& hash)
|
||||
QVariantMap RemoteComponent::HeaderToMap(const qhttp::THeaderHash& hash)
|
||||
{
|
||||
QVariantMap variantMap;
|
||||
foreach (const QString& key, hash.keys())
|
||||
variantMap[key] = hash[key];
|
||||
variantMap.insert(key, hash.value(key.toUtf8()));
|
||||
return variantMap;
|
||||
}
|
||||
|
||||
@ -187,23 +187,23 @@ void RemoteComponent::handleCommand(QHttpRequest* request, QHttpResponse* respon
|
||||
QVariantMap headerMap = HeaderToMap(request->headers());
|
||||
QString identifier = headerMap["x-plex-client-identifier"].toString();
|
||||
|
||||
response->setHeader("Access-Control-Allow-Origin", "*");
|
||||
response->setHeader("X-Plex-Client-Identifier", SettingsComponent::Get().value(SETTINGS_SECTION_WEBCLIENT, "clientID").toString());
|
||||
response->addHeader("Access-Control-Allow-Origin", "*");
|
||||
response->addHeader("X-Plex-Client-Identifier", SettingsComponent::Get().value(SETTINGS_SECTION_WEBCLIENT, "clientID").toByteArray());
|
||||
|
||||
// handle CORS requests here
|
||||
if ((request->method() == QHttpRequest::HTTP_OPTIONS) && headerMap.contains("access-control-request-method"))
|
||||
if ((request->method() == qhttp::EHTTP_OPTIONS) && headerMap.contains("access-control-request-method"))
|
||||
{
|
||||
response->setHeader("Content-Type", "text/plain");
|
||||
response->setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT, HEAD");
|
||||
response->setHeader("Access-Control-Max-Age", "1209600");
|
||||
response->setHeader("Connection", "close");
|
||||
response->addHeader("Content-Type", "text/plain");
|
||||
response->addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT, HEAD");
|
||||
response->addHeader("Access-Control-Max-Age", "1209600");
|
||||
response->addHeader("Connection", "close");
|
||||
|
||||
if (headerMap.contains("access-control-request-headers"))
|
||||
{
|
||||
response->setHeader("Access-Control-Allow-Headers", headerMap["access-control-request-headers"].toString());
|
||||
response->addHeader("Access-Control-Allow-Headers", headerMap.value("access-control-request-headers").toByteArray());
|
||||
}
|
||||
|
||||
response->writeHead(QHttpResponse::STATUS_OK);
|
||||
response->setStatusCode(qhttp::ESTATUS_OK);
|
||||
response->end();
|
||||
return;
|
||||
}
|
||||
@ -211,32 +211,35 @@ void RemoteComponent::handleCommand(QHttpRequest* request, QHttpResponse* respon
|
||||
// we want to handle the subscription events in the host
|
||||
// since we are going to handle the updating later.
|
||||
//
|
||||
if (request->path() == "/player/timeline/subscribe")
|
||||
if (request->url().path() == "/player/timeline/subscribe")
|
||||
{
|
||||
handleSubscription(request, response, false);
|
||||
return;
|
||||
}
|
||||
else if (request->path() == "/player/timeline/unsubscribe")
|
||||
else if (request->url().path() == "/player/timeline/unsubscribe")
|
||||
{
|
||||
subscriberRemove(request->headers()["x-plex-client-identifier"]);
|
||||
response->writeHead(QHttpResponse::STATUS_OK);
|
||||
response->setStatusCode(qhttp::ESTATUS_OK);
|
||||
response->end();
|
||||
return;
|
||||
}
|
||||
else if ((request->path() == "/player/timeline/poll"))
|
||||
else if ((request->url().path() == "/player/timeline/poll"))
|
||||
{
|
||||
if (m_subscriberMap.contains(identifier) == false)
|
||||
handleSubscription(request, response, true);
|
||||
|
||||
RemotePollSubscriber *subscriber = (RemotePollSubscriber *)m_subscriberMap[identifier];
|
||||
subscriber->reSubscribe();
|
||||
subscriber->setHTTPResponse(response);
|
||||
|
||||
// if we don't have to wait, just ship the update right away
|
||||
// otherwise, this will wait until next update
|
||||
if (!(queryMap.contains("wait") && ( queryMap["wait"].toList()[0].toInt() == 1)))
|
||||
if (subscriber)
|
||||
{
|
||||
subscriber->sendUpdate();
|
||||
subscriber->reSubscribe();
|
||||
subscriber->setHTTPResponse(response);
|
||||
|
||||
// if we don't have to wait, just ship the update right away
|
||||
// otherwise, this will wait until next update
|
||||
if (! (queryMap.contains("wait") && (queryMap["wait"].toList()[0].toInt() == 1)))
|
||||
{
|
||||
subscriber->sendUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
@ -247,7 +250,7 @@ void RemoteComponent::handleCommand(QHttpRequest* request, QHttpResponse* respon
|
||||
if (headerMap.contains("x-plex-client-identifier") == false || queryMap.contains("commandID") == false)
|
||||
{
|
||||
QLOG_WARN() << "Can't find a X-Plex-Client-Identifier header";
|
||||
response->writeHead(QHttpResponse::STATUS_NOT_ACCEPTABLE);
|
||||
response->setStatusCode(qhttp::ESTATUS_NOT_ACCEPTABLE);
|
||||
response->end();
|
||||
return;
|
||||
}
|
||||
@ -266,7 +269,7 @@ void RemoteComponent::handleCommand(QHttpRequest* request, QHttpResponse* respon
|
||||
if (m_subscriberMap.contains(identifier) == false)
|
||||
{
|
||||
QLOG_WARN() << "Failed to lock up subscriber" << identifier;
|
||||
response->writeHead(QHttpResponse::STATUS_NOT_ACCEPTABLE);
|
||||
response->setStatusCode(qhttp::ESTATUS_NOT_ACCEPTABLE);
|
||||
response->end();
|
||||
return;
|
||||
}
|
||||
@ -278,7 +281,7 @@ void RemoteComponent::handleCommand(QHttpRequest* request, QHttpResponse* respon
|
||||
QVariantMap arg = {
|
||||
{ "method", request->methodString() },
|
||||
{ "headers", headerMap },
|
||||
{ "path", request->path() },
|
||||
{ "path", request->url().path() },
|
||||
{ "query", queryMap },
|
||||
{ "commandID", m_commandId}
|
||||
};
|
||||
@ -340,11 +343,11 @@ void RemoteComponent::commandResponse(const QVariantMap& responseArguments)
|
||||
{
|
||||
QVariantMap headers = responseArguments["headers"].toMap();
|
||||
foreach (const QString& key, headers.keys())
|
||||
response->setHeader(key, headers[key].toString());
|
||||
response->addHeader(key.toUtf8(), headers[key].toByteArray());
|
||||
}
|
||||
|
||||
// write the response HTTP code
|
||||
response->writeHead(responseCode);
|
||||
response->setStatusCode((qhttp::TStatusCode)responseCode);
|
||||
|
||||
// handle optional body argument
|
||||
if (responseArguments.contains("body") && responseArguments["body"].type() == QVariant::String)
|
||||
@ -363,7 +366,7 @@ void RemoteComponent::handleSubscription(QHttpRequest* request, QHttpResponse* r
|
||||
headers.contains("x-plex-device-name") == false)
|
||||
{
|
||||
QLOG_ERROR() << "Missing X-Plex headers in /timeline/subscribe request";
|
||||
response->writeHead(QHttpResponse::STATUS_BAD_REQUEST);
|
||||
response->setStatusCode(qhttp::ESTATUS_BAD_REQUEST);
|
||||
response->end();
|
||||
return;
|
||||
}
|
||||
@ -374,7 +377,7 @@ void RemoteComponent::handleSubscription(QHttpRequest* request, QHttpResponse* r
|
||||
if (query.contains("commandID") == false || ((query.contains("port") == false) && !poll))
|
||||
{
|
||||
QLOG_ERROR() << "Missing arguments to /timeline/subscribe request";
|
||||
response->writeHead(QHttpResponse::STATUS_BAD_REQUEST);
|
||||
response->setStatusCode(qhttp::ESTATUS_BAD_REQUEST);
|
||||
response->end();
|
||||
return;
|
||||
}
|
||||
@ -427,7 +430,7 @@ void RemoteComponent::handleSubscription(QHttpRequest* request, QHttpResponse* r
|
||||
{
|
||||
subscriber->sendUpdate();
|
||||
|
||||
response->writeHead(QHttpResponse::STATUS_OK);
|
||||
response->setStatusCode(qhttp::ESTATUS_OK);
|
||||
response->end();
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,8 @@
|
||||
#include "ComponentManager.h"
|
||||
#include "GDMManager.h"
|
||||
#include "server/HTTPServer.h"
|
||||
#include "qhttpresponse.h"
|
||||
#include "qhttpserverresponse.hpp"
|
||||
#include "qhttpserver.hpp"
|
||||
#include "RemoteSubscriber.h"
|
||||
|
||||
class RemoteComponent : public ComponentBase
|
||||
@ -36,7 +37,7 @@ public:
|
||||
void handleResource(QHttpRequest* request, QHttpResponse* response);
|
||||
void handleCommand(QHttpRequest* request, QHttpResponse* response);
|
||||
|
||||
static QVariantMap HeaderToMap(const HeaderHash& hash);
|
||||
static QVariantMap HeaderToMap(const qhttp::THeaderHash& hash);
|
||||
static QVariantMap QueryToMap(const QUrl& url);
|
||||
|
||||
Q_INVOKABLE void commandResponse(const QVariantMap& responseArguments);
|
||||
|
@ -177,8 +177,8 @@ RemotePollSubscriber::RemotePollSubscriber(const QString &clientIdentifier, cons
|
||||
void RemotePollSubscriber::setHTTPResponse(QHttpResponse *response)
|
||||
{
|
||||
m_response = response;
|
||||
m_response->setHeader("Content-Type", "application/xml");
|
||||
m_response->setHeader("Access-Control-Expose-Headers", "X-Plex-Client-Identifier");
|
||||
m_response->addHeader("Content-Type", "application/xml");
|
||||
m_response->addHeader("Access-Control-Expose-Headers", "X-Plex-Client-Identifier");
|
||||
|
||||
connect(m_response, &QHttpResponse::done, this, &RemotePollSubscriber::responseDone);
|
||||
}
|
||||
@ -189,7 +189,7 @@ void RemotePollSubscriber::sendUpdate()
|
||||
if (m_response)
|
||||
{
|
||||
// if we have a response, we are handling a poll request
|
||||
m_response->writeHead(QHttpResponse::STATUS_OK);
|
||||
m_response->setStatusCode(qhttp::ESTATUS_OK);
|
||||
m_response->write(getTimeline());
|
||||
|
||||
m_response->end();
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include <QNetworkReply>
|
||||
#include <QQueue>
|
||||
|
||||
#include "qhttpresponse.h"
|
||||
#include "qhttpserverresponse.hpp"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
class RemoteSubscriber : public QObject
|
||||
@ -73,12 +73,12 @@ protected:
|
||||
class RemotePollSubscriber : public RemoteSubscriber
|
||||
{
|
||||
public:
|
||||
RemotePollSubscriber(const QString& clientIdentifier, const QString& deviceName, QHttpResponse *response, QObject* parent = 0);
|
||||
void setHTTPResponse(QHttpResponse *response);
|
||||
RemotePollSubscriber(const QString& clientIdentifier, const QString& deviceName, qhttp::server::QHttpResponse *response, QObject* parent = 0);
|
||||
void setHTTPResponse(qhttp::server::QHttpResponse *response);
|
||||
virtual void sendUpdate();
|
||||
|
||||
private :
|
||||
QHttpResponse* m_response;
|
||||
qhttp::server::QHttpResponse* m_response;
|
||||
|
||||
public Q_SLOTS:
|
||||
void responseDone();
|
||||
|
5
src/remote/RokuRemoteComponent.cpp
Normal file
5
src/remote/RokuRemoteComponent.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by Tobias Hieta on 02/02/16.
|
||||
//
|
||||
|
||||
#include "RokuRemoteComponent.h"
|
21
src/remote/RokuRemoteComponent.h
Normal file
21
src/remote/RokuRemoteComponent.h
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// Created by Tobias Hieta on 02/02/16.
|
||||
//
|
||||
|
||||
#ifndef PLEXMEDIAPLAYER_ROKUREMOTECOMPONENT_H
|
||||
#define PLEXMEDIAPLAYER_ROKUREMOTECOMPONENT_H
|
||||
|
||||
#include "ComponentManager.h"
|
||||
|
||||
class RokuRemoteComponent : public ComponentBase
|
||||
{
|
||||
Q_OBJECT
|
||||
DEFINE_SINGLETON(RokuRemoteComponent);
|
||||
|
||||
public:
|
||||
virtual bool componentInitialize() override { return true; };
|
||||
virtual const char* componentName() override { return "rokuremote"; };
|
||||
virtual bool componentExport() override { return false; };
|
||||
};
|
||||
|
||||
#endif //PLEXMEDIAPLAYER_ROKUREMOTECOMPONENT_H
|
@ -34,25 +34,25 @@ bool HttpServer::start()
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
void HttpServer::writeError(QHttpResponse* response, QHttpResponse::StatusCode errorCode)
|
||||
void HttpServer::writeError(QHttpResponse* response, qhttp::TStatusCode errorCode)
|
||||
{
|
||||
QByteArray errorStyle = "<style>h1 {color:red;}</style>";
|
||||
response->writeHead(errorCode);
|
||||
response->setStatusCode(errorCode);
|
||||
response->write(errorStyle);
|
||||
|
||||
QByteArray error;
|
||||
switch (errorCode)
|
||||
{
|
||||
case QHttpResponse::STATUS_FORBIDDEN:
|
||||
case qhttp::ESTATUS_FORBIDDEN:
|
||||
error = "Access to file denied. You might want to check permissions";
|
||||
break;
|
||||
case QHttpResponse::STATUS_NOT_FOUND:
|
||||
case qhttp::ESTATUS_NOT_FOUND:
|
||||
error = "Could not find that file. Install might be broken?";
|
||||
break;
|
||||
case QHttpResponse::STATUS_METHOD_NOT_ALLOWED:
|
||||
case qhttp::ESTATUS_METHOD_NOT_ALLOWED:
|
||||
error = "That method is not allowed.";
|
||||
break;
|
||||
case QHttpResponse::STATUS_NOT_IMPLEMENTED:
|
||||
case qhttp::ESTATUS_NOT_IMPLEMENTED:
|
||||
error = "This request is not yet implemented";
|
||||
break;
|
||||
default:
|
||||
@ -73,20 +73,20 @@ bool HttpServer::writeFile(const QString& file, QHttpResponse* response)
|
||||
QFile fp(file);
|
||||
if (fp.open(QFile::ReadOnly))
|
||||
{
|
||||
response->writeHead(QHttpResponse::STATUS_OK);
|
||||
response->setStatusCode(qhttp::ESTATUS_OK);
|
||||
response->write(fp.readAll());
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
writeError(response, QHttpResponse::STATUS_FORBIDDEN);
|
||||
writeError(response, qhttp::ESTATUS_FORBIDDEN);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
writeError(response, QHttpResponse::STATUS_NOT_FOUND);
|
||||
writeError(response, qhttp::ESTATUS_NOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -96,10 +96,10 @@ void HttpServer::handleWebClientRequest(QHttpRequest* request, QHttpResponse* re
|
||||
{
|
||||
switch (request->method())
|
||||
{
|
||||
case QHttpRequest::HTTP_GET:
|
||||
case qhttp::EHTTP_GET:
|
||||
{
|
||||
// cut the WEB_CLIENT_PATH prefix
|
||||
QString relativeUrl = request->path();
|
||||
QString relativeUrl = request->url().path();
|
||||
relativeUrl.replace(WEB_CLIENT_PATH, "");
|
||||
QString rUrl = m_baseUrl + relativeUrl;
|
||||
|
||||
@ -109,7 +109,7 @@ void HttpServer::handleWebClientRequest(QHttpRequest* request, QHttpResponse* re
|
||||
|
||||
default:
|
||||
{
|
||||
writeError(response, QHttpResponse::STATUS_METHOD_NOT_ALLOWED);
|
||||
writeError(response, qhttp::ESTATUS_METHOD_NOT_ALLOWED);
|
||||
QLOG_WARN() << "Method" << qPrintable(request->methodString()) << "is not supported";
|
||||
}
|
||||
}
|
||||
@ -132,10 +132,10 @@ void HttpServer::handleRemoteController(QHttpRequest* request, QHttpResponse* re
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
void HttpServer::handleFilesRequest(QHttpRequest* request, QHttpResponse* response)
|
||||
{
|
||||
if (request->path() == "/files/qwebchannel.js")
|
||||
if (request->url().path() == "/files/qwebchannel.js")
|
||||
writeFile(":/qtwebchannel/qwebchannel.js", response);
|
||||
else
|
||||
writeError(response, QHttpResponse::STATUS_NOT_FOUND);
|
||||
writeError(response, qhttp::ESTATUS_NOT_FOUND);
|
||||
|
||||
response->end();
|
||||
}
|
||||
@ -145,7 +145,7 @@ void HttpServer::handleRequest(QHttpRequest* request, QHttpResponse* response)
|
||||
{
|
||||
QLOG_DEBUG() << "Incoming request to:" << request->url().toString() << "from" << request->remoteAddress();
|
||||
|
||||
QString path = request->path();
|
||||
QString path = request->url().path();
|
||||
if (path.startsWith(WEB_CLIENT_PATH))
|
||||
{
|
||||
handleWebClientRequest(request, response);
|
||||
@ -164,12 +164,12 @@ void HttpServer::handleRequest(QHttpRequest* request, QHttpResponse* response)
|
||||
}
|
||||
else if (path == "/")
|
||||
{
|
||||
response->writeHead(QHttpResponse::STATUS_OK);
|
||||
response->setStatusCode(qhttp::ESTATUS_OK);
|
||||
response->end("You should not be here :-)");
|
||||
}
|
||||
else
|
||||
{
|
||||
writeError(response, QHttpResponse::STATUS_NOT_FOUND);
|
||||
writeError(response, qhttp::ESTATUS_NOT_FOUND);
|
||||
response->end();
|
||||
}
|
||||
}
|
||||
|
@ -3,31 +3,34 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <qhttprequest.h>
|
||||
#include <qhttpresponse.h>
|
||||
#include "qhttpserverrequest.hpp"
|
||||
#include "qhttpserver.hpp"
|
||||
#include "qhttpserverresponse.hpp"
|
||||
|
||||
#include "qhttpserver.h"
|
||||
using namespace qhttp::server;
|
||||
|
||||
class HttpServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
HttpServer(QObject* parent);
|
||||
bool start();
|
||||
|
||||
private slots:
|
||||
void handleRequest(QHttpRequest *request, QHttpResponse *response);
|
||||
void handleWebClientRequest(QHttpRequest * request, QHttpResponse * response);
|
||||
void handleRequest(QHttpRequest* request, QHttpResponse* response);
|
||||
void handleWebClientRequest(QHttpRequest* request, QHttpResponse* response);
|
||||
void handleResource(QHttpRequest* request, QHttpResponse* response);
|
||||
void handleRemoteController(QHttpRequest* request, QHttpResponse* response);
|
||||
void handleFilesRequest(QHttpRequest* request, QHttpResponse* response);
|
||||
void writeError(QHttpResponse* response, QHttpResponse::StatusCode errorCode);
|
||||
void writeError(QHttpResponse* response, qhttp::TStatusCode errorCode);
|
||||
|
||||
private:
|
||||
bool writeFile(const QString& file, QHttpResponse* response);
|
||||
|
||||
QHttpServer* m_server;
|
||||
|
||||
QString m_baseUrl;
|
||||
|
||||
quint16 m_port;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user