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 }