mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-27 00:09:53 -05:00
Removed unused qhttpserver code
Updated README.md
This commit is contained in:
parent
db37b7b933
commit
9e124e4a75
@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
Fork of [KeePassX](https://www.keepassx.org/) with keepasshttp support for use with [PassIFox](https://addons.mozilla.org/en-us/firefox/addon/passifox/) for Mozilla Firefox and [chromeIPass](https://chrome.google.com/webstore/detail/chromeipass/ompiailgknfdndiefoaoiligalphfdae) for Google Chrome.
|
Fork of [KeePassX](https://www.keepassx.org/) with keepasshttp support for use with [PassIFox](https://addons.mozilla.org/en-us/firefox/addon/passifox/) for Mozilla Firefox and [chromeIPass](https://chrome.google.com/webstore/detail/chromeipass/ompiailgknfdndiefoaoiligalphfdae) for Google Chrome.
|
||||||
|
|
||||||
My intention is to keep this repository as up-to-date with the main keePassX repo as possible and, time allowing, clean-up the keepasshttp implementation enough for it to be merged with upstream.
|
KeePassHttp implementation has been forked from jdachtera's repository, which in turn was based on code from code with Francois Ferrand's [keepassx-http](https://gitorious.org/keepassx/keepassx-http/source/master:) repository.
|
||||||
|
|
||||||
|
My intention is to keep this repository as up-to-date with the main keePassX repo as possible and, time allowing, clean-up the keepasshttp implementation enough for it to be merged with upstream. I have started removing any additions to the code that were not strictly related to implemeting the keepasshttp protocol in KeePassX.
|
||||||
|
|
||||||
#### Build Dependencies
|
#### Build Dependencies
|
||||||
|
|
||||||
@ -19,6 +21,7 @@ The following libraries are required:
|
|||||||
* Qt 4 (>= 4.6)
|
* Qt 4 (>= 4.6)
|
||||||
* libgcrypt
|
* libgcrypt
|
||||||
* zlib
|
* zlib
|
||||||
|
* libmicrohttpd
|
||||||
* QJSON
|
* QJSON
|
||||||
|
|
||||||
#### Build Steps
|
#### Build Steps
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
set(qhttpserver_MOC_HDRS
|
|
||||||
qhttpserver.h
|
|
||||||
qhttpresponse.h
|
|
||||||
qhttprequest.h
|
|
||||||
qhttpconnection.h
|
|
||||||
)
|
|
||||||
|
|
||||||
IF (NOT Qt5Core_FOUND)
|
|
||||||
qt4_wrap_cpp(qhttpserver_MOC_SRCS ${qhttpserver_MOC_HDRS})
|
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
set (qhttpserver_SRCS qhttpconnection.cpp qhttprequest.cpp qhttpresponse.cpp qhttpserver.cpp
|
|
||||||
http-parser/http_parser.c http-parser/url_parser.c)
|
|
||||||
set (qhttpserver_HEADERS qhttpconnection.h qhttprequest.h qhttpresponse.h qhttpserver.h
|
|
||||||
http-parser/http_parser.h)
|
|
||||||
|
|
||||||
INCLUDE_DIRECTORIES(http-parser)
|
|
||||||
|
|
||||||
add_library (qhttpserver STATIC ${qhttpserver_SRCS} ${qhttpserver_MOC_SRCS} ${qhttpserver_HEADERS})
|
|
@ -1,19 +0,0 @@
|
|||||||
Copyright (C) 2011-2012 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.
|
|
@ -1,40 +0,0 @@
|
|||||||
# Authors ordered by first contribution.
|
|
||||||
Ryan Dahl <ry@tinyclouds.org>
|
|
||||||
Jeremy Hinegardner <jeremy@hinegardner.org>
|
|
||||||
Sergey Shepelev <temotor@gmail.com>
|
|
||||||
Joe Damato <ice799@gmail.com>
|
|
||||||
tomika <tomika_nospam@freemail.hu>
|
|
||||||
Phoenix Sol <phoenix@burninglabs.com>
|
|
||||||
Cliff Frey <cliff@meraki.com>
|
|
||||||
Ewen Cheslack-Postava <ewencp@cs.stanford.edu>
|
|
||||||
Santiago Gala <sgala@apache.org>
|
|
||||||
Tim Becker <tim.becker@syngenio.de>
|
|
||||||
Jeff Terrace <jterrace@gmail.com>
|
|
||||||
Ben Noordhuis <info@bnoordhuis.nl>
|
|
||||||
Nathan Rajlich <nathan@tootallnate.net>
|
|
||||||
Mark Nottingham <mnot@mnot.net>
|
|
||||||
Aman Gupta <aman@tmm1.net>
|
|
||||||
Tim Becker <tim.becker@kuriositaet.de>
|
|
||||||
Sean Cunningham <sean.cunningham@mandiant.com>
|
|
||||||
Peter Griess <pg@std.in>
|
|
||||||
Salman Haq <salman.haq@asti-usa.com>
|
|
||||||
Cliff Frey <clifffrey@gmail.com>
|
|
||||||
Jon Kolb <jon@b0g.us>
|
|
||||||
Fouad Mardini <f.mardini@gmail.com>
|
|
||||||
Paul Querna <pquerna@apache.org>
|
|
||||||
Felix Geisendörfer <felix@debuggable.com>
|
|
||||||
koichik <koichik@improvement.jp>
|
|
||||||
Andre Caron <andre.l.caron@gmail.com>
|
|
||||||
Ivo Raisr <ivosh@ivosh.net>
|
|
||||||
James McLaughlin <jamie@lacewing-project.org>
|
|
||||||
David Gwynne <loki@animata.net>
|
|
||||||
LE ROUX Thomas <thomas@november-eleven.fr>
|
|
||||||
Randy Rizun <rrizun@ortivawireless.com>
|
|
||||||
Andre Louis Caron <andre.louis.caron@usherbrooke.ca>
|
|
||||||
Simon Zimmermann <simonz05@gmail.com>
|
|
||||||
Erik Dubbelboer <erik@dubbelboer.com>
|
|
||||||
Martell Malone <martellmalone@gmail.com>
|
|
||||||
Bertrand Paquet <bpaquet@octo.com>
|
|
||||||
BogDan Vatra <bogdan@kde.org>
|
|
||||||
Peter Faiman <peter@thepicard.org>
|
|
||||||
Corey Richardson <corey@octayn.net>
|
|
@ -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
|
|
@ -1,23 +0,0 @@
|
|||||||
http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
|
|
||||||
Igor Sysoev.
|
|
||||||
|
|
||||||
Additional changes are licensed under the same terms as NGINX and
|
|
||||||
copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|
||||||
|
|
||||||
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.
|
|
@ -1,178 +0,0 @@
|
|||||||
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
|
|
||||||
applications. It does not make any syscalls nor allocations, it does not
|
|
||||||
buffer data, it can be interrupted at anytime. Depending on your
|
|
||||||
architecture, it only requires about 40 bytes of data per message
|
|
||||||
stream (in a web server that is per connection).
|
|
||||||
|
|
||||||
Features:
|
|
||||||
|
|
||||||
* No dependencies
|
|
||||||
* Handles persistent streams (keep-alive).
|
|
||||||
* Decodes chunked encoding.
|
|
||||||
* Upgrade support
|
|
||||||
* Defends against buffer overflow attacks.
|
|
||||||
|
|
||||||
The parser extracts the following information from HTTP messages:
|
|
||||||
|
|
||||||
* Header fields and values
|
|
||||||
* Content-Length
|
|
||||||
* Request method
|
|
||||||
* Response status code
|
|
||||||
* Transfer-Encoding
|
|
||||||
* HTTP version
|
|
||||||
* Request URL
|
|
||||||
* Message body
|
|
||||||
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
One `http_parser` object is used per TCP connection. Initialize the struct
|
|
||||||
using `http_parser_init()` and set the callbacks. That might look something
|
|
||||||
like this for a request parser:
|
|
||||||
|
|
||||||
http_parser_settings settings;
|
|
||||||
settings.on_url = my_url_callback;
|
|
||||||
settings.on_header_field = my_header_field_callback;
|
|
||||||
/* ... */
|
|
||||||
|
|
||||||
http_parser *parser = malloc(sizeof(http_parser));
|
|
||||||
http_parser_init(parser, HTTP_REQUEST);
|
|
||||||
parser->data = my_socket;
|
|
||||||
|
|
||||||
When data is received on the socket execute the parser and check for errors.
|
|
||||||
|
|
||||||
size_t len = 80*1024, nparsed;
|
|
||||||
char buf[len];
|
|
||||||
ssize_t recved;
|
|
||||||
|
|
||||||
recved = recv(fd, buf, len, 0);
|
|
||||||
|
|
||||||
if (recved < 0) {
|
|
||||||
/* Handle error. */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start up / continue the parser.
|
|
||||||
* Note we pass recved==0 to signal that EOF has been recieved.
|
|
||||||
*/
|
|
||||||
nparsed = http_parser_execute(parser, &settings, buf, recved);
|
|
||||||
|
|
||||||
if (parser->upgrade) {
|
|
||||||
/* handle new protocol */
|
|
||||||
} else if (nparsed != recved) {
|
|
||||||
/* Handle error. Usually just close the connection. */
|
|
||||||
}
|
|
||||||
|
|
||||||
HTTP needs to know where the end of the stream is. For example, sometimes
|
|
||||||
servers send responses without Content-Length and expect the client to
|
|
||||||
consume input (for the body) until EOF. To tell http_parser about EOF, give
|
|
||||||
`0` as the forth parameter to `http_parser_execute()`. Callbacks and errors
|
|
||||||
can still be encountered during an EOF, so one must still be prepared
|
|
||||||
to receive them.
|
|
||||||
|
|
||||||
Scalar valued message information such as `status_code`, `method`, and the
|
|
||||||
HTTP version are stored in the parser structure. This data is only
|
|
||||||
temporally stored in `http_parser` and gets reset on each new message. If
|
|
||||||
this information is needed later, copy it out of the structure during the
|
|
||||||
`headers_complete` callback.
|
|
||||||
|
|
||||||
The parser decodes the transfer-encoding for both requests and responses
|
|
||||||
transparently. That is, a chunked encoding is decoded before being sent to
|
|
||||||
the on_body callback.
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
a request like
|
|
||||||
|
|
||||||
GET /demo HTTP/1.1
|
|
||||||
Upgrade: WebSocket
|
|
||||||
Connection: Upgrade
|
|
||||||
Host: example.com
|
|
||||||
Origin: http://example.com
|
|
||||||
WebSocket-Protocol: sample
|
|
||||||
|
|
||||||
followed by non-HTTP data.
|
|
||||||
|
|
||||||
(See http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more
|
|
||||||
information the Web Socket 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
|
|
||||||
http_parser_execute() will stop parsing at the end of the headers and return.
|
|
||||||
|
|
||||||
The user is expected to check if `parser->upgrade` has been set to 1 after
|
|
||||||
`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied
|
|
||||||
offset by the return value of `http_parser_execute()`.
|
|
||||||
|
|
||||||
|
|
||||||
Callbacks
|
|
||||||
---------
|
|
||||||
|
|
||||||
During the `http_parser_execute()` call, the callbacks set in
|
|
||||||
`http_parser_settings` will be executed. The parser maintains state and
|
|
||||||
never looks behind, so buffering the data is not necessary. If you need to
|
|
||||||
save certain data for later usage, you can do that from the callbacks.
|
|
||||||
|
|
||||||
There are two types of callbacks:
|
|
||||||
|
|
||||||
* notification `typedef int (*http_cb) (http_parser*);`
|
|
||||||
Callbacks: on_message_begin, on_headers_complete, on_message_complete.
|
|
||||||
* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
|
|
||||||
Callbacks: (requests only) on_uri,
|
|
||||||
(common) on_header_field, on_header_value, on_body;
|
|
||||||
|
|
||||||
Callbacks must return 0 on success. Returning a non-zero value indicates
|
|
||||||
error to the parser, making it exit immediately.
|
|
||||||
|
|
||||||
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
|
|
||||||
valid for the lifetime of callback. You can also `read()` into a heap allocated
|
|
||||||
buffer to avoid copying memory around if this fits your application.
|
|
||||||
|
|
||||||
Reading headers may be a tricky task if you read/parse headers partially.
|
|
||||||
Basically, you need to remember whether last header callback was field or value
|
|
||||||
and apply following logic:
|
|
||||||
|
|
||||||
(on_header_field and on_header_value shortened to on_h_*)
|
|
||||||
------------------------ ------------ --------------------------------------------
|
|
||||||
| State (prev. callback) | Callback | Description/action |
|
|
||||||
------------------------ ------------ --------------------------------------------
|
|
||||||
| nothing (first call) | on_h_field | Allocate new buffer and copy callback data |
|
|
||||||
| | | into it |
|
|
||||||
------------------------ ------------ --------------------------------------------
|
|
||||||
| value | on_h_field | New header started. |
|
|
||||||
| | | Copy current name,value buffers to headers |
|
|
||||||
| | | list and allocate new buffer for new name |
|
|
||||||
------------------------ ------------ --------------------------------------------
|
|
||||||
| field | on_h_field | Previous name continues. Reallocate name |
|
|
||||||
| | | buffer and append callback data to it |
|
|
||||||
------------------------ ------------ --------------------------------------------
|
|
||||||
| field | on_h_value | Value for current header started. Allocate |
|
|
||||||
| | | new buffer and copy callback data to it |
|
|
||||||
------------------------ ------------ --------------------------------------------
|
|
||||||
| value | on_h_value | Value continues. Reallocate value buffer |
|
|
||||||
| | | and append callback data to it |
|
|
||||||
------------------------ ------------ --------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
Parsing URLs
|
|
||||||
------------
|
|
||||||
|
|
||||||
A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`.
|
|
||||||
Users of this library may wish to use it to parse URLs constructed from
|
|
||||||
consecutive `on_url` callbacks.
|
|
||||||
|
|
||||||
See examples of reading in headers:
|
|
||||||
|
|
||||||
* [partial example](http://gist.github.com/155877) in C
|
|
||||||
* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C
|
|
||||||
* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript
|
|
File diff suppressed because it is too large
Load Diff
@ -1,111 +0,0 @@
|
|||||||
# This file is used with the GYP meta build system.
|
|
||||||
# http://code.google.com/p/gyp/
|
|
||||||
# To build try this:
|
|
||||||
# svn co http://gyp.googlecode.com/svn/trunk gyp
|
|
||||||
# ./gyp/gyp -f make --depth=`pwd` http_parser.gyp
|
|
||||||
# ./out/Debug/test
|
|
||||||
{
|
|
||||||
'target_defaults': {
|
|
||||||
'default_configuration': 'Debug',
|
|
||||||
'configurations': {
|
|
||||||
# TODO: hoist these out and put them somewhere common, because
|
|
||||||
# RuntimeLibrary MUST MATCH across the entire project
|
|
||||||
'Debug': {
|
|
||||||
'defines': [ 'DEBUG', '_DEBUG' ],
|
|
||||||
'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ],
|
|
||||||
'msvs_settings': {
|
|
||||||
'VCCLCompilerTool': {
|
|
||||||
'RuntimeLibrary': 1, # static debug
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'Release': {
|
|
||||||
'defines': [ 'NDEBUG' ],
|
|
||||||
'cflags': [ '-Wall', '-Wextra', '-O3' ],
|
|
||||||
'msvs_settings': {
|
|
||||||
'VCCLCompilerTool': {
|
|
||||||
'RuntimeLibrary': 0, # static release
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'msvs_settings': {
|
|
||||||
'VCCLCompilerTool': {
|
|
||||||
},
|
|
||||||
'VCLibrarianTool': {
|
|
||||||
},
|
|
||||||
'VCLinkerTool': {
|
|
||||||
'GenerateDebugInformation': 'true',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'conditions': [
|
|
||||||
['OS == "win"', {
|
|
||||||
'defines': [
|
|
||||||
'WIN32'
|
|
||||||
],
|
|
||||||
}]
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
'targets': [
|
|
||||||
{
|
|
||||||
'target_name': 'http_parser',
|
|
||||||
'type': 'static_library',
|
|
||||||
'include_dirs': [ '.' ],
|
|
||||||
'direct_dependent_settings': {
|
|
||||||
'defines': [ 'HTTP_PARSER_STRICT=0' ],
|
|
||||||
'include_dirs': [ '.' ],
|
|
||||||
},
|
|
||||||
'defines': [ 'HTTP_PARSER_STRICT=0' ],
|
|
||||||
'sources': [ './http_parser.c', ],
|
|
||||||
'conditions': [
|
|
||||||
['OS=="win"', {
|
|
||||||
'msvs_settings': {
|
|
||||||
'VCCLCompilerTool': {
|
|
||||||
# Compile as C++. http_parser.c is actually C99, but C++ is
|
|
||||||
# close enough in this case.
|
|
||||||
'CompileAs': 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}]
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
'target_name': 'http_parser_strict',
|
|
||||||
'type': 'static_library',
|
|
||||||
'include_dirs': [ '.' ],
|
|
||||||
'direct_dependent_settings': {
|
|
||||||
'defines': [ 'HTTP_PARSER_STRICT=1' ],
|
|
||||||
'include_dirs': [ '.' ],
|
|
||||||
},
|
|
||||||
'defines': [ 'HTTP_PARSER_STRICT=1' ],
|
|
||||||
'sources': [ './http_parser.c', ],
|
|
||||||
'conditions': [
|
|
||||||
['OS=="win"', {
|
|
||||||
'msvs_settings': {
|
|
||||||
'VCCLCompilerTool': {
|
|
||||||
# Compile as C++. http_parser.c is actually C99, but C++ is
|
|
||||||
# close enough in this case.
|
|
||||||
'CompileAs': 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}]
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
'target_name': 'test-nonstrict',
|
|
||||||
'type': 'executable',
|
|
||||||
'dependencies': [ 'http_parser' ],
|
|
||||||
'sources': [ 'test.c' ]
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
'target_name': 'test-strict',
|
|
||||||
'type': 'executable',
|
|
||||||
'dependencies': [ 'http_parser_strict' ],
|
|
||||||
'sources': [ 'test.c' ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,302 +0,0 @@
|
|||||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|
||||||
*
|
|
||||||
* 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 http_parser_h
|
|
||||||
#define http_parser_h
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define HTTP_PARSER_VERSION_MAJOR 2
|
|
||||||
#define HTTP_PARSER_VERSION_MINOR 0
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
|
|
||||||
#include <BaseTsd.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
typedef __int8 int8_t;
|
|
||||||
typedef unsigned __int8 uint8_t;
|
|
||||||
typedef __int16 int16_t;
|
|
||||||
typedef unsigned __int16 uint16_t;
|
|
||||||
typedef __int32 int32_t;
|
|
||||||
typedef unsigned __int32 uint32_t;
|
|
||||||
typedef __int64 int64_t;
|
|
||||||
typedef unsigned __int64 uint64_t;
|
|
||||||
#else
|
|
||||||
#include <stdint.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
|
|
||||||
* faster
|
|
||||||
*/
|
|
||||||
#ifndef HTTP_PARSER_STRICT
|
|
||||||
# define HTTP_PARSER_STRICT 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Maximium header size allowed */
|
|
||||||
#define HTTP_MAX_HEADER_SIZE (80*1024)
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct http_parser http_parser;
|
|
||||||
typedef struct http_parser_settings http_parser_settings;
|
|
||||||
|
|
||||||
|
|
||||||
/* Callbacks should return non-zero to indicate an error. The parser will
|
|
||||||
* then halt execution.
|
|
||||||
*
|
|
||||||
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
|
|
||||||
* returning '1' from on_headers_complete will tell the parser that it
|
|
||||||
* should not expect a body. This is used when receiving a response to a
|
|
||||||
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
|
|
||||||
* chunked' headers that indicate the presence of a body.
|
|
||||||
*
|
|
||||||
* http_data_cb does not return data chunks. It will be call arbitrarally
|
|
||||||
* many times for each string. E.G. you might get 10 callbacks for "on_url"
|
|
||||||
* each providing just a few characters more data.
|
|
||||||
*/
|
|
||||||
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
|
|
||||||
typedef int (*http_cb) (http_parser*);
|
|
||||||
|
|
||||||
|
|
||||||
/* Request Methods */
|
|
||||||
#define HTTP_METHOD_MAP(XX) \
|
|
||||||
XX(0, DELETE, DELETE) \
|
|
||||||
XX(1, GET, GET) \
|
|
||||||
XX(2, HEAD, HEAD) \
|
|
||||||
XX(3, POST, POST) \
|
|
||||||
XX(4, PUT, PUT) \
|
|
||||||
/* pathological */ \
|
|
||||||
XX(5, CONNECT, CONNECT) \
|
|
||||||
XX(6, OPTIONS, OPTIONS) \
|
|
||||||
XX(7, TRACE, TRACE) \
|
|
||||||
/* webdav */ \
|
|
||||||
XX(8, COPY, COPY) \
|
|
||||||
XX(9, LOCK, LOCK) \
|
|
||||||
XX(10, MKCOL, MKCOL) \
|
|
||||||
XX(11, MOVE, MOVE) \
|
|
||||||
XX(12, PROPFIND, PROPFIND) \
|
|
||||||
XX(13, PROPPATCH, PROPPATCH) \
|
|
||||||
XX(14, SEARCH, SEARCH) \
|
|
||||||
XX(15, UNLOCK, UNLOCK) \
|
|
||||||
/* subversion */ \
|
|
||||||
XX(16, REPORT, REPORT) \
|
|
||||||
XX(17, MKACTIVITY, MKACTIVITY) \
|
|
||||||
XX(18, CHECKOUT, CHECKOUT) \
|
|
||||||
XX(19, MERGE, MERGE) \
|
|
||||||
/* upnp */ \
|
|
||||||
XX(20, MSEARCH, M-SEARCH) \
|
|
||||||
XX(21, NOTIFY, NOTIFY) \
|
|
||||||
XX(22, SUBSCRIBE, SUBSCRIBE) \
|
|
||||||
XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \
|
|
||||||
/* RFC-5789 */ \
|
|
||||||
XX(24, PATCH, PATCH) \
|
|
||||||
XX(25, PURGE, PURGE) \
|
|
||||||
|
|
||||||
enum http_method
|
|
||||||
{
|
|
||||||
#define XX(num, name, string) HTTP_##name = num,
|
|
||||||
HTTP_METHOD_MAP(XX)
|
|
||||||
#undef XX
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
|
|
||||||
|
|
||||||
|
|
||||||
/* Flag values for http_parser.flags field */
|
|
||||||
enum flags
|
|
||||||
{ F_CHUNKED = 1 << 0
|
|
||||||
, F_CONNECTION_KEEP_ALIVE = 1 << 1
|
|
||||||
, F_CONNECTION_CLOSE = 1 << 2
|
|
||||||
, F_TRAILING = 1 << 3
|
|
||||||
, F_UPGRADE = 1 << 4
|
|
||||||
, F_SKIPBODY = 1 << 5
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Map for errno-related constants
|
|
||||||
*
|
|
||||||
* The provided argument should be a macro that takes 2 arguments.
|
|
||||||
*/
|
|
||||||
#define HTTP_ERRNO_MAP(XX) \
|
|
||||||
/* No error */ \
|
|
||||||
XX(OK, "success") \
|
|
||||||
\
|
|
||||||
/* Callback-related errors */ \
|
|
||||||
XX(CB_message_begin, "the on_message_begin callback failed") \
|
|
||||||
XX(CB_url, "the on_url callback failed") \
|
|
||||||
XX(CB_header_field, "the on_header_field callback failed") \
|
|
||||||
XX(CB_header_value, "the on_header_value callback failed") \
|
|
||||||
XX(CB_headers_complete, "the on_headers_complete callback failed") \
|
|
||||||
XX(CB_body, "the on_body callback failed") \
|
|
||||||
XX(CB_message_complete, "the on_message_complete callback failed") \
|
|
||||||
\
|
|
||||||
/* Parsing-related errors */ \
|
|
||||||
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
|
|
||||||
XX(HEADER_OVERFLOW, \
|
|
||||||
"too many header bytes seen; overflow detected") \
|
|
||||||
XX(CLOSED_CONNECTION, \
|
|
||||||
"data received after completed connection: close message") \
|
|
||||||
XX(INVALID_VERSION, "invalid HTTP version") \
|
|
||||||
XX(INVALID_STATUS, "invalid HTTP status code") \
|
|
||||||
XX(INVALID_METHOD, "invalid HTTP method") \
|
|
||||||
XX(INVALID_URL, "invalid URL") \
|
|
||||||
XX(INVALID_HOST, "invalid host") \
|
|
||||||
XX(INVALID_PORT, "invalid port") \
|
|
||||||
XX(INVALID_PATH, "invalid path") \
|
|
||||||
XX(INVALID_QUERY_STRING, "invalid query string") \
|
|
||||||
XX(INVALID_FRAGMENT, "invalid fragment") \
|
|
||||||
XX(LF_EXPECTED, "LF character expected") \
|
|
||||||
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
|
|
||||||
XX(INVALID_CONTENT_LENGTH, \
|
|
||||||
"invalid character in content-length header") \
|
|
||||||
XX(INVALID_CHUNK_SIZE, \
|
|
||||||
"invalid character in chunk size header") \
|
|
||||||
XX(INVALID_CONSTANT, "invalid constant string") \
|
|
||||||
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
|
|
||||||
XX(STRICT, "strict mode assertion failed") \
|
|
||||||
XX(PAUSED, "parser is paused") \
|
|
||||||
XX(UNKNOWN, "an unknown error occurred")
|
|
||||||
|
|
||||||
|
|
||||||
/* Define HPE_* values for each errno value above */
|
|
||||||
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
|
|
||||||
enum http_errno {
|
|
||||||
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
|
|
||||||
};
|
|
||||||
#undef HTTP_ERRNO_GEN
|
|
||||||
|
|
||||||
|
|
||||||
/* Get an http_errno value from an http_parser */
|
|
||||||
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
|
|
||||||
|
|
||||||
|
|
||||||
struct http_parser {
|
|
||||||
/** PRIVATE **/
|
|
||||||
unsigned char type : 2; /* enum http_parser_type */
|
|
||||||
unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */
|
|
||||||
unsigned char state; /* enum state from http_parser.c */
|
|
||||||
unsigned char header_state; /* enum header_state from http_parser.c */
|
|
||||||
unsigned char index; /* index into current matcher */
|
|
||||||
|
|
||||||
uint32_t nread; /* # bytes read in various scenarios */
|
|
||||||
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
|
|
||||||
|
|
||||||
/** READ-ONLY **/
|
|
||||||
unsigned short http_major;
|
|
||||||
unsigned short http_minor;
|
|
||||||
unsigned short status_code; /* responses only */
|
|
||||||
unsigned char method; /* requests only */
|
|
||||||
unsigned char http_errno : 7;
|
|
||||||
|
|
||||||
/* 1 = Upgrade header was present and the parser has exited because of that.
|
|
||||||
* 0 = No upgrade header present.
|
|
||||||
* Should be checked when http_parser_execute() returns in addition to
|
|
||||||
* error checking.
|
|
||||||
*/
|
|
||||||
unsigned char upgrade : 1;
|
|
||||||
|
|
||||||
/** PUBLIC **/
|
|
||||||
void *data; /* A pointer to get hook to the "connection" or "socket" object */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct http_parser_settings {
|
|
||||||
http_cb on_message_begin;
|
|
||||||
http_data_cb on_url;
|
|
||||||
http_data_cb on_header_field;
|
|
||||||
http_data_cb on_header_value;
|
|
||||||
http_cb on_headers_complete;
|
|
||||||
http_data_cb on_body;
|
|
||||||
http_cb on_message_complete;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
enum http_parser_url_fields
|
|
||||||
{ UF_SCHEMA = 0
|
|
||||||
, UF_HOST = 1
|
|
||||||
, UF_PORT = 2
|
|
||||||
, UF_PATH = 3
|
|
||||||
, UF_QUERY = 4
|
|
||||||
, UF_FRAGMENT = 5
|
|
||||||
, UF_USERINFO = 6
|
|
||||||
, UF_MAX = 7
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Result structure for http_parser_parse_url().
|
|
||||||
*
|
|
||||||
* Callers should index into field_data[] with UF_* values iff field_set
|
|
||||||
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
|
|
||||||
* because we probably have padding left over), we convert any port to
|
|
||||||
* a uint16_t.
|
|
||||||
*/
|
|
||||||
struct http_parser_url {
|
|
||||||
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
|
|
||||||
uint16_t port; /* Converted UF_PORT string */
|
|
||||||
|
|
||||||
struct {
|
|
||||||
uint16_t off; /* Offset into buffer in which field starts */
|
|
||||||
uint16_t len; /* Length of run in buffer */
|
|
||||||
} field_data[UF_MAX];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void http_parser_init(http_parser *parser, enum http_parser_type type);
|
|
||||||
|
|
||||||
|
|
||||||
size_t http_parser_execute(http_parser *parser,
|
|
||||||
const http_parser_settings *settings,
|
|
||||||
const char *data,
|
|
||||||
size_t len);
|
|
||||||
|
|
||||||
|
|
||||||
/* If http_should_keep_alive() in the on_headers_complete or
|
|
||||||
* on_message_complete callback returns 0, then this should be
|
|
||||||
* the last message on the connection.
|
|
||||||
* If you are the server, respond with the "Connection: close" header.
|
|
||||||
* If you are the client, close the connection.
|
|
||||||
*/
|
|
||||||
int http_should_keep_alive(const http_parser *parser);
|
|
||||||
|
|
||||||
/* Returns a string version of the HTTP method. */
|
|
||||||
const char *http_method_str(enum http_method m);
|
|
||||||
|
|
||||||
/* Return a string name of the given error */
|
|
||||||
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);
|
|
||||||
|
|
||||||
/* Parse a URL; return nonzero on failure */
|
|
||||||
int http_parser_parse_url(const char *buf, size_t buflen,
|
|
||||||
int is_connect,
|
|
||||||
struct http_parser_url *u);
|
|
||||||
|
|
||||||
/* Pause or un-pause the parser; a nonzero value pauses */
|
|
||||||
void http_parser_pause(http_parser *parser, int paused);
|
|
||||||
|
|
||||||
/* Checks if this is the final chunk of the body. */
|
|
||||||
int http_body_is_final(const http_parser *parser);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
|
||||||
}
|
|
@ -1,202 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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 <QtNetwork/QTcpSocket>
|
|
||||||
#include <QtNetwork/QHostAddress>
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
|
|
||||||
#include "qhttprequest.h"
|
|
||||||
#include "qhttpresponse.h"
|
|
||||||
|
|
||||||
QHttpConnection::QHttpConnection(QTcpSocket *socket, QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, m_socket(socket)
|
|
||||||
, m_parser(0)
|
|
||||||
, m_request(0)
|
|
||||||
{
|
|
||||||
qDebug() << "Got new connection" << socket->peerAddress() << socket->peerPort();
|
|
||||||
|
|
||||||
m_parser = (http_parser*)malloc(sizeof(http_parser));
|
|
||||||
http_parser_init(m_parser, HTTP_REQUEST);
|
|
||||||
|
|
||||||
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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
QHttpConnection::~QHttpConnection()
|
|
||||||
{
|
|
||||||
delete m_socket;
|
|
||||||
m_socket = 0;
|
|
||||||
|
|
||||||
free(m_parser);
|
|
||||||
m_parser = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHttpConnection::socketDisconnected()
|
|
||||||
{
|
|
||||||
if(m_request) {
|
|
||||||
if(m_request->successful()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_request->setSuccessful(false);
|
|
||||||
Q_EMIT m_request->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHttpConnection::flush()
|
|
||||||
{
|
|
||||||
m_socket->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/********************
|
|
||||||
* Static Callbacks *
|
|
||||||
*******************/
|
|
||||||
int QHttpConnection::MessageBegin(http_parser *parser)
|
|
||||||
{
|
|
||||||
QHttpConnection *theConnection = (QHttpConnection *)parser->data;
|
|
||||||
theConnection->m_currentHeaders.clear();
|
|
||||||
theConnection->m_request = new QHttpRequest(theConnection);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int QHttpConnection::HeadersComplete(http_parser *parser)
|
|
||||||
{
|
|
||||||
QHttpConnection *theConnection = (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));
|
|
||||||
|
|
||||||
// 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()));
|
|
||||||
|
|
||||||
// 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 = (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 = (QHttpConnection *)parser->data;
|
|
||||||
Q_ASSERT(theConnection->m_request);
|
|
||||||
|
|
||||||
QString url = QString::fromAscii(at, length);
|
|
||||||
theConnection->m_request->setUrl(QUrl(url));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int QHttpConnection::HeaderField(http_parser *parser, const char *at, size_t length)
|
|
||||||
{
|
|
||||||
QHttpConnection *theConnection = (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::fromAscii(at, length);
|
|
||||||
theConnection->m_currentHeaderField += fieldSuffix;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int QHttpConnection::HeaderValue(http_parser *parser, const char *at, size_t length)
|
|
||||||
{
|
|
||||||
QHttpConnection *theConnection = (QHttpConnection *)parser->data;
|
|
||||||
Q_ASSERT(theConnection->m_request);
|
|
||||||
|
|
||||||
QString valueSuffix = QString::fromAscii(at, length);
|
|
||||||
theConnection->m_currentHeaderValue += valueSuffix;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int QHttpConnection::Body(http_parser *parser, const char *at, size_t length)
|
|
||||||
{
|
|
||||||
QHttpConnection *theConnection = (QHttpConnection *)parser->data;
|
|
||||||
Q_ASSERT(theConnection->m_request);
|
|
||||||
|
|
||||||
Q_EMIT theConnection->m_request->data(QByteArray(at, length));
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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 <QtCore/QObject>
|
|
||||||
#include <QtCore/QHash>
|
|
||||||
|
|
||||||
#include "http_parser.h"
|
|
||||||
|
|
||||||
class QTcpSocket;
|
|
||||||
|
|
||||||
class QHttpRequest;
|
|
||||||
class QHttpResponse;
|
|
||||||
|
|
||||||
typedef QHash<QString, QString> HeaderHash;
|
|
||||||
|
|
||||||
class QHttpConnection : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
QHttpConnection(QTcpSocket *socket, QObject *parent = 0);
|
|
||||||
virtual ~QHttpConnection();
|
|
||||||
|
|
||||||
void write(const QByteArray &data);
|
|
||||||
void flush();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void newRequest(QHttpRequest*, QHttpResponse*);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void parseRequest();
|
|
||||||
void socketDisconnected();
|
|
||||||
|
|
||||||
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_settings m_parserSettings;
|
|
||||||
http_parser *m_parser;
|
|
||||||
|
|
||||||
// since there can only be one request at any time
|
|
||||||
// even with pipelining
|
|
||||||
QHttpRequest *m_request;
|
|
||||||
|
|
||||||
// the ones we are reading in from the parser
|
|
||||||
HeaderHash m_currentHeaders;
|
|
||||||
QString m_currentHeaderField;
|
|
||||||
QString m_currentHeaderValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,245 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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 <QtCore/QObject>
|
|
||||||
#include <QtCore/QHash>
|
|
||||||
#include <QtCore/QMetaEnum>
|
|
||||||
#include <QtCore/QMetaType>
|
|
||||||
#include <QtCore/QUrl>
|
|
||||||
|
|
||||||
class QTcpSocket;
|
|
||||||
|
|
||||||
class QHttpConnection;
|
|
||||||
|
|
||||||
typedef QHash<QString, QString> HeaderHash;
|
|
||||||
|
|
||||||
/* Request Methods */
|
|
||||||
|
|
||||||
/*! \class QHttpRequest
|
|
||||||
*
|
|
||||||
* The QHttpRequest class represents the header and data
|
|
||||||
* sent by the client.
|
|
||||||
*
|
|
||||||
* Header data is available immediately.
|
|
||||||
*
|
|
||||||
* Body data is streamed as it comes in via the data(const QByteArray&) 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 <strong>read-only</strong> by users of %QHttpServer.
|
|
||||||
*/
|
|
||||||
class 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);
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~QHttpRequest();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Request Methods
|
|
||||||
* 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
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the method string for the request
|
|
||||||
*/
|
|
||||||
const QString methodString() const { return MethodToString(method()); }
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* The method used for the request.
|
|
||||||
*/
|
|
||||||
HttpMethod method() const { return m_method; };
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* The complete URL for the request. This
|
|
||||||
* includes the path and query string.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const QUrl& url() const { return m_url; };
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* The path portion of the query URL.
|
|
||||||
*
|
|
||||||
* \sa url()
|
|
||||||
*/
|
|
||||||
const QString path() const { return m_url.path(); };
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* The HTTP version used by the client as a
|
|
||||||
* 'x.x' string.
|
|
||||||
*/
|
|
||||||
const QString& httpVersion() const { return m_version; };
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Any query string included as part of a request.
|
|
||||||
* Usually used to send data in a GET request.
|
|
||||||
*/
|
|
||||||
const QString& queryString() const;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Get a hash of the headers sent by the client.
|
|
||||||
* NOTE: All header names are <strong>lowercase</strong>
|
|
||||||
* so that Content-Length becomes content-length and so on.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
const HeaderHash& headers() const { return m_headers; };
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Get the value of a header
|
|
||||||
*
|
|
||||||
* \param field Name of the header field (lowercase).
|
|
||||||
* \return Value of the header or null QString()
|
|
||||||
*/
|
|
||||||
QString header(const QString &field) { return m_headers[field]; };
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* IP Address of the client in dotted decimal format
|
|
||||||
*/
|
|
||||||
const QString& remoteAddress() const { return m_remoteAddress; };
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Outbound connection port for the client.
|
|
||||||
*/
|
|
||||||
quint16 remotePort() const { return m_remotePort; };
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Post data
|
|
||||||
*/
|
|
||||||
const QByteArray &body() const { return m_body; }
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Set immediately before end has been emitted,
|
|
||||||
* stating whether the message was properly received.
|
|
||||||
* Defaults to false untiil the message has completed.
|
|
||||||
*/
|
|
||||||
bool successful() const { return m_success; }
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* connect to data and store all data in a QByteArray
|
|
||||||
* accessible at body()
|
|
||||||
*/
|
|
||||||
void storeBody()
|
|
||||||
{
|
|
||||||
connect(this, SIGNAL(data(const QByteArray &)),
|
|
||||||
this, SLOT(appendBody(const QByteArray &)),
|
|
||||||
Qt::UniqueConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
/*!
|
|
||||||
* This signal is emitted whenever body data is encountered
|
|
||||||
* in a message.
|
|
||||||
* This may be emitted zero or more times.
|
|
||||||
*/
|
|
||||||
void data(const QByteArray &);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Emitted at the end of the HTTP request.
|
|
||||||
* No data() signals will be emitted after this.
|
|
||||||
*/
|
|
||||||
void end();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QHttpRequest(QHttpConnection *connection, QObject *parent = 0);
|
|
||||||
|
|
||||||
static QString MethodToString(HttpMethod method)
|
|
||||||
{
|
|
||||||
int index = staticMetaObject.indexOfEnumerator("HttpMethod");
|
|
||||||
return staticMetaObject.enumerator(index).valueToKey(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;
|
|
||||||
|
|
||||||
friend class QHttpConnection;
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void appendBody(const QByteArray &body)
|
|
||||||
{
|
|
||||||
m_body.append(body);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,193 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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 <QtCore/QDateTime>
|
|
||||||
|
|
||||||
#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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QHttpResponse::~QHttpResponse()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHttpResponse::setHeader(const QString &field, const QString &value)
|
|
||||||
{
|
|
||||||
if(m_finished) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_headers[field] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHttpResponse::writeHeader(const char *field, const QString &value)
|
|
||||||
{
|
|
||||||
if(m_finished) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_connection->write(field);
|
|
||||||
m_connection->write(": ");
|
|
||||||
m_connection->write(value.toUtf8());
|
|
||||||
m_connection->write("\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHttpResponse::writeHeaders()
|
|
||||||
{
|
|
||||||
if(m_finished) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_FOREACH(QString name, m_headers.keys())
|
|
||||||
{
|
|
||||||
QString value = m_headers[name];
|
|
||||||
if( name.compare("connection", Qt::CaseInsensitive) == 0 )
|
|
||||||
{
|
|
||||||
m_sentConnectionHeader = true;
|
|
||||||
if( value == "close" )
|
|
||||||
m_last = true;
|
|
||||||
else
|
|
||||||
m_keepAlive = true;
|
|
||||||
}
|
|
||||||
else if( name.compare("transfer-encoding", Qt::CaseInsensitive) == 0 )
|
|
||||||
{
|
|
||||||
m_sentTransferEncodingHeader = true;
|
|
||||||
if( value == "chunked" )
|
|
||||||
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.toAscii(), value.toAscii());
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !m_sentDate )
|
|
||||||
{
|
|
||||||
writeHeader("Date", QDateTime::currentDateTimeUtc().toString("ddd, dd MMM yyyy hh:mm:ss G'M'T"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHttpResponse::writeHead(int status)
|
|
||||||
{
|
|
||||||
if(m_finished) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( m_headerWritten ) return;
|
|
||||||
|
|
||||||
m_connection->write(QString("HTTP/1.1 %1 %2\r\n").arg(status).arg(STATUS_CODES[status]).toAscii());
|
|
||||||
|
|
||||||
writeHeaders();
|
|
||||||
|
|
||||||
m_connection->write("\r\n");
|
|
||||||
m_headerWritten = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHttpResponse::write(const QByteArray &data)
|
|
||||||
{
|
|
||||||
if(m_finished) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !m_headerWritten )
|
|
||||||
{
|
|
||||||
qDebug() << "You MUST call writeHead() before writing body data";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_connection->write(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHttpResponse::write(const QString &data)
|
|
||||||
{
|
|
||||||
if(m_finished) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_connection->write(data.toUtf8());
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHttpResponse::end(const QString &data)
|
|
||||||
{
|
|
||||||
if(m_finished) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_finished = true;
|
|
||||||
|
|
||||||
write(data);
|
|
||||||
|
|
||||||
Q_EMIT done();
|
|
||||||
deleteLater();
|
|
||||||
// TODO: end connection and delete ourselves
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHttpResponse::connectionClosed()
|
|
||||||
{
|
|
||||||
m_finished = true;
|
|
||||||
deleteLater();
|
|
||||||
}
|
|
@ -1,174 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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 <QtCore/QObject>
|
|
||||||
#include <QtCore/QHash>
|
|
||||||
|
|
||||||
//
|
|
||||||
class QTcpSocket;
|
|
||||||
|
|
||||||
class QHttpConnection;
|
|
||||||
|
|
||||||
typedef QHash<QString, QString> HeaderHash;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* The QHttpResponse class handles sending
|
|
||||||
* data back to the client in response to a request.
|
|
||||||
*
|
|
||||||
* The way to respond is to:
|
|
||||||
* <ol>
|
|
||||||
* <li>Set headers (optional).</li>
|
|
||||||
* <li>Call writeHead() with the HTTP status code.</li>
|
|
||||||
* <li>Call write() zero or more times.</li>
|
|
||||||
* <li>Call end() when you are ready to end the request.</li>
|
|
||||||
* </ol>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class QHttpResponse : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
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();
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
/*!
|
|
||||||
* Write the header of the response
|
|
||||||
* using @c status as the response status
|
|
||||||
* code. Any headers should be set before this
|
|
||||||
* is called.
|
|
||||||
*/
|
|
||||||
void writeHead(int status);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Write the block of data to the client.
|
|
||||||
*
|
|
||||||
* \note
|
|
||||||
* writeHead() has to be called before write(), otherwise the call will
|
|
||||||
* fail.
|
|
||||||
*/
|
|
||||||
void write(const QByteArray &data);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Write a QString instead of a QByteArray.
|
|
||||||
* \see write(const QByteArray &);
|
|
||||||
*/
|
|
||||||
void write(const QString &data);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* End 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
|
|
||||||
*/
|
|
||||||
void end(const QString &data=QString());
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Set a response header @c field to @c value
|
|
||||||
*/
|
|
||||||
void setHeader(const QString &field, const QString &value);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
/*!
|
|
||||||
* Emitted once the response is finished.
|
|
||||||
* You should NOT interact with this object
|
|
||||||
* after done() has been emitted as the object
|
|
||||||
* is scheduled for deletion at any time.
|
|
||||||
*/
|
|
||||||
void done();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QHttpResponse(QHttpConnection *connection);
|
|
||||||
|
|
||||||
void writeHeaders();
|
|
||||||
void writeHeader(const char *field, const QString &value);
|
|
||||||
|
|
||||||
QHttpConnection *m_connection;
|
|
||||||
|
|
||||||
bool m_headerWritten;
|
|
||||||
HeaderHash m_headers;
|
|
||||||
friend class QHttpConnection;
|
|
||||||
|
|
||||||
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
|
|
@ -1,125 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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 <QtNetwork/QTcpServer>
|
|
||||||
#include <QtNetwork/QTcpSocket>
|
|
||||||
#include <QtCore/QVariant>
|
|
||||||
#include <QtCore/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)
|
|
||||||
{
|
|
||||||
m_tcpServer = new QTcpServer;
|
|
||||||
|
|
||||||
connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection()));
|
|
||||||
return m_tcpServer->listen(address, port);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QHttpServer::listen(quint16 port)
|
|
||||||
{
|
|
||||||
return listen(QHostAddress::Any, port);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QHttpServer::close()
|
|
||||||
{
|
|
||||||
m_tcpServer->close();
|
|
||||||
}
|
|
@ -1,230 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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 <QtCore/QObject>
|
|
||||||
#include <QtNetwork/QHostAddress>
|
|
||||||
|
|
||||||
class QTcpServer;
|
|
||||||
|
|
||||||
class QHttpRequest;
|
|
||||||
class QHttpResponse;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* A map of request or response headers
|
|
||||||
*/
|
|
||||||
typedef QHash<QString, QString> HeaderHash;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Maps status codes to string reason phrases
|
|
||||||
*/
|
|
||||||
extern QHash<int, QString> STATUS_CODES;
|
|
||||||
|
|
||||||
/*! \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 want to
|
|
||||||
* demonstrate at <a href="http://conf.kde.in">conf.kde.in 2011</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.
|
|
||||||
*
|
|
||||||
* \example helloworld/helloworld.cpp
|
|
||||||
* \example helloworld/helloworld.h
|
|
||||||
* \example greeting/greeting.cpp
|
|
||||||
* \example greeting/greeting.h
|
|
||||||
* \example bodydata/bodydata.cpp
|
|
||||||
* \example bodydata/bodydata.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*! \class QHttpServer
|
|
||||||
* 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 and respond to requests.
|
|
||||||
*
|
|
||||||
* <ol>
|
|
||||||
* <li>Create an instance of QHttpServer.</li>
|
|
||||||
* <li>Connect a slot to the newRequest(QHttpRequest*, QHttpResponse*)
|
|
||||||
* 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>
|
|
||||||
*
|
|
||||||
* helloworld.cpp
|
|
||||||
* \include helloworld/helloworld.cpp
|
|
||||||
* helloworld.h
|
|
||||||
* \include helloworld/helloworld.h
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class QHttpServer : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
* Create a new HTTP Server
|
|
||||||
*/
|
|
||||||
QHttpServer(QObject *parent = 0);
|
|
||||||
virtual ~QHttpServer();
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Start the server bound to the @c address and @c port.
|
|
||||||
* This function returns immediately!
|
|
||||||
*
|
|
||||||
* \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=0);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* 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 listening for connections
|
|
||||||
*/
|
|
||||||
void close();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
/*!
|
|
||||||
* This signal is emitted whenever a client
|
|
||||||
* makes a new request to the server.
|
|
||||||
*
|
|
||||||
* The slot should use the @c request and @c response
|
|
||||||
* objects to communicate with the client.
|
|
||||||
*
|
|
||||||
* \section memorymanagement Memory Management
|
|
||||||
*
|
|
||||||
* The QHttpRequest and QHttpResponse deletion policies
|
|
||||||
* are such.
|
|
||||||
*
|
|
||||||
* QHttpRequest is <strong>never</strong> 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 <strong>NOT</strong> 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 <strong>NOT</strong> 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(0)
|
|
||||||
* {
|
|
||||||
* QHttpServer *s = new QHttpServer;
|
|
||||||
* connect(s, SIGNAL(newRequest(...)), this, SLOT(handle(...)));
|
|
||||||
* s.listen(8000);
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* void MyApp::handle(QHttpRequest *request, QHttpResponse *response)
|
|
||||||
* {
|
|
||||||
* if( request->url() matches 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 ended.
|
|
||||||
* // 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
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void newRequest(QHttpRequest *request, QHttpResponse *response);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void newConnection();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QTcpServer *m_tcpServer;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue
Block a user