Merge lp:~jamesh/storage-provider-webdav/http-errors into lp:storage-provider-webdav/devel
- http-errors
- Merge into devel
Proposed by
James Henstridge
Status: | Merged |
---|---|
Approved by: | Michi Henning |
Approved revision: | 27 |
Merged at revision: | 19 |
Proposed branch: | lp:~jamesh/storage-provider-webdav/http-errors |
Merge into: | lp:storage-provider-webdav/devel |
Diff against target: |
1740 lines (+710/-185) 33 files modified
src/CMakeLists.txt (+1/-0) src/CopyMoveHandler.cpp (+6/-4) src/CopyMoveHandler.h (+2/-2) src/CreateFolderHandler.cpp (+9/-7) src/CreateFolderHandler.h (+3/-3) src/DavDownloadJob.cpp (+50/-35) src/DavDownloadJob.h (+9/-6) src/DavProvider.cpp (+20/-13) src/DavProvider.h (+3/-0) src/DavUploadJob.cpp (+9/-19) src/DavUploadJob.h (+5/-5) src/DeleteHandler.cpp (+7/-8) src/DeleteHandler.h (+2/-2) src/ListHandler.cpp (+3/-3) src/ListHandler.h (+1/-2) src/LookupHandler.cpp (+1/-1) src/LookupHandler.h (+1/-1) src/MetadataHandler.cpp (+2/-2) src/MetadataHandler.h (+1/-2) src/PropFindHandler.cpp (+52/-26) src/PropFindHandler.h (+9/-4) src/RetrieveMetadataHandler.cpp (+3/-4) src/RetrieveMetadataHandler.h (+1/-2) src/RootsHandler.cpp (+2/-1) src/RootsHandler.h (+1/-1) src/http_error.cpp (+163/-0) src/http_error.h (+12/-0) tests/CMakeLists.txt (+1/-0) tests/davprovider/davprovider_test.cpp (+60/-29) tests/http_error/CMakeLists.txt (+8/-0) tests/http_error/http_error_test.cpp (+260/-0) tests/utils/ProviderEnvironment.cpp (+2/-2) tests/utils/ProviderEnvironment.h (+1/-1) |
To merge this branch: | bzr merge lp:~jamesh/storage-provider-webdav/http-errors |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michi Henning (community) | Approve | ||
unity-api-1-bot | continuous-integration | Approve | |
Review via email: mp+311222@code.launchpad.net |
Commit message
Improve HTTP error handling for all D-Bus methods.
Description of the change
Improve HTTP error handling for all D-Bus methods.
I also changed the various handler classes to store a shared_
To post a comment you must log in.
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote : | # |
review:
Approve
(continuous-integration)
Revision history for this message
Michi Henning (michihenning) wrote : | # |
Looks bloody awesome, thanks!
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/CMakeLists.txt' |
2 | --- src/CMakeLists.txt 2016-11-12 03:15:39 +0000 |
3 | +++ src/CMakeLists.txt 2016-11-18 02:39:30 +0000 |
4 | @@ -4,6 +4,7 @@ |
5 | DavDownloadJob.cpp |
6 | DavUploadJob.cpp |
7 | MultiStatusParser.cpp |
8 | + http_error.cpp |
9 | item_id.cpp |
10 | CopyMoveHandler.cpp |
11 | CreateFolderHandler.cpp |
12 | |
13 | === modified file 'src/CopyMoveHandler.cpp' |
14 | --- src/CopyMoveHandler.cpp 2016-11-14 06:18:18 +0000 |
15 | +++ src/CopyMoveHandler.cpp 2016-11-18 02:39:30 +0000 |
16 | @@ -1,11 +1,12 @@ |
17 | #include "CopyMoveHandler.h" |
18 | #include "RetrieveMetadataHandler.h" |
19 | #include "item_id.h" |
20 | +#include "http_error.h" |
21 | |
22 | using namespace std; |
23 | using namespace unity::storage::provider; |
24 | |
25 | -CopyMoveHandler::CopyMoveHandler(DavProvider const& provider, |
26 | +CopyMoveHandler::CopyMoveHandler(shared_ptr<DavProvider> const& provider, |
27 | string const& item_id, |
28 | string const& new_parent_id, |
29 | string const& new_name, |
30 | @@ -15,7 +16,7 @@ |
31 | new_item_id_(make_child_id(new_parent_id, new_name, is_folder(item_id))), |
32 | context_(ctx) |
33 | { |
34 | - QUrl const base_url = provider.base_url(ctx); |
35 | + QUrl const base_url = provider->base_url(ctx); |
36 | QNetworkRequest request(id_to_url(item_id_, base_url)); |
37 | request.setRawHeader(QByteArrayLiteral("Destination"), |
38 | id_to_url(new_item_id_, base_url).toEncoded()); |
39 | @@ -23,7 +24,7 @@ |
40 | request.setRawHeader(QByteArrayLiteral("Overwrite"), |
41 | QByteArrayLiteral("F")); |
42 | |
43 | - reply_.reset(provider.send_request( |
44 | + reply_.reset(provider->send_request( |
45 | request, copy ? QByteArrayLiteral("COPY") : QByteArrayLiteral("MOVE"), |
46 | nullptr, ctx)); |
47 | connect(reply_.get(), &QNetworkReply::finished, |
48 | @@ -43,7 +44,8 @@ |
49 | |
50 | if (status != 201 && status != 204) |
51 | { |
52 | - promise_.set_exception(RemoteCommsException("Error from request: " + to_string(status))); |
53 | + promise_.set_exception( |
54 | + translate_http_error(reply_.get(), QByteArray(), item_id_)); |
55 | deleteLater(); |
56 | return; |
57 | } |
58 | |
59 | === modified file 'src/CopyMoveHandler.h' |
60 | --- src/CopyMoveHandler.h 2016-11-12 03:15:39 +0000 |
61 | +++ src/CopyMoveHandler.h 2016-11-18 02:39:30 +0000 |
62 | @@ -13,7 +13,7 @@ |
63 | class CopyMoveHandler : public QObject { |
64 | Q_OBJECT |
65 | public: |
66 | - CopyMoveHandler(DavProvider const& provider, |
67 | + CopyMoveHandler(std::shared_ptr<DavProvider> const& provider, |
68 | std::string const& item_id, |
69 | std::string const& new_parent_id, |
70 | std::string const& new_name, |
71 | @@ -29,7 +29,7 @@ |
72 | private: |
73 | boost::promise<unity::storage::provider::Item> promise_; |
74 | |
75 | - DavProvider const& provider_; |
76 | + std::shared_ptr<DavProvider> const provider_; |
77 | std::string const item_id_; |
78 | std::string const new_item_id_; |
79 | unity::storage::provider::Context const context_; |
80 | |
81 | === modified file 'src/CreateFolderHandler.cpp' |
82 | --- src/CreateFolderHandler.cpp 2016-11-12 03:15:39 +0000 |
83 | +++ src/CreateFolderHandler.cpp 2016-11-18 02:39:30 +0000 |
84 | @@ -1,22 +1,23 @@ |
85 | #include "CreateFolderHandler.h" |
86 | #include "RetrieveMetadataHandler.h" |
87 | #include "item_id.h" |
88 | +#include "http_error.h" |
89 | |
90 | using namespace std; |
91 | using namespace unity::storage::provider; |
92 | |
93 | -CreateFolderHandler::CreateFolderHandler(DavProvider const& provider, |
94 | +CreateFolderHandler::CreateFolderHandler(shared_ptr<DavProvider> const& provider, |
95 | string const& parent_id, |
96 | string const& name, |
97 | Context const& ctx) |
98 | : provider_(provider), item_id_(make_child_id(parent_id, name, true)), |
99 | context_(ctx) |
100 | { |
101 | - QUrl const base_url = provider.base_url(ctx); |
102 | + QUrl const base_url = provider->base_url(ctx); |
103 | QNetworkRequest request(id_to_url(item_id_, base_url)); |
104 | - mkcol_.reset(provider.send_request(request, QByteArrayLiteral("MKCOL"), |
105 | - nullptr, ctx)); |
106 | - connect(mkcol_.get(), &QNetworkReply::finished, |
107 | + reply_.reset(provider->send_request(request, QByteArrayLiteral("MKCOL"), |
108 | + nullptr, ctx)); |
109 | + connect(reply_.get(), &QNetworkReply::finished, |
110 | this, &CreateFolderHandler::onFinished); |
111 | } |
112 | |
113 | @@ -29,11 +30,12 @@ |
114 | |
115 | void CreateFolderHandler::onFinished() |
116 | { |
117 | - auto status = mkcol_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
118 | + auto status = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
119 | |
120 | if (status != 201) |
121 | { |
122 | - promise_.set_exception(RemoteCommsException("Error from MKCOL: " + to_string(status))); |
123 | + promise_.set_exception( |
124 | + translate_http_error(reply_.get(), QByteArray(), item_id_)); |
125 | deleteLater(); |
126 | return; |
127 | } |
128 | |
129 | === modified file 'src/CreateFolderHandler.h' |
130 | --- src/CreateFolderHandler.h 2016-11-10 09:41:35 +0000 |
131 | +++ src/CreateFolderHandler.h 2016-11-18 02:39:30 +0000 |
132 | @@ -13,7 +13,7 @@ |
133 | class CreateFolderHandler : public QObject { |
134 | Q_OBJECT |
135 | public: |
136 | - CreateFolderHandler(DavProvider const& provider, |
137 | + CreateFolderHandler(std::shared_ptr<DavProvider> const& provider, |
138 | std::string const& parent_id, std::string const& name, |
139 | unity::storage::provider::Context const& ctx); |
140 | ~CreateFolderHandler(); |
141 | @@ -26,10 +26,10 @@ |
142 | private: |
143 | boost::promise<unity::storage::provider::Item> promise_; |
144 | |
145 | - DavProvider const& provider_; |
146 | + std::shared_ptr<DavProvider> const provider_; |
147 | std::string const item_id_; |
148 | unity::storage::provider::Context const context_; |
149 | |
150 | - std::unique_ptr<QNetworkReply> mkcol_; |
151 | + std::unique_ptr<QNetworkReply> reply_; |
152 | std::unique_ptr<RetrieveMetadataHandler> metadata_; |
153 | }; |
154 | |
155 | === modified file 'src/DavDownloadJob.cpp' |
156 | --- src/DavDownloadJob.cpp 2016-11-15 11:32:58 +0000 |
157 | +++ src/DavDownloadJob.cpp 2016-11-18 02:39:30 +0000 |
158 | @@ -2,6 +2,7 @@ |
159 | #include "DavProvider.h" |
160 | #include "RetrieveMetadataHandler.h" |
161 | #include "item_id.h" |
162 | +#include "http_error.h" |
163 | |
164 | #include <unity/storage/provider/Exceptions.h> |
165 | |
166 | @@ -27,14 +28,14 @@ |
167 | |
168 | } |
169 | |
170 | -DavDownloadJob::DavDownloadJob(DavProvider const& provider, |
171 | +DavDownloadJob::DavDownloadJob(shared_ptr<DavProvider> const& provider, |
172 | string const& item_id, |
173 | string const& match_etag, |
174 | Context const& ctx) |
175 | : QObject(), DownloadJob(make_download_id()), provider_(provider), |
176 | item_id_(item_id) |
177 | { |
178 | - QUrl base_url = provider.base_url(ctx); |
179 | + QUrl base_url = provider->base_url(ctx); |
180 | QNetworkRequest request(id_to_url(item_id, base_url)); |
181 | if (!match_etag.empty()) |
182 | { |
183 | @@ -42,15 +43,15 @@ |
184 | QByteArray::fromStdString(match_etag)); |
185 | } |
186 | |
187 | - reply_ = provider.send_request( |
188 | - request, QByteArrayLiteral("GET"), nullptr, ctx); |
189 | - assert(reply_ != nullptr); |
190 | + reply_.reset(provider->send_request( |
191 | + request, QByteArrayLiteral("GET"), nullptr, ctx)); |
192 | + assert(reply_.get() != nullptr); |
193 | reply_->setReadBufferSize(CHUNK_SIZE); |
194 | - connect(reply_, &QNetworkReply::finished, |
195 | + connect(reply_.get(), &QNetworkReply::finished, |
196 | this, &DavDownloadJob::onReplyFinished); |
197 | - connect(reply_, &QIODevice::readyRead, |
198 | + connect(reply_.get(), &QIODevice::readyRead, |
199 | this, &DavDownloadJob::onReplyReadyRead); |
200 | - connect(reply_, &QIODevice::readChannelFinished, |
201 | + connect(reply_.get(), &QIODevice::readChannelFinished, |
202 | this, &DavDownloadJob::onReplyReadChannelFinished); |
203 | writer_.setSocketDescriptor( |
204 | dup(write_socket()), QLocalSocket::ConnectedState, QIODevice::WriteOnly); |
205 | @@ -58,50 +59,56 @@ |
206 | this, &DavDownloadJob::onSocketBytesWritten); |
207 | } |
208 | |
209 | -DavDownloadJob::~DavDownloadJob() |
210 | -{ |
211 | - if (!reply_.isNull()) |
212 | - { |
213 | - reply_->deleteLater(); |
214 | - } |
215 | -} |
216 | +DavDownloadJob::~DavDownloadJob() = default; |
217 | |
218 | void DavDownloadJob::onReplyFinished() |
219 | { |
220 | - // If we haven't seen HTTP response headers and are in an error |
221 | - // state, report that. |
222 | - if (!seen_header_ && reply_->error() != QNetworkReply::NoError) |
223 | + if (!seen_header_ || is_error_) |
224 | { |
225 | - handle_error(RemoteCommsException("Error connecting to server: " + |
226 | - reply_->errorString().toStdString())); |
227 | + try |
228 | + { |
229 | + boost::rethrow_exception( |
230 | + translate_http_error(reply_.get(), error_body_, item_id_)); |
231 | + } |
232 | + catch (...) |
233 | + { |
234 | + handle_error(std::current_exception()); |
235 | + } |
236 | } |
237 | } |
238 | |
239 | void DavDownloadJob::onReplyReadyRead() |
240 | { |
241 | - if (error_) |
242 | - { |
243 | - return; |
244 | - } |
245 | - |
246 | if (!seen_header_) |
247 | { |
248 | seen_header_ = true; |
249 | auto status = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
250 | if (status != 200) |
251 | { |
252 | - handle_error(RemoteCommsException("Unexpected status code: " + |
253 | - to_string(status))); |
254 | - return; |
255 | + is_error_ = true; |
256 | } |
257 | } |
258 | |
259 | - maybe_send_chunk(); |
260 | + if (is_error_) |
261 | + { |
262 | + if (error_body_.size() < MAX_ERROR_BODY_LENGTH) |
263 | + { |
264 | + error_body_.append(reply_->readAll()); |
265 | + } |
266 | + else |
267 | + { |
268 | + reply_->close(); |
269 | + } |
270 | + } |
271 | + else |
272 | + { |
273 | + maybe_send_chunk(); |
274 | + } |
275 | } |
276 | |
277 | void DavDownloadJob::onReplyReadChannelFinished() |
278 | { |
279 | - if (error_) |
280 | + if (is_error_) |
281 | { |
282 | return; |
283 | } |
284 | @@ -112,7 +119,7 @@ |
285 | |
286 | void DavDownloadJob::onSocketBytesWritten(int64_t bytes) |
287 | { |
288 | - if (error_) |
289 | + if (is_error_) |
290 | { |
291 | return; |
292 | } |
293 | @@ -146,7 +153,8 @@ |
294 | int n_read = reply_->read(buffer, CHUNK_SIZE); |
295 | if (n_read < 0) |
296 | { |
297 | - handle_error(RemoteCommsException("Failed to read from server")); |
298 | + handle_error(RemoteCommsException("Failed to read from server: " + |
299 | + reply_->errorString().toStdString())); |
300 | return; |
301 | } |
302 | bytes_read_ += n_read; |
303 | @@ -154,17 +162,24 @@ |
304 | int n_written = writer_.write(buffer, n_read); |
305 | if (n_written < 0) |
306 | { |
307 | - handle_error(ResourceException("Error writing to socket", 0)); |
308 | + handle_error(ResourceException( |
309 | + "Error writing to socket: " |
310 | + + writer_.errorString().toStdString(), 0)); |
311 | return; |
312 | } |
313 | } |
314 | |
315 | void DavDownloadJob::handle_error(StorageException const& exc) |
316 | { |
317 | - error_ = true; |
318 | + handle_error(std::make_exception_ptr(exc)); |
319 | +} |
320 | + |
321 | +void DavDownloadJob::handle_error(std::exception_ptr ep) |
322 | +{ |
323 | + is_error_ = true; |
324 | reply_->close(); |
325 | writer_.close(); |
326 | - report_error(std::make_exception_ptr(exc)); |
327 | + report_error(ep); |
328 | } |
329 | |
330 | |
331 | |
332 | === modified file 'src/DavDownloadJob.h' |
333 | --- src/DavDownloadJob.h 2016-11-09 11:48:39 +0000 |
334 | +++ src/DavDownloadJob.h 2016-11-18 02:39:30 +0000 |
335 | @@ -1,9 +1,9 @@ |
336 | #pragma once |
337 | |
338 | +#include <QByteArray> |
339 | #include <QLocalSocket> |
340 | #include <QNetworkReply> |
341 | #include <QObject> |
342 | -#include <QPointer> |
343 | #include <QUrl> |
344 | #include <unity/storage/provider/Exceptions.h> |
345 | #include <unity/storage/provider/ProviderBase.h> |
346 | @@ -19,8 +19,8 @@ |
347 | { |
348 | Q_OBJECT |
349 | public: |
350 | - DavDownloadJob(DavProvider const& provider, std::string const& item_id, |
351 | - std::string const& match_etag, |
352 | + DavDownloadJob(std::shared_ptr<DavProvider> const& provider, |
353 | + std::string const& item_id, std::string const& match_etag, |
354 | unity::storage::provider::Context const& ctx); |
355 | ~DavDownloadJob(); |
356 | |
357 | @@ -37,15 +37,18 @@ |
358 | private: |
359 | void maybe_send_chunk(); |
360 | void handle_error(unity::storage::provider::StorageException const& exc); |
361 | + void handle_error(std::exception_ptr ep); |
362 | |
363 | - DavProvider const& provider_; |
364 | + std::shared_ptr<DavProvider> const provider_; |
365 | std::string const item_id_; |
366 | QLocalSocket writer_; |
367 | - QPointer<QNetworkReply> reply_; |
368 | + std::unique_ptr<QNetworkReply> reply_; |
369 | |
370 | bool seen_header_ = false; |
371 | - bool error_ = false; |
372 | bool read_channel_finished_ = false; |
373 | int64_t bytes_read_ = 0; |
374 | int64_t bytes_written_ = 0; |
375 | + |
376 | + bool is_error_ = false; |
377 | + QByteArray error_body_; |
378 | }; |
379 | |
380 | === modified file 'src/DavProvider.cpp' |
381 | --- src/DavProvider.cpp 2016-11-16 02:58:18 +0000 |
382 | +++ src/DavProvider.cpp 2016-11-18 02:39:30 +0000 |
383 | @@ -31,11 +31,16 @@ |
384 | |
385 | DavProvider::~DavProvider() = default; |
386 | |
387 | +std::shared_ptr<DavProvider> DavProvider::shared_from_this() |
388 | +{ |
389 | + return static_pointer_cast<DavProvider>(ProviderBase::shared_from_this()); |
390 | +} |
391 | + |
392 | boost::future<ItemList> DavProvider::roots( |
393 | vector<string> const& metadata_keys, Context const& ctx) |
394 | { |
395 | Q_UNUSED(metadata_keys); |
396 | - auto handler = new RootsHandler(*this, ctx); |
397 | + auto handler = new RootsHandler(shared_from_this(), ctx); |
398 | return handler->get_future(); |
399 | } |
400 | |
401 | @@ -48,7 +53,7 @@ |
402 | { |
403 | throw InvalidArgumentException("Invalid paging token: " + page_token); |
404 | } |
405 | - auto handler = new ListHandler(*this, item_id, ctx); |
406 | + auto handler = new ListHandler(shared_from_this(), item_id, ctx); |
407 | return handler->get_future(); |
408 | } |
409 | |
410 | @@ -58,7 +63,7 @@ |
411 | { |
412 | Q_UNUSED(metadata_keys); |
413 | string item_id = make_child_id(parent_id, name); |
414 | - auto handler = new LookupHandler(*this, item_id, ctx); |
415 | + auto handler = new LookupHandler(shared_from_this(), item_id, ctx); |
416 | return handler->get_future(); |
417 | } |
418 | |
419 | @@ -67,7 +72,7 @@ |
420 | Context const& ctx) |
421 | { |
422 | Q_UNUSED(metadata_keys); |
423 | - auto handler = new MetadataHandler(*this, item_id, ctx); |
424 | + auto handler = new MetadataHandler(shared_from_this(), item_id, ctx); |
425 | return handler->get_future(); |
426 | } |
427 | |
428 | @@ -76,7 +81,8 @@ |
429 | vector<string> const& metadata_keys, Context const& ctx) |
430 | { |
431 | Q_UNUSED(metadata_keys); |
432 | - auto handler = new CreateFolderHandler(*this, parent_id, name, ctx); |
433 | + auto handler = new CreateFolderHandler( |
434 | + shared_from_this(), parent_id, name, ctx); |
435 | return handler->get_future(); |
436 | } |
437 | |
438 | @@ -89,7 +95,8 @@ |
439 | string item_id = make_child_id(parent_id, name); |
440 | boost::promise<unique_ptr<UploadJob>> p; |
441 | p.set_value(unique_ptr<UploadJob>(new DavUploadJob( |
442 | - *this, item_id, size, content_type, allow_overwrite, string(), ctx))); |
443 | + shared_from_this(), item_id, size, content_type, allow_overwrite, |
444 | + string(), ctx))); |
445 | return p.get_future(); |
446 | } |
447 | |
448 | @@ -100,7 +107,7 @@ |
449 | Q_UNUSED(metadata_keys); |
450 | boost::promise<unique_ptr<UploadJob>> p; |
451 | p.set_value(unique_ptr<UploadJob>(new DavUploadJob( |
452 | - *this, item_id, size, string(), true, old_etag, ctx))); |
453 | + shared_from_this(), item_id, size, string(), true, old_etag, ctx))); |
454 | return p.get_future(); |
455 | } |
456 | |
457 | @@ -109,14 +116,14 @@ |
458 | { |
459 | boost::promise<unique_ptr<DownloadJob>> p; |
460 | p.set_value(unique_ptr<DownloadJob>(new DavDownloadJob( |
461 | - *this, item_id, match_etag, ctx))); |
462 | + shared_from_this(), item_id, match_etag, ctx))); |
463 | return p.get_future(); |
464 | } |
465 | |
466 | boost::future<void> DavProvider::delete_item( |
467 | string const& item_id, Context const& ctx) |
468 | { |
469 | - auto handler = new DeleteHandler(*this, item_id, ctx); |
470 | + auto handler = new DeleteHandler(shared_from_this(), item_id, ctx); |
471 | return handler->get_future(); |
472 | } |
473 | |
474 | @@ -125,8 +132,8 @@ |
475 | vector<string> const& metadata_keys, Context const& ctx) |
476 | { |
477 | Q_UNUSED(metadata_keys); |
478 | - auto handler = new CopyMoveHandler(*this, item_id, new_parent_id, |
479 | - new_name, false, ctx); |
480 | + auto handler = new CopyMoveHandler( |
481 | + shared_from_this(), item_id, new_parent_id, new_name, false, ctx); |
482 | return handler->get_future(); |
483 | } |
484 | |
485 | @@ -135,8 +142,8 @@ |
486 | vector<string> const& metadata_keys, Context const& ctx) |
487 | { |
488 | Q_UNUSED(metadata_keys); |
489 | - auto handler = new CopyMoveHandler(*this, item_id, new_parent_id, |
490 | - new_name, true, ctx); |
491 | + auto handler = new CopyMoveHandler( |
492 | + shared_from_this(), item_id, new_parent_id, new_name, true, ctx); |
493 | return handler->get_future(); |
494 | } |
495 | |
496 | |
497 | === modified file 'src/DavProvider.h' |
498 | --- src/DavProvider.h 2016-11-15 11:32:58 +0000 |
499 | +++ src/DavProvider.h 2016-11-18 02:39:30 +0000 |
500 | @@ -78,4 +78,7 @@ |
501 | |
502 | protected: |
503 | std::unique_ptr<QNetworkAccessManager> const network_; |
504 | + |
505 | +private: |
506 | + inline std::shared_ptr<DavProvider> shared_from_this(); |
507 | }; |
508 | |
509 | === modified file 'src/DavUploadJob.cpp' |
510 | --- src/DavUploadJob.cpp 2016-11-15 11:32:58 +0000 |
511 | +++ src/DavUploadJob.cpp 2016-11-18 02:39:30 +0000 |
512 | @@ -2,6 +2,7 @@ |
513 | #include "DavProvider.h" |
514 | #include "RetrieveMetadataHandler.h" |
515 | #include "item_id.h" |
516 | +#include "http_error.h" |
517 | |
518 | #include <unity/storage/provider/Exceptions.h> |
519 | |
520 | @@ -25,12 +26,12 @@ |
521 | |
522 | } |
523 | |
524 | -DavUploadJob::DavUploadJob(DavProvider const& provider, string const& item_id, |
525 | - int64_t size, string const& content_type, |
526 | - bool allow_overwrite, string const& old_etag, |
527 | - Context const& ctx) |
528 | +DavUploadJob::DavUploadJob(shared_ptr<DavProvider> const& provider, |
529 | + string const& item_id, int64_t size, |
530 | + string const& content_type, bool allow_overwrite, |
531 | + string const& old_etag, Context const& ctx) |
532 | : QObject(), UploadJob(make_upload_id()), provider_(provider), |
533 | - item_id_(item_id), base_url_(provider.base_url(ctx)), size_(size), |
534 | + item_id_(item_id), base_url_(provider->base_url(ctx)), size_(size), |
535 | context_(ctx) |
536 | { |
537 | QNetworkRequest request(id_to_url(item_id, base_url_)); |
538 | @@ -54,27 +55,15 @@ |
539 | |
540 | reader_.setSocketDescriptor( |
541 | dup(read_socket()), QLocalSocket::ConnectedState, QIODevice::ReadOnly); |
542 | - reply_.reset(provider.send_request( |
543 | + reply_.reset(provider->send_request( |
544 | request, QByteArrayLiteral("PUT"), &reader_, ctx)); |
545 | assert(reply_.get() != nullptr); |
546 | - connect(reply_.get(), static_cast<void(QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), |
547 | - this, &DavUploadJob::onReplyError); |
548 | connect(reply_.get(), &QNetworkReply::finished, |
549 | this, &DavUploadJob::onReplyFinished); |
550 | } |
551 | |
552 | DavUploadJob::~DavUploadJob() = default; |
553 | |
554 | -void DavUploadJob::onReplyError(QNetworkReply::NetworkError code) |
555 | -{ |
556 | - if (promise_set_) |
557 | - { |
558 | - return; |
559 | - } |
560 | - promise_.set_exception(RemoteCommsException("Error from QNetworkReply: " + to_string(code))); |
561 | - promise_set_ = true; |
562 | -} |
563 | - |
564 | void DavUploadJob::onReplyFinished() |
565 | { |
566 | if (promise_set_) |
567 | @@ -85,7 +74,8 @@ |
568 | // Is this a success status code? |
569 | if (status / 100 != 2) |
570 | { |
571 | - promise_.set_exception(RemoteCommsException("Error from PUT: " + to_string(status))); |
572 | + promise_.set_exception( |
573 | + translate_http_error(reply_.get(), QByteArray(), item_id_)); |
574 | promise_set_ = true; |
575 | return; |
576 | } |
577 | |
578 | === modified file 'src/DavUploadJob.h' |
579 | --- src/DavUploadJob.h 2016-11-01 02:53:02 +0000 |
580 | +++ src/DavUploadJob.h 2016-11-18 02:39:30 +0000 |
581 | @@ -18,9 +18,10 @@ |
582 | { |
583 | Q_OBJECT |
584 | public: |
585 | - DavUploadJob(DavProvider const& provider, std::string const& item_id, |
586 | - int64_t size, std::string const& content_type, |
587 | - bool allow_overwrite, std::string const& old_etag, |
588 | + DavUploadJob(std::shared_ptr<DavProvider> const& provider, |
589 | + std::string const& item_id, int64_t size, |
590 | + std::string const& content_type, bool allow_overwrite, |
591 | + std::string const& old_etag, |
592 | unity::storage::provider::Context const& ctx); |
593 | ~DavUploadJob(); |
594 | |
595 | @@ -28,11 +29,10 @@ |
596 | boost::future<unity::storage::provider::Item> finish() override; |
597 | |
598 | private Q_SLOTS: |
599 | - void onReplyError(QNetworkReply::NetworkError code); |
600 | void onReplyFinished(); |
601 | |
602 | private: |
603 | - DavProvider const& provider_; |
604 | + std::shared_ptr<DavProvider> const provider_; |
605 | std::string const item_id_; |
606 | QUrl const base_url_; |
607 | int64_t const size_; |
608 | |
609 | === modified file 'src/DeleteHandler.cpp' |
610 | --- src/DeleteHandler.cpp 2016-11-11 07:06:07 +0000 |
611 | +++ src/DeleteHandler.cpp 2016-11-18 02:39:30 +0000 |
612 | @@ -1,22 +1,20 @@ |
613 | #include "DeleteHandler.h" |
614 | - |
615 | -#include <unity/storage/provider/Exceptions.h> |
616 | - |
617 | #include "DavProvider.h" |
618 | #include "item_id.h" |
619 | +#include "http_error.h" |
620 | |
621 | using namespace std; |
622 | using namespace unity::storage::provider; |
623 | |
624 | -DeleteHandler::DeleteHandler(DavProvider const& provider, |
625 | +DeleteHandler::DeleteHandler(shared_ptr<DavProvider> const& provider, |
626 | string const& item_id, |
627 | Context const& ctx) |
628 | : provider_(provider), item_id_(item_id) |
629 | { |
630 | - QUrl const base_url = provider.base_url(ctx); |
631 | + QUrl const base_url = provider->base_url(ctx); |
632 | QNetworkRequest request(id_to_url(item_id_, base_url)); |
633 | - reply_.reset(provider.send_request(request, QByteArrayLiteral("DELETE"), |
634 | - nullptr, ctx)); |
635 | + reply_.reset(provider->send_request(request, QByteArrayLiteral("DELETE"), |
636 | + nullptr, ctx)); |
637 | connect(reply_.get(), &QNetworkReply::finished, |
638 | this, &DeleteHandler::onFinished); |
639 | } |
640 | @@ -38,7 +36,8 @@ |
641 | } |
642 | else |
643 | { |
644 | - promise_.set_exception(RemoteCommsException("Error from DELETE: " + to_string(status))); |
645 | + promise_.set_exception( |
646 | + translate_http_error(reply_.get(), QByteArray(), item_id_)); |
647 | } |
648 | |
649 | deleteLater(); |
650 | |
651 | === modified file 'src/DeleteHandler.h' |
652 | --- src/DeleteHandler.h 2016-11-11 03:53:48 +0000 |
653 | +++ src/DeleteHandler.h 2016-11-18 02:39:30 +0000 |
654 | @@ -12,7 +12,7 @@ |
655 | class DeleteHandler : public QObject { |
656 | Q_OBJECT |
657 | public: |
658 | - DeleteHandler(DavProvider const& provider, std::string const& item_id, |
659 | + DeleteHandler(std::shared_ptr<DavProvider> const& provider, std::string const& item_id, |
660 | unity::storage::provider::Context const& ctx); |
661 | ~DeleteHandler(); |
662 | |
663 | @@ -24,7 +24,7 @@ |
664 | private: |
665 | boost::promise<void> promise_; |
666 | |
667 | - DavProvider const& provider_; |
668 | + std::shared_ptr<DavProvider> const provider_; |
669 | std::string const item_id_; |
670 | |
671 | std::unique_ptr<QNetworkReply> reply_; |
672 | |
673 | === modified file 'src/ListHandler.cpp' |
674 | --- src/ListHandler.cpp 2016-10-31 01:40:28 +0000 |
675 | +++ src/ListHandler.cpp 2016-11-18 02:39:30 +0000 |
676 | @@ -8,9 +8,9 @@ |
677 | using namespace std; |
678 | using namespace unity::storage::provider; |
679 | |
680 | -ListHandler::ListHandler(DavProvider const& provider, |
681 | +ListHandler::ListHandler(std::shared_ptr<DavProvider> const& provider, |
682 | string const& parent_id, Context const& ctx) |
683 | - : PropFindHandler(provider, parent_id, 1, ctx), parent_id_(parent_id) |
684 | + : PropFindHandler(provider, parent_id, 1, ctx) |
685 | { |
686 | } |
687 | |
688 | @@ -34,7 +34,7 @@ |
689 | // itself, so remove it from the list. |
690 | items_.erase(remove_if(items_.begin(), items_.end(), |
691 | [&](Item const& item) -> bool { |
692 | - return item.item_id == parent_id_; |
693 | + return item.item_id == item_id_; |
694 | }), items_.end()); |
695 | |
696 | promise_.set_value(make_tuple(move(items_), string())); |
697 | |
698 | === modified file 'src/ListHandler.h' |
699 | --- src/ListHandler.h 2016-10-31 07:32:53 +0000 |
700 | +++ src/ListHandler.h 2016-11-18 02:39:30 +0000 |
701 | @@ -10,7 +10,7 @@ |
702 | class ListHandler : public PropFindHandler { |
703 | Q_OBJECT |
704 | public: |
705 | - ListHandler(DavProvider const& provider, |
706 | + ListHandler(std::shared_ptr<DavProvider> const& provider, |
707 | std::string const& parent_id, |
708 | unity::storage::provider::Context const& ctx); |
709 | ~ListHandler(); |
710 | @@ -18,7 +18,6 @@ |
711 | boost::future<std::tuple<unity::storage::provider::ItemList,std::string>> get_future(); |
712 | |
713 | private: |
714 | - std::string const parent_id_; |
715 | boost::promise<std::tuple<unity::storage::provider::ItemList,std::string>> promise_; |
716 | |
717 | protected: |
718 | |
719 | === modified file 'src/LookupHandler.cpp' |
720 | --- src/LookupHandler.cpp 2016-09-28 01:30:31 +0000 |
721 | +++ src/LookupHandler.cpp 2016-11-18 02:39:30 +0000 |
722 | @@ -5,7 +5,7 @@ |
723 | using namespace std; |
724 | using namespace unity::storage::provider; |
725 | |
726 | -LookupHandler::LookupHandler(DavProvider const& provider, |
727 | +LookupHandler::LookupHandler(std::shared_ptr<DavProvider> const& provider, |
728 | string const& item_id, Context const& ctx) |
729 | : PropFindHandler(provider, item_id, 0, ctx) |
730 | { |
731 | |
732 | === modified file 'src/LookupHandler.h' |
733 | --- src/LookupHandler.h 2016-10-31 07:32:53 +0000 |
734 | +++ src/LookupHandler.h 2016-11-18 02:39:30 +0000 |
735 | @@ -9,7 +9,7 @@ |
736 | class LookupHandler : public PropFindHandler { |
737 | Q_OBJECT |
738 | public: |
739 | - LookupHandler(DavProvider const& provider, |
740 | + LookupHandler(std::shared_ptr<DavProvider> const& provider, |
741 | std::string const& item_id, |
742 | unity::storage::provider::Context const& ctx); |
743 | ~LookupHandler(); |
744 | |
745 | === modified file 'src/MetadataHandler.cpp' |
746 | --- src/MetadataHandler.cpp 2016-10-31 01:40:28 +0000 |
747 | +++ src/MetadataHandler.cpp 2016-11-18 02:39:30 +0000 |
748 | @@ -5,9 +5,9 @@ |
749 | using namespace std; |
750 | using namespace unity::storage::provider; |
751 | |
752 | -MetadataHandler::MetadataHandler(DavProvider const& provider, |
753 | +MetadataHandler::MetadataHandler(shared_ptr<DavProvider> const& provider, |
754 | string const& item_id, Context const& ctx) |
755 | - : PropFindHandler(provider, item_id, 0, ctx), item_id_(item_id) |
756 | + : PropFindHandler(provider, item_id, 0, ctx) |
757 | { |
758 | } |
759 | |
760 | |
761 | === modified file 'src/MetadataHandler.h' |
762 | --- src/MetadataHandler.h 2016-10-31 07:32:53 +0000 |
763 | +++ src/MetadataHandler.h 2016-11-18 02:39:30 +0000 |
764 | @@ -9,7 +9,7 @@ |
765 | class MetadataHandler : public PropFindHandler { |
766 | Q_OBJECT |
767 | public: |
768 | - MetadataHandler(DavProvider const& provider, |
769 | + MetadataHandler(std::shared_ptr<DavProvider> const& provider, |
770 | std::string const& item_id, |
771 | unity::storage::provider::Context const& ctx); |
772 | ~MetadataHandler(); |
773 | @@ -17,7 +17,6 @@ |
774 | boost::future<unity::storage::provider::Item> get_future(); |
775 | |
776 | private: |
777 | - std::string const item_id_; |
778 | boost::promise<unity::storage::provider::Item> promise_; |
779 | |
780 | protected: |
781 | |
782 | === modified file 'src/PropFindHandler.cpp' |
783 | --- src/PropFindHandler.cpp 2016-10-31 07:32:08 +0000 |
784 | +++ src/PropFindHandler.cpp 2016-11-18 02:39:30 +0000 |
785 | @@ -1,5 +1,6 @@ |
786 | #include "PropFindHandler.h" |
787 | #include "item_id.h" |
788 | +#include "http_error.h" |
789 | |
790 | #include <cassert> |
791 | |
792 | @@ -21,10 +22,12 @@ |
793 | </D:propfind>)"); |
794 | } |
795 | |
796 | -PropFindHandler::PropFindHandler(DavProvider const& provider, string const& item_id, int depth, Context const& ctx) |
797 | - : provider_(provider), base_url_(provider.base_url(ctx)) |
798 | +PropFindHandler::PropFindHandler(shared_ptr<DavProvider> const& provider, |
799 | + string const& item_id, int depth, |
800 | + Context const& ctx) |
801 | + : provider_(provider), base_url_(provider->base_url(ctx)), item_id_(item_id) |
802 | { |
803 | - QNetworkRequest request(id_to_url(item_id, base_url_)); |
804 | + QNetworkRequest request(id_to_url(item_id_, base_url_)); |
805 | request.setRawHeader(QByteArrayLiteral("Depth"), QByteArray::number(depth)); |
806 | request.setHeader(QNetworkRequest::ContentTypeHeader, |
807 | QStringLiteral("application/xml; charset=\"utf-8\"")); |
808 | @@ -32,14 +35,14 @@ |
809 | request_body_.setData(PROPFIND_BODY); |
810 | request_body_.open(QIODevice::ReadOnly); |
811 | |
812 | - reply_.reset(provider.send_request(request, QByteArrayLiteral("PROPFIND"), |
813 | - &request_body_, ctx)); |
814 | + reply_.reset(provider->send_request(request, QByteArrayLiteral("PROPFIND"), |
815 | + &request_body_, ctx)); |
816 | assert(reply_.get() != nullptr); |
817 | |
818 | - connect(reply_.get(), static_cast<void(QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), |
819 | - this, &PropFindHandler::onError); |
820 | connect(reply_.get(), &QIODevice::readyRead, |
821 | - this, &PropFindHandler::onReadyRead); |
822 | + this, &PropFindHandler::onReplyReadyRead); |
823 | + connect(reply_.get(), &QNetworkReply::finished, |
824 | + this, &PropFindHandler::onReplyFinished); |
825 | } |
826 | |
827 | PropFindHandler::~PropFindHandler() |
828 | @@ -58,12 +61,17 @@ |
829 | |
830 | void PropFindHandler::reportError(StorageException const& error) |
831 | { |
832 | + reportError(boost::copy_exception(error)); |
833 | +} |
834 | + |
835 | +void PropFindHandler::reportError(boost::exception_ptr const& ep) |
836 | +{ |
837 | if (finished_) |
838 | { |
839 | return; |
840 | } |
841 | |
842 | - error_ = boost::copy_exception(error); |
843 | + error_ = ep; |
844 | finished_ = true; |
845 | finish(); |
846 | } |
847 | @@ -79,28 +87,46 @@ |
848 | finish(); |
849 | } |
850 | |
851 | -void PropFindHandler::onError(QNetworkReply::NetworkError code) |
852 | +void PropFindHandler::onReplyReadyRead() |
853 | { |
854 | - reportError(RemoteCommsException("Error from QNetworkReply: " + |
855 | - to_string(code))); |
856 | + if (!seen_headers_) |
857 | + { |
858 | + seen_headers_ = true; |
859 | + auto status = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
860 | + if (status == 207) |
861 | + { |
862 | + disconnect(reply_.get(), &QIODevice::readyRead, |
863 | + this, &PropFindHandler::onReplyReadyRead); |
864 | + parser_.reset(new MultiStatusParser(reply_->request().url(), reply_.get())); |
865 | + connect(parser_.get(), &MultiStatusParser::response, |
866 | + this, &PropFindHandler::onParserResponse); |
867 | + connect(parser_.get(), &MultiStatusParser::finished, |
868 | + this, &PropFindHandler::onParserFinished); |
869 | + } |
870 | + else |
871 | + { |
872 | + is_error_ = true; |
873 | + } |
874 | + } |
875 | + if (is_error_) |
876 | + { |
877 | + if (error_body_.size() < MAX_ERROR_BODY_LENGTH) |
878 | + { |
879 | + error_body_.append(reply_->readAll()); |
880 | + } |
881 | + else |
882 | + { |
883 | + reply_->close(); |
884 | + } |
885 | + } |
886 | } |
887 | |
888 | -void PropFindHandler::onReadyRead() |
889 | +void PropFindHandler::onReplyFinished() |
890 | { |
891 | - disconnect(reply_.get(), &QIODevice::readyRead, |
892 | - this, &PropFindHandler::onReadyRead); |
893 | - auto status = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
894 | - |
895 | - if (status != 207) |
896 | + if (!seen_headers_ || is_error_) |
897 | { |
898 | - reportError(RemoteCommsException("Expected 207 response, but got " + to_string(status))); |
899 | - return; |
900 | + reportError(translate_http_error(reply_.get(), error_body_, item_id_)); |
901 | } |
902 | - parser_.reset(new MultiStatusParser(reply_->request().url(), reply_.get())); |
903 | - connect(parser_.get(), &MultiStatusParser::response, |
904 | - this, &PropFindHandler::onParserResponse); |
905 | - connect(parser_.get(), &MultiStatusParser::finished, |
906 | - this, &PropFindHandler::onParserFinished); |
907 | } |
908 | |
909 | void PropFindHandler::onParserResponse(QUrl const& href, vector<MultiStatusProperty> const& properties, int status) |
910 | @@ -112,7 +138,7 @@ |
911 | } |
912 | try |
913 | { |
914 | - Item item = provider_.make_item(href, base_url_, properties); |
915 | + Item item = provider_->make_item(href, base_url_, properties); |
916 | items_.emplace_back(move(item)); |
917 | } |
918 | catch (StorageException const& error) |
919 | |
920 | === modified file 'src/PropFindHandler.h' |
921 | --- src/PropFindHandler.h 2016-10-31 07:32:08 +0000 |
922 | +++ src/PropFindHandler.h 2016-11-18 02:39:30 +0000 |
923 | @@ -13,7 +13,7 @@ |
924 | class PropFindHandler : public QObject { |
925 | Q_OBJECT |
926 | public: |
927 | - PropFindHandler(DavProvider const& provider, |
928 | + PropFindHandler(std::shared_ptr<DavProvider> const& provider, |
929 | std::string const& item_id, int depth, |
930 | unity::storage::provider::Context const& ctx); |
931 | ~PropFindHandler(); |
932 | @@ -22,8 +22,8 @@ |
933 | |
934 | private Q_SLOTS: |
935 | // From QNetworkReply |
936 | - void onError(QNetworkReply::NetworkError code); |
937 | - void onReadyRead(); |
938 | + void onReplyReadyRead(); |
939 | + void onReplyFinished(); |
940 | |
941 | // From MultiStatusParser |
942 | void onParserResponse(QUrl const& href, std::vector<MultiStatusProperty> const& properties, int status); |
943 | @@ -31,20 +31,25 @@ |
944 | |
945 | private: |
946 | void reportError(unity::storage::provider::StorageException const& error); |
947 | + void reportError(boost::exception_ptr const& ep); |
948 | void reportSuccess(); |
949 | |
950 | + bool seen_headers_ = false; |
951 | + bool is_error_ = false; |
952 | bool finished_ = false; |
953 | boost::promise<unity::storage::provider::ItemList> promise_; |
954 | |
955 | - DavProvider const& provider_; |
956 | + std::shared_ptr<DavProvider> const provider_; |
957 | QUrl base_url_; |
958 | QBuffer request_body_; |
959 | std::unique_ptr<QNetworkReply> reply_; |
960 | std::unique_ptr<MultiStatusParser> parser_; |
961 | + QByteArray error_body_; |
962 | |
963 | protected: |
964 | virtual void finish() = 0; |
965 | |
966 | + std::string const item_id_; |
967 | unity::storage::provider::ItemList items_; |
968 | boost::exception_ptr error_; |
969 | }; |
970 | |
971 | === modified file 'src/RetrieveMetadataHandler.cpp' |
972 | --- src/RetrieveMetadataHandler.cpp 2016-11-01 02:53:02 +0000 |
973 | +++ src/RetrieveMetadataHandler.cpp 2016-11-18 02:39:30 +0000 |
974 | @@ -7,12 +7,11 @@ |
975 | using namespace std; |
976 | using namespace unity::storage::provider; |
977 | |
978 | -RetrieveMetadataHandler::RetrieveMetadataHandler(DavProvider const& provider, |
979 | +RetrieveMetadataHandler::RetrieveMetadataHandler(shared_ptr<DavProvider> const& provider, |
980 | string const& item_id, |
981 | Context const& ctx, |
982 | Callback callback) |
983 | - : PropFindHandler(provider, item_id, 0, ctx), item_id_(item_id), |
984 | - callback_(callback) |
985 | + : PropFindHandler(provider, item_id, 0, ctx), callback_(callback) |
986 | { |
987 | } |
988 | |
989 | @@ -22,7 +21,7 @@ |
990 | { |
991 | Item item; |
992 | boost::exception_ptr ex = error_; |
993 | - |
994 | + |
995 | if (items_.size() != 1) |
996 | { |
997 | ex = boost::copy_exception(RemoteCommsException("Unexpectedly received " + to_string(items_.size()) + " items from PROPFIND request")); |
998 | |
999 | === modified file 'src/RetrieveMetadataHandler.h' |
1000 | --- src/RetrieveMetadataHandler.h 2016-11-01 02:53:02 +0000 |
1001 | +++ src/RetrieveMetadataHandler.h 2016-11-18 02:39:30 +0000 |
1002 | @@ -13,14 +13,13 @@ |
1003 | typedef std::function<void(unity::storage::provider::Item const& item, |
1004 | boost::exception_ptr const& error)> Callback; |
1005 | |
1006 | - RetrieveMetadataHandler(DavProvider const& provider, |
1007 | + RetrieveMetadataHandler(std::shared_ptr<DavProvider> const& provider, |
1008 | std::string const& item_id, |
1009 | unity::storage::provider::Context const& ctx, |
1010 | Callback callback); |
1011 | ~RetrieveMetadataHandler(); |
1012 | |
1013 | private: |
1014 | - std::string const item_id_; |
1015 | Callback const callback_; |
1016 | |
1017 | protected: |
1018 | |
1019 | === modified file 'src/RootsHandler.cpp' |
1020 | --- src/RootsHandler.cpp 2016-10-31 01:40:28 +0000 |
1021 | +++ src/RootsHandler.cpp 2016-11-18 02:39:30 +0000 |
1022 | @@ -5,7 +5,8 @@ |
1023 | using namespace std; |
1024 | using namespace unity::storage::provider; |
1025 | |
1026 | -RootsHandler::RootsHandler(DavProvider const& provider, Context const& ctx) |
1027 | +RootsHandler::RootsHandler(shared_ptr<DavProvider> const& provider, |
1028 | + Context const& ctx) |
1029 | : PropFindHandler(provider, ".", 0, ctx) |
1030 | { |
1031 | } |
1032 | |
1033 | === modified file 'src/RootsHandler.h' |
1034 | --- src/RootsHandler.h 2016-10-31 07:32:53 +0000 |
1035 | +++ src/RootsHandler.h 2016-11-18 02:39:30 +0000 |
1036 | @@ -9,7 +9,7 @@ |
1037 | class RootsHandler : public PropFindHandler { |
1038 | Q_OBJECT |
1039 | public: |
1040 | - RootsHandler(DavProvider const& provider, |
1041 | + RootsHandler(std::shared_ptr<DavProvider> const& provider, |
1042 | unity::storage::provider::Context const& ctx); |
1043 | ~RootsHandler(); |
1044 | |
1045 | |
1046 | === added file 'src/http_error.cpp' |
1047 | --- src/http_error.cpp 1970-01-01 00:00:00 +0000 |
1048 | +++ src/http_error.cpp 2016-11-18 02:39:30 +0000 |
1049 | @@ -0,0 +1,163 @@ |
1050 | +#include "http_error.h" |
1051 | + |
1052 | +#include <QNetworkReply> |
1053 | +#include <QXmlDefaultHandler> |
1054 | +#include <QXmlInputSource> |
1055 | +#include <QXmlSimpleReader> |
1056 | +#include <unity/storage/provider/Exceptions.h> |
1057 | + |
1058 | +#include <cassert> |
1059 | + |
1060 | +using namespace std; |
1061 | +using namespace unity::storage::provider; |
1062 | + |
1063 | +namespace { |
1064 | + |
1065 | +class ErrorParser : public QXmlDefaultHandler |
1066 | +{ |
1067 | +public: |
1068 | + bool startElement(QString const& namespace_uri, QString const& local_name, |
1069 | + QString const& qname, QXmlAttributes const& attrs) override; |
1070 | + bool endElement(QString const& namespace_uri, QString const& local_name, |
1071 | + QString const& qname) override; |
1072 | + bool characters(QString const& data) override; |
1073 | + |
1074 | + string exception_; |
1075 | + string message_; |
1076 | + |
1077 | +private: |
1078 | + static QString const SABREDAV_NS; |
1079 | + |
1080 | + QString char_data_; |
1081 | +}; |
1082 | + |
1083 | +QString const ErrorParser::SABREDAV_NS("http://sabredav.org/ns"); |
1084 | + |
1085 | +bool ErrorParser::startElement(QString const& namespace_uri, |
1086 | + QString const& local_name, |
1087 | + QString const& qname, |
1088 | + QXmlAttributes const& attrs) |
1089 | +{ |
1090 | + Q_UNUSED(namespace_uri); |
1091 | + Q_UNUSED(local_name); |
1092 | + Q_UNUSED(qname); |
1093 | + Q_UNUSED(attrs); |
1094 | + |
1095 | + char_data_.clear(); |
1096 | + return true; |
1097 | +} |
1098 | + |
1099 | +bool ErrorParser::endElement(QString const& namespace_uri, |
1100 | + QString const& local_name, |
1101 | + QString const& qname) |
1102 | +{ |
1103 | + Q_UNUSED(qname); |
1104 | + if (namespace_uri == SABREDAV_NS) |
1105 | + { |
1106 | + if (local_name == "exception") |
1107 | + { |
1108 | + exception_ = char_data_.toStdString(); |
1109 | + } |
1110 | + else if (local_name == "message") |
1111 | + { |
1112 | + message_ = char_data_.toStdString(); |
1113 | + } |
1114 | + } |
1115 | + char_data_.clear(); |
1116 | + return true; |
1117 | +} |
1118 | + |
1119 | +bool ErrorParser::characters(QString const& data) |
1120 | +{ |
1121 | + char_data_ += data; |
1122 | + return true; |
1123 | +} |
1124 | + |
1125 | +std::string make_message(QNetworkReply *reply, |
1126 | + QByteArray body) |
1127 | +{ |
1128 | + if (body.isEmpty()) |
1129 | + { |
1130 | + body = reply->readAll(); |
1131 | + } |
1132 | + auto content_type = reply->header(QNetworkRequest::ContentTypeHeader).toString(); |
1133 | + int semicolon = content_type.indexOf(';'); |
1134 | + if (semicolon >= 0) |
1135 | + { |
1136 | + content_type = content_type.left(semicolon); |
1137 | + } |
1138 | + if (content_type == "text/plain") |
1139 | + { |
1140 | + return body.toStdString(); |
1141 | + } |
1142 | + else if (content_type == "application/xml") |
1143 | + { |
1144 | + ErrorParser parser; |
1145 | + QXmlSimpleReader reader; |
1146 | + QXmlInputSource source; |
1147 | + reader.setContentHandler(&parser); |
1148 | + source.setData(body); |
1149 | + if (reader.parse(source)) { |
1150 | + return parser.exception_ + ": " + parser.message_; |
1151 | + } |
1152 | + } |
1153 | + // Fall back to returning the HTTP status string |
1154 | + return reply->attribute( |
1155 | + QNetworkRequest::HttpReasonPhraseAttribute).toString().toStdString(); |
1156 | +} |
1157 | + |
1158 | +} |
1159 | + |
1160 | +boost::exception_ptr translate_http_error(QNetworkReply *reply, |
1161 | + QByteArray const& body, |
1162 | + string const& item_id) |
1163 | +{ |
1164 | + assert(reply != nullptr); |
1165 | + assert(reply->isFinished()); |
1166 | + |
1167 | + auto status = reply->attribute( |
1168 | + QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
1169 | + if (status == 0) |
1170 | + { |
1171 | + return boost::copy_exception( |
1172 | + RemoteCommsException(reply->errorString().toStdString())); |
1173 | + } |
1174 | + |
1175 | + auto message = make_message(reply, body); |
1176 | + |
1177 | + switch (status) |
1178 | + { |
1179 | + case 400: // Bad Request |
1180 | + return boost::copy_exception(RemoteCommsException(message)); |
1181 | + |
1182 | + case 401: // Unauthorised |
1183 | + // This should be separate from Forbidden, triggering reauth. |
1184 | + case 403: // Forbidden |
1185 | + case 451: // Unavailable for Legal Reasons |
1186 | + return boost::copy_exception(PermissionException(message)); |
1187 | + |
1188 | + case 404: // Not found |
1189 | + case 410: // Gone |
1190 | + return boost::copy_exception(NotExistsException(message, item_id)); |
1191 | + |
1192 | + case 405: // Method Not Allowed |
1193 | + // MKCOL on an existing resource results in a 405 error |
1194 | + if (reply->request().attribute(QNetworkRequest::CustomVerbAttribute) == "MKCOL") |
1195 | + { |
1196 | + return boost::copy_exception(ExistsException(message, item_id, "fixme")); |
1197 | + } |
1198 | + break; |
1199 | + |
1200 | + case 409: // Conflict |
1201 | + case 412: // Precondition failed |
1202 | + return boost::copy_exception(ConflictException(message)); |
1203 | + |
1204 | + case 507: // Insufficient Storage |
1205 | + return boost::copy_exception(QuotaException(message)); |
1206 | + |
1207 | + default: |
1208 | + break; |
1209 | + } |
1210 | + return boost::copy_exception( |
1211 | + UnknownException("HTTP " + to_string(status) + ": " + message)); |
1212 | +} |
1213 | |
1214 | === added file 'src/http_error.h' |
1215 | --- src/http_error.h 1970-01-01 00:00:00 +0000 |
1216 | +++ src/http_error.h 2016-11-18 02:39:30 +0000 |
1217 | @@ -0,0 +1,12 @@ |
1218 | +#pragma once |
1219 | + |
1220 | +#include <boost/thread.hpp> |
1221 | + |
1222 | +class QByteArray; |
1223 | +class QNetworkReply; |
1224 | + |
1225 | +static constexpr int MAX_ERROR_BODY_LENGTH = 64 * 1024; |
1226 | + |
1227 | +boost::exception_ptr translate_http_error(QNetworkReply *reply, |
1228 | + QByteArray const& body, |
1229 | + std::string const& item_id={}); |
1230 | |
1231 | === modified file 'tests/CMakeLists.txt' |
1232 | --- tests/CMakeLists.txt 2016-09-23 03:28:37 +0000 |
1233 | +++ tests/CMakeLists.txt 2016-11-18 02:39:30 +0000 |
1234 | @@ -15,6 +15,7 @@ |
1235 | multistatus |
1236 | item_id |
1237 | davprovider |
1238 | + http_error |
1239 | ) |
1240 | |
1241 | set(UNIT_TEST_TARGETS "") |
1242 | |
1243 | === modified file 'tests/davprovider/davprovider_test.cpp' |
1244 | --- tests/davprovider/davprovider_test.cpp 2016-11-12 08:19:57 +0000 |
1245 | +++ tests/davprovider/davprovider_test.cpp 2016-11-18 02:39:30 +0000 |
1246 | @@ -95,7 +95,7 @@ |
1247 | |
1248 | dav_env_.reset(new DavEnvironment(tmp_dir_->path())); |
1249 | provider_env_.reset(new ProviderEnvironment( |
1250 | - unique_ptr<provider::ProviderBase>(new TestDavProvider(dav_env_->base_url())), |
1251 | + make_shared<TestDavProvider>(dav_env_->base_url()), |
1252 | 1, *dbus_env_)); |
1253 | } |
1254 | |
1255 | @@ -171,8 +171,7 @@ |
1256 | auto account = get_client(); |
1257 | make_file("foo.txt"); |
1258 | make_file("bar.txt"); |
1259 | - //make_dir("folder"); |
1260 | - make_file("folder"); |
1261 | + make_dir("folder"); |
1262 | make_file("I\u00F1t\u00EBrn\u00E2ti\u00F4n\u00E0liz\u00E6ti\u00F8n"); |
1263 | |
1264 | shared_ptr<Root> root; |
1265 | @@ -217,16 +216,10 @@ |
1266 | EXPECT_EQ("bar.txt", items[1]->name()); |
1267 | EXPECT_EQ(ItemType::file, items[1]->type()); |
1268 | |
1269 | - // FIXME: SabreDAV doesn't provide an ETag for folders, which |
1270 | - // currently trips up storage-framework's client side metadata |
1271 | - // validation. |
1272 | - |
1273 | - //EXPECT_EQ("folder/", items[2]->native_identity()); |
1274 | - EXPECT_EQ("folder", items[2]->native_identity()); |
1275 | + EXPECT_EQ("folder/", items[2]->native_identity()); |
1276 | EXPECT_EQ(".", items[2]->parent_ids().at(0)); |
1277 | EXPECT_EQ("folder", items[2]->name()); |
1278 | - //EXPECT_EQ(ItemType::folder, items[2]->type()); |
1279 | - EXPECT_EQ(ItemType::file, items[2]->type()); |
1280 | + EXPECT_EQ(ItemType::folder, items[2]->type()); |
1281 | |
1282 | EXPECT_EQ("foo.txt", items[3]->native_identity()); |
1283 | EXPECT_EQ(".", items[3]->parent_ids().at(0)); |
1284 | @@ -309,6 +302,46 @@ |
1285 | EXPECT_EQ(ItemType::file, item->type()); |
1286 | } |
1287 | |
1288 | +TEST_F(DavProviderTests, metadata_not_found) |
1289 | +{ |
1290 | + auto account = get_client(); |
1291 | + |
1292 | + shared_ptr<Root> root; |
1293 | + { |
1294 | + QFutureWatcher<QVector<shared_ptr<Root>>> watcher; |
1295 | + QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
1296 | + watcher.setFuture(account->roots()); |
1297 | + if (spy.count() == 0) |
1298 | + { |
1299 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
1300 | + } |
1301 | + auto roots = watcher.result(); |
1302 | + ASSERT_EQ(1, roots.size()); |
1303 | + root = roots[0]; |
1304 | + } |
1305 | + |
1306 | + shared_ptr<Item> item; |
1307 | + { |
1308 | + QFutureWatcher<shared_ptr<Item>> watcher; |
1309 | + QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
1310 | + watcher.setFuture(root->get("foo.txt")); |
1311 | + if (spy.count() == 0) |
1312 | + { |
1313 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
1314 | + } |
1315 | + try |
1316 | + { |
1317 | + watcher.result(); |
1318 | + FAIL(); |
1319 | + } |
1320 | + catch (NotExistsException const& e) |
1321 | + { |
1322 | + EXPECT_TRUE(e.error_message().startsWith("Sabre\\DAV\\Exception\\NotFound: ")) |
1323 | + << e.error_message().toStdString(); |
1324 | + } |
1325 | + } |
1326 | +} |
1327 | + |
1328 | TEST_F(DavProviderTests, create_folder) |
1329 | { |
1330 | auto account = get_client(); |
1331 | @@ -327,11 +360,6 @@ |
1332 | root = roots[0]; |
1333 | } |
1334 | |
1335 | - // FIXME: the test webdav server returns no ETag for folders, so |
1336 | - // the client throws away the create_folder() response. We can |
1337 | - // reenable this when porting to storage-framework 0.2. |
1338 | - return; |
1339 | - |
1340 | shared_ptr<Folder> folder; |
1341 | { |
1342 | QFutureWatcher<shared_ptr<Folder>> watcher; |
1343 | @@ -382,9 +410,9 @@ |
1344 | watcher.result(); |
1345 | FAIL(); |
1346 | } |
1347 | - catch (RemoteCommsException const& e) |
1348 | + catch (ExistsException const& e) |
1349 | { |
1350 | - EXPECT_EQ("Error from MKCOL: 405", e.error_message()); |
1351 | + EXPECT_EQ("Sabre\\DAV\\Exception\\MethodNotAllowed: The resource you tried to create already exists", e.error_message()); |
1352 | } |
1353 | } |
1354 | } |
1355 | @@ -421,9 +449,9 @@ |
1356 | watcher.result(); |
1357 | FAIL(); |
1358 | } |
1359 | - catch (RemoteCommsException const& e) |
1360 | + catch (ExistsException const& e) |
1361 | { |
1362 | - EXPECT_EQ("Error from MKCOL: 405", e.error_message()); |
1363 | + EXPECT_EQ("Sabre\\DAV\\Exception\\MethodNotAllowed: The resource you tried to create already exists", e.error_message()); |
1364 | } |
1365 | } |
1366 | } |
1367 | @@ -537,9 +565,9 @@ |
1368 | watcher.result(); |
1369 | FAIL(); |
1370 | } |
1371 | - catch (RemoteCommsException const& e) |
1372 | + catch (ConflictException const& e) |
1373 | { |
1374 | - EXPECT_EQ("Error from QNetworkReply: 299", e.error_message()); |
1375 | + EXPECT_EQ("Sabre\\DAV\\Exception\\PreconditionFailed: An If-None-Match header was specified, but the ETag matched (or * was specified).", e.error_message()); |
1376 | } |
1377 | } |
1378 | } |
1379 | @@ -686,9 +714,9 @@ |
1380 | watcher.result(); |
1381 | FAIL(); |
1382 | } |
1383 | - catch (RemoteCommsException const& e) |
1384 | + catch (ConflictException const& e) |
1385 | { |
1386 | - EXPECT_EQ("Error from QNetworkReply: 299", e.error_message()); |
1387 | + EXPECT_EQ("Sabre\\DAV\\Exception\\PreconditionFailed: An If-Match header was specified, but none of the specified the ETags matched.", e.error_message()); |
1388 | } |
1389 | } |
1390 | } |
1391 | @@ -738,7 +766,7 @@ |
1392 | } |
1393 | catch (RemoteCommsException const& e) |
1394 | { |
1395 | - EXPECT_EQ("Error from QNetworkReply: 99", e.error_message()); |
1396 | + EXPECT_EQ("Unknown error", e.error_message()); |
1397 | } |
1398 | } |
1399 | } |
1400 | @@ -1017,9 +1045,10 @@ |
1401 | watcher.waitForFinished(); // to check for errors |
1402 | FAIL(); |
1403 | } |
1404 | - catch (RemoteCommsException const& e) |
1405 | + catch (NotExistsException const& e) |
1406 | { |
1407 | - EXPECT_EQ("Unexpected status code: 404", e.error_message()); |
1408 | + EXPECT_TRUE(e.error_message().startsWith("Sabre\\DAV\\Exception\\NotFound: ")) |
1409 | + << e.error_message().toStdString(); |
1410 | } |
1411 | } |
1412 | } |
1413 | @@ -1117,9 +1146,11 @@ |
1414 | watcher.waitForFinished(); // to catch any errors |
1415 | FAIL(); |
1416 | } |
1417 | - catch (RemoteCommsException const& e) |
1418 | + catch (NotExistsException const& e) |
1419 | { |
1420 | - EXPECT_EQ("Error from DELETE: 404", e.error_message()); |
1421 | + EXPECT_TRUE(e.error_message().startsWith( |
1422 | + "Sabre\\DAV\\Exception\\NotFound: ")) |
1423 | + << e.error_message().toStdString(); |
1424 | } |
1425 | } |
1426 | } |
1427 | |
1428 | === added directory 'tests/http_error' |
1429 | === added file 'tests/http_error/CMakeLists.txt' |
1430 | --- tests/http_error/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
1431 | +++ tests/http_error/CMakeLists.txt 2016-11-18 02:39:30 +0000 |
1432 | @@ -0,0 +1,8 @@ |
1433 | +add_executable(http_error_test http_error_test.cpp) |
1434 | +target_link_libraries(http_error_test |
1435 | + dav-provider-lib |
1436 | + testutils |
1437 | + Qt5::Test |
1438 | + gtest |
1439 | +) |
1440 | +add_test(http_error_test http_error_test) |
1441 | |
1442 | === added file 'tests/http_error/http_error_test.cpp' |
1443 | --- tests/http_error/http_error_test.cpp 1970-01-01 00:00:00 +0000 |
1444 | +++ tests/http_error/http_error_test.cpp 2016-11-18 02:39:30 +0000 |
1445 | @@ -0,0 +1,260 @@ |
1446 | +#include "../../src/http_error.h" |
1447 | + |
1448 | +#include <gtest/gtest.h> |
1449 | +#include <QBuffer> |
1450 | +#include <QByteArray> |
1451 | +#include <QCoreApplication> |
1452 | +#include <QNetworkReply> |
1453 | +#include <QNetworkRequest> |
1454 | +#include <unity/storage/provider/Exceptions.h> |
1455 | + |
1456 | +using namespace std; |
1457 | +using namespace unity::storage::provider; |
1458 | + |
1459 | +class FakeReply : public QNetworkReply |
1460 | +{ |
1461 | +public: |
1462 | + FakeReply(QString const& method, int status, QString const& reason, |
1463 | + QString const& content_type, QByteArray const& body) |
1464 | + { |
1465 | + QNetworkRequest request; |
1466 | + request.setAttribute(QNetworkRequest::CustomVerbAttribute, method); |
1467 | + setRequest(request); |
1468 | + |
1469 | + setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status); |
1470 | + setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reason); |
1471 | + |
1472 | + setHeader(QNetworkRequest::ContentTypeHeader, content_type); |
1473 | + setHeader(QNetworkRequest::ContentLengthHeader, body.size()); |
1474 | + buffer_.setData(body); |
1475 | + buffer_.open(QIODevice::ReadOnly); |
1476 | + open(QIODevice::ReadOnly); |
1477 | + setFinished(true); |
1478 | + } |
1479 | + |
1480 | + FakeReply(QNetworkReply::NetworkError code, QString const& message) |
1481 | + { |
1482 | + setError(code, message); |
1483 | + setFinished(true); |
1484 | + } |
1485 | + |
1486 | + void abort() override |
1487 | + { |
1488 | + close(); |
1489 | + } |
1490 | + |
1491 | + qint64 bytesAvailable() const override |
1492 | + { |
1493 | + return QNetworkReply::bytesAvailable() + buffer_.bytesAvailable(); |
1494 | + } |
1495 | + |
1496 | + bool isSequential() const override |
1497 | + { |
1498 | + return true; |
1499 | + } |
1500 | + |
1501 | + qint64 size() const override |
1502 | + { |
1503 | + return buffer_.size(); |
1504 | + } |
1505 | + |
1506 | + qint64 readData(char *data, qint64 length) override |
1507 | + { |
1508 | + return buffer_.read(data, length); |
1509 | + } |
1510 | + |
1511 | +private: |
1512 | + QBuffer buffer_; |
1513 | +}; |
1514 | + |
1515 | +template <typename E> |
1516 | +E expect_throw(boost::exception_ptr p) |
1517 | +{ |
1518 | + try |
1519 | + { |
1520 | + boost::rethrow_exception(p); |
1521 | + } |
1522 | + catch (E const& e) |
1523 | + { |
1524 | + return e; |
1525 | + } |
1526 | + throw std::runtime_error("No exception thrown"); |
1527 | +} |
1528 | + |
1529 | + |
1530 | +TEST(HttpErrorTests, network_error) |
1531 | +{ |
1532 | + FakeReply reply(QNetworkReply::HostNotFoundError, "Host not found"); |
1533 | + |
1534 | + auto e = expect_throw<RemoteCommsException>( |
1535 | + translate_http_error(&reply, QByteArray())); |
1536 | + EXPECT_EQ("Host not found", e.error_message()); |
1537 | +} |
1538 | + |
1539 | +TEST(HttpErrorTests, text_plain_body) |
1540 | +{ |
1541 | + FakeReply reply("GET", 400, "Bad Request", |
1542 | + "text/plain;charset=US-ASCII", "error message"); |
1543 | + auto e = expect_throw<RemoteCommsException>( |
1544 | + translate_http_error(&reply, QByteArray())); |
1545 | + EXPECT_EQ("error message", e.error_message()); |
1546 | +} |
1547 | + |
1548 | +TEST(HttpErrorTests, application_xml_body) |
1549 | +{ |
1550 | + static const char xml[] = R"(<?xml version="1.0" encoding="utf-8"?> |
1551 | +<d:error xmlns:d='DAV:' xmlns:s='http://sabredav.org/ns'> |
1552 | + <s:exception>exception_name</s:exception> |
1553 | + <s:message>error message</s:message> |
1554 | + <s:sabredav-version>1.8.12</s:sabredav-version> |
1555 | +</d:error> |
1556 | +)"; |
1557 | + FakeReply reply("GET", 400, "Bad Request", |
1558 | + "application/xml;charset=utf-8", xml); |
1559 | + |
1560 | + auto e = expect_throw<RemoteCommsException>( |
1561 | + translate_http_error(&reply, QByteArray())); |
1562 | + EXPECT_EQ("exception_name: error message", e.error_message()); |
1563 | +} |
1564 | + |
1565 | +TEST(HttpErrorTests, fallback_body) |
1566 | +{ |
1567 | + FakeReply reply("GET", 400, "Bad Request", |
1568 | + "application/octet-stream", "foo"); |
1569 | + auto e = expect_throw<RemoteCommsException>( |
1570 | + translate_http_error(&reply, QByteArray())); |
1571 | + EXPECT_EQ("Bad Request", e.error_message()); |
1572 | +} |
1573 | + |
1574 | +TEST(HttpErrorTests, body_provided_separately) |
1575 | +{ |
1576 | + static const char xml[] = R"(<?xml version="1.0" encoding="utf-8"?> |
1577 | +<d:error xmlns:d='DAV:' xmlns:s='http://sabredav.org/ns'> |
1578 | + <s:exception>exception_name</s:exception> |
1579 | + <s:message>error message</s:message> |
1580 | + <s:sabredav-version>1.8.12</s:sabredav-version> |
1581 | +</d:error> |
1582 | +)"; |
1583 | + FakeReply reply("GET", 400, "Bad Request", |
1584 | + "application/xml;charset=utf-8", "foo"); |
1585 | + |
1586 | + auto e = expect_throw<RemoteCommsException>( |
1587 | + translate_http_error(&reply, xml)); |
1588 | + EXPECT_EQ("exception_name: error message", e.error_message()); |
1589 | +} |
1590 | + |
1591 | +TEST(HttpErrorTests, 401) |
1592 | +{ |
1593 | + FakeReply reply("GET", 401, "Unauthorised", "text/plain", "message"); |
1594 | + |
1595 | + auto e = expect_throw<PermissionException>( |
1596 | + translate_http_error(&reply, QByteArray())); |
1597 | + EXPECT_EQ("message", e.error_message()); |
1598 | +} |
1599 | + |
1600 | +TEST(HttpErrorTests, 403) |
1601 | +{ |
1602 | + FakeReply reply("GET", 403, "Forbidden", "text/plain", "message"); |
1603 | + |
1604 | + auto e = expect_throw<PermissionException>( |
1605 | + translate_http_error(&reply, QByteArray())); |
1606 | + EXPECT_EQ("message", e.error_message()); |
1607 | +} |
1608 | + |
1609 | +TEST(HttpErrorTests, 451) |
1610 | +{ |
1611 | + FakeReply reply("GET", 451, "Unavailable for Legal Reasons", |
1612 | + "text/plain", "message"); |
1613 | + |
1614 | + auto e = expect_throw<PermissionException>( |
1615 | + translate_http_error(&reply, QByteArray())); |
1616 | + EXPECT_EQ("message", e.error_message()); |
1617 | +} |
1618 | + |
1619 | +TEST(HttpErrorTests, 404) |
1620 | +{ |
1621 | + FakeReply reply("GET", 404, "Not Found", "text/plain", "File not found"); |
1622 | + |
1623 | + auto e = expect_throw<NotExistsException>( |
1624 | + translate_http_error(&reply, QByteArray(), "item_id")); |
1625 | + EXPECT_EQ("File not found", e.error_message()); |
1626 | + EXPECT_EQ("item_id", e.key()); |
1627 | +} |
1628 | + |
1629 | +TEST(HttpErrorTests, 410) |
1630 | +{ |
1631 | + FakeReply reply("GET", 410, "Gone", "text/plain", "File not found"); |
1632 | + |
1633 | + auto e = expect_throw<NotExistsException>( |
1634 | + translate_http_error(&reply, QByteArray(), "item_id")); |
1635 | + EXPECT_EQ("File not found", e.error_message()); |
1636 | + EXPECT_EQ("item_id", e.key()); |
1637 | +} |
1638 | + |
1639 | +TEST(HttpErrorTests, 405) |
1640 | +{ |
1641 | + // 405 for MKCOL means the target exists |
1642 | + { |
1643 | + FakeReply reply("MKCOL", 405, "Method Not Allowed", |
1644 | + "text/plain", "message"); |
1645 | + |
1646 | + auto e = expect_throw<ExistsException>( |
1647 | + translate_http_error(&reply, QByteArray(), "item_id")); |
1648 | + EXPECT_EQ("message", e.error_message()); |
1649 | + EXPECT_EQ("item_id", e.native_identity()); |
1650 | + } |
1651 | + // Otherwise, it is unknown |
1652 | + { |
1653 | + FakeReply reply("GET", 405, "Method Not Allowed", |
1654 | + "text/plain", "message"); |
1655 | + |
1656 | + auto e = expect_throw<UnknownException>( |
1657 | + translate_http_error(&reply, QByteArray(), "item_id")); |
1658 | + EXPECT_EQ("HTTP 405: message", e.error_message()); |
1659 | + } |
1660 | +} |
1661 | + |
1662 | +TEST(HttpErrorTests, 409) |
1663 | +{ |
1664 | + FakeReply reply("GET", 409, "Conflict", "text/plain", "message"); |
1665 | + |
1666 | + auto e = expect_throw<ConflictException>( |
1667 | + translate_http_error(&reply, QByteArray())); |
1668 | + EXPECT_EQ("message", e.error_message()); |
1669 | +} |
1670 | + |
1671 | +TEST(HttpErrorTests, 412) |
1672 | +{ |
1673 | + FakeReply reply("GET", 412, "Precondition failed", "text/plain", "message"); |
1674 | + |
1675 | + auto e = expect_throw<ConflictException>( |
1676 | + translate_http_error(&reply, QByteArray())); |
1677 | + EXPECT_EQ("message", e.error_message()); |
1678 | +} |
1679 | + |
1680 | +TEST(HttpErrorTests, 507) |
1681 | +{ |
1682 | + FakeReply reply("GET", 507, "Insufficient Storage", |
1683 | + "text/plain", "message"); |
1684 | + |
1685 | + auto e = expect_throw<QuotaException>( |
1686 | + translate_http_error(&reply, QByteArray())); |
1687 | + EXPECT_EQ("message", e.error_message()); |
1688 | +} |
1689 | + |
1690 | +TEST(HttpErrorTests, other) |
1691 | +{ |
1692 | + FakeReply reply("GET", 500, "Internal Server Error", |
1693 | + "text/plain", "message"); |
1694 | + |
1695 | + auto e = expect_throw<UnknownException>( |
1696 | + translate_http_error(&reply, QByteArray())); |
1697 | + EXPECT_EQ("HTTP 500: message", e.error_message()); |
1698 | +} |
1699 | + |
1700 | +int main(int argc, char**argv) |
1701 | +{ |
1702 | + QCoreApplication app(argc, argv); |
1703 | + ::testing::InitGoogleTest(&argc, argv); |
1704 | + return RUN_ALL_TESTS(); |
1705 | +} |
1706 | |
1707 | === modified file 'tests/utils/ProviderEnvironment.cpp' |
1708 | --- tests/utils/ProviderEnvironment.cpp 2016-09-28 15:26:03 +0000 |
1709 | +++ tests/utils/ProviderEnvironment.cpp 2016-11-18 02:39:30 +0000 |
1710 | @@ -16,7 +16,7 @@ |
1711 | |
1712 | } |
1713 | |
1714 | -ProviderEnvironment::ProviderEnvironment(unique_ptr<ProviderBase>&& provider, |
1715 | +ProviderEnvironment::ProviderEnvironment(shared_ptr<ProviderBase> const& provider, |
1716 | OnlineAccounts::AccountId account_id, |
1717 | DBusEnvironment const& dbus_env) |
1718 | { |
1719 | @@ -29,7 +29,7 @@ |
1720 | account_manager_->waitForReady(); |
1721 | OnlineAccounts::Account* account = account_manager_->account(account_id); |
1722 | assert(account != nullptr); |
1723 | - server_.reset(new testing::TestServer(move(provider), account, |
1724 | + server_.reset(new testing::TestServer(provider, account, |
1725 | *server_connection_, |
1726 | OBJECT_PATH.toStdString())); |
1727 | |
1728 | |
1729 | === modified file 'tests/utils/ProviderEnvironment.h' |
1730 | --- tests/utils/ProviderEnvironment.h 2016-09-19 11:01:16 +0000 |
1731 | +++ tests/utils/ProviderEnvironment.h 2016-11-18 02:39:30 +0000 |
1732 | @@ -12,7 +12,7 @@ |
1733 | class ProviderEnvironment |
1734 | { |
1735 | public: |
1736 | - ProviderEnvironment(std::unique_ptr<unity::storage::provider::ProviderBase>&& provider, |
1737 | + ProviderEnvironment(std::shared_ptr<unity::storage::provider::ProviderBase> const& provider, |
1738 | OnlineAccounts::AccountId account_id, |
1739 | DBusEnvironment const& dbus_env); |
1740 | ~ProviderEnvironment(); |
PASSED: Continuous integration, rev:27 /jenkins. canonical. com/unity- api-1/job/ lp-storage- provider- webdav- ci/32/ /jenkins. canonical. com/unity- api-1/job/ build/1100 /jenkins. canonical. com/unity- api-1/job/ build-0- fetch/1107 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 898 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 898/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= zesty/898 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= zesty/898/ artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 898 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 898/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= zesty/898 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= zesty/898/ artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 898 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 898/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= zesty/898 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= zesty/898/ artifact/ output/ *zip*/output. zip
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild: /jenkins. canonical. com/unity- api-1/job/ lp-storage- provider- webdav- ci/32/rebuild
https:/