Skip to content
Browse files

First working code.

Requests are parsed partially, responses are sent partially, but not legal HTTP replies.

TODO
----
* Memory management
* Handle proper response headers based on what the user has sent
* Handle proper request headers
* Provide connection information in request object
  • Loading branch information...
1 parent 2df2096 commit f3f11ccadb64ace30a99830dba7227fb5af4a02e @nikhilm committed
View
3 examples/examples.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+SUBDIRS += \
+helloworld
View
37 examples/helloworld/helloworld.cpp
@@ -0,0 +1,37 @@
+#include "helloworld.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+
+#include <qhttpserver.h>
+#include <qhttprequest.h>
+#include <qhttpresponse.h>
+
+Hello::Hello()
+{
+ QHttpServer *server = new QHttpServer;
+ server->listen(QHostAddress::Any, 5000);
+ connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),
+ this, SLOT(handle(QHttpRequest*, QHttpResponse*)));
+}
+
+void Hello::handle(QHttpRequest *req, QHttpResponse *resp)
+{
+ qDebug() << "Handling request";
+ qDebug() << "Method" << req->method();
+ qDebug() << "HTTP Version" << req->httpVersion();
+ qDebug() << "URL" << req->url();
+ resp->setHeader("Content-Type", "text/html");
+ resp->writeHead(200);
+ resp->write(QString("Hello World"));
+ resp->end();
+}
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ Hello hello;
+
+ app.exec();
+}
View
14 examples/helloworld/helloworld.h
@@ -0,0 +1,14 @@
+#include <QObject>
+
+class QHttpRequest;
+class QHttpResponse;
+
+class Hello : public QObject
+{
+ Q_OBJECT
+public:
+ Hello();
+
+private slots:
+ void handle(QHttpRequest *req, QHttpResponse *resp);
+};
View
9 examples/helloworld/helloworld.pro
@@ -0,0 +1,9 @@
+TARGET=helloworld
+QT += network
+QT -= gui
+
+INCLUDEPATH += ../../src
+LIBS += -L../../lib -lqhttpserver
+
+SOURCES=helloworld.cpp
+HEADERS=helloworld.h
View
3 qhttpserver.pro
@@ -1,4 +1,5 @@
TEMPLATE = subdirs
SUBDIRS += \
src\
- tests
+ tests\
+ examples
View
63 src/Makefile
@@ -1,6 +1,6 @@
#############################################################################
# Makefile for building: libqhttpserver.so.0.1.0
-# Generated by qmake (2.01a) (Qt 4.7.1) on: Sun Jan 30 17:37:37 2011
+# Generated by qmake (2.01a) (Qt 4.7.1) on: Tue Feb 1 13:14:27 2011
# Project: src.pro
# Template: lib
# Command: /usr/bin/qmake -o Makefile src.pro
@@ -43,15 +43,28 @@ OBJECTS_DIR = ../build/
####### Files
-SOURCES = qhttpserver.cpp \
- ../http-parser/http_parser.c ../build/moc_qhttpserver.cpp
-OBJECTS = ../build/qhttpserver.o \
+SOURCES = qhttpconnection.cpp \
+ qhttprequest.cpp \
+ qhttpresponse.cpp \
+ qhttpserver.cpp \
+ ../http-parser/http_parser.c ../build/moc_qhttpconnection.cpp \
+ ../build/moc_qhttpserver.cpp \
+ ../build/moc_qhttprequest.cpp \
+ ../build/moc_qhttpresponse.cpp
+OBJECTS = ../build/qhttpconnection.o \
+ ../build/qhttprequest.o \
+ ../build/qhttpresponse.o \
+ ../build/qhttpserver.o \
../build/http_parser.o \
- ../build/moc_qhttpserver.o
+ ../build/moc_qhttpconnection.o \
+ ../build/moc_qhttpserver.o \
+ ../build/moc_qhttprequest.o \
+ ../build/moc_qhttpresponse.o
DIST = /usr/share/qt/mkspecs/common/g++.conf \
/usr/share/qt/mkspecs/common/unix.conf \
/usr/share/qt/mkspecs/common/linux.conf \
/usr/share/qt/mkspecs/qconfig.pri \
+ /usr/share/qt/mkspecs/modules/qt_phonon.pri \
/usr/share/qt/mkspecs/modules/qt_webkit_version.pri \
/usr/share/qt/mkspecs/features/qt_functions.prf \
/usr/share/qt/mkspecs/features/qt_config.prf \
@@ -129,6 +142,7 @@ Makefile: src.pro /usr/share/qt/mkspecs/linux-g++/qmake.conf /usr/share/qt/mksp
/usr/share/qt/mkspecs/common/unix.conf \
/usr/share/qt/mkspecs/common/linux.conf \
/usr/share/qt/mkspecs/qconfig.pri \
+ /usr/share/qt/mkspecs/modules/qt_phonon.pri \
/usr/share/qt/mkspecs/modules/qt_webkit_version.pri \
/usr/share/qt/mkspecs/features/qt_functions.prf \
/usr/share/qt/mkspecs/features/qt_config.prf \
@@ -154,6 +168,7 @@ Makefile: src.pro /usr/share/qt/mkspecs/linux-g++/qmake.conf /usr/share/qt/mksp
/usr/share/qt/mkspecs/common/unix.conf:
/usr/share/qt/mkspecs/common/linux.conf:
/usr/share/qt/mkspecs/qconfig.pri:
+/usr/share/qt/mkspecs/modules/qt_phonon.pri:
/usr/share/qt/mkspecs/modules/qt_webkit_version.pri:
/usr/share/qt/mkspecs/features/qt_functions.prf:
/usr/share/qt/mkspecs/features/qt_config.prf:
@@ -179,7 +194,7 @@ qmake: FORCE
dist:
@$(CHK_DIR_EXISTS) ../build/qhttpserver0.1.0 || $(MKDIR) ../build/qhttpserver0.1.0
- $(COPY_FILE) --parents $(SOURCES) $(DIST) ../build/qhttpserver0.1.0/ && $(COPY_FILE) --parents ../http-parser/http_parser.h qhttpserver.h ../build/qhttpserver0.1.0/ && $(COPY_FILE) --parents qhttpserver.cpp ../http-parser/http_parser.c ../build/qhttpserver0.1.0/ && (cd `dirname ../build/qhttpserver0.1.0` && $(TAR) qhttpserver0.1.0.tar qhttpserver0.1.0 && $(COMPRESS) qhttpserver0.1.0.tar) && $(MOVE) `dirname ../build/qhttpserver0.1.0`/qhttpserver0.1.0.tar.gz . && $(DEL_FILE) -r ../build/qhttpserver0.1.0
+ $(COPY_FILE) --parents $(SOURCES) $(DIST) ../build/qhttpserver0.1.0/ && $(COPY_FILE) --parents ../http-parser/http_parser.h qhttpconnection.h qhttpserver.h qhttprequest.h qhttpresponse.h ../build/qhttpserver0.1.0/ && $(COPY_FILE) --parents qhttpconnection.cpp qhttprequest.cpp qhttpresponse.cpp qhttpserver.cpp ../http-parser/http_parser.c ../build/qhttpserver0.1.0/ && (cd `dirname ../build/qhttpserver0.1.0` && $(TAR) qhttpserver0.1.0.tar qhttpserver0.1.0 && $(COMPRESS) qhttpserver0.1.0.tar) && $(MOVE) `dirname ../build/qhttpserver0.1.0`/qhttpserver0.1.0.tar.gz . && $(DEL_FILE) -r ../build/qhttpserver0.1.0
clean:compiler_clean
@@ -201,12 +216,21 @@ mocclean: compiler_moc_header_clean compiler_moc_source_clean
mocables: compiler_moc_header_make_all compiler_moc_source_make_all
-compiler_moc_header_make_all: ../build/moc_qhttpserver.cpp
+compiler_moc_header_make_all: ../build/moc_qhttpconnection.cpp ../build/moc_qhttpserver.cpp ../build/moc_qhttprequest.cpp ../build/moc_qhttpresponse.cpp
compiler_moc_header_clean:
- -$(DEL_FILE) ../build/moc_qhttpserver.cpp
+ -$(DEL_FILE) ../build/moc_qhttpconnection.cpp ../build/moc_qhttpserver.cpp ../build/moc_qhttprequest.cpp ../build/moc_qhttpresponse.cpp
+../build/moc_qhttpconnection.cpp: qhttpconnection.h
+ /usr/bin/moc $(DEFINES) $(INCPATH) qhttpconnection.h -o ../build/moc_qhttpconnection.cpp
+
../build/moc_qhttpserver.cpp: qhttpserver.h
/usr/bin/moc $(DEFINES) $(INCPATH) qhttpserver.h -o ../build/moc_qhttpserver.cpp
+../build/moc_qhttprequest.cpp: qhttprequest.h
+ /usr/bin/moc $(DEFINES) $(INCPATH) qhttprequest.h -o ../build/moc_qhttprequest.cpp
+
+../build/moc_qhttpresponse.cpp: qhttpresponse.h
+ /usr/bin/moc $(DEFINES) $(INCPATH) qhttpresponse.h -o ../build/moc_qhttpresponse.cpp
+
compiler_rcc_make_all:
compiler_rcc_clean:
compiler_image_collection_make_all: qmake_image_collection.cpp
@@ -226,15 +250,36 @@ compiler_clean: compiler_moc_header_clean
####### Compile
-../build/qhttpserver.o: qhttpserver.cpp qhttpserver.h
+../build/qhttpconnection.o: qhttpconnection.cpp qhttpconnection.h \
+ qhttprequest.h \
+ qhttpresponse.h
+ $(CXX) -c $(CXXFLAGS) $(INCPATH) -o ../build/qhttpconnection.o qhttpconnection.cpp
+
+../build/qhttprequest.o: qhttprequest.cpp qhttprequest.h
+ $(CXX) -c $(CXXFLAGS) $(INCPATH) -o ../build/qhttprequest.o qhttprequest.cpp
+
+../build/qhttpresponse.o: qhttpresponse.cpp qhttpresponse.h
+ $(CXX) -c $(CXXFLAGS) $(INCPATH) -o ../build/qhttpresponse.o qhttpresponse.cpp
+
+../build/qhttpserver.o: qhttpserver.cpp qhttpserver.h \
+ qhttpconnection.h
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o ../build/qhttpserver.o qhttpserver.cpp
../build/http_parser.o: ../http-parser/http_parser.c
$(CC) -c $(CFLAGS) $(INCPATH) -o ../build/http_parser.o ../http-parser/http_parser.c
+../build/moc_qhttpconnection.o: ../build/moc_qhttpconnection.cpp
+ $(CXX) -c $(CXXFLAGS) $(INCPATH) -o ../build/moc_qhttpconnection.o ../build/moc_qhttpconnection.cpp
+
../build/moc_qhttpserver.o: ../build/moc_qhttpserver.cpp
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o ../build/moc_qhttpserver.o ../build/moc_qhttpserver.cpp
+../build/moc_qhttprequest.o: ../build/moc_qhttprequest.cpp
+ $(CXX) -c $(CXXFLAGS) $(INCPATH) -o ../build/moc_qhttprequest.o ../build/moc_qhttprequest.cpp
+
+../build/moc_qhttpresponse.o: ../build/moc_qhttpresponse.cpp
+ $(CXX) -c $(CXXFLAGS) $(INCPATH) -o ../build/moc_qhttpresponse.o ../build/moc_qhttpresponse.cpp
+
####### Install
install_target: first FORCE
View
175 src/qhttpconnection.cpp
@@ -0,0 +1,175 @@
+#include "qhttpconnection.h"
+
+#include <QTcpSocket>
+#include <QHostAddress>
+#include <QDebug>
+
+#include <qhttprequest.h>
+#include <qhttpresponse.h>
+
+QHttpConnection::QHttpConnection(QTcpSocket *socket, QObject *parent)
+ : QObject(parent)
+ , m_socket(socket)
+ , m_parser(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_path = Path;
+ m_parserSettings.on_query_string = 0;
+ //m_parserSettings.on_query_string = QueryString;
+ m_parserSettings.on_url = Url;
+ m_parserSettings.on_fragment = Fragment;
+ 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(deleteLater()));
+}
+
+QHttpConnection::~QHttpConnection()
+{
+ qDebug() << "Disconnected. Deleting this connection object";
+ delete m_socket;
+ m_socket = 0;
+
+ free(m_parser);
+ m_parser = 0;
+}
+
+void QHttpConnection::parseRequest()
+{
+ Q_ASSERT(m_parser);
+
+ while(m_socket->bytesAvailable())
+ {
+ QByteArray arr = m_socket->read(80*1024);
+
+ if( arr.size() < 0 ) {
+ // TODO
+ qDebug() << "Handle closed connection";
+ }
+ else {
+ int nparsed = http_parser_execute(m_parser, &m_parserSettings, arr.data(), arr.size());
+ if( nparsed != arr.size() ) {
+ qDebug() << "ERROR PARSING!";
+ }
+ }
+ }
+}
+
+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_request = new QHttpRequest(theConnection);
+ return 0;
+}
+
+int QHttpConnection::HeadersComplete(http_parser *parser)
+{
+ qDebug() << "header complete";
+ qDebug() << parser->method << parser->http_major << parser->http_minor;
+ QHttpConnection *theConnection = (QHttpConnection *)parser->data;
+ Q_ASSERT(theConnection->m_request);
+
+ /** set method **/
+ QString method;
+ switch(parser->method)
+ {
+ case HTTP_DELETE: method = "DELETE"; break;
+ case HTTP_GET: method = "GET"; break;
+ case HTTP_HEAD: method = "HEAD"; break;
+ case HTTP_POST: method = "POST"; break;
+ case HTTP_PUT: method = "PUT"; break;
+ case HTTP_CONNECT: method = "CONNECT"; break;
+ case HTTP_OPTIONS: method = "OPTIONS"; break;
+ case HTTP_TRACE: method = "TRACE"; break;
+ }
+ theConnection->m_request->setMethod(method);
+
+ /** set version **/
+ theConnection->m_request->setVersion(QString("%1.%2").arg(parser->http_major).arg(parser->http_minor));
+
+ // TODO don't create a response object just like that
+ emit theConnection->newRequest(theConnection->m_request, new QHttpResponse(theConnection));
+ return 0;
+}
+
+int QHttpConnection::MessageComplete(http_parser *parser)
+{
+ qDebug() << "Message complete";
+ return 0;
+}
+
+int QHttpConnection::Path(http_parser *parser, const char *at, size_t length)
+{
+ QHttpConnection *theConnection = (QHttpConnection *)parser->data;
+ Q_ASSERT(theConnection->m_request);
+ QString path = QString::fromAscii(at, length);
+
+ QUrl url = theConnection->m_request->url();
+ url.setPath(path);
+ theConnection->m_request->setUrl(url);
+ qDebug() << "GOT Path" << theConnection->m_request->url();
+ return 0;
+}
+
+int QHttpConnection::QueryString(http_parser *parser, const char *at, size_t length)
+{
+ QHttpConnection *theConnection = (QHttpConnection *)parser->data;
+ Q_ASSERT(theConnection->m_request);
+
+ qDebug() << "GOT query string" << QString::fromAscii(at, length) << "FAILING DUE TO NON-IMPLEMENTATION";
+ Q_ASSERT(false);
+ return 0;
+}
+
+int QHttpConnection::Url(http_parser *parser, const char *at, size_t length)
+{
+ QHttpConnection *theConnection = (QHttpConnection *)parser->data;
+ theConnection->m_request->setUrl(QString::fromAscii(at, length));
+
+ qDebug() << "GOT URL" << theConnection->m_request->url();
+ return 0;
+}
+
+int QHttpConnection::Fragment(http_parser *parser, const char *at, size_t length)
+{
+ return 0;
+}
+
+int QHttpConnection::HeaderField(http_parser *parser, const char *at, size_t length)
+{
+ return 0;
+}
+
+int QHttpConnection::HeaderValue(http_parser *parser, const char *at, size_t length)
+{
+ return 0;
+}
+
+int QHttpConnection::Body(http_parser *parser, const char *at, size_t length)
+{
+ return 0;
+}
View
73 src/qhttpconnection.h
@@ -0,0 +1,73 @@
+/*
+Copyright 2009,2010 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 <QObject>
+
+#include <http_parser.h>
+
+class QTcpSocket;
+
+class QHttpRequest;
+class QHttpResponse;
+
+class QHttpConnection : public QObject
+{
+ Q_OBJECT
+
+public:
+ QHttpConnection(QTcpSocket *socket, QObject *parent = 0);
+ virtual ~QHttpConnection();
+
+ void write(const QByteArray &data);
+ void flush();
+
+signals:
+ void newRequest(QHttpRequest*, QHttpResponse*);
+
+private slots:
+ void parseRequest();
+
+private:
+ static int MessageBegin(http_parser *parser);
+ static int Path(http_parser *parser, const char *at, size_t length);
+ static int QueryString(http_parser *parser, const char *at, size_t length);
+ static int Url(http_parser *parser, const char *at, size_t length);
+ static int Fragment(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;
+};
+
+#endif
View
14 src/qhttprequest.cpp
@@ -0,0 +1,14 @@
+#include "qhttprequest.h"
+
+#include "qhttpconnection.h"
+
+QHttpRequest::QHttpRequest(QHttpConnection *connection, QObject *parent)
+ : QObject(parent)
+ , m_connection(connection)
+{
+}
+
+QHttpRequest::~QHttpRequest()
+{
+}
+
View
66 src/qhttprequest.h
@@ -0,0 +1,66 @@
+/*
+Copyright 2009,2010 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 <QObject>
+#include <QHash>
+#include <QUrl>
+
+class QTcpSocket;
+
+class QHttpConnection;
+
+class QHttpRequest : public QObject
+{
+ Q_OBJECT
+
+public:
+ virtual ~QHttpRequest();
+
+ const QString method() const { return m_method; };
+ QUrl url() const { return m_url; };
+ QString path() const;
+ QString httpVersion() const { return m_version; };
+ QString queryString() const;
+
+signals:
+ void data(const QByteArray &);
+ void end();
+
+private:
+ QHttpRequest(QHttpConnection *connection, QObject *parent = 0);
+
+ void setMethod(const QString &method) { m_method = method; }
+ void setVersion(const QString &version) { m_version = version; }
+ void setUrl(const QUrl &url) { m_url = url; }
+
+ QHttpConnection *m_connection;
+ QHash<QString, QString> m_headers;
+ QString m_method;
+ QUrl m_url;
+ QString m_version;
+
+ friend class QHttpConnection;
+};
+
+#endif
View
59 src/qhttpresponse.cpp
@@ -0,0 +1,59 @@
+#include "qhttpresponse.h"
+
+#include "qhttpserver.h"
+#include "qhttpconnection.h"
+
+QHttpResponse::QHttpResponse(QHttpConnection *connection)
+ // TODO: parent child relation
+ : QObject(0)
+ , m_connection(connection)
+ , m_headerWritten(false)
+{
+}
+
+QHttpResponse::~QHttpResponse()
+{
+}
+
+void QHttpResponse::setHeader(const QString &field, const QString &value)
+{
+ m_headers[field] = value;
+}
+
+void QHttpResponse::writeHead(int status)
+{
+ if( m_headerWritten ) return;
+
+ m_connection->write(QString("HTTP/1.1 %1 %2\r\n").arg(status).arg(STATUS_CODES[status]).toAscii());
+
+ foreach(QString name, m_headers.keys())
+ {
+ m_connection->write(name.toAscii());
+ m_connection->write(": ");
+ m_connection->write(m_headers[name].toAscii());
+ m_connection->write("\r\n");
+ }
+
+ m_connection->write("\r\n");
+ m_headerWritten = true;
+}
+
+void QHttpResponse::write(const QByteArray &data)
+{
+ if( !m_headerWritten )
+ writeHead(200);
+
+ m_connection->write(data);
+}
+
+void QHttpResponse::write(const QString &data)
+{
+ m_connection->write(data.toAscii());
+}
+
+void QHttpResponse::end(const QByteArray &data)
+{
+ write(data);
+ m_connection->flush();
+ // TODO: end connection and delete ourselves
+}
View
60 src/qhttpresponse.h
@@ -0,0 +1,60 @@
+/*
+Copyright 2009,2010 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 <QObject>
+#include <QHash>
+
+//
+class QTcpSocket;
+
+class QHttpConnection;
+
+class QHttpResponse : public QObject
+{
+ Q_OBJECT
+
+public:
+ virtual ~QHttpResponse();
+
+ void writeHead(int status);
+ void write(const QByteArray &data);
+ void write(const QString &data);
+ void end(const QByteArray &data=QByteArray());
+
+ void setHeader(const QString &field, const QString &value);
+
+signals:
+ void done();
+
+private:
+ QHttpResponse(QHttpConnection *connection);
+
+ QHttpConnection *m_connection;
+
+ bool m_headerWritten;
+ QHash<QString, QString> m_headers;
+ friend class QHttpConnection;
+};
+
+#endif
View
172 src/qhttpserver.cpp
@@ -26,70 +26,82 @@ IN THE SOFTWARE.
#include <QVariant>
#include <QDebug>
-#include <http_parser.h>
+#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::parseRequest()
-{
- QTcpSocket *socket = qobject_cast<QTcpSocket*>(QObject::sender());
-
- QVariant x = socket->property("http_parser");
- http_parser *parser = (http_parser*)qvariant_cast<void*>(x);
- Q_ASSERT(parser);
-
- while(socket->bytesAvailable())
- {
- QByteArray arr = socket->read(80*1024);
-
- if( arr.size() < 0 ) {
- // TODO
- qDebug() << "Handle closed connection";
- }
- else {
- int nparsed = http_parser_execute(parser, (const http_parser_settings*)parser->data, arr.data(), arr.size());
- if( nparsed != arr.size() ) {
- qDebug() << "ERROR PARSING!";
- }
- }
- }
-}
-
void QHttpServer::newConnection()
{
Q_ASSERT(m_tcpServer);
while(m_tcpServer->hasPendingConnections()) {
- QTcpSocket *socket = m_tcpServer->nextPendingConnection();
- qDebug() << "Got new connection" << socket->peerAddress() << socket->peerPort();
-
- http_parser *parser = (http_parser*)malloc(sizeof(http_parser));
- http_parser_init(parser, HTTP_REQUEST);
-
- http_parser_settings *settings = (http_parser_settings*)malloc(sizeof(http_parser_settings));
- parser->data = (void*)settings;
-
- settings->on_message_begin = MessageBegin;
- settings->on_path = Path;
- settings->on_query_string = QueryString;
- settings->on_url = Url;
- settings->on_fragment = Fragment;
- settings->on_header_field = HeaderField;
- settings->on_header_value = HeaderValue;
- settings->on_headers_complete = HeadersComplete;
- settings->on_body = Body;
- settings->on_message_complete = MessageComplete;
-
- socket->setProperty("http_parser", QVariant::fromValue((void*)parser));
-
- connect(socket, SIGNAL(readyRead()), this, SLOT(parseRequest()));
+ QHttpConnection *connection = new QHttpConnection(m_tcpServer->nextPendingConnection(), this);
+ connect(connection, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),
+ this, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)));
}
}
@@ -101,65 +113,3 @@ bool QHttpServer::listen(const QHostAddress &address, quint16 port)
return m_tcpServer->listen(address, port);
}
-/********************
- * Static Callbacks *
- *******************/
-int QHttpServer::MessageBegin(http_parser *parser)
-{
- qDebug() << "Message begin";
- return 0;
-}
-
-int QHttpServer::HeadersComplete(http_parser *parser)
-{
- qDebug() << "header complete";
- return 0;
-}
-
-int QHttpServer::MessageComplete(http_parser *parser)
-{
- qDebug() << "Message complete";
- return 0;
-}
-
-int QHttpServer::Path(http_parser *parser, const char *at, size_t length)
-{
- qDebug() << "PATH" << at << length;
- return 0;
-}
-
-int QHttpServer::QueryString(http_parser *parser, const char *at, size_t length)
-{
- qDebug() << "QS" << at << length;
- return 0;
-}
-
-int QHttpServer::Url(http_parser *parser, const char *at, size_t length)
-{
- qDebug() << "url" << at << length;
- return 0;
-}
-
-int QHttpServer::Fragment(http_parser *parser, const char *at, size_t length)
-{
- qDebug() << "fragment" << at << length;
- return 0;
-}
-
-int QHttpServer::HeaderField(http_parser *parser, const char *at, size_t length)
-{
- qDebug() << "headerfield" << at << length;
- return 0;
-}
-
-int QHttpServer::HeaderValue(http_parser *parser, const char *at, size_t length)
-{
- qDebug() << "headervalue" << at << length;
- return 0;
-}
-
-int QHttpServer::Body(http_parser *parser, const char *at, size_t length)
-{
- qDebug() << "body" << at << length;
- return 0;
-}
View
23 src/qhttpserver.h
@@ -25,10 +25,13 @@ IN THE SOFTWARE.
#include <QObject>
#include <QHostAddress>
-struct http_parser;
-
class QTcpServer;
+class QHttpRequest;
+class QHttpResponse;
+
+extern QHash<int, QString> STATUS_CODES;
+
class QHttpServer : public QObject
{
Q_OBJECT
@@ -39,21 +42,11 @@ class QHttpServer : public QObject
bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
+signals:
+ void newRequest(QHttpRequest *request, QHttpResponse *response);
+
private slots:
void newConnection();
- void parseRequest();
-
-private:
- static int MessageBegin(http_parser *parser);
- static int Path(http_parser *parser, const char *at, size_t length);
- static int QueryString(http_parser *parser, const char *at, size_t length);
- static int Url(http_parser *parser, const char *at, size_t length);
- static int Fragment(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:
QTcpServer *m_tcpServer;
View
6 src/src.pro
@@ -11,12 +11,12 @@ CONFIG += dll debug
INCLUDEPATH += $$QHTTPSERVER_BASE/http-parser
-PRIVATE_HEADERS += $$QHTTPSERVER_BASE/http-parser/http_parser.h
+PRIVATE_HEADERS += $$QHTTPSERVER_BASE/http-parser/http_parser.h qhttpconnection.h
-PUBLIC_HEADERS += qhttpserver.h
+PUBLIC_HEADERS += qhttpserver.h qhttprequest.h qhttpresponse.h
HEADERS = $$PRIVATE_HEADERS $$PUBLIC_HEADERS
-SOURCES = qhttpserver.cpp $$QHTTPSERVER_BASE/http-parser/http_parser.c
+SOURCES = *.cpp $$QHTTPSERVER_BASE/http-parser/http_parser.c
OBJECTS_DIR = $$QHTTPSERVER_BASE/build
MOC_DIR = $$QHTTPSERVER_BASE/build
View
5 tests/Makefile
@@ -1,6 +1,6 @@
#############################################################################
# Makefile for building: tests
-# Generated by qmake (2.01a) (Qt 4.7.1) on: Sun Jan 30 17:38:46 2011
+# Generated by qmake (2.01a) (Qt 4.7.1) on: Tue Feb 1 13:14:33 2011
# Project: tests.pro
# Template: app
# Command: /usr/bin/qmake -o Makefile tests.pro
@@ -49,6 +49,7 @@ DIST = /usr/share/qt/mkspecs/common/g++.conf \
/usr/share/qt/mkspecs/common/unix.conf \
/usr/share/qt/mkspecs/common/linux.conf \
/usr/share/qt/mkspecs/qconfig.pri \
+ /usr/share/qt/mkspecs/modules/qt_phonon.pri \
/usr/share/qt/mkspecs/modules/qt_webkit_version.pri \
/usr/share/qt/mkspecs/features/qt_functions.prf \
/usr/share/qt/mkspecs/features/qt_config.prf \
@@ -101,6 +102,7 @@ Makefile: tests.pro /usr/share/qt/mkspecs/linux-g++/qmake.conf /usr/share/qt/mk
/usr/share/qt/mkspecs/common/unix.conf \
/usr/share/qt/mkspecs/common/linux.conf \
/usr/share/qt/mkspecs/qconfig.pri \
+ /usr/share/qt/mkspecs/modules/qt_phonon.pri \
/usr/share/qt/mkspecs/modules/qt_webkit_version.pri \
/usr/share/qt/mkspecs/features/qt_functions.prf \
/usr/share/qt/mkspecs/features/qt_config.prf \
@@ -125,6 +127,7 @@ Makefile: tests.pro /usr/share/qt/mkspecs/linux-g++/qmake.conf /usr/share/qt/mk
/usr/share/qt/mkspecs/common/unix.conf:
/usr/share/qt/mkspecs/common/linux.conf:
/usr/share/qt/mkspecs/qconfig.pri:
+/usr/share/qt/mkspecs/modules/qt_phonon.pri:
/usr/share/qt/mkspecs/modules/qt_webkit_version.pri:
/usr/share/qt/mkspecs/features/qt_functions.prf:
/usr/share/qt/mkspecs/features/qt_config.prf:
View
BIN tests/test.o
Binary file not shown.
View
BIN tests/tests
Binary file not shown.

0 comments on commit f3f11cc

Please sign in to comment.
Something went wrong with that request. Please try again.