123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470 |
- /** base classes for private implementations.
- * https://github.com/azadkuh/qhttp
- *
- * @author amir zamani
- * @version 2.0.0
- * @date 2014-07-11
- */
- #ifndef QHTTPBASE_HPP
- #define QHTTPBASE_HPP
- #include "qhttpfwd.hpp"
- #include <QTcpSocket>
- #include <QLocalSocket>
- #include <QHostAddress>
- #include <QUrl>
- #include <QBasicTimer>
- #include "http-parser/http_parser.h"
- ///////////////////////////////////////////////////////////////////////////////
- namespace qhttp {
- ///////////////////////////////////////////////////////////////////////////////
- class QSocket
- {
- public:
- void close() {
- if ( itcpSocket )
- itcpSocket->close();
- if ( ilocalSocket )
- ilocalSocket->close();
- }
- void release() {
- close();
- if ( itcpSocket )
- itcpSocket->deleteLater();
- if ( ilocalSocket )
- ilocalSocket->deleteLater();
- itcpSocket = nullptr;
- ilocalSocket = nullptr;
- }
- void flush() {
- if ( itcpSocket )
- itcpSocket->flush();
- else if ( ilocalSocket )
- ilocalSocket->flush();
- }
- bool isOpen() const {
- if ( ibackendType == ETcpSocket && itcpSocket )
- return itcpSocket->isOpen() && itcpSocket->state() == QTcpSocket::ConnectedState;
- else if ( ibackendType == ELocalSocket && ilocalSocket )
- return ilocalSocket->isOpen() && ilocalSocket->state() == QLocalSocket::ConnectedState;
- return false;
- }
- void connectTo(const QUrl& url) {
- if ( ilocalSocket )
- ilocalSocket->connectToServer(url.path());
- }
- void connectTo(const QString& host, quint16 port) {
- if ( itcpSocket )
- itcpSocket->connectToHost(host, port);
- }
- qint64 readRaw(char* buffer, int maxlen) {
- if ( itcpSocket )
- return itcpSocket->read(buffer, maxlen);
- else if ( ilocalSocket )
- return ilocalSocket->read(buffer, maxlen);
- return 0;
- }
- void writeRaw(const QByteArray& data) {
- if ( itcpSocket )
- itcpSocket->write(data);
- else if ( ilocalSocket )
- ilocalSocket->write(data);
- }
- qint64 bytesAvailable() {
- if ( itcpSocket )
- return itcpSocket->bytesAvailable();
- else if ( ilocalSocket )
- return ilocalSocket->bytesAvailable();
- return 0;
- }
- void disconnectAllQtConnections() {
- if ( itcpSocket )
- QObject::disconnect(itcpSocket, 0, 0, 0);
- if ( ilocalSocket )
- QObject::disconnect(ilocalSocket, 0, 0, 0);
- }
- public:
- TBackend ibackendType = ETcpSocket;
- QTcpSocket* itcpSocket = nullptr;
- QLocalSocket* ilocalSocket = nullptr;
- };
- ///////////////////////////////////////////////////////////////////////////////
- class HttpBase
- {
- public:
- THeaderHash iheaders;
- QString iversion;
- };
- ///////////////////////////////////////////////////////////////////////////////
- class HttpRequestBase : public HttpBase
- {
- public:
- QUrl iurl;
- THttpMethod imethod;
- };
- ///////////////////////////////////////////////////////////////////////////////
- class HttpResponseBase : public HttpBase
- {
- public:
- HttpResponseBase() {
- iversion = "1.1";
- }
- public:
- TStatusCode istatus = ESTATUS_BAD_REQUEST;
- };
- ///////////////////////////////////////////////////////////////////////////////
- // usage in client::QHttpResponse, server::QHttpRequest
- template<class TBase>
- class HttpReader : public TBase
- {
- public:
- enum TReadState {
- EEmpty,
- EPartial,
- EComplete,
- ESent
- };
- public:
- void collectData(int atMost) {
- icollectCapacity = atMost;
- icollectedData.clear();
- icollectedData.reserve(atMost);
- }
- bool shouldCollect() const {
- return icollectCapacity > 0;
- }
- bool append(const char* data, size_t length) {
- int currentLength = icollectedData.length();
- if ( (currentLength + (int)length) >= icollectCapacity )
- return false; // capacity if full
- icollectedData.append(data, length);
- return true;
- }
- public:
- TReadState ireadState = EEmpty;
- bool isuccessful = false;
- int icollectCapacity = 0;
- QByteArray icollectedData;
- };
- ///////////////////////////////////////////////////////////////////////////////
- // usage in client::QHttpRequest, server::QHttpResponse
- template<class TBase, class TImpl>
- class HttpWriter : public TBase
- {
- public:
- bool addHeader(const QByteArray &field, const QByteArray &value) {
- if ( ifinished )
- return false;
- TBase::iheaders.insert(field.toLower(), value);
- return true;
- }
- bool writeHeader(const QByteArray& field, const QByteArray& value) {
- if ( ifinished )
- return false;
- QByteArray buffer = QByteArray(field)
- .append(": ")
- .append(value)
- .append("\r\n");
- isocket.writeRaw(buffer);
- return true;
- }
- bool writeData(const QByteArray& data) {
- if ( ifinished )
- return false;
- ensureWritingHeaders();
- isocket.writeRaw(data);
- return true;
- }
- bool endPacket(const QByteArray& data) {
- if ( !writeData(data) )
- return false;
- isocket.flush();
- ifinished = true;
- return true;
- }
- void ensureWritingHeaders() {
- if ( ifinished || iheaderWritten )
- return;
- TImpl* me = static_cast<TImpl*>(this);
- isocket.writeRaw(me->makeTitle());
- writeHeaders();
- iheaderWritten = true;
- }
- void writeHeaders(bool doFlush = false) {
- if ( ifinished || iheaderWritten )
- return;
- if ( TBase::iheaders.keyHasValue("connection", "keep-alive") )
- ikeepAlive = true;
- else
- TBase::iheaders.insert("connection", "close");
- TImpl* me = static_cast<TImpl*>(this);
- me->prepareHeadersToWrite();
- for ( auto cit = TBase::iheaders.constBegin(); cit != TBase::iheaders.constEnd(); cit++ ) {
- const QByteArray& field = cit.key();
- const QByteArray& value = cit.value();
- writeHeader(field, value);
- }
- isocket.writeRaw("\r\n");
- if ( doFlush )
- isocket.flush();
- }
- public:
- QSocket isocket;
- bool ifinished = false;
- bool iheaderWritten = false;
- bool ikeepAlive = false;
- };
- ///////////////////////////////////////////////////////////////////////////////
- // usage in client::QHttpClient, server::QHttpConnection
- template<class TImpl>
- class HttpParser
- {
- public:
- explicit HttpParser(http_parser_type type) {
- // create http_parser object
- iparser.data = static_cast<TImpl*>(this);
- http_parser_init(&iparser, type);
- memset(&iparserSettings, 0, sizeof(http_parser_settings));
- iparserSettings.on_message_begin = onMessageBegin;
- iparserSettings.on_url = onUrl;
- iparserSettings.on_status = onStatus;
- iparserSettings.on_header_field = onHeaderField;
- iparserSettings.on_header_value = onHeaderValue;
- iparserSettings.on_headers_complete = onHeadersComplete;
- iparserSettings.on_body = onBody;
- iparserSettings.on_message_complete = onMessageComplete;
- }
- size_t parse(const char* data, size_t length) {
- return http_parser_execute(&iparser,
- &iparserSettings,
- data,
- length);
- }
- public: // callback functions for http_parser_settings
- static int onMessageBegin(http_parser* parser) {
- TImpl *me = static_cast<TImpl*>(parser->data);
- return me->messageBegin(parser);
- }
- static int onUrl(http_parser* parser, const char* at, size_t length) {
- TImpl *me = static_cast<TImpl*>(parser->data);
- return me->url(parser, at, length);
- }
- static int onStatus(http_parser* parser, const char* at, size_t length) {
- TImpl *me = static_cast<TImpl*>(parser->data);
- return me->status(parser, at, length);
- }
- static int onHeaderField(http_parser* parser, const char* at, size_t length) {
- TImpl *me = static_cast<TImpl*>(parser->data);
- return me->headerField(parser, at, length);
- }
- static int onHeaderValue(http_parser* parser, const char* at, size_t length) {
- TImpl *me = static_cast<TImpl*>(parser->data);
- return me->headerValue(parser, at, length);
- }
- static int onHeadersComplete(http_parser* parser) {
- TImpl *me = static_cast<TImpl*>(parser->data);
- return me->headersComplete(parser);
- }
- static int onBody(http_parser* parser, const char* at, size_t length) {
- TImpl *me = static_cast<TImpl*>(parser->data);
- return me->body(parser, at, length);
- }
- static int onMessageComplete(http_parser* parser) {
- TImpl *me = static_cast<TImpl*>(parser->data);
- return me->messageComplete(parser);
- }
- protected:
- // The ones we are reading in from the parser
- QByteArray itempHeaderField;
- QByteArray itempHeaderValue;
- // if connection has a timeout, these fields will be used
- quint32 itimeOut = 0;
- QBasicTimer itimer;
- // uniform socket object
- QSocket isocket;
- // if connection should persist
- bool ikeepAlive = false;
- protected:
- http_parser iparser;
- http_parser_settings iparserSettings;
- };
- ///////////////////////////////////////////////////////////////////////////////
- #if 0
- template<class T>
- class HttpWriterBase
- {
- public:
- explicit HttpWriterBase() {
- }
- virtual ~HttpWriterBase() {
- }
- void initialize() {
- reset();
- if ( itcpSocket ) {
- // first disconnects previous dangling lambdas
- QObject::disconnect(itcpSocket, &QTcpSocket::bytesWritten, 0, 0);
- QObject::connect(itcpSocket, &QTcpSocket::bytesWritten, [this](qint64 ){
- if ( itcpSocket->bytesToWrite() == 0 )
- static_cast<T*>(this)->allBytesWritten();
- });
- } else if ( ilocalSocket ) {
- // first disconnects previous dangling lambdas
- QObject::disconnect(ilocalSocket, &QLocalSocket::bytesWritten, 0, 0);
- QObject::connect(ilocalSocket, &QLocalSocket::bytesWritten, [this](qint64 ){
- if ( ilocalSocket->bytesToWrite() == 0 )
- static_cast<T*>(this)->allBytesWritten();
- });
- }
- }
- void reset() {
- iheaderWritten = false;
- ifinished = false;
- }
- public:
- bool addHeader(const QByteArray &field, const QByteArray &value) {
- if ( ifinished )
- return false;
- static_cast<T*>(this)->iheaders.insert(field.toLower(), value);
- return true;
- }
- bool writeHeader(const QByteArray& field, const QByteArray& value) {
- if ( ifinished )
- return false;
- QByteArray buffer = QByteArray(field)
- .append(": ")
- .append(value)
- .append("\r\n");
- writeRaw(buffer);
- return true;
- }
- bool writeData(const QByteArray& data) {
- if ( ifinished )
- return false;
- static_cast<T*>(this)->ensureWritingHeaders();
- writeRaw(data);
- return true;
- }
- bool endPacket(const QByteArray& data) {
- if ( !writeData(data) )
- return false;
- if ( itcpSocket )
- itcpSocket->flush();
- else if ( ilocalSocket )
- ilocalSocket->flush();
- ifinished = true;
- return true;
- }
- protected:
- void writeRaw(const QByteArray &data) {
- if ( itcpSocket )
- itcpSocket->write(data);
- else if ( ilocalSocket )
- ilocalSocket->write(data);
- }
- public:
- QTcpSocket* itcpSocket = nullptr;
- QLocalSocket* ilocalSocket = nullptr;
- bool iheaderWritten;
- bool ifinished;
- };
- #endif
- ///////////////////////////////////////////////////////////////////////////////
- } // namespace qhttp
- ///////////////////////////////////////////////////////////////////////////////
- #endif // QHTTPBASE_HPP
|