Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qhttpnetworkreply.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6
7#ifndef QT_NO_SSL
8# include <QtNetwork/qsslkey.h>
9# include <QtNetwork/qsslcipher.h>
10# include <QtNetwork/qsslconfiguration.h>
11#endif
12
13#include <private/qdecompresshelper_p.h>
14
16
17using namespace Qt::StringLiterals;
18
21{
22}
23
25{
27 if (d->connection) {
28 d->connection->d_func()->removeReply(this);
29 }
30}
31
33{
34 return d_func()->url;
35}
37{
39 d->url = url;
40}
41
43{
44 return d_func()->redirectUrl;
45}
46
48{
50 d->redirectUrl = url;
51}
52
54{
55 return (statusCode == 301 || statusCode == 302 || statusCode == 303
56 || statusCode == 305 || statusCode == 307 || statusCode == 308);
57}
58
60{
61 return d_func()->contentLength();
62}
63
65{
67 d->setContentLength(length);
68}
69
71{
72 return d_func()->parser.headers();
73}
74
76{
77 return d_func()->headerField(name, defaultValue);
78}
79
81{
83 d->setHeaderField(name, data);
84}
85
87{
89 d->appendHeaderField(name, data);
90}
91
93{
95 d->parseHeader(header);
96}
97
99{
100 return d_func()->request;
101}
102
104{
106 d->request = request;
107 d->ssl = request.isSsl();
108}
109
111{
112 return d_func()->parser.getStatusCode();
113}
114
116{
118 d->parser.setStatusCode(code);
119}
120
122{
123 return d_func()->errorString;
124}
125
127{
128 return d_func()->httpErrorCode;
129}
130
132{
133 return d_func()->parser.getReasonPhrase();
134}
135
137{
138 d_func()->parser.setReasonPhrase(reason);
139}
140
142{
144 d->errorString = error;
145}
146
148{
149 return d_func()->parser.getMajorVersion();
150}
151
153{
154 return d_func()->parser.getMinorVersion();
155}
156
158{
159 d_func()->parser.setMajorVersion(version);
160}
161
163{
164 d_func()->parser.setMinorVersion(version);
165}
166
168{
169 Q_D(const QHttpNetworkReply);
170 if (d->connection)
171 return d->connection->d_func()->uncompressedBytesAvailable(*this);
172 else
173 return -1;
174}
175
177{
178 Q_D(const QHttpNetworkReply);
179 if (d->connection)
180 return d->connection->d_func()->uncompressedBytesAvailableNextBlock(*this);
181 else
182 return -1;
183}
184
186{
187 Q_D(const QHttpNetworkReply);
188 return (d->responseData.bufferCount() > 0);
189}
190
192{
194 if (d->responseData.bufferCount() == 0)
195 return QByteArray();
196
197 // we'll take the last buffer, so schedule another read from http
198 if (d->downstreamLimited && d->responseData.bufferCount() == 1 && !isFinished())
199 d->connection->d_func()->readMoreLater(this);
200 return d->responseData.read();
201}
202
204{
206 return d->responseData.readAll();
207}
208
210{
212 return d->responseData.read(amount);
213}
214
215
217{
219 return d->responseData.sizeNextBlock();
220}
221
223{
225 d->downstreamLimited = dsl;
226 d->connection->d_func()->readMoreLater(this);
227}
228
230{
232 d->readBufferMaxSize = size;
233}
234
236{
238 return !d->isChunked() && !d->autoDecompress &&
239 d->bodyLength > 0 && d->parser.getStatusCode() == 200;
240}
241
243{
246 d->userProvidedDownloadBuffer = b;
247}
248
250{
252 return d->userProvidedDownloadBuffer;
253}
254
256{
259}
260
262{
263 return d_func()->state == QHttpNetworkReplyPrivate::Aborted;
264}
265
267{
268 return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
269}
270
272{
273 return d_func()->pipeliningUsed;
274}
275
277{
278 return d_func()->h2Used;
279}
280
282{
283 d_func()->h2Used = h2;
284}
285
287{
288 return d_func()->removedContentLength;
289}
290
292{
293 return d_func()->isRedirecting();
294}
295
297{
298 return d_func()->connection;
299}
300
301
304 , state(NothingDoneState)
305 , ssl(false),
306 bodyLength(0), contentRead(0), totalProgress(0),
307 chunkedTransferEncoding(false),
308 connectionCloseEnabled(true),
309 forceConnectionCloseEnabled(false),
310 lastChunkRead(false),
311 currentChunkSize(0), currentChunkRead(0), readBufferMaxSize(0),
312 totallyUploadedData(0),
313 removedContentLength(-1),
315 autoDecompress(false), responseData(), requestIsPrepared(false)
316 ,pipeliningUsed(false), h2Used(false), downstreamLimited(false)
317 ,userProvidedDownloadBuffer(nullptr)
318
319{
320 QString scheme = newUrl.scheme();
321 if (scheme == "preconnect-http"_L1 || scheme == "preconnect-https"_L1)
322 // make sure we do not close the socket after preconnecting
324}
325
327
329{
331 bodyLength = 0;
332 contentRead = 0;
333 totalProgress = 0;
336 lastChunkRead = false;
338 parser.clear();
339}
340
341// TODO: Isn't everything HTTP layer related? We don't need to set connection and connectionChannel to 0 at all
343{
344 connection = nullptr;
345 connectionChannel = nullptr;
346 autoDecompress = false;
348}
349
350// QHttpNetworkReplyPrivate
352{
353 return (state != ReadingDataState ? 0 : fragment.size());
354}
355
357{
358 return QDecompressHelper::isSupportedEncoding(headerField("content-encoding"));
359}
360
362{
363 Q_D(const QHttpNetworkReply);
364 return d->isCompressed();
365}
366
368{
369 // The header "Content-Encoding = gzip" is retained.
370 // Content-Length is removed since the actual one sent by the server is for compressed data
371 QByteArray name("content-length");
373 bool parseOk = false;
374 qint64 value = contentLength.toLongLong(&parseOk);
375 if (parseOk) {
378 }
379}
380
381bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
382{
383 challenge.clear();
384 // find out the type of authentication protocol requested.
385 QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
386 // pick the best protocol (has to match parsing in QAuthenticatorPrivate)
388 for (int i = 0; i<challenges.size(); i++) {
389 QByteArray line = challenges.at(i);
390 // todo use qstrincmp
391 if (!line.toLower().startsWith("negotiate"))
392 challenge = line;
393 }
394 return !challenge.isEmpty();
395}
396
398{
399 if (fragment.isEmpty()) {
400 // reserve bytes for the status line. This is better than always append() which reallocs the byte array
401 fragment.reserve(32);
402 }
403
404 qint64 bytes = 0;
405 char c;
406 qint64 haveRead = 0;
407
408 do {
409 haveRead = socket->read(&c, 1);
410 if (haveRead == -1)
411 return -1; // unexpected EOF
412 else if (haveRead == 0)
413 break; // read more later
414 else if (haveRead == 1 && fragment.size() == 0 && (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31))
415 continue; // Ignore all whitespace that was trailing froma previous request on that socket
416
417 bytes++;
418
419 // allow both CRLF & LF (only) line endings
420 if (c == '\n') {
421 // remove the CR at the end
422 if (fragment.endsWith('\r')) {
424 }
425 bool ok = parseStatus(fragment);
427 fragment.clear();
428 if (!ok) {
429 return -1;
430 }
431 break;
432 } else {
434 }
435
436 // is this a valid reply?
437 if (fragment.size() == 5 && !fragment.startsWith("HTTP/")) {
438 fragment.clear();
439 return -1;
440 }
441 } while (haveRead == 1);
442
443 return bytes;
444}
445
447{
448 return parser.parseStatus(status);
449}
450
452{
453 if (fragment.isEmpty()) {
454 // according to http://dev.opera.com/articles/view/mama-http-headers/ the average size of the header
455 // block is 381 bytes.
456 // reserve bytes. This is better than always append() which reallocs the byte array.
457 fragment.reserve(512);
458 }
459
460 qint64 bytes = 0;
461 char c = 0;
462 bool allHeaders = false;
463 qint64 haveRead = 0;
464 do {
465 haveRead = socket->read(&c, 1);
466 if (haveRead == 0) {
467 // read more later
468 break;
469 } else if (haveRead == -1) {
470 // connection broke down
471 return -1;
472 } else {
474 bytes++;
475
476 if (c == '\n') {
477 // check for possible header endings. As per HTTP rfc,
478 // the header endings will be marked by CRLFCRLF. But
479 // we will allow CRLFCRLF, CRLFLF, LFCRLF, LFLF
480 if (fragment.endsWith("\n\r\n")
481 || fragment.endsWith("\n\n"))
482 allHeaders = true;
483
484 // there is another case: We have no headers. Then the fragment equals just the line ending
485 if ((fragment.size() == 2 && fragment.endsWith("\r\n"))
486 || (fragment.size() == 1 && fragment.endsWith("\n")))
487 allHeaders = true;
488 }
489 }
490 } while (!allHeaders && haveRead > 0);
491
492 // we received all headers now parse them
493 if (allHeaders) {
496 fragment.clear(); // next fragment
497 bodyLength = contentLength(); // cache the length
498
499 // cache isChunked() since it is called often
500 chunkedTransferEncoding = headerField("transfer-encoding").toLower().contains("chunked");
501
502 // cache isConnectionCloseEnabled since it is called often
503 QByteArray connectionHeaderField = headerField("connection");
504 // check for explicit indication of close or the implicit connection close of HTTP/1.0
505 connectionCloseEnabled = (connectionHeaderField.toLower().contains("close") ||
506 headerField("proxy-connection").toLower().contains("close")) ||
507 (parser.getMajorVersion() == 1 && parser.getMinorVersion() == 0 &&
508 (connectionHeaderField.isEmpty() && !headerField("proxy-connection").toLower().contains("keep-alive")));
509 }
510 return bytes;
511}
512
514{
516}
517
519{
521}
522
524{
526}
527
529{
531}
532
533// note this function can only be used for non-chunked, non-compressed with
534// known content length
536{
537 // This first read is to flush the buffer inside the socket
538 qint64 haveRead = 0;
539 haveRead = socket->read(b, bodyLength - contentRead);
540 if (haveRead == -1) {
541 return -1;
542 }
543 contentRead += haveRead;
544
545 if (contentRead == bodyLength) {
547 }
548
549 return haveRead;
550}
551
552// note this function can only be used for non-chunked, non-compressed with
553// known content length
555{
556
559 toBeRead = qMin(toBeRead, readBufferMaxSize);
560
561 if (!toBeRead)
562 return 0;
563
564 QByteArray bd;
565 bd.resize(toBeRead);
566 qint64 haveRead = socket->read(bd.data(), toBeRead);
567 if (haveRead == -1) {
568 bd.clear();
569 return 0; // ### error checking here;
570 }
571 bd.resize(haveRead);
572
573 rb->append(bd);
574
575 if (contentRead + haveRead == bodyLength) {
577 }
578
579 contentRead += haveRead;
580 return haveRead;
581}
582
583
585{
586 qint64 bytes = 0;
587
588 if (isChunked()) {
589 // chunked transfer encoding (rfc 2616, sec 3.6)
591 } else if (bodyLength > 0) {
592 // we have a Content-Length
594 if (contentRead + bytes == bodyLength)
596 } else {
597 // no content length. just read what's possible
599 }
600 contentRead += bytes;
601 return bytes;
602}
603
605{
606 // FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable()
607 qint64 bytes = 0;
609 Q_ASSERT(out);
610
611 int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
612
614 toBeRead = qMin<qint64>(toBeRead, readBufferMaxSize);
615
616 while (toBeRead > 0) {
617 QByteArray byteData;
618 byteData.resize(toBeRead);
619 qint64 haveRead = socket->read(byteData.data(), byteData.size());
620 if (haveRead <= 0) {
621 // ### error checking here
622 byteData.clear();
623 return bytes;
624 }
625
626 byteData.resize(haveRead);
627 out->append(byteData);
628 bytes += haveRead;
629 size -= haveRead;
630
631 toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
632 }
633 return bytes;
634
635}
636
638{
639 qint64 bytes = 0;
640 while (socket->bytesAvailable()) {
641
642 if (readBufferMaxSize && (bytes > readBufferMaxSize))
643 break;
644
646 // For the first chunk and when we're done with a chunk
649 if (bytes) {
650 // After a chunk
651 char crlf[2];
652 // read the "\r\n" after the chunk
653 qint64 haveRead = socket->read(crlf, 2);
654 // FIXME: This code is slightly broken and not optimal. What if the 2 bytes are not available yet?!
655 // For nice reasons (the toLong in getChunkSize accepting \n at the beginning
656 // it right now still works, but we should definitely fix this.
657
658 if (haveRead != 2)
659 return bytes; // FIXME
660 bytes += haveRead;
661 }
662 // Note that chunk size gets stored in currentChunkSize, what is returned is the bytes read
664 if (currentChunkSize == -1)
665 break;
666 }
667 // if the chunk size is 0, end of the stream
668 if (currentChunkSize == 0 || lastChunkRead) {
669 lastChunkRead = true;
670 // try to read the "\r\n" after the chunk
671 char crlf[2];
672 qint64 haveRead = socket->read(crlf, 2);
673 if (haveRead > 0)
674 bytes += haveRead;
675
676 if ((haveRead == 2 && crlf[0] == '\r' && crlf[1] == '\n') || (haveRead == 1 && crlf[0] == '\n'))
678 else if (haveRead == 1 && crlf[0] == '\r')
679 break; // Still waiting for the last \n
680 else if (haveRead > 0) {
681 // If we read something else then CRLF, we need to close the channel.
684 }
685 break;
686 }
687
688 // otherwise, try to begin reading this chunk / to read what is missing for this chunk
690 currentChunkRead += haveRead;
691 bytes += haveRead;
692
693 // ### error checking here
694
695 }
696 return bytes;
697}
698
700{
701 qint64 bytes = 0;
702 char crlf[2];
703 *chunkSize = -1;
704
706 // FIXME rewrite to permanent loop without bytesAvailable
707 while (bytesAvailable > bytes) {
708 qint64 sniffedBytes = socket->peek(crlf, 2);
709 int fragmentSize = fragment.size();
710
711 // check the next two bytes for a "\r\n", skip blank lines
712 if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n')
713 ||(fragmentSize > 1 && fragment.endsWith('\r') && crlf[0] == '\n'))
714 {
715 bytes += socket->read(crlf, 1); // read the \r or \n
716 if (crlf[0] == '\r')
717 bytes += socket->read(crlf, 1); // read the \n
718 bool ok = false;
719 // ignore the chunk-extension
721 *chunkSize = fragment.toLong(&ok, 16);
722 fragment.clear();
723 break; // size done
724 } else {
725 // read the fragment to the buffer
726 char c = 0;
727 qint64 haveRead = socket->read(&c, 1);
728 if (haveRead < 0) {
729 return -1; // FIXME
730 }
731 bytes += haveRead;
733 }
734 }
735
736 return bytes;
737}
738
740{
741 // We're in the process of redirecting - if the HTTP status code says so and
742 // followRedirect is switched on
745}
746
748{
749 // for 401 & 407 don't emit the data signals. Content along with these
750 // responses are sent only if the authentication fails.
751 return parser.getStatusCode() != 401 && parser.getStatusCode() != 407;
752}
753
755{
756 int statusCode = parser.getStatusCode();
757 // check whether we can expect content after the headers (rfc 2616, sec4.4)
758 if ((statusCode >= 100 && statusCode < 200)
759 || statusCode == 204 || statusCode == 304)
760 return false;
762 return false; // no body expected for HEAD request
763 qint64 expectedContentLength = contentLength();
764 if (expectedContentLength == 0)
765 return false;
766 if (expectedContentLength == -1 && bodyLength == 0) {
767 // The content-length header was stripped, but its value was 0.
768 // This would be the case for an explicitly zero-length compressed response.
769 return false;
770 }
771 return true;
772}
773
775{
777}
778
779
780// SSL support below
781#ifndef QT_NO_SSL
782
784{
785 Q_D(const QHttpNetworkReply);
786
787 if (!d->connectionChannel)
788 return QSslConfiguration();
789
790 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(d->connectionChannel->socket);
791 if (!sslSocket)
792 return QSslConfiguration();
793
794 return sslSocket->sslConfiguration();
795}
796
798{
800 if (d->connection)
801 d->connection->setSslConfiguration(config);
802}
803
805{
807 if (d->connection)
808 d->connection->ignoreSslErrors();
809}
810
812{
814 if (d->connection)
815 d->connection->ignoreSslErrors(errors);
816}
817
818
819#endif //QT_NO_SSL
820
821
823
824#include "moc_qhttpnetworkreply_p.cpp"
The QAbstractSocket class provides the base functionality common to all socket types.
qint64 bytesAvailable() const override
Returns the number of incoming bytes that are waiting to be read.
\inmodule QtCore
Definition qbytearray.h:57
QByteArray trimmed() const &
Definition qbytearray.h:198
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
bool endsWith(char c) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qbytearray.h:174
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
void reserve(qsizetype size)
Attempts to allocate memory for at least size bytes.
Definition qbytearray.h:557
qsizetype indexOf(char c, qsizetype from=0) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
long toLong(bool *ok=nullptr, int base=10) const
void truncate(qsizetype pos)
Truncates the byte array at index position pos.
bool startsWith(QByteArrayView bv) const
Definition qbytearray.h:170
bool contains(char c) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qbytearray.h:583
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
void clear()
Clears the contents of the byte array and makes it null.
QByteArray toLower() const &
Definition qbytearray.h:190
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QByteArray mid(qsizetype index, qsizetype len=-1) const
Returns a byte array containing len bytes from this byte array, starting at position pos.
void append(const QByteDataBuffer &other)
Definition qbytedata_p.h:48
static bool isSupportedEncoding(const QByteArray &encoding)
void appendHeaderField(const QByteArray &name, const QByteArray &data)
bool parseHeaders(QByteArrayView headers)
bool parseStatus(QByteArrayView status)
void removeHeaderField(const QByteArray &name)
QByteArray firstHeaderField(const QByteArray &name, const QByteArray &defaultValue=QByteArray()) const
QList< QByteArray > headerFieldValues(const QByteArray &name) const
QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue=QByteArray()) const
bool findChallenge(bool forProxy, QByteArray &challenge) const
qint64 readReplyBodyRaw(QAbstractSocket *in, QByteDataBuffer *out, qint64 size)
QPointer< QHttpNetworkConnection > connection
qint64 readBodyVeryFast(QAbstractSocket *socket, char *b)
enum QHttpNetworkReplyPrivate::ReplyState state
QPointer< QHttpNetworkConnectionChannel > connectionChannel
qint64 readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
bool parseStatus(const QByteArray &status)
qint64 readStatus(QAbstractSocket *socket)
qint64 readBody(QAbstractSocket *socket, QByteDataBuffer *out)
QHttpNetworkReplyPrivate(const QUrl &newUrl=QUrl())
qint64 getChunkSize(QAbstractSocket *in, qint64 *chunkSize)
qint64 readReplyBodyChunked(QAbstractSocket *in, QByteDataBuffer *out)
void parseHeader(const QByteArray &header)
qint64 readHeader(QAbstractSocket *socket)
void appendHeaderField(const QByteArray &name, const QByteArray &data)
QHttpNetworkConnection * connection()
void setStatusCode(int code)
void setUrl(const QUrl &url) override
QString errorString() const
bool readAnyAvailable() const
qint64 bytesAvailableNextBlock() const
qint64 removedContentLength() const
qint64 bytesAvailable() const
static bool isHttpRedirect(int statusCode)
QByteArray read(qint64 amount)
void setReasonPhrase(const QString &reason)
void parseHeader(const QByteArray &header)
void setErrorString(const QString &error)
bool isPipeliningUsed() const
void setMajorVersion(int version)
void setUserProvidedDownloadBuffer(char *)
void setHttp2WasUsed(bool h2Used)
void setReadBufferSize(qint64 size)
void setSslConfiguration(const QSslConfiguration &config)
QNetworkReply::NetworkError errorCode() const
bool supportsUserProvidedDownloadBuffer()
void setDownstreamLimited(bool t)
QHttpNetworkReply(const QUrl &url=QUrl(), QObject *parent=nullptr)
int minorVersion() const override
void setRequest(const QHttpNetworkRequest &request)
QString reasonPhrase() const
qint64 contentLength() const override
QList< QPair< QByteArray, QByteArray > > header() const override
QSslConfiguration sslConfiguration() const
QHttpNetworkRequest request() const
void setMinorVersion(int version)
void setRedirectUrl(const QUrl &url)
QUrl url() const override
void setHeaderField(const QByteArray &name, const QByteArray &data) override
void setContentLength(qint64 length) override
void appendHeaderField(const QByteArray &name, const QByteArray &data)
QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue=QByteArray()) const override
int majorVersion() const override
Operation operation() const
qint64 peek(char *data, qint64 maxlen)
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
NetworkError
Indicates all possible error conditions found during the processing of the request.
\inmodule QtCore
Definition qobject.h:90
The QSslConfiguration class holds the configuration and state of an SSL connection.
The QSslSocket class provides an SSL encrypted socket for both clients and servers.
Definition qsslsocket.h:29
QSslConfiguration sslConfiguration() const
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
QString toLower() const &
Definition qstring.h:368
\inmodule QtCore
Definition qurl.h:94
QString url(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2814
QString scheme() const
Returns the scheme of the URL.
Definition qurl.cpp:1983
else opt state
[0]
Combined button and popup list for selecting options.
DBusConnection const char DBusError * error
DBusConnection * connection
static QString header(const QString &name)
EGLConfig config
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLboolean GLboolean GLboolean b
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLenum GLsizei length
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
const GLubyte * c
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
long long qint64
Definition qtypes.h:55
QTextStream out(stdout)
[7]
QUrl url("example.com")
[constructor-url-reference]
QObject::connect nullptr
QTcpSocket * socket
[1]
QNetworkRequest request(url)
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent