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>
16 #include "WebServer.h"
19 #define BOOST_TEST_REQ_ERR(REQ, EXPECERR) \
21 BOOST_REQUIRE( REQ->hasError() ); \
22 BOOST_REQUIRE( REQ->error().isError() ); \
23 BOOST_REQUIRE_EQUAL( REQ->error().type(), EXPECERR ); \
26 #define BOOST_TEST_REQ_SUCCESS(REQ) \
28 BOOST_REQUIRE( !REQ->hasError() ); \
29 BOOST_REQUIRE( !REQ->error().isError() ); \
30 BOOST_REQUIRE_EQUAL( REQ->error().type(), zyppng::NetworkRequestError::NoError ); \
33 namespace bdata = boost::unit_test::data;
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"
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"
46 "Resource is no longer available!";
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"
52 "Sorry you are not authorized.";
54 bool withSSL[] = { true, false };
56 // convert a string in its byte representation
57 std::vector<unsigned char> convertHexStrToVector( const std::string &str )
59 std::vector<unsigned char> bytes;
60 for ( size_t i = 0; i < str.length(); i+=2 )
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)) \
73 bytes.back() = (bytes.back() << 4) | v;
79 BOOST_DATA_TEST_CASE(nwdispatcher_basic, bdata::make( withSSL ), withSSL)
81 std::string dummyContent = "This is just some dummy content,\nto test downloading and signals.";
83 auto ev = zyppng::EventDispatcher::createMain();
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() );
89 BOOST_REQUIRE( !web.isStopped() );
91 zyppng::TransferSettings set = web.transferSettings();
92 zyppng::NetworkRequestDispatcher disp;
94 disp.sigQueueFinished().connect( [&ev]( const zyppng::NetworkRequestDispatcher& ){
98 bool gotStarted = false;
99 bool gotFinished = false;
100 bool gotProgress = false;
101 off_t lastProgress = 0;
104 zypp::filesystem::TmpFile targetFile;
105 zyppng::Url weburl (web.url());
106 weburl.setPathName("/handler/getData");
108 zyppng::NetworkRequest::Ptr reqData = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
109 reqData->transferSettings() = set;
110 reqData->sigStarted().connect( [ &gotStarted ]( zyppng::NetworkRequest& ){
113 reqData->sigFinished().connect( [ &gotFinished ]( zyppng::NetworkRequest&, const zyppng::NetworkRequestError & ){
116 reqData->sigProgress().connect( [ & ]( zyppng::NetworkRequest &, off_t dltotal, off_t dlnow, off_t, off_t ){
118 lastProgress = dlnow;
122 disp.enqueue( reqData );
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() );
133 BOOST_DATA_TEST_CASE(nwdispatcher_http_errors, bdata::make( withSSL ), withSSL)
135 auto makeErrorResponder = [] ( std::string err ) -> WebServer::RequestHandler {
136 return WebServer::makeResponse( err, "This is a error." );
139 auto ev = zyppng::EventDispatcher::createMain();
140 WebServer web((zypp::Pathname(TESTS_SRC_DIR)/"data"/"dummywebroot").c_str(), 10001, withSSL );
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 ) {
152 BOOST_REQUIRE( web.start() );
154 zyppng::TransferSettings set = web.transferSettings();
156 zyppng::NetworkRequestDispatcher disp;
157 disp.sigQueueFinished().connect( [&ev]( const zyppng::NetworkRequestDispatcher& ){
161 zyppng::Url weburl (web.url());
162 weburl.setPathName("/handler/get404");
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
240 BOOST_DATA_TEST_CASE(nwdispatcher_http_download, bdata::make( withSSL ), withSSL )
242 auto ev = zyppng::EventDispatcher::createMain();
243 zyppng::NetworkRequestDispatcher disp;
244 disp.sigQueueFinished().connect( [&ev]( const zyppng::NetworkRequestDispatcher& ){
247 //start request dispatching, does not need to have requests enqueued
250 WebServer web((zypp::Pathname(TESTS_SRC_DIR)/"zypp/data/Fetcher/remote-site").c_str(), 10001, withSSL );
251 BOOST_REQUIRE( web.start() );
253 zyppng::TransferSettings set = web.transferSettings();
255 zyppng::Url weburl (web.url());
256 weburl.setPathName("/complexdir/subdir1/subdir1-file1.txt");
258 zypp::filesystem::TmpFile targetFile;
259 zyppng::NetworkRequest::Ptr reqDLFile = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
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 " );
264 reqDLFile->transferSettings() = set;
265 reqDLFile->setDigest( dig );
266 reqDLFile->setExpectedChecksum( convertHexStrToVector("f1d2d2f924e986ac86fdf7b36c94bcdf32beec15") );
267 disp.enqueue( reqDLFile );
269 BOOST_TEST_REQ_SUCCESS( reqDLFile );
271 //modify the checksum -> request should fail now
272 reqDLFile->setExpectedChecksum( convertHexStrToVector("f1d2d2f924e986ac86fdf7b36c94bcdf32beec20") );
273 disp.enqueue( reqDLFile );
275 BOOST_TEST_REQ_ERR( reqDLFile, zyppng::NetworkRequestError::InvalidChecksum );
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 );
285 BOOST_TEST_REQ_SUCCESS( reqDLFile );
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 );
296 BOOST_DATA_TEST_CASE(nwdispatcher_delay_download, bdata::make( withSSL ), withSSL )
298 auto ev = zyppng::EventDispatcher::createMain();
299 zyppng::NetworkRequestDispatcher disp;
300 disp.sigQueueFinished().connect( [&ev]( const zyppng::NetworkRequestDispatcher& ){
306 WebServer web((zypp::Pathname(TESTS_SRC_DIR)/"data"/"dummywebroot").c_str(), 10001, withSSL );
308 web.addRequestHandler("stalled", []( WebServer::Request &r ){
309 std::this_thread::sleep_for( std::chrono::milliseconds ( 2000 ) );
310 r.rout << "Status: 200\r\n"
315 BOOST_REQUIRE( web.start() );
317 zyppng::TransferSettings set = web.transferSettings();
320 zypp::filesystem::TmpFile targetFile;
322 zyppng::Url weburl (web.url());
323 weburl.setPathName("/handler/stalled");
325 zyppng::NetworkRequest::Ptr reqDLFile = std::make_shared<zyppng::NetworkRequest>( weburl, targetFile.path() );
326 reqDLFile->transferSettings() = set;
328 disp.enqueue( reqDLFile );
331 BOOST_TEST_REQ_ERR( reqDLFile, zyppng::NetworkRequestError::Timeout );