1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * =========================================================================== */
24 
25 #include "KStableHttpFile.hpp" // InnerKFileFromKStableHttpFile
26 
27 #include "HttpFixture.hpp"
28 
29 #include <ktst/unit_test.hpp>
30 
31 #include <kfg/kfg-priv.h>
32 #include <kns/kns-mgr-priv.h>
33 #include <kfs/file.h>
34 #include <klib/time.h>
35 #include <kproc/timeout.h>
36 #include <cloud/manager.h>
37 #include <cloud/impl.h>
38 
39 #include "../../libs/kns/http-file-priv.h"
40 #include "../../libs/kns/mgr-priv.h"
41 
42 #include <cassert>
43 #include <sstream>
44 
45 using namespace std;
46 using namespace ncbi::NK;
47 
48 #define RELEASE(type, obj) do { rc_t rc2 = type##Release(obj); \
49     if (rc2 != 0 && rc == 0) { rc = rc2; } obj = NULL; } while (false)
50 
51 KConfig * kfg = NULL;
52 
53 static rc_t argsHandler ( int argc, char * argv [] );
54 TEST_SUITE_WITH_ARGS_HANDLER ( HttpRefreshTestSuite, argsHandler );
55 
56 class CloudFixture : public HttpFixture
57 {
58 public:
CloudFixture()59     CloudFixture()
60     : m_cloud ( nullptr )
61     {
62         CloudMgr * cloudMgr;
63         THROW_ON_RC ( CloudMgrMake ( & cloudMgr, nullptr, m_mgr ) );
64         THROW_ON_RC ( CloudMgrCurrentProvider ( cloudMgr, & m_cloudProviderId ) );
65         if ( m_cloudProviderId != cloud_provider_none )
66         {
67             THROW_ON_RC ( CloudMgrGetCurrentCloud ( cloudMgr, & m_cloud ) );
68         }
69         THROW_ON_RC ( CloudMgrRelease ( cloudMgr ) );
70     }
71 
~CloudFixture()72     ~CloudFixture()
73     {
74         CloudRelease ( m_cloud );
75     }
76 
77 public:
78     // fake responses.
79     // Call these methods in the exact order oif intended responses since they do not check the requests
RespondWithRedirect(const string & url,KTime_t expTime)80     void RespondWithRedirect( const string & url, KTime_t expTime )
81     {
82         char expirationStr[100];
83         KTimeIso8601 ( expTime, expirationStr, sizeof expirationStr );
84         TestStream::AddResponse( string ( "HTTP/1.1 307 Temporary Redirect\r\n" ) +
85                             "Location: " + url + "\r\n" +
86                             "Expires: " + expirationStr + "\r\n" );
87     }
RespondToHEAD()88     void RespondToHEAD()
89     {
90         ostringstream ostr;
91         ostr << "HTTP/1.1 200 OK\r\nAccept-Ranges: bytes\r\nContent-Length: " << sizeof m_buf << "\r\n";
92         TestStream::AddResponse( ostr.str() );
93     }
RespondToHEAD(const string & data)94     void RespondToHEAD( const string & data )
95     {   /* in cases when HEAD is converted into a POST or a GET, we actually read up to 256 bytes from the beginning */
96         ostringstream ostr;
97         ostr << "HTTP/1.1 206 Partial Content\r\n" <<
98                 "Content-Range: bytes 0-" << ( data.size() - 1 ) << "/" << data.size() << "\r\n" <<
99                 "Content-Length: " << data.size()  << "\r\n" <<
100                 "Accept-Ranges: bytes" << "\r\n" <<
101                 "\r\n" <<
102                 data <<
103                 "\r\n";
104         TestStream::AddResponse( ostr.str() );
105     }
106 
RespondToGET()107     void RespondToGET()
108     {
109         ostringstream ostr;
110         ostr << "HTTP/1.1 206 Partial Content\r\n" <<
111                 "Content-Range: bytes 0-" << ( sizeof m_buf - 1 ) << "/" << sizeof m_buf << "\r\n" <<
112                 "Content-Length: " << sizeof m_buf  << "\r\n" <<
113                 "\r\n" <<
114                 string(sizeof m_buf, 'z') <<
115                 "\r\n";
116         TestStream::AddResponse( ostr.str() );
117     }
RespondToGET_Full(const string & data)118     void RespondToGET_Full( const string & data )
119     {
120         ostringstream ostr;
121         ostr << "HTTP/1.1 200 \r\n" <<
122                 "Content-Length: " << data.size()  << "\r\n" <<
123                 "\r\n" <<
124                 data <<
125                 "\r\n";
126         TestStream::AddResponse( ostr.str() );
127     }
128 
EnvironmentTokenPresent(const string & url)129     bool EnvironmentTokenPresent( const string & url )
130     {
131         return url.find("&ident=") != string::npos;
132     }
StringPresent(const string & url,const string & header)133     bool StringPresent( const string & url, const string & header )
134     {
135         return url.find(header) != string::npos;
136     }
137 
138     const bool EnvTokenRequired = true;
139     const bool PayRequired = true;
MakeHttpFile(const string & url,bool ce_required,bool payer_required)140     const struct KHttpFile& MakeHttpFile ( const string & url, bool ce_required, bool payer_required )
141     {
142         THROW_ON_RC ( KNSManagerMakeReliableHttpFile( m_mgr, ( const KFile** ) &  m_file, & m_stream, 0x01010000, true, ce_required, payer_required, url . c_str () ) );
143         THROW_ON_FALSE ( m_file != NULL ) ;
144         if (getenv("NCBI_VDB_HTTP_FILE_NO_RETRY") != NULL)
145             return * reinterpret_cast < const struct KHttpFile* > ( m_file );
146         else {
147             const struct KFile* f = InnerKFileFromKStableHttpFile(m_file);
148             const struct KHttpFile* hf =
149                 reinterpret_cast <const struct KHttpFile*> (f);
150             return *hf;
151         }
152     }
153 
SetUpForExpiration(const string & url,bool ce_required,bool payer_required)154     const struct KHttpFile& SetUpForExpiration( const string & url, bool ce_required, bool payer_required )
155     {
156         KTime_t expTime = KTimeStamp () + 65;
157         RespondWithRedirect ( AwsUrl, expTime );
158         RespondToHEAD();
159 
160         const struct KHttpFile& httpFile = MakeHttpFile( url, ce_required, payer_required );
161 
162         // read a portion of the file
163         RespondToGET();
164         THROW_ON_RC( KFileTimedRead ( m_file, 0, m_buf, sizeof m_buf, & num_read, NULL ) );
165 
166         // wait 6s to cross the refresh threshold, read again, see the URL refreshed and the expiration updated
167         cout << "Sleep 6 sec" << endl;
168         KSleep(6);
169         return httpFile;
170     }
171 
VerifyRequest(const string & method,const string & url,bool tokenPresent,bool autorizationPresent,bool payerPresent)172     void VerifyRequest ( const string & method, const string & url, bool tokenPresent, bool autorizationPresent, bool payerPresent )
173     {
174         // make sure there is no environment token added to the original URL
175         THROW_ON_FALSE ( ! EnvironmentTokenPresent ( TestStream::m_requests.front() ) );
176         // make sure there are no cloud-related headers added to the redirect URL
177         string redirReq = TestStream::m_requests.back();
178         THROW_ON_FALSE ( ! StringPresent ( redirReq, "Authorization" ) );
179         THROW_ON_FALSE ( ! StringPresent ( redirReq, "Date" ) );
180         THROW_ON_FALSE ( ! StringPresent ( redirReq, "x-amz-request-payer" ) );
181     }
182 
SetEnv()183     void SetEnv()
184     {
185         putenv ( (char*)"AWS_ACCESS_KEY_ID=access_key_id" );
186         putenv ( (char*)"AWS_SECRET_ACCESS_KEY=secret_access_key" );
187     }
188 
189     char m_buf[1024];
190     size_t num_read;
191     static constexpr const char * AwsUrl = "https://amazonaws.com/accession";
192     static constexpr const char * NonCloudUrl = "https://ncbi.nlm.nih.gov/accession";
193 
194     CloudProviderId m_cloudProviderId;
195     Cloud * m_cloud;
196 };
197 
FIXTURE_TEST_CASE(HttpRefreshTestSuite_RedirectSignedURL_NotCloud,CloudFixture)198 FIXTURE_TEST_CASE( HttpRefreshTestSuite_RedirectSignedURL_NotCloud, CloudFixture )
199 {
200     //make sure not in a cloud
201     if ( m_cloudProviderId != cloud_provider_none )
202     {
203         return;
204     }
205 
206     //TestEnv::verbosity = LogLevel::e_message;
207     string url = MakeURL(GetName());
208 
209     // simulates a 2-stage (redirect, real) response to a HEAD request from the "signer" service.
210     // Pretend this is the object we need expiring 65 seconds in the future; the refresh timer will be set to 60 seconds before that
211     KTime_t expTime = KTimeStamp () + 65;
212     RespondWithRedirect ( NonCloudUrl, expTime );
213     RespondToHEAD ();
214 
215     const struct KHttpFile& httpFile = MakeHttpFile( url, ! EnvTokenRequired, ! PayRequired );
216 
217     // make sure both original and redirection URLs and the expiration time are reflected on the HttpFile object
218     REQUIRE_EQ ( url, string ( (const char*) httpFile . orig_url_buffer . base ) );
219     REQUIRE_EQ ( string ( NonCloudUrl ), string ( (const char*) httpFile . url_buffer . base ) );
220     REQUIRE ( httpFile . url_is_temporary );
221     REQUIRE_EQ ( expTime, KTimeMakeTime ( & httpFile . url_expiration ) );
222 
223     // make sure there is no environment token added to the original URL
224     REQUIRE ( ! EnvironmentTokenPresent ( TestStream::m_requests.front() ) );
225     // make sure there are no cloud-related headers added to the redirect URL
226     string redirReq = TestStream::m_requests.back();
227     REQUIRE ( ! StringPresent ( redirReq, "Authorization" ) );
228     REQUIRE ( ! StringPresent ( redirReq, "Date" ) );
229     REQUIRE ( ! StringPresent ( redirReq, "x-amz-request-payer" ) );
230 }
231 
232 // for AWS, autorization is only added with the payer info
FIXTURE_TEST_CASE(HttpRefreshTestSuite_RedirectSignedURL_AWS_NoAuth_NoPayer,CloudFixture)233 FIXTURE_TEST_CASE( HttpRefreshTestSuite_RedirectSignedURL_AWS_NoAuth_NoPayer, CloudFixture )
234 {
235     //make sure not in a cloud
236     if ( m_cloudProviderId != cloud_provider_aws )
237     {
238         return;
239     }
240 
241     SetEnv();
242 
243     RespondWithRedirect ( AwsUrl, KTimeStamp () + 65 );
244     RespondToHEAD ( string( 256, 'q' ) );
245 
246     MakeHttpFile( MakeURL(GetName()), EnvTokenRequired, ! PayRequired );
247 
248     // make sure AWS autorization headers but no payer info header are added to the redirect URL
249     string redirReq = TestStream::m_requests.back();
250     REQUIRE ( ! StringPresent ( redirReq, "Authorization: AWS access_key_id:" ) );
251     REQUIRE ( ! StringPresent ( redirReq, "x-amz-request-payer: requester" ) );
252 }
253 
FIXTURE_TEST_CASE(HttpRefreshTestSuite_RedirectSignedURL_AWS_Token_NoPayer,CloudFixture)254 FIXTURE_TEST_CASE( HttpRefreshTestSuite_RedirectSignedURL_AWS_Token_NoPayer, CloudFixture )
255 {
256     //make sure not in a cloud
257     if ( m_cloudProviderId != cloud_provider_aws )
258     {
259         return;
260     }
261 
262     SetEnv();
263 
264     m_mgr -> accept_aws_charges = false;
265 
266     RespondWithRedirect ( AwsUrl, KTimeStamp () + 65 );
267     RespondToHEAD ( string( 256, 'q' ) );
268 
269     MakeHttpFile( MakeURL(GetName()), EnvTokenRequired, ! PayRequired );
270 
271     // make sure there is an environment token added to the original URL
272     string origReq = TestStream::m_requests.front();
273     REQUIRE ( EnvironmentTokenPresent ( origReq ) );
274     // make sure HEAD was converted into POST 0..255, with User-agent header appended "-head" to, for analytics purposes
275     REQUIRE ( ! StringPresent ( origReq, "HEAD" ) );
276     REQUIRE ( StringPresent ( origReq, "POST" ) );
277     REQUIRE ( StringPresent ( origReq, "0-255" ) );
278     REQUIRE ( StringPresent ( origReq, "-head" ) );
279 
280     // make sure there is no payer info added to the redirect URL
281     string lastReq = TestStream::m_requests.back();
282     REQUIRE ( ! StringPresent ( lastReq, "x-amz-request-payer" ) );
283     // User-agent header restored for future requests
284     const char * suff;
285     REQUIRE_RC ( KNSManagerGetUserAgent ( & suff ) );
286     REQUIRE ( ! StringPresent ( string(suff), "-head" ) );
287 }
288 
289 #if UNIMPLEMENTED
FIXTURE_TEST_CASE(HttpRefreshTestSuite_RedirectSignedURL_AWS_NoToken_Payer,CloudFixture)290 FIXTURE_TEST_CASE( HttpRefreshTestSuite_RedirectSignedURL_AWS_NoToken_Payer, CloudFixture )
291 {
292     if ( m_cloudProviderId != cloud_provider_aws )
293     {
294         return;
295     }
296 
297     //TestEnv::verbosity = LogLevel::e_message;
298     string url = MakeURL(GetName());
299 
300     SetEnv();
301 
302     KTime_t expTime = KTimeStamp () + 65;
303     RespondWithRedirect ( AwsUrl, expTime );
304     // HEAD will be converted to GET and return the initial portion of the target file
305     RespondToHEAD(string(2048, 'a'));
306 
307     MakeHttpFile( url, ! EnvTokenRequired, PayRequired );
308 
309     // make sure there is no environment token added to the original URL
310     string origReq = TestStream::m_requests.front();
311     REQUIRE ( ! EnvironmentTokenPresent ( origReq ) );
312     // payment info is required and no token present, HEAD is converted into GET 0..255,
313     // User-Agent appended -head to
314     REQUIRE ( ! StringPresent ( origReq, "HEAD" ) );
315     REQUIRE ( StringPresent ( origReq, "GET" ) );
316     REQUIRE ( StringPresent ( origReq, "-head" ) );
317 
318     // make sure there are authorization and payer info added to the redirect URL
319     string redirReq = TestStream::m_requests.back();
320 cout<<redirReq<<endl;
321     REQUIRE ( StringPresent ( redirReq, "Authorization: AWS access_key_id:" ) );
322     REQUIRE ( StringPresent ( redirReq, "Date: " ) );
323     REQUIRE ( StringPresent ( redirReq, "x-amz-request-payer" ) );
324 }
325 //TODO: user does not agree to pay
326 #endif
327 
328 #if UNIMPLEMENTED
FIXTURE_TEST_CASE(HttpRefreshTestSuite_RedirectSignedURL_AWS_Token_Payer,CloudFixture)329 FIXTURE_TEST_CASE( HttpRefreshTestSuite_RedirectSignedURL_AWS_Token_Payer, CloudFixture )
330 {
331     if ( m_cloudProviderId != cloud_provider_aws )
332     {
333         return;
334     }
335 
336     //TestEnv::verbosity = LogLevel::e_message;
337     string url = MakeURL(GetName());
338 
339     SetEnv();
340 
341     m_mgr -> accept_aws_charges = true;
342 
343     KTime_t expTime = KTimeStamp () + 65;
344     RespondWithRedirect ( AwsUrl, expTime );
345     // HEAD will be converted to POST and return the initial portion of the target file
346     RespondToHEAD(string(2048, 'a'));
347 
348     MakeHttpFile( url, EnvTokenRequired, PayRequired );
349 
350     // make sure there is an environment token added to the original URL
351     string origReq = TestStream::m_requests.front();
352     REQUIRE ( EnvironmentTokenPresent ( origReq ) );
353     // when token is required, HEAD is converted to POST
354     REQUIRE ( ! StringPresent ( origReq, "HEAD" ) );
355     REQUIRE ( StringPresent ( origReq, "POST" ) );
356 
357     // make sure there is authorization and payer info added to the redirect URL
358     string redirReq = TestStream::m_requests.back();
359     REQUIRE ( StringPresent ( redirReq, "Authorization: AWS access_key_id:" ) );
360     REQUIRE ( StringPresent ( redirReq, "Date: " ) );
361     REQUIRE ( StringPresent ( redirReq, "x-amz-request-payer" ) );
362 }
363 #endif
364 
365 // Refresh temporary URL on a read within 1 min of expiration
366 
FIXTURE_TEST_CASE(HttpRefreshTestSuite_ReadCloseToExpiration_AWS_NoToken_NoPayer,CloudFixture)367 FIXTURE_TEST_CASE( HttpRefreshTestSuite_ReadCloseToExpiration_AWS_NoToken_NoPayer, CloudFixture )
368 {
369     if ( m_cloudProviderId != cloud_provider_aws )
370     {
371         return;
372     }
373 
374     string url = MakeURL(GetName());
375 
376     const struct KHttpFile& httpFile = SetUpForExpiration ( url, ! EnvTokenRequired, ! PayRequired );
377 
378     // another simulated response from the "signer" service. The URL is now different, with new expiration
379     KTime_t newExpTime = KTimeStamp () + 65;
380     string newAwsHost = "ELSEWHERE.in.the.cloud";
381     RespondWithRedirect ( MakeURL( newAwsHost ), newExpTime );
382     RespondToGET();
383 
384     // this Read will notice that the expiration time is nigh, re-issue GET with the original URL
385     // and get redirected by the "signer" to a new temporary URL
386     REQUIRE_RC( KFileTimedRead ( m_file, 0, m_buf, sizeof m_buf, & num_read, NULL ) );
387     REQUIRE_EQ ( newExpTime, KTimeMakeTime ( & httpFile . url_expiration ) );
388 
389     {   // verify that the second to last request (refresh the temporary URL)
390         // was done on the original URL with a GET
391         string req = * ( ++ TestStream::m_requests . rbegin() );
392         REQUIRE ( StringPresent ( req, "GET" ) );
393         REQUIRE ( StringPresent ( req, GetName() ) );
394         REQUIRE ( ! EnvironmentTokenPresent ( req ) );
395     }
396     {   // verify that the last request (read the data) was done on the new redirected URL
397         // with a GET, no token
398         string req = TestStream::m_requests . back();
399         REQUIRE ( StringPresent ( req, "GET" ) );
400         REQUIRE ( StringPresent ( req, newAwsHost ) );
401         REQUIRE ( ! EnvironmentTokenPresent ( req ) );
402     }
403 
404     // the next Read (right away) will not refresh the URL or expiration
405     RespondToGET();
406     REQUIRE_RC( KFileTimedRead ( m_file, 0, m_buf, sizeof m_buf, & num_read, NULL ) );
407     REQUIRE_EQ ( newExpTime, KTimeMakeTime ( & httpFile . url_expiration ) ); // expiration did not change
408 
409     {   // verify that the last request was done on the same redirected URL
410         string req = TestStream::m_requests . back();
411         REQUIRE ( StringPresent ( req, newAwsHost ) );
412         REQUIRE ( ! EnvironmentTokenPresent ( req ) );
413         REQUIRE ( ! StringPresent ( req, "x-amz-request-payer: requester" ) );
414     }
415 }
416 
417 #if UNIMPLEMENTED
FIXTURE_TEST_CASE(HttpRefreshTestSuite_ReadCloseToExpiration_AWS_Token_NoPayer,CloudFixture)418 FIXTURE_TEST_CASE( HttpRefreshTestSuite_ReadCloseToExpiration_AWS_Token_NoPayer, CloudFixture )
419 {
420     if ( m_cloudProviderId != cloud_provider_aws )
421     {
422         return;
423     }
424 
425     //TestEnv::verbosity = LogLevel::e_message;
426     string url = MakeURL(GetName());
427 
428     const struct KHttpFile& httpFile = SetUpForExpiration ( url, EnvTokenRequired, ! PayRequired );
429 
430     // another simulated response from the "signer" service. The URL is now different, with new expiration
431     KTime_t newExpTime = KTimeStamp () + 65;
432     string newAwsHost = "ELSEWHERE.in.the.cloud";
433     RespondWithRedirect ( MakeURL( newAwsHost ), newExpTime );
434     RespondToGET(); // will be converted to POST
435 
436     // this Read will notice that the expiration time is nigh, use the original URL and get redirected to the "signer" for a new temporary URL
437     REQUIRE_RC( KFileTimedRead ( m_file, 0, m_buf, sizeof m_buf, & num_read, NULL ) );
438     REQUIRE_EQ ( newExpTime, KTimeMakeTime ( & httpFile . url_expiration ) );
439 
440     {   // verify that the second to last request was done on the original URL,
441         // as a POST with a CE token
442         string req = * ( ++ TestStream::m_requests . rbegin() );
443         REQUIRE ( StringPresent ( req, GetName() ) );
444         REQUIRE ( EnvironmentTokenPresent ( req ) );
445         REQUIRE ( StringPresent ( req, "POST" ) );
446     }
447 
448     {   // verify that the last request was done on the new redirected URL with a GET
449         // without the token
450         string req = TestStream::m_requests . back();
451         REQUIRE ( StringPresent ( req, newAwsHost ) );
452         REQUIRE ( ! EnvironmentTokenPresent ( req ) );
453         REQUIRE ( StringPresent ( req, "GET" ) );
454     }
455 
456     // the next Read (right away) will not refresh the URL or expiration
457     RespondToGET();
458     REQUIRE_RC( KFileTimedRead ( m_file, 0, m_buf, sizeof m_buf, & num_read, NULL ) );
459     REQUIRE_EQ ( newExpTime, KTimeMakeTime ( & httpFile . url_expiration ) ); // expiration did not change
460     // verify that the last request was done on the same redirected URL
461     REQUIRE ( StringPresent ( TestStream::m_requests . back(), newAwsHost ) );
462 }
463 #endif
464 
465 #if UNIMPLEMENTED
FIXTURE_TEST_CASE(HttpRefreshTestSuite_ReadCloseToExpiration_AWS_NoToken_Payer,CloudFixture)466 FIXTURE_TEST_CASE( HttpRefreshTestSuite_ReadCloseToExpiration_AWS_NoToken_Payer, CloudFixture )
467 {
468     if ( m_cloudProviderId != cloud_provider_aws )
469     {
470         return;
471     }
472 
473     //TestEnv::verbosity = LogLevel::e_message;
474     string url = MakeURL(GetName());
475 
476     const struct KHttpFile& httpFile = SetUpForExpiration ( url, ! EnvTokenRequired, PayRequired );
477 
478     // another simulated response from the "signer" service. The URL is now different, with new expiration
479     KTime_t newExpTime = KTimeStamp () + 65;
480     string newAwsHost = "ELSEWHERE.in.the.cloud";
481     RespondWithRedirect ( MakeURL( newAwsHost ), newExpTime );
482     RespondToGET(); // will be converted to POST
483 
484     // this Read will notice that the expiration time is nigh, use the original URL and get redirected to the "signer" for a new temporary URL
485     REQUIRE_RC( KFileTimedRead ( m_file, 0, m_buf, sizeof m_buf, & num_read, NULL ) );
486     REQUIRE_EQ ( newExpTime, KTimeMakeTime ( & httpFile . url_expiration ) );
487 
488     {   // verify that the second to last request was done on the original URL,
489         // as a POST with a CE token
490         string req = * ( ++ TestStream::m_requests . rbegin() );
491         REQUIRE ( StringPresent ( req, GetName() ) );
492         REQUIRE ( ! EnvironmentTokenPresent ( req ) );
493         REQUIRE ( StringPresent ( req, "POST" ) );
494     }
495 
496     {   // verify that the last request was done on the new redirected URL with a GET
497         // without the token
498         string req = TestStream::m_requests . back();
499         REQUIRE ( StringPresent ( req, newAwsHost ) );
500         REQUIRE ( ! EnvironmentTokenPresent ( req ) );
501         REQUIRE ( StringPresent ( req, "GET" ) );
502     }
503 
504     // the next Read (right away) will not refresh the URL or expiration
505     RespondToGET();
506     REQUIRE_RC( KFileTimedRead ( m_file, 0, m_buf, sizeof m_buf, & num_read, NULL ) );
507     REQUIRE_EQ ( newExpTime, KTimeMakeTime ( & httpFile . url_expiration ) ); // expiration did not change
508     // verify that the last request was done on the same redirected URL
509     REQUIRE ( StringPresent ( TestStream::m_requests . back(), newAwsHost ) );
510 }
511 #endif
512 
513 //TODO
514 //FIXTURE_TEST_CASE( HttpRefreshTestSuite_ReadCloseToExpiration_AWS_NoToken_NoPayer, CloudFixture )
515 //FIXTURE_TEST_CASE( HttpRefreshTestSuite_ReadCloseToExpiration_GCP_xxToken_xxPayer, CloudFixture )
516 
FIXTURE_TEST_CASE(HttpRefreshTestSuite_HeadAsPost_ShortFile,CloudFixture)517 FIXTURE_TEST_CASE( HttpRefreshTestSuite_HeadAsPost_ShortFile, CloudFixture )
518 {
519     string url = MakeURL(GetName());
520 
521     // HEAD will be converted to POST and return the initial portion of the target file
522     string data (10, 'a');// this is shorter than POST will request (256)
523     RespondToHEAD(data);
524 
525     MakeHttpFile( url, EnvTokenRequired, ! PayRequired );
526 
527     RespondToGET_Full(data); // return the complete file
528     REQUIRE_RC( KFileTimedRead ( m_file, 0, m_buf, sizeof m_buf, & num_read, NULL ) );
529 }
530 
531 //////////////////////////////////////////// Main
532 
533 #include <kapp/args.h> // Args
534 #include <klib/debug.h>
535 #include <kfg/config.h>
536 
argsHandler(int argc,char * argv[])537 static rc_t argsHandler ( int argc, char * argv [] ) {
538     Args * args = NULL;
539     rc_t rc = ArgsMakeAndHandle ( & args, argc, argv, 0, NULL, 0 );
540     ArgsWhack ( args );
541     return rc;
542 }
543 
544 extern "C" {
545     const char UsageDefaultName[] = "test-refresh-expired";
UsageSummary(const char * progname)546     rc_t CC UsageSummary ( const char     * progname) { return 0; }
Usage(const struct Args * args)547     rc_t CC Usage        ( const struct Args * args ) { return 0; }
KAppVersion(void)548     ver_t CC KAppVersion ( void ) { return 0; }
549 
KMain(int argc,char * argv[])550     rc_t CC KMain ( int argc, char * argv [] )
551     {
552         //if ( 1 ) assert ( ! KDbgSetString ( "KNS-HTTP" ) );
553         KConfigDisableUserSettings ();
554 
555         rc_t rc = KConfigMakeEmpty ( & kfg );
556         // turn off certificate validation to download from storage.googleapis.com
557         if ( rc == 0 )
558             rc = KConfigWriteString ( kfg, "/tls/allow-all-certs", "true" );
559 
560         // in order to run in a cloud, give permission to submit computing environment
561         if (rc == 0)
562             rc = KConfigWriteString(kfg, "/libs/cloud/report_instance_identity", "true");
563 
564         if ( rc == 0 )
565             rc = HttpRefreshTestSuite ( argc, argv );
566 
567         RELEASE ( KConfig, kfg );
568 
569         return rc;
570     }
571 }
572 
573