1 | #include "shttphelper.h" |
---|
2 | |
---|
3 | #include <QBuffer> |
---|
4 | #include <QDebug> |
---|
5 | #include <QSslError> |
---|
6 | #include <QThread> |
---|
7 | #include <QUrl> |
---|
8 | |
---|
9 | #include "exceptions/httpexceptions.h" |
---|
10 | |
---|
11 | SHttpHelper::SHttpHelper() { |
---|
12 | // TODO: Proxy... |
---|
13 | |
---|
14 | http_ = new QHttp(); |
---|
15 | |
---|
16 | connect(http_, SIGNAL(responseHeaderReceived(const QHttpResponseHeader&)), |
---|
17 | this, SLOT(responseHeaderReceived(const QHttpResponseHeader&))); |
---|
18 | connect(http_, SIGNAL(requestFinished(int, bool)), |
---|
19 | this, SLOT(httpRequestFinished(int, bool))); |
---|
20 | connect(http_, SIGNAL(sslErrors(const QList<QSslError>&)), |
---|
21 | this, SLOT(sslErrors(const QList<QSslError>&))); |
---|
22 | |
---|
23 | |
---|
24 | cookieManager_ = new SHttpCookieManager(http_); |
---|
25 | |
---|
26 | defaultHeader_ = new QHttpRequestHeader(); |
---|
27 | defaultHeader_->setContentType("application/x-www-form-urlencoded"); |
---|
28 | defaultHeader_->setValue("user-agent", "Mozilla/3.0 (compatible; SMSSender; http://www.gorrion.ch/p/smssender)"); //TODO: include version, if needed (don't generate the instance here, but in a central bchttpmanager or so) |
---|
29 | } |
---|
30 | |
---|
31 | SHttpHelper::~SHttpHelper() { |
---|
32 | delete defaultHeader_; |
---|
33 | delete cookieManager_; |
---|
34 | delete http_; |
---|
35 | } |
---|
36 | |
---|
37 | |
---|
38 | QHttp* SHttpHelper::http() const { |
---|
39 | return http_; |
---|
40 | } |
---|
41 | SHttpCookieManager* SHttpHelper::cookieManager() const { |
---|
42 | return cookieManager_; |
---|
43 | } |
---|
44 | |
---|
45 | QHttpRequestHeader SHttpHelper::defaultHeader(const QUrl& url) const { |
---|
46 | QHttpRequestHeader req = QHttpRequestHeader(*defaultHeader_); // return a copy, to not get touched by further modifications |
---|
47 | cookieManager_->addCookieToHeader(&req, url); |
---|
48 | |
---|
49 | return req; |
---|
50 | } |
---|
51 | |
---|
52 | QString SHttpHelper::get(const QString& destination, const QMap<QString, QString>& requests /* = QMap<QString, QString>() */) { |
---|
53 | waitForRequest(false); // Wait until a running request has been finished |
---|
54 | |
---|
55 | startRequest("GET", destination, requests, QMap<QString, QString>()); |
---|
56 | waitForRequest(); |
---|
57 | return getRequestResult(); |
---|
58 | } |
---|
59 | |
---|
60 | QString SHttpHelper::post(const QString& destination, const QMap<QString, QString>& requests /* = QMap<QString, QString>() */, |
---|
61 | const QMap<QString, QString>& posts /* = QMap<QString, QString>() */) |
---|
62 | { |
---|
63 | waitForRequest(false); // Wait until a running request has been finished |
---|
64 | |
---|
65 | startRequest("POST", destination, requests, posts); |
---|
66 | waitForRequest(); |
---|
67 | return getRequestResult(); |
---|
68 | } |
---|
69 | |
---|
70 | void SHttpHelper::startRequest(const QString& method, const QString& destination, const QMap<QString, QString>& requests /* = QMap<QString, QString>() */, |
---|
71 | const QMap<QString, QString>& posts /* = QMap<QString, QString>() */) |
---|
72 | { |
---|
73 | requestResult_ = ""; |
---|
74 | |
---|
75 | QUrl url_ = QUrl(destination); |
---|
76 | |
---|
77 | // Add request parameters to url_ |
---|
78 | QMapIterator<QString, QString> i(requests); |
---|
79 | while (i.hasNext()){ |
---|
80 | i.next(); |
---|
81 | url_.addQueryItem(i.key(), i.value()); |
---|
82 | } |
---|
83 | |
---|
84 | QString uri_ = url_.scheme() + "://" + url_.host() + url_.encodedPath() + "?" + url_.encodedQuery(); |
---|
85 | //qDebug() << uri_; |
---|
86 | |
---|
87 | /* Create the request record */ |
---|
88 | request_.finished = false; |
---|
89 | request_.method = method; |
---|
90 | request_.posts = getParamStr(posts); |
---|
91 | |
---|
92 | /* Do the request */ |
---|
93 | doRequest(uri_); |
---|
94 | } |
---|
95 | |
---|
96 | int SHttpHelper::doRequest(const QString& destination) { |
---|
97 | QUrl url_ = QUrl(destination); |
---|
98 | |
---|
99 | /* Connection */ |
---|
100 | QHttp::ConnectionMode mode = url_.scheme().toLower() == "https" ? QHttp::ConnectionModeHttps : QHttp::ConnectionModeHttp; |
---|
101 | http_->setHost(url_.host(), mode, url_.port(0)); |
---|
102 | |
---|
103 | if (!url_.userName().isEmpty()) |
---|
104 | http_->setUser(url_.userName(), url_.password()); |
---|
105 | |
---|
106 | /* Header */ |
---|
107 | QHttpRequestHeader header_ = defaultHeader(url_); |
---|
108 | header_.setValue("Host", url_.host()); |
---|
109 | header_.setValue("Connection", "Keep-Alive"); |
---|
110 | |
---|
111 | QByteArray path_ = url_.encodedPath(); |
---|
112 | if (path_.isEmpty()) |
---|
113 | path_ = "/"; |
---|
114 | |
---|
115 | header_.setRequest(request_.method, path_ + "?" + url_.encodedQuery()); |
---|
116 | |
---|
117 | /* Start the request */ |
---|
118 | //qDebug() << destination; |
---|
119 | //qDebug() << header_.toString(); |
---|
120 | //qDebug() << request_.posts; |
---|
121 | |
---|
122 | request_.httpRequestId = http_->request(header_, request_.posts.toUtf8()/*, req.buffer*/); |
---|
123 | qDebug() << "Started request " + QString::number(request_.httpRequestId) + ": " + request_.method + ": " + url_.path(); |
---|
124 | |
---|
125 | return request_.httpRequestId; |
---|
126 | } |
---|
127 | |
---|
128 | |
---|
129 | QString SHttpHelper::getParamStr(const QMap<QString, QString>& params) { |
---|
130 | QString params_ = ""; |
---|
131 | |
---|
132 | QMapIterator<QString, QString> i(params); |
---|
133 | while (i.hasNext()) { |
---|
134 | i.next(); |
---|
135 | if (i.key().trimmed() != ""){ |
---|
136 | params_ += i.key() + "=" + i.value() + "&"; |
---|
137 | } |
---|
138 | } |
---|
139 | params_.remove(QRegExp("[&]$")); // remove last & |
---|
140 | |
---|
141 | return params_; |
---|
142 | } |
---|
143 | |
---|
144 | /*bool HttpAccountHelper::isRequestFinished(const int& requestId) const { |
---|
145 | return finishedRequests_.contains(requestId); |
---|
146 | }*/ |
---|
147 | |
---|
148 | void SHttpHelper::waitForRequest(bool createLoopIfNotExisting) { |
---|
149 | if (!requestLoop_.isRunning() && !createLoopIfNotExisting){ |
---|
150 | return; |
---|
151 | } |
---|
152 | |
---|
153 | qDebug() << "Waiting for the request to finish..."; |
---|
154 | while (!request_.finished && thread()->isRunning()) { |
---|
155 | thread()->wait(100); |
---|
156 | } |
---|
157 | /* while (!request_.finished && thread()->isRunning()) { |
---|
158 | thread()->wait(100); |
---|
159 | }*/ |
---|
160 | // requestLoop_.exec(QEventLoop::ExcludeUserInputEvents|QEventLoop::ExcludeSocketNotifiers|QEventLoop::WaitForMoreEvents); |
---|
161 | qDebug() << "The request finished -> continuing"; |
---|
162 | /* while (!request_.finished) { |
---|
163 | qApp->processEvents(QEventLoop::Wai)(tForMore | QEventLoop::ExcludeUserInput); |
---|
164 | } |
---|
165 | qDebug() << "Starting request loop"; |
---|
166 | requestLoop_.exec(); |
---|
167 | qDebug() << "Request loop quitted";*/ |
---|
168 | } |
---|
169 | |
---|
170 | QString SHttpHelper::getRequestResult() const { |
---|
171 | return requestResult_; |
---|
172 | } |
---|
173 | |
---|
174 | void SHttpHelper::responseHeaderReceived(const QHttpResponseHeader& responseHeader) { |
---|
175 | //qDebug() << responseHeader.statusCode() << " " << responseHeader.reasonPhrase(); |
---|
176 | //qDebug() << responseHeader.values(); |
---|
177 | |
---|
178 | switch (responseHeader.statusCode()) { |
---|
179 | case 200: |
---|
180 | break; // Ok |
---|
181 | |
---|
182 | case 301: // Moved Permanently |
---|
183 | case 302: // Found |
---|
184 | case 303: // See Other |
---|
185 | case 307: // Temporary Redirect |
---|
186 | qDebug() << "Have to redirect [" + QString::number(request_.httpRequestId) + "]"; |
---|
187 | |
---|
188 | request_.httpRequestId = -1; // To avoid error in httpRequestFinished |
---|
189 | http_->abort(); |
---|
190 | |
---|
191 | // Since the responseHeaderReceived-Slot could eventually not be called yet in cookieManager -> this would lead to a wrong header in doRequest |
---|
192 | cookieManager_->processResponseHeader(responseHeader); |
---|
193 | |
---|
194 | //cookiemanager has not handled the response yet... |
---|
195 | doRequest(responseHeader.value("location")); // Do the redirect |
---|
196 | break; |
---|
197 | |
---|
198 | default: |
---|
199 | throw Http::EReadException("Unknown response code!"); |
---|
200 | break; |
---|
201 | } |
---|
202 | } |
---|
203 | |
---|
204 | void SHttpHelper::httpRequestFinished(int httpRequestId, bool error) { |
---|
205 | if (request_.httpRequestId != httpRequestId) { |
---|
206 | qDebug() << "Request [" + QString::number(httpRequestId) + "] finished (but is not parsed, since it is detached)"; |
---|
207 | return; |
---|
208 | } |
---|
209 | |
---|
210 | qDebug() << "Quitting request loop"; |
---|
211 | requestLoop_.quit(); |
---|
212 | request_.finished = true; |
---|
213 | |
---|
214 | if (error) { |
---|
215 | qDebug() << "Request [" + QString::number(httpRequestId) + "] finished with an error: " + http_->errorString(); |
---|
216 | throw Http::EReadException(http_->errorString()); // TODO: Perhaps this error has to be thrown in the waitForRequest by passing a status value to requestLoop_.exit() |
---|
217 | } else { |
---|
218 | qDebug() << "Request [" + QString::number(httpRequestId) + "] finished"; |
---|
219 | requestResult_ = /*request.buffer.readAll()*/ http_->readAll(); |
---|
220 | //qDebug() << requestResult_; |
---|
221 | } |
---|
222 | |
---|
223 | requestLoop_.processEvents(); |
---|
224 | } |
---|
225 | |
---|
226 | void SHttpHelper::sslErrors(const QList<QSslError>& errors) { |
---|
227 | QListIterator<QSslError> i(errors); |
---|
228 | while (i.hasNext()){ |
---|
229 | QSslError err = i.next(); |
---|
230 | qDebug() << "SSL-error: " + err.errorString(); |
---|
231 | } |
---|
232 | } |
---|