Removed unused qhttpserver code

Updated README.md
This commit is contained in:
IvanF 2015-01-30 00:58:52 -08:00
parent db37b7b933
commit 9e124e4a75
20 changed files with 4 additions and 7605 deletions

View File

@ -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

View File

@ -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})

View File

@ -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.

View File

@ -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>

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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' ]
}
]
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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()
{
}

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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();
}

View File

@ -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