Imported Upstream version 17.22.1
[platform/upstream/libzypp.git] / tests / zyppng / media / NetworkRequestDispatcher_test.cc
1 #include <boost/test/unit_test.hpp>
2 #include <boost/test/data/test_case.hpp>
3 #include <zypp/zyppng/base/EventDispatcher>
4 #include <zypp/zyppng/media/network/request.h>
5 #include <zypp/zyppng/media/network/networkrequestdispatcher.h>
6 #include <zypp/zyppng/media/network/networkrequesterror.h>
7 #include <zypp/TmpPath.h>
8 #include <zypp/base/String.h>
9 #include <zypp/Digest.h>
10 #include <zypp/PathInfo.h>
11
12 #include <iostream>
13 #include <thread>
14 #include <chrono>
15
16 #include "WebServer.h"
17
18
19 #define BOOST_TEST_REQ_ERR(REQ, EXPECERR) \
20   do { \
21   BOOST_REQUIRE( REQ->hasError() ); \
22   BOOST_REQUIRE( REQ->error().isError() ); \
23   BOOST_REQUIRE_EQUAL( REQ->error().type(), EXPECERR ); \
24   } while(false)
25
26 #define BOOST_TEST_REQ_SUCCESS(REQ) \
27   do { \
28   BOOST_REQUIRE( !REQ->hasError() ); \
29   BOOST_REQUIRE( !REQ->error().isError() ); \
30   BOOST_REQUIRE_EQUAL( REQ->error().type(), zyppng::NetworkRequestError::NoError ); \
31   } while(false)
32
33 namespace bdata = boost::unit_test::data;
34
35 const char * err404 = "Status: 404 Not Found\r\n"
36                      "Date: Tue, 21 May 2019 08:30:59 GMT\r\n"
37                      "Server: Apache/2.4.23 (Linux/SUSE)\r\n"
38                      "X-Prefix: 93.192.0.0/10\r\n"
39                      "X-AS: 3320\r\n"
40                      "Vary: accept-language,accept-charset"
41                      "Accept-Ranges: bytes"
42                      "Transfer-Encoding: chunked"
43                      "Content-Type: text/html; charset=utf-8"
44                      "Content-Language: en\r\n"
45                      "\r\n"
46                      "Resource is no longer available!";
47
48 const char * err401 = "Status: 401 Unauthorized\r\n"
49                      "Content-Type: text/html; charset=utf-8\r\n"
50                      "WWW-Authenticate: Basic realm=\"User Visible Realm\", charset=\"UTF-8\" \r\n"
51                      "\r\n"
52                      "Sorry you are not authorized.";
53
54 bool withSSL[] = { true, false };
55
56 // convert a string in its byte representation
57 std::vector<unsigned char> convertHexStrToVector( const std::string &str )
58 {
59   std::vector<unsigned char> bytes;
60   for ( size_t i = 0; i < str.length(); i+=2 )
61   {
62 #define c2h(c) (((c)>='0' && (c)<='9') ? ((c)-'0')              \
63                 : ((c)>='a' && (c)<='f') ? ((c)-('a'-10))       \
64                 : ((c)>='A' && (c)<='F') ? ((c)-('A'-10))       \
65                 : -1)
66     int v = c2h(str[i]);
67     if (v < 0)
68       return {};
69     bytes.push_back(v);
70     v = c2h(str[i+1]);
71     if (v < 0)
72       return {};
73     bytes.back() = (bytes.back() << 4) | v;
74 #undef c2h
75   }
76   return bytes;
77 }
78
79 BOOST_DATA_TEST_CASE(nwdispatcher_basic, bdata::make( withSSL ), withSSL)
80 {
81   std::string dummyContent = "This is just some dummy content,\nto test downloading and signals.";
82
83   auto ev = zyppng::EventDispatcher::createMain();
84
85   WebServer web((zypp::Pathname(TESTS_SRC_DIR)/"data"/"dummywebroot").c_str(), 10001, withSSL );
86   web.addRequestHandler("getData", WebServer::makeResponse("200 OK", dummyContent ) );
87   BOOST_REQUIRE( web.start() );
88
89   BOOST_REQUIRE( !web.isStopped() );
90
91   zyppng::TransferSettings set = web.transferSettings();
92   zyppng::NetworkRequestDispatcher disp;
93   disp.run();
94   disp.sigQueueFinished().connect( [&ev]( const zyppng::NetworkRequestDispatcher& ){
95     ev->quit();
96   });
97
98   bool gotStarted = false;
99   bool gotFinished = false;
100   bool gotProgress = false;
101   off_t lastProgress = 0;
102   off_t totalDL = 0;
103
104   zypp::filesystem::TmpFile targetFile;
105   zyppng::Url weburl (web.url());
106   weburl.setPathName("/handler/getData");
107
108   zyppng::NetworkRequest::Ptr reqData = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
109   reqData->transferSettings() = set;
110   reqData->sigStarted().connect( [ &gotStarted ]( zyppng::NetworkRequest& ){
111     gotStarted = true;
112   });
113   reqData->sigFinished().connect( [ &gotFinished ]( zyppng::NetworkRequest&, const zyppng::NetworkRequestError & ){
114     gotFinished = true;
115   });
116   reqData->sigProgress().connect( [ & ]( zyppng::NetworkRequest &, off_t dltotal, off_t dlnow, off_t, off_t ){
117     gotProgress= true;
118     lastProgress = dlnow;
119     totalDL = dltotal;
120   });
121
122   disp.enqueue( reqData );
123   ev->run();
124
125   BOOST_TEST_REQ_SUCCESS( reqData );
126   BOOST_REQUIRE( gotStarted );
127   BOOST_REQUIRE( gotFinished );
128   BOOST_REQUIRE( gotProgress );
129   BOOST_REQUIRE_EQUAL( totalDL, dummyContent.length() );
130   BOOST_REQUIRE_EQUAL( lastProgress, dummyContent.length() );
131 }
132
133 BOOST_DATA_TEST_CASE(nwdispatcher_http_errors, bdata::make( withSSL ), withSSL)
134 {
135   auto makeErrorResponder = [] ( std::string err ) -> WebServer::RequestHandler  {
136     return WebServer::makeResponse( err, "This is a error." );
137   };
138
139   auto ev = zyppng::EventDispatcher::createMain();
140   WebServer web((zypp::Pathname(TESTS_SRC_DIR)/"data"/"dummywebroot").c_str(), 10001, withSSL );
141
142   web.addRequestHandler("get404", WebServer::makeResponse( err404 ) );
143   web.addRequestHandler("get401", WebServer::makeResponse( err401 ) );
144   web.addRequestHandler("get502", makeErrorResponder( "502 Bad Gateway" ) );
145   web.addRequestHandler("get503", makeErrorResponder( "503 Service Unavailable" ) );
146   web.addRequestHandler("get504", makeErrorResponder( "504 Gateway Timeout" ) );
147   web.addRequestHandler("get403", makeErrorResponder( "403 Forbidden" ) );
148   web.addRequestHandler("get410", makeErrorResponder( "410 Gone" ) );
149   web.addRequestHandler("get418", makeErrorResponder( "418 I'm a teapot" ) );
150   web.addRequestHandler("delayMe", []( WebServer::Request &req ) {
151   });
152   BOOST_REQUIRE( web.start() );
153
154   zyppng::TransferSettings set = web.transferSettings();
155
156   zyppng::NetworkRequestDispatcher disp;
157   disp.sigQueueFinished().connect( [&ev]( const zyppng::NetworkRequestDispatcher& ){
158     ev->quit();
159   });
160
161   zyppng::Url weburl (web.url());
162   weburl.setPathName("/handler/get404");
163
164   zypp::filesystem::TmpFile targetFile;
165   zyppng::NetworkRequest::Ptr req404 = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
166   req404->transferSettings() = set;
167   disp.enqueue( req404 );
168
169   weburl = zyppng::Url( "bad://127.0.0.1" );
170   BOOST_REQUIRE( !disp.supportsProtocol(weburl) );
171   zyppng::NetworkRequest::Ptr reqInvProto = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
172   reqInvProto->transferSettings() = set;
173   disp.enqueue( reqInvProto );
174
175   weburl = zyppng::Url( web.url() );
176   weburl.setPathName("/handler/get401");
177   zyppng::NetworkRequest::Ptr reqUnauthorized = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
178   reqUnauthorized->transferSettings() = set;
179   disp.enqueue( reqUnauthorized );
180
181   weburl = zyppng::Url( web.url() );
182   weburl.setPathName("/handler/get401");
183   zyppng::NetworkRequest::Ptr reqAuthFailed = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
184   reqAuthFailed->transferSettings() = set;
185   reqAuthFailed->transferSettings().setUsername("test");
186   reqAuthFailed->transferSettings().setPassword("test");
187   disp.enqueue( reqAuthFailed );
188
189   weburl = zyppng::Url( web.url() );
190   weburl.setPathName("/handler/get502");
191   zyppng::NetworkRequest::Ptr req502 = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
192   req502->transferSettings() = set;
193   disp.enqueue( req502 );
194
195   weburl = zyppng::Url( web.url() );
196   weburl.setPathName("/handler/get503");
197   zyppng::NetworkRequest::Ptr req503 = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
198   req503->transferSettings() = set;
199   disp.enqueue( req503 );
200
201   weburl = zyppng::Url( web.url() );
202   weburl.setPathName("/handler/get504");
203   zyppng::NetworkRequest::Ptr req504 = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
204   req504->transferSettings() = set;
205   disp.enqueue( req504 );
206
207   weburl = zyppng::Url( web.url() );
208   weburl.setPathName("/handler/get403");
209   zyppng::NetworkRequest::Ptr req403 = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
210   req403->transferSettings() = set;
211   disp.enqueue( req403 );
212
213   weburl = zyppng::Url( web.url() );
214   weburl.setPathName("/handler/get410");
215   zyppng::NetworkRequest::Ptr req410 = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
216   req410->transferSettings() = set;
217   disp.enqueue( req410 );
218
219   weburl = zyppng::Url( web.url() );
220   weburl.setPathName("/handler/get418");
221   zyppng::NetworkRequest::Ptr req418 = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
222   req418->transferSettings() = set;
223   disp.enqueue( req418 );
224
225   disp.run();
226   ev->run();
227
228   BOOST_TEST_REQ_ERR( req404, zyppng::NetworkRequestError::NotFound );
229   BOOST_TEST_REQ_ERR( reqInvProto, zyppng::NetworkRequestError::UnsupportedProtocol );
230   BOOST_TEST_REQ_ERR( reqUnauthorized, zyppng::NetworkRequestError::Unauthorized );
231   BOOST_TEST_REQ_ERR( reqAuthFailed, zyppng::NetworkRequestError::AuthFailed );
232   BOOST_TEST_REQ_ERR( req502, zyppng::NetworkRequestError::TemporaryProblem );
233   BOOST_TEST_REQ_ERR( req503, zyppng::NetworkRequestError::TemporaryProblem );
234   BOOST_TEST_REQ_ERR( req504, zyppng::NetworkRequestError::Timeout );
235   BOOST_TEST_REQ_ERR( req403, zyppng::NetworkRequestError::Forbidden );
236   BOOST_TEST_REQ_ERR( req410, zyppng::NetworkRequestError::NotFound );
237   BOOST_TEST_REQ_ERR( req418, zyppng::NetworkRequestError::ServerReturnedError );
238 }
239
240 BOOST_DATA_TEST_CASE(nwdispatcher_http_download, bdata::make( withSSL ), withSSL )
241 {
242   auto ev = zyppng::EventDispatcher::createMain();
243   zyppng::NetworkRequestDispatcher disp;
244   disp.sigQueueFinished().connect( [&ev]( const zyppng::NetworkRequestDispatcher& ){
245     ev->quit();
246   });
247   //start request dispatching, does not need to have requests enqueued
248   disp.run();
249
250   WebServer web((zypp::Pathname(TESTS_SRC_DIR)/"zypp/data/Fetcher/remote-site").c_str(), 10001, withSSL );
251   BOOST_REQUIRE( web.start() );
252
253   zyppng::TransferSettings set = web.transferSettings();
254
255   zyppng::Url weburl (web.url());
256   weburl.setPathName("/complexdir/subdir1/subdir1-file1.txt");
257
258   zypp::filesystem::TmpFile targetFile;
259   zyppng::NetworkRequest::Ptr reqDLFile = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
260
261   std::shared_ptr<zypp::Digest> dig = std::make_shared<zypp::Digest>();
262   BOOST_REQUIRE_MESSAGE( dig->create( zypp::Digest::sha1() ), "Unable to create Digest " );
263
264   reqDLFile->transferSettings() = set;
265   reqDLFile->setDigest( dig );
266   reqDLFile->setExpectedChecksum( convertHexStrToVector("f1d2d2f924e986ac86fdf7b36c94bcdf32beec15") );
267   disp.enqueue( reqDLFile );
268   ev->run();
269   BOOST_TEST_REQ_SUCCESS( reqDLFile );
270
271   //modify the checksum -> request should fail now
272   reqDLFile->setExpectedChecksum( convertHexStrToVector("f1d2d2f924e986ac86fdf7b36c94bcdf32beec20") );
273   disp.enqueue( reqDLFile );
274   ev->run();
275   BOOST_TEST_REQ_ERR( reqDLFile, zyppng::NetworkRequestError::InvalidChecksum );
276
277   weburl = web.url();
278   weburl.setPathName("/file-1.txt");
279   reqDLFile = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
280   reqDLFile->transferSettings() = set;
281   reqDLFile->setUrl( weburl );
282   reqDLFile->setRequestRange( 0, 7 );
283   disp.enqueue( reqDLFile );
284   ev->run();
285   BOOST_TEST_REQ_SUCCESS( reqDLFile );
286   {
287     zypp::filesystem::PathInfo targetFileInfo( targetFile.path() );
288     BOOST_REQUIRE( targetFileInfo.isExist() );
289     BOOST_REQUIRE( targetFileInfo.isFile() );
290     std::string fileSum = zypp::filesystem::md5sum( targetFile.path() );
291     fileSum = zypp::str::trim( fileSum );
292     BOOST_REQUIRE_EQUAL( std::string("16d2b386b2034b9488996466aaae0b57"), fileSum );
293   }
294 }
295
296 BOOST_DATA_TEST_CASE(nwdispatcher_delay_download, bdata::make( withSSL ), withSSL )
297 {
298   auto ev = zyppng::EventDispatcher::createMain();
299   zyppng::NetworkRequestDispatcher disp;
300   disp.sigQueueFinished().connect( [&ev]( const zyppng::NetworkRequestDispatcher& ){
301     ev->quit();
302   });
303
304   disp.run();
305
306   WebServer web((zypp::Pathname(TESTS_SRC_DIR)/"data"/"dummywebroot").c_str(), 10001, withSSL );
307
308   web.addRequestHandler("stalled", []( WebServer::Request &r ){
309     std::this_thread::sleep_for( std::chrono::milliseconds ( 2000 ) );
310     r.rout << "Status: 200\r\n"
311               "\r\n"
312               "Hello";
313   });
314
315   BOOST_REQUIRE( web.start() );
316
317   zyppng::TransferSettings set = web.transferSettings();
318   set.setTimeout( 1 );
319
320   zypp::filesystem::TmpFile targetFile;
321
322   zyppng::Url weburl (web.url());
323   weburl.setPathName("/handler/stalled");
324
325   zyppng::NetworkRequest::Ptr reqDLFile = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
326   reqDLFile->transferSettings() = set;
327
328   disp.enqueue( reqDLFile );
329   ev->run();
330
331   BOOST_TEST_REQ_ERR( reqDLFile, zyppng::NetworkRequestError::Timeout );
332 }
333