TTKMusicPlayer  3.7.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 {
145  deleteLater();
147 }
148 
150 {
152  d->invalidateRequest();
153 }
154 
156 {
158  d->writeCount(count);
159 }
160 
162 {
164  d->parseRequest();
165 }
166 
167 void QHttpConnection::write(const QByteArray &data)
168 {
170  d->write(data);
171 }
172 
174 {
176  d->m_socket->flush();
177 }
178 
180 {
182  d->m_socket->waitForBytesWritten();
183 }
184 
186 {
188  QHttpResponse *response = TTKObjectCast(QHttpResponse*, sender());
189  if(response->isLast())
190  {
191  d->m_socket->disconnectFromHost();
192  }
193 }
194 
195 /* URL Utilities */
196 #define HAS_URL_FIELD(info, field) (info.field_set &(1 << (field)))
197 
198 #define GET_FIELD(data, info, field) \
199  QString::fromLatin1(data + info.field_data[field].off, info.field_data[field].len)
200 
201 #define CHECK_AND_GET_FIELD(data, info, field) \
202  (HAS_URL_FIELD(info, field) ? GET_FIELD(data, info, field) : QString())
203 
204 QUrl createUrl(const char *urlData, const http_parser_url &urlInfo)
205 {
206  QUrl url;
207  url.setScheme(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_SCHEMA));
208  url.setHost(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_HOST));
209  // Port is dealt with separately since it is available as an integer.
210 #if TTK_QT_VERSION_CHECK(5,0,0)
211  url.setPath(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_PATH), QUrl::TolerantMode);
212  url.setQuery(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_QUERY));
213 #else
214  url.setPath(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_PATH));
215  if(HAS_URL_FIELD(urlInfo, UF_QUERY))
216  {
217  url.setEncodedQuery(QByteArray(urlData + urlInfo.field_data[UF_QUERY].off, urlInfo.field_data[UF_QUERY].len));
218  }
219 #endif
220  url.setFragment(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_FRAGMENT));
221  url.setUserInfo(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_USERINFO));
222 
223  if(HAS_URL_FIELD(urlInfo, UF_PORT))
224  {
225  url.setPort(urlInfo.port);
226  }
227 
228  return url;
229 }
230 
231 #undef CHECK_AND_SET_FIELD
232 #undef GET_FIELD
233 #undef HAS_URL_FIELD
234 
235 /********************
236  * Static Callbacks *
237  *******************/
238 
240 {
242  theConnection->m_currentHeaders.clear();
243  theConnection->m_currentUrl.clear();
244  theConnection->m_currentUrl.reserve(128);
245 
246  // The QHttpRequest should not be parented to this, since it's memory
247  // management is the responsibility of the user of the library.
248  theConnection->m_request = new QHttpRequest(theConnection->m_parent);
249 
250  // Invalidate the request when it is deleted to prevent keep-alive requests
251  // from calling a signal on a deleted object.
252  QObject::connect(theConnection->m_request, SIGNAL(destroyed(QObject*)), theConnection->m_parent, SLOT(invalidateRequest()));
253 
254  return 0;
255 }
256 
258 {
260  Q_ASSERT(theConnection->m_request);
261 
264 
266  theConnection->m_request->setVersion(QString("%1.%2").arg(parser->http_major).arg(parser->http_minor));
267 
269  struct http_parser_url urlInfo;
270  int r = http_parser_parse_url(theConnection->m_currentUrl.constData(),
271  theConnection->m_currentUrl.length(),
272  parser->method == HTTP_CONNECT, &urlInfo);
273  Q_ASSERT(r == 0);
274  Q_UNUSED(r);
275 
276  theConnection->m_request->setUrl(createUrl(theConnection->m_currentUrl.constData(), urlInfo));
277 
278  // Insert last remaining header
279  theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] = theConnection->m_currentHeaderValue;
280  theConnection->m_request->setHeaders(theConnection->m_currentHeaders);
281 
283  theConnection->m_request->setRemoteAddress(theConnection->m_socket->peerAddress().toString());
284  theConnection->m_request->setRemotePort(theConnection->m_socket->peerPort());
285 
286  QHttpResponse *response = new QHttpResponse(theConnection->m_parent);
287  if(parser->http_major < 1 || parser->http_minor < 1)
288  {
289  response->setKeepAlive(false);
290  }
291 
292  QObject::connect(theConnection->m_parent, SIGNAL(destroyed()), response, SLOT(connectionClosed()));
293  QObject::connect(response, SIGNAL(done()), theConnection->m_parent, SLOT(responseDone()));
294 
295  // we are good to go!
296  Q_EMIT theConnection->m_parent->newRequest(theConnection->m_request, response);
297  return 0;
298 }
299 
301 {
302  // TODO: do cleanup and prepare for next request
304  Q_ASSERT(theConnection->m_request);
305 
306  theConnection->m_request->setSuccessful(true);
307  Q_EMIT theConnection->m_request->end();
308  return 0;
309 }
310 
311 int QHttpConnectionPrivate::Url(http_parser *parser, const char *at, size_t length)
312 {
314  Q_ASSERT(theConnection->m_request);
315 
316  theConnection->m_currentUrl.append(at, length);
317  return 0;
318 }
319 
320 int QHttpConnectionPrivate::HeaderField(http_parser *parser, const char *at, size_t length)
321 {
323  Q_ASSERT(theConnection->m_request);
324 
325  // insert the header we parsed previously
326  // into the header map
327  if(!theConnection->m_currentHeaderField.isEmpty() && !theConnection->m_currentHeaderValue.isEmpty())
328  {
329  // header names are always lower-cased
330  theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] = theConnection->m_currentHeaderValue;
331  // clear header value. this sets up a nice
332  // feedback loop where the next time
333  // HeaderValue is called, it can simply append
334  theConnection->m_currentHeaderField = QString();
335  theConnection->m_currentHeaderValue = QString();
336  }
337 
338  QString fieldSuffix = QString::fromLatin1(at, length);
339  theConnection->m_currentHeaderField += fieldSuffix;
340  return 0;
341 }
342 
343 int QHttpConnectionPrivate::HeaderValue(http_parser *parser, const char *at, size_t length)
344 {
346  Q_ASSERT(theConnection->m_request);
347 
348  QString valueSuffix = QString::fromLatin1(at, length);
349  theConnection->m_currentHeaderValue += valueSuffix;
350  return 0;
351 }
352 
353 int QHttpConnectionPrivate::Body(http_parser *parser, const char *at, size_t length)
354 {
356  Q_ASSERT(theConnection->m_request);
357 
358  Q_EMIT theConnection->m_request->data(QByteArray(at, length));
359  return 0;
360 }
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:159
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()
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_url::@1 field_data[UF_MAX]
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:60