TTKMusicPlayer  4.2.0.0
TTKMusicPlayer imitates Kugou UI, the music player uses of qmmp core library based on Qt for windows and linux
qhttpconnection.cpp
Go to the documentation of this file.
1 #include "qhttpconnection.h"
2 
3 #include <QTcpSocket>
4 #include <QHostAddress>
5 
6 #include "http_parser.h"
7 #include "qhttprequest.h"
8 #include "qhttpresponse.h"
9 
13 class QHttpConnectionPrivate : public TTKPrivate<QHttpConnection>
14 {
15 public:
18 
19  void invalidateRequest();
20  void writeCount(qint64 count);
21  void write(const QByteArray &data);
22  void parseRequest();
23 
24  static int MessageBegin(http_parser *parser);
25  static int Url(http_parser *parser, const char *at, size_t length);
26  static int HeaderField(http_parser *parser, const char *at, size_t length);
27  static int HeaderValue(http_parser *parser, const char *at, size_t length);
28  static int HeadersComplete(http_parser *parser);
29  static int Body(http_parser *parser, const char *at, size_t length);
30  static int MessageComplete(http_parser *parser);
31 
33  QTcpSocket *m_socket;
36 
37  // Since there can only be one request at any time even with pipelining.
39 
40  QByteArray m_currentUrl;
41  // The ones we are reading in from the parser
45 
46  // Keep track of transmit buffer status
47  qint64 m_transmitLen;
48  qint64 m_transmitPos;
49 
50 };
51 
53  : m_socket(nullptr),
54  m_parser(nullptr),
55  m_parserSettings(nullptr),
56  m_request(nullptr),
57  m_transmitLen(0),
58  m_transmitPos(0)
59 {
60  m_parser = (http_parser *)malloc(sizeof(http_parser));
62 
71 
72  m_parser->data = this;
73 }
74 
76 {
77 // delete m_socket;
78  m_socket = nullptr;
79 
80  free(m_parser);
81  m_parser = nullptr;
82 
83  delete m_parserSettings;
84  m_parserSettings = nullptr;
85 }
86 
88 {
89  if(m_request && !m_request->successful())
90  {
91  Q_EMIT m_request->end();
92  }
93 
94  m_request = nullptr;
95 }
96 
98 {
99  Q_ASSERT(m_transmitPos + count <= m_transmitLen);
100 
101  m_transmitPos += count;
102 
104  {
105  m_transmitLen = 0;
106  m_transmitPos = 0;
107  Q_EMIT m_parent->allBytesWritten();
108  }
109 }
110 
112 {
113  Q_ASSERT(m_parser);
114 
115  while(m_socket->bytesAvailable())
116  {
117  QByteArray arr = m_socket->readAll();
118  http_parser_execute(m_parser, m_parserSettings, arr.constData(), arr.length());
119  }
120 }
121 
122 void QHttpConnectionPrivate::write(const QByteArray &data)
123 {
124  m_socket->write(data);
125  m_transmitLen += data.length();
126 }
127 
128 
129 
130 QHttpConnection::QHttpConnection(QTcpSocket *socket, QObject *parent)
131  : QObject(parent)
132 {
135  d->m_socket = socket;
136  d->m_parent = this;
137 
138  connect(socket, SIGNAL(readyRead()), this, SLOT(parseRequest()));
139  connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
140  connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(updateWriteCount(qint64)));
141 }
142 
144 {
146 // deleteLater();
148  d->m_socket->deleteLater();
149  deleteLater();
150 }
151 
153 {
155  d->invalidateRequest();
156 }
157 
159 {
161  d->writeCount(count);
162 }
163 
165 {
167  d->parseRequest();
168 }
169 
170 void QHttpConnection::write(const QByteArray &data)
171 {
173  d->write(data);
174 }
175 
177 {
179  d->m_socket->flush();
180 }
181 
183 {
185  d->m_socket->waitForBytesWritten();
186 }
187 
189 {
191  QHttpResponse *response = TTKObjectCast(QHttpResponse*, sender());
192  if(response->isLast())
193  {
194  d->m_socket->disconnectFromHost();
195  }
196 }
197 
198 /* URL Utilities */
199 #define HAS_URL_FIELD(info, field) (info.field_set &(1 << (field)))
200 
201 #define GET_FIELD(data, info, field) \
202  QString::fromLatin1(data + info.field_data[field].off, info.field_data[field].len)
203 
204 #define CHECK_AND_GET_FIELD(data, info, field) \
205  (HAS_URL_FIELD(info, field) ? GET_FIELD(data, info, field) : QString())
206 
207 QUrl createUrl(const char *urlData, const http_parser_url &urlInfo)
208 {
209  QUrl url;
210  url.setScheme(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_SCHEMA));
211  url.setHost(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_HOST));
212  // Port is dealt with separately since it is available as an integer.
213 #if TTK_QT_VERSION_CHECK(5,0,0)
214  url.setPath(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_PATH), QUrl::TolerantMode);
215  url.setQuery(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_QUERY));
216 #else
217  url.setPath(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_PATH));
218  if(HAS_URL_FIELD(urlInfo, UF_QUERY))
219  {
220  url.setEncodedQuery(QByteArray(urlData + urlInfo.field_data[UF_QUERY].off, urlInfo.field_data[UF_QUERY].len));
221  }
222 #endif
223  url.setFragment(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_FRAGMENT));
224  url.setUserInfo(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_USERINFO));
225 
226  if(HAS_URL_FIELD(urlInfo, UF_PORT))
227  {
228  url.setPort(urlInfo.port);
229  }
230 
231  return url;
232 }
233 
234 #undef CHECK_AND_SET_FIELD
235 #undef GET_FIELD
236 #undef HAS_URL_FIELD
237 
238 /********************
239  * Static Callbacks *
240  *******************/
241 
243 {
245  theConnection->m_currentHeaders.clear();
246  theConnection->m_currentUrl.clear();
247  theConnection->m_currentUrl.reserve(128);
248 
249  // The QHttpRequest should not be parented to this, since it's memory
250  // management is the responsibility of the user of the library.
251  theConnection->m_request = new QHttpRequest(theConnection->m_parent);
252 
253  // Invalidate the request when it is deleted to prevent keep-alive requests
254  // from calling a signal on a deleted object.
255  QObject::connect(theConnection->m_request, SIGNAL(destroyed(QObject*)), theConnection->m_parent, SLOT(invalidateRequest()));
256 
257  return 0;
258 }
259 
261 {
263  Q_ASSERT(theConnection->m_request);
264 
267 
269  theConnection->m_request->setVersion(QString("%1.%2").arg(parser->http_major).arg(parser->http_minor));
270 
272  struct http_parser_url urlInfo;
273  int r = http_parser_parse_url(theConnection->m_currentUrl.constData(),
274  theConnection->m_currentUrl.length(),
275  parser->method == HTTP_CONNECT, &urlInfo);
276  Q_ASSERT(r == 0);
277  Q_UNUSED(r);
278 
279  theConnection->m_request->setUrl(createUrl(theConnection->m_currentUrl.constData(), urlInfo));
280 
281  // Insert last remaining header
282  theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] = theConnection->m_currentHeaderValue;
283  theConnection->m_request->setHeaders(theConnection->m_currentHeaders);
284 
286  theConnection->m_request->setRemoteAddress(theConnection->m_socket->peerAddress().toString());
287  theConnection->m_request->setRemotePort(theConnection->m_socket->peerPort());
288 
289  QHttpResponse *response = new QHttpResponse(theConnection->m_parent);
290  if(parser->http_major < 1 || parser->http_minor < 1)
291  {
292  response->setKeepAlive(false);
293  }
294 
295  QObject::connect(theConnection->m_parent, SIGNAL(destroyed()), response, SLOT(connectionClosed()));
296  QObject::connect(response, SIGNAL(done()), theConnection->m_parent, SLOT(responseDone()));
297 
298  // we are good to go!
299  Q_EMIT theConnection->m_parent->newRequest(theConnection->m_request, response);
300  return 0;
301 }
302 
304 {
305  // TODO: do cleanup and prepare for next request
307  Q_ASSERT(theConnection->m_request);
308 
309  theConnection->m_request->setSuccessful(true);
310  Q_EMIT theConnection->m_request->end();
311  return 0;
312 }
313 
314 int QHttpConnectionPrivate::Url(http_parser *parser, const char *at, size_t length)
315 {
317  Q_ASSERT(theConnection->m_request);
318 
319  theConnection->m_currentUrl.append(at, length);
320  return 0;
321 }
322 
323 int QHttpConnectionPrivate::HeaderField(http_parser *parser, const char *at, size_t length)
324 {
326  Q_ASSERT(theConnection->m_request);
327 
328  // insert the header we parsed previously
329  // into the header map
330  if(!theConnection->m_currentHeaderField.isEmpty() && !theConnection->m_currentHeaderValue.isEmpty())
331  {
332  // header names are always lower-cased
333  theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] = theConnection->m_currentHeaderValue;
334  // clear header value. this sets up a nice
335  // feedback loop where the next time
336  // HeaderValue is called, it can simply append
337  theConnection->m_currentHeaderField = QString();
338  theConnection->m_currentHeaderValue = QString();
339  }
340 
341  QString fieldSuffix = QString::fromLatin1(at, length);
342  theConnection->m_currentHeaderField += fieldSuffix;
343  return 0;
344 }
345 
346 int QHttpConnectionPrivate::HeaderValue(http_parser *parser, const char *at, size_t length)
347 {
349  Q_ASSERT(theConnection->m_request);
350 
351  QString valueSuffix = QString::fromLatin1(at, length);
352  theConnection->m_currentHeaderValue += valueSuffix;
353  return 0;
354 }
355 
356 int QHttpConnectionPrivate::Body(http_parser *parser, const char *at, size_t length)
357 {
359  Q_ASSERT(theConnection->m_request);
360 
361  Q_EMIT theConnection->m_request->data(QByteArray(at, length));
362  return 0;
363 }
static int HeaderField(http_parser *parser, const char *at, size_t length)
HttpMethod
Request method enumeration.
Definition: qhttprequest.h:53
#define TTKStaticCast(x, y)
Definition: ttkglobal.h:231
QHttpConnection * m_parent
The class of the http response.
Definition: qhttpresponse.h:29
The class of the http connection.
The class of the http connection private.
unsigned int method
Definition: http_parser.h:221
void write(const QByteArray &data)
void setUrl(const QUrl &url)
bool isLast() const
http_data_cb on_header_field
Definition: http_parser.h:240
http_cb on_headers_complete
Definition: http_parser.h:242
unsigned short http_minor
Definition: http_parser.h:219
void writeCount(qint64 count)
QHttpConnection(QTcpSocket *socket, QObject *parent=nullptr)
void setKeepAlive(bool alive)
void newRequest(QHttpRequest *request, QHttpResponse *response)
void setHeaders(const HeaderHash headers)
size_t http_parser_execute(http_parser *parser, const http_parser_settings *settings, const char *data, size_t len)
Definition: http_parser.c:626
static int MessageComplete(http_parser *parser)
void setRemotePort(quint16 port)
http_data_cb on_body
Definition: http_parser.h:243
The class of the http request.
Definition: qhttprequest.h:32
static int HeaderValue(http_parser *parser, const char *at, size_t length)
void data(const QByteArray &data)
Emitted when new body data has been received.
http_data_cb on_url
Definition: http_parser.h:238
void end()
Emitted when the request has been fully received.
void setSuccessful(bool success)
int http_parser_parse_url(const char *buf, size_t buflen, int is_connect, struct http_parser_url *u)
Definition: http_parser.c:2307
void setVersion(const QString &version)
bool successful() const
If this request was successfully received.
unsigned short http_major
READ-ONLY.
Definition: http_parser.h:218
void setRemoteAddress(const QString &address)
void setMethod(HttpMethod method)
void write(const QByteArray &data)
void allBytesWritten()
struct http_parser_url::@3 field_data[UF_MAX]
void http_parser_init(http_parser *parser, enum http_parser_type t)
Definition: http_parser.c:2138
http_data_cb on_header_value
Definition: http_parser.h:241
QHash< QString, QString > HeaderHash
void * data
PUBLIC.
Definition: http_parser.h:232
static int HeadersComplete(http_parser *parser)
QUrl createUrl(const char *urlData, const http_parser_url &urlInfo)
void updateWriteCount(qint64)
void free(voidpf ptr)
static int Url(http_parser *parser, const char *at, size_t length)
#define TTK_INIT_PRIVATE(Class)
Definition: ttkprivate.h:33
#define CHECK_AND_GET_FIELD(data, info, field)
#define HAS_URL_FIELD(info, field)
The class of the ttk private base.
Definition: ttkprivate.h:48
voidp malloc(uInt size)
static int MessageBegin(http_parser *parser)
struct http_parser_settings http_parser_settings
Definition: http_parser.h:67
static int Body(http_parser *parser, const char *at, size_t length)
http_cb on_message_complete
Definition: http_parser.h:244
http_parser_settings * m_parserSettings
#define TTK_D(Class)
Definition: ttkprivate.h:41
#define TTKObjectCast(x, y)
Definition: ttkqtglobal.h:82