2017-02-24 18:59:41 +01:00
# pragma once
2012-06-03 19:01:08 +02:00
2016-10-12 11:32:24 +02:00
# include <functional>
2013-06-01 00:50:08 +02:00
# include <memory>
2017-02-27 20:51:36 +01:00
# include <thread>
2020-09-29 12:44:47 +02:00
# include <cstdint>
2016-10-12 11:32:24 +02:00
2021-05-14 22:46:03 -07:00
# include "Common/File/Path.h"
2021-05-01 08:36:25 -07:00
# include "Common/Net/NetBuffer.h"
2020-10-04 20:48:47 +02:00
# include "Common/Net/Resolve.h"
2012-06-03 19:01:08 +02:00
namespace net {
class Connection {
2012-10-30 13:20:55 +01:00
public :
Connection ( ) ;
virtual ~ Connection ( ) ;
2012-06-03 19:01:08 +02:00
2012-10-30 13:20:55 +01:00
// Inits the sockaddr_in.
2018-04-30 17:06:54 -07:00
bool Resolve ( const char * host , int port , DNSType type = DNSType : : ANY ) ;
2012-06-03 19:01:08 +02:00
2017-03-22 00:00:52 -07:00
bool Connect ( int maxTries = 2 , double timeout = 20.0f , bool * cancelConnect = nullptr ) ;
2012-10-30 13:20:55 +01:00
void Disconnect ( ) ;
2012-06-03 19:01:08 +02:00
2012-10-30 13:20:55 +01:00
// Only to be used for bring-up and debugging.
uintptr_t sock ( ) const { return sock_ ; }
2012-06-03 19:01:08 +02:00
2012-10-30 13:20:55 +01:00
protected :
// Store the remote host here, so we can send it along through HTTP/1.1 requests.
// TODO: Move to http::client?
std : : string host_ ;
2021-04-30 22:59:41 -07:00
int port_ = - 1 ;
2012-06-03 19:01:08 +02:00
2021-04-30 22:59:41 -07:00
addrinfo * resolved_ = nullptr ;
2012-10-30 13:20:55 +01:00
private :
2021-04-30 22:59:41 -07:00
uintptr_t sock_ = - 1 ;
2012-06-03 19:01:08 +02:00
} ;
2012-10-30 13:20:55 +01:00
} // namespace net
2012-06-03 19:01:08 +02:00
namespace http {
2019-06-23 13:12:13 -07:00
bool GetHeaderValue ( const std : : vector < std : : string > & responseHeaders , const std : : string & header , std : : string * value ) ;
2023-07-18 15:13:44 +02:00
class RequestParams {
public :
2021-08-22 08:17:26 -07:00
RequestParams ( ) { }
explicit RequestParams ( const char * r ) : resource ( r ) { }
RequestParams ( const std : : string & r , const char * a ) : resource ( r ) , acceptMime ( a ) { }
std : : string resource ;
const char * acceptMime = " */* " ;
} ;
2012-06-03 19:01:08 +02:00
class Client : public net : : Connection {
2012-10-30 13:20:55 +01:00
public :
Client ( ) ;
~ Client ( ) ;
2012-06-03 19:01:08 +02:00
2013-05-31 23:04:42 +02:00
// Return value is the HTTP return code. 200 means OK. < 0 means some local error.
2023-07-18 15:13:44 +02:00
int GET ( const RequestParams & req , Buffer * output , net : : RequestProgress * progress ) ;
int GET ( const RequestParams & req , Buffer * output , std : : vector < std : : string > & responseHeaders , net : : RequestProgress * progress ) ;
2012-06-03 19:01:08 +02:00
2012-10-30 13:20:55 +01:00
// Return value is the HTTP return code.
2023-07-18 15:13:44 +02:00
int POST ( const RequestParams & req , const std : : string & data , const std : : string & mime , Buffer * output , net : : RequestProgress * progress ) ;
int POST ( const RequestParams & req , const std : : string & data , Buffer * output , net : : RequestProgress * progress ) ;
2012-06-03 19:01:08 +02:00
2014-11-25 08:49:05 -08:00
// HEAD, PUT, DELETE aren't implemented yet, but can be done with SendRequest.
2014-11-23 16:12:54 -08:00
2023-07-18 15:13:44 +02:00
int SendRequest ( const char * method , const RequestParams & req , const char * otherHeaders , net : : RequestProgress * progress ) ;
int SendRequestWithData ( const char * method , const RequestParams & req , const std : : string & data , const char * otherHeaders , net : : RequestProgress * progress ) ;
int ReadResponseHeaders ( net : : Buffer * readbuf , std : : vector < std : : string > & responseHeaders , net : : RequestProgress * progress ) ;
2014-11-23 16:12:54 -08:00
// If your response contains a response, you must read it.
2023-07-18 15:13:44 +02:00
int ReadResponseEntity ( net : : Buffer * readbuf , const std : : vector < std : : string > & responseHeaders , Buffer * output , net : : RequestProgress * progress ) ;
2014-11-23 16:12:54 -08:00
2018-12-27 10:15:58 -08:00
void SetDataTimeout ( double t ) {
dataTimeout_ = t ;
}
2022-12-27 15:08:57 -08:00
void SetUserAgent ( const std : : string & value ) {
2021-04-30 23:12:42 -07:00
userAgent_ = value ;
}
2018-12-27 10:15:58 -08:00
protected :
2021-04-30 23:12:42 -07:00
std : : string userAgent_ ;
2014-11-23 16:12:54 -08:00
const char * httpVersion_ ;
2021-05-01 09:37:36 -07:00
double dataTimeout_ = 900.0 ;
2012-06-03 19:01:08 +02:00
} ;
2023-06-17 22:29:32 +02:00
enum class RequestMethod {
GET ,
POST ,
} ;
2023-07-18 15:13:44 +02:00
enum class ProgressBarMode {
NONE ,
VISIBLE ,
DELAYED ,
} ;
2013-05-31 23:04:42 +02:00
class Download {
public :
2023-07-19 00:46:32 +02:00
Download ( const std : : string & url , const std : : string & name , bool * cancelled ) : url_ ( url ) , name_ ( name ) , progress_ ( cancelled ) { }
virtual ~ Download ( ) { }
2013-05-31 23:04:42 +02:00
2023-07-19 00:46:32 +02:00
virtual void SetAccept ( const char * mime ) = 0 ;
virtual void SetUserAgent ( const std : : string & userAgent ) = 0 ;
2023-07-18 15:13:44 +02:00
2023-07-19 00:46:32 +02:00
// NOTE: Completion callbacks (which these are) are deferred until RunCallback is called. This is so that
// the call will end up on the thread that calls g_DownloadManager.Update().
void SetCallback ( std : : function < void ( Download & ) > callback ) {
callback_ = callback ;
}
void RunCallback ( ) {
if ( callback_ ) {
callback_ ( * this ) ;
}
2023-07-18 15:13:44 +02:00
}
2023-07-19 00:46:32 +02:00
virtual void Start ( ) = 0 ;
virtual void Join ( ) = 0 ;
2020-06-28 10:03:03 +02:00
2023-07-19 00:46:32 +02:00
virtual bool Done ( ) const = 0 ;
virtual bool Failed ( ) const = 0 ;
virtual int ResultCode ( ) const = 0 ;
2013-06-26 19:24:49 +02:00
2013-06-01 18:59:03 +02:00
// Returns 1.0 when done. That one value can be compared exactly - or just use Done().
2021-05-01 10:19:27 -07:00
float Progress ( ) const { return progress_ . progress ; }
2021-05-01 10:40:34 -07:00
float SpeedKBps ( ) const { return progress_ . kBps ; }
2023-07-19 00:46:32 +02:00
std : : string url ( ) const { return url_ ; }
virtual const Path & outfile ( ) const = 0 ;
virtual void Cancel ( ) = 0 ;
virtual bool IsCancelled ( ) const = 0 ;
virtual Buffer & buffer ( ) = 0 ;
virtual const Buffer & buffer ( ) const = 0 ;
protected :
std : : function < void ( Download & ) > callback_ ;
std : : string url_ ;
std : : string name_ ;
net : : RequestProgress progress_ ;
private :
} ;
2013-06-01 18:59:03 +02:00
2023-07-19 00:46:32 +02:00
// Really an asynchronous request.
class HTTPDownload : public Download {
public :
HTTPDownload ( RequestMethod method , const std : : string & url , const std : : string & postData , const std : : string & postMime , const Path & outfile , ProgressBarMode progressBarMode = ProgressBarMode : : DELAYED , const std : : string & name = " " ) ;
~ HTTPDownload ( ) ;
void SetAccept ( const char * mime ) override {
acceptMime_ = mime ;
}
void SetUserAgent ( const std : : string & userAgent ) override {
userAgent_ = userAgent ;
}
void Start ( ) override ;
void Join ( ) override ;
bool Done ( ) const override { return completed_ ; }
bool Failed ( ) const override { return failed_ ; }
2013-05-31 23:04:42 +02:00
2013-11-29 17:33:56 +01:00
// NOTE! The value of ResultCode is INVALID until Done() returns true.
2023-07-19 00:46:32 +02:00
int ResultCode ( ) const override { return resultCode_ ; }
2013-06-02 23:44:28 +02:00
2023-07-19 00:46:32 +02:00
const Path & outfile ( ) const override { return outfile_ ; }
2013-05-31 23:04:42 +02:00
2013-06-01 18:59:03 +02:00
// If not downloading to a file, access this to get the result.
2023-07-19 00:46:32 +02:00
Buffer & buffer ( ) override { return buffer_ ; }
const Buffer & buffer ( ) const override { return buffer_ ; }
2013-06-01 18:59:03 +02:00
2023-07-19 00:46:32 +02:00
void Cancel ( ) override {
2013-06-25 23:27:45 +02:00
cancelled_ = true ;
}
2023-07-19 00:46:32 +02:00
bool IsCancelled ( ) const override {
2015-10-06 19:07:09 +02:00
return cancelled_ ;
}
2013-05-31 23:04:42 +02:00
private :
2020-06-28 10:03:03 +02:00
void Do ( ) ; // Actually does the download. Runs on thread.
2023-06-17 22:29:32 +02:00
int Perform ( const std : : string & url ) ;
2019-06-23 12:04:59 -07:00
std : : string RedirectLocation ( const std : : string & baseUrl ) ;
2013-06-25 23:27:45 +02:00
void SetFailed ( int code ) ;
2021-08-22 12:21:44 +02:00
2023-06-17 22:29:32 +02:00
RequestMethod method_ ;
std : : string postData_ ;
2023-07-14 15:24:34 +02:00
std : : string userAgent_ ;
2013-05-31 23:04:42 +02:00
Buffer buffer_ ;
2019-06-23 12:04:59 -07:00
std : : vector < std : : string > responseHeaders_ ;
2021-05-14 22:46:03 -07:00
Path outfile_ ;
2019-09-28 11:40:10 -07:00
std : : thread thread_ ;
2021-08-22 08:29:48 -07:00
const char * acceptMime_ = " */* " ;
2023-06-17 22:31:01 +02:00
std : : string postMime_ ;
2019-09-28 11:40:10 -07:00
int resultCode_ = 0 ;
bool completed_ = false ;
bool failed_ = false ;
bool cancelled_ = false ;
2023-07-18 15:13:44 +02:00
ProgressBarMode progressBarMode_ ;
2020-06-28 10:03:03 +02:00
bool joined_ = false ;
2013-05-31 23:04:42 +02:00
} ;
2013-06-02 23:44:28 +02:00
using std : : shared_ptr ;
2013-05-31 23:04:42 +02:00
class Downloader {
public :
2013-06-25 23:27:45 +02:00
~ Downloader ( ) {
CancelAll ( ) ;
}
2013-11-18 16:27:39 +01:00
2023-07-18 15:13:44 +02:00
std : : shared_ptr < Download > StartDownload ( const std : : string & url , const Path & outfile , ProgressBarMode mode , const char * acceptMime = nullptr ) ;
2013-05-31 23:04:42 +02:00
2013-12-10 11:48:31 +01:00
std : : shared_ptr < Download > StartDownloadWithCallback (
2013-11-26 13:56:37 +01:00
const std : : string & url ,
2021-05-14 22:46:03 -07:00
const Path & outfile ,
2023-07-18 15:13:44 +02:00
ProgressBarMode mode ,
2021-08-22 08:29:48 -07:00
std : : function < void ( Download & ) > callback ,
2023-07-18 15:52:14 +02:00
const std : : string & name = " " ,
2021-08-22 08:29:48 -07:00
const char * acceptMime = nullptr ) ;
2013-11-26 13:56:37 +01:00
2023-06-17 22:29:32 +02:00
std : : shared_ptr < Download > AsyncPostWithCallback (
const std : : string & url ,
const std : : string & postData ,
2023-06-17 23:19:23 +02:00
const std : : string & postMime , // Use postMime = "application/x-www-form-urlencoded" for standard form-style posts, such as used by retroachievements. For encoding form data manually we have MultipartFormDataEncoder.
2023-07-18 15:13:44 +02:00
ProgressBarMode mode ,
2023-07-18 15:52:14 +02:00
std : : function < void ( Download & ) > callback ,
const std : : string & name = " " ) ;
2023-06-17 22:29:32 +02:00
2013-05-31 23:04:42 +02:00
// Drops finished downloads from the list.
void Update ( ) ;
2013-06-25 23:27:45 +02:00
void CancelAll ( ) ;
2023-06-17 22:29:32 +02:00
void WaitForAll ( ) ;
2023-07-14 15:24:34 +02:00
void SetUserAgent ( const std : : string & userAgent ) {
userAgent_ = userAgent ;
}
2023-06-17 22:29:32 +02:00
2013-05-31 23:04:42 +02:00
private :
2013-06-02 22:58:55 +10:00
std : : vector < std : : shared_ptr < Download > > downloads_ ;
2023-06-17 22:31:47 +02:00
// These get copied to downloads_ in Update(). It's so that callbacks can add new downloads
// while running.
std : : vector < std : : shared_ptr < Download > > newDownloads_ ;
2023-07-14 15:24:34 +02:00
std : : string userAgent_ ;
2013-05-31 23:04:42 +02:00
} ;
2017-03-22 00:00:52 -07:00
} // http