File indexing completed on 2025-02-02 04:36:32

0001 /*
0002  * Copyright 2011 Nikhil Marathe <nsm.nikhil@gmail.com>
0003  * 
0004  * Permission is hereby granted, free of charge, to any person obtaining a copy
0005  * of this software and associated documentation files (the "Software"), to
0006  * deal in the Software without restriction, including without limitation the
0007  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
0008  * sell copies of the Software, and to permit persons to whom the Software is
0009  * furnished to do so, subject to the following conditions:
0010  * 
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial portions of the Software.
0013  * 
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0017  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0018  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
0019  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
0020  * IN THE SOFTWARE. 
0021  */
0022 
0023 #include "qhttpconnection.h"
0024 
0025 #include <QTcpSocket>
0026 #include <QHostAddress>
0027 #include <QDebug>
0028 
0029 #include "qhttprequest.h"
0030 #include "qhttpresponse.h"
0031 
0032 QHttpConnection::QHttpConnection(QTcpSocket *socket, QObject *parent)
0033     : QObject(parent)
0034     , m_socket(socket)
0035     , m_parser(0)
0036     , m_request(0)
0037 {
0038     qDebug() << "Got new connection" << socket->peerAddress() << socket->peerPort();
0039 
0040     m_parser = (http_parser*)malloc(sizeof(http_parser));
0041     http_parser_init(m_parser, HTTP_REQUEST);
0042 
0043     m_parserSettings.on_message_begin = MessageBegin;
0044     m_parserSettings.on_url = Url;
0045     m_parserSettings.on_header_field = HeaderField;
0046     m_parserSettings.on_header_value = HeaderValue;
0047     m_parserSettings.on_headers_complete = HeadersComplete;
0048     m_parserSettings.on_body = Body;
0049     m_parserSettings.on_message_complete = MessageComplete;
0050 
0051     m_parser->data = this;
0052 
0053     connect(socket, SIGNAL(readyRead()), this, SLOT(parseRequest()));
0054     connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
0055 }
0056 
0057 QHttpConnection::~QHttpConnection()
0058 {
0059     delete m_socket;
0060     m_socket = 0;
0061 
0062     free(m_parser);
0063     m_parser = 0;
0064 }
0065 
0066 void QHttpConnection::socketDisconnected()
0067 {
0068     if(m_request) {
0069         if(m_request->successful()) {
0070           return;
0071         }
0072         m_request->setSuccessful(false);
0073         Q_EMIT m_request->end();
0074     }
0075 
0076     deleteLater();
0077 }
0078 
0079 void QHttpConnection::parseRequest()
0080 {
0081     Q_ASSERT(m_parser);
0082 
0083     while(m_socket->bytesAvailable())
0084     {
0085         QByteArray arr = m_socket->readAll();
0086         http_parser_execute(m_parser, &m_parserSettings, arr.constData(), arr.size());
0087     }
0088 }
0089 
0090 void QHttpConnection::write(const QByteArray &data)
0091 {
0092     m_socket->write(data);
0093 }
0094 
0095 void QHttpConnection::flush()
0096 {
0097     m_socket->flush();
0098 }
0099 
0100 void QHttpConnection::disconnectFromHost()
0101 {
0102     m_socket->disconnectFromHost();
0103 }
0104 
0105 /********************
0106  * Static Callbacks *
0107  *******************/
0108 int QHttpConnection::MessageBegin(http_parser *parser)
0109 {
0110     QHttpConnection *theConnection = (QHttpConnection *)parser->data;
0111     theConnection->m_currentHeaders.clear();
0112     theConnection->m_request = new QHttpRequest(theConnection);
0113     return 0;
0114 }
0115 
0116 int QHttpConnection::HeadersComplete(http_parser *parser)
0117 {
0118     QHttpConnection *theConnection = (QHttpConnection *)parser->data;
0119     Q_ASSERT(theConnection->m_request);
0120 
0121     /** set method **/
0122     theConnection->m_request->setMethod(static_cast<QHttpRequest::HttpMethod>(parser->method));
0123 
0124     /** set version **/
0125     theConnection->m_request->setVersion(QString("%1.%2").arg(parser->http_major).arg(parser->http_minor));
0126 
0127     // Insert last remaining header
0128     theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] = theConnection->m_currentHeaderValue;
0129     theConnection->m_request->setHeaders(theConnection->m_currentHeaders);
0130 
0131     /** set client information **/
0132     theConnection->m_request->m_remoteAddress = theConnection->m_socket->peerAddress().toString();
0133     theConnection->m_request->m_remotePort = theConnection->m_socket->peerPort();
0134 
0135     QHttpResponse *response = new QHttpResponse(theConnection);
0136     if( parser->http_major < 1 || parser->http_minor < 1 )
0137         response->m_keepAlive = false;
0138 
0139     connect(theConnection, SIGNAL(destroyed()), response, SLOT(connectionClosed()));
0140     connect(response, SIGNAL(done()), theConnection, SLOT(disconnectFromHost()));
0141 
0142     // we are good to go!
0143     Q_EMIT theConnection->newRequest(theConnection->m_request, response);
0144     return 0;
0145 }
0146 
0147 int QHttpConnection::MessageComplete(http_parser *parser)
0148 {
0149     // TODO: do cleanup and prepare for next request
0150     QHttpConnection *theConnection = (QHttpConnection *)parser->data;
0151     Q_ASSERT(theConnection->m_request);
0152 
0153     theConnection->m_request->setSuccessful(true);
0154     Q_EMIT theConnection->m_request->end();
0155     return 0;
0156 }
0157 
0158 int QHttpConnection::Url(http_parser *parser, const char *at, size_t length)
0159 {
0160     QHttpConnection *theConnection = (QHttpConnection *)parser->data;
0161     Q_ASSERT(theConnection->m_request);
0162 
0163     QString url = QString::fromLatin1(at, length);
0164     theConnection->m_request->setUrl(QUrl(url));
0165     return 0;
0166 }
0167 
0168 int QHttpConnection::HeaderField(http_parser *parser, const char *at, size_t length)
0169 {
0170     QHttpConnection *theConnection = (QHttpConnection *)parser->data;
0171     Q_ASSERT(theConnection->m_request);
0172 
0173     // insert the header we parsed previously
0174     // into the header map
0175     if( !theConnection->m_currentHeaderField.isEmpty() && !theConnection->m_currentHeaderValue.isEmpty() )
0176     {
0177         // header names are always lower-cased
0178         theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] = theConnection->m_currentHeaderValue;
0179         // clear header value. this sets up a nice
0180         // feedback loop where the next time
0181         // HeaderValue is called, it can simply append
0182         theConnection->m_currentHeaderField = QString();
0183         theConnection->m_currentHeaderValue = QString();
0184     }
0185 
0186     QString fieldSuffix = QString::fromLatin1(at, length);
0187     theConnection->m_currentHeaderField += fieldSuffix;
0188     return 0;
0189 }
0190 
0191 int QHttpConnection::HeaderValue(http_parser *parser, const char *at, size_t length)
0192 {
0193     QHttpConnection *theConnection = (QHttpConnection *)parser->data;
0194     Q_ASSERT(theConnection->m_request);
0195 
0196     QString valueSuffix = QString::fromLatin1(at, length);
0197     theConnection->m_currentHeaderValue += valueSuffix;
0198     return 0;
0199 }
0200 
0201 int QHttpConnection::Body(http_parser *parser, const char *at, size_t length)
0202 {
0203     QHttpConnection *theConnection = (QHttpConnection *)parser->data;
0204     Q_ASSERT(theConnection->m_request);
0205 
0206     Q_EMIT theConnection->m_request->data(QByteArray(at, length));
0207     return 0;
0208 }