ppsspp/Common/Net/HTTPClient.h

256 lines
7.1 KiB
C
Raw Normal View History

#pragma once
#include <functional>
2013-06-01 00:50:08 +02:00
#include <memory>
#include <thread>
#include <cstdint>
2021-05-14 22:46:03 -07:00
#include "Common/File/Path.h"
#include "Common/Net/NetBuffer.h"
#include "Common/Net/Resolve.h"
namespace net {
class Connection {
2012-10-30 13:20:55 +01:00
public:
Connection();
virtual ~Connection();
2012-10-30 13:20:55 +01:00
// Inits the sockaddr_in.
bool Resolve(const char *host, int port, DNSType type = DNSType::ANY);
bool Connect(int maxTries = 2, double timeout = 20.0f, bool *cancelConnect = nullptr);
2012-10-30 13:20:55 +01:00
void Disconnect();
2012-10-30 13:20:55 +01:00
// Only to be used for bring-up and debugging.
uintptr_t sock() const { return sock_; }
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_;
int port_ = -1;
addrinfo *resolved_ = nullptr;
2012-10-30 13:20:55 +01:00
private:
uintptr_t sock_ = -1;
};
2012-10-30 13:20:55 +01:00
} // namespace net
namespace http {
bool GetHeaderValue(const std::vector<std::string> &responseHeaders, const std::string &header, std::string *value);
class RequestParams {
public:
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 = "*/*";
};
class Client : public net::Connection {
2012-10-30 13:20:55 +01:00
public:
Client();
~Client();
// Return value is the HTTP return code. 200 means OK. < 0 means some local error.
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-10-30 13:20:55 +01:00
// Return value is the HTTP return code.
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);
2014-11-25 08:49:05 -08:00
// HEAD, PUT, DELETE aren't implemented yet, but can be done with SendRequest.
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);
// If your response contains a response, you must read it.
int ReadResponseEntity(net::Buffer *readbuf, const std::vector<std::string> &responseHeaders, Buffer *output, net::RequestProgress *progress);
void SetDataTimeout(double t) {
dataTimeout_ = t;
}
void SetUserAgent(const std::string &value) {
2021-04-30 23:12:42 -07:00
userAgent_ = value;
}
protected:
2021-04-30 23:12:42 -07:00
std::string userAgent_;
const char *httpVersion_;
double dataTimeout_ = 900.0;
};
enum class RequestMethod {
GET,
POST,
};
enum class ProgressBarMode {
NONE,
VISIBLE,
DELAYED,
};
class Download {
public:
Download(const std::string &url, const std::string &name, bool *cancelled) : url_(url), name_(name), progress_(cancelled) {}
virtual ~Download() {}
virtual void SetAccept(const char *mime) = 0;
virtual void SetUserAgent(const std::string &userAgent) = 0;
// 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);
}
}
virtual void Start() = 0;
virtual void Join() = 0;
virtual bool Done() const = 0;
virtual bool Failed() const = 0;
virtual int ResultCode() const = 0;
2013-06-01 18:59:03 +02:00
// Returns 1.0 when done. That one value can be compared exactly - or just use Done().
float Progress() const { return progress_.progress; }
float SpeedKBps() const { return progress_.kBps; }
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
// 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-11-29 17:33:56 +01:00
// NOTE! The value of ResultCode is INVALID until Done() returns true.
int ResultCode() const override { return resultCode_; }
2013-06-02 23:44:28 +02:00
const Path &outfile() const override { return outfile_; }
2013-06-01 18:59:03 +02:00
// If not downloading to a file, access this to get the result.
Buffer &buffer() override { return buffer_; }
const Buffer &buffer() const override { return buffer_; }
2013-06-01 18:59:03 +02:00
void Cancel() override {
cancelled_ = true;
}
bool IsCancelled() const override {
2015-10-06 19:07:09 +02:00
return cancelled_;
}
private:
void Do(); // Actually does the download. Runs on thread.
int Perform(const std::string &url);
2019-06-23 12:04:59 -07:00
std::string RedirectLocation(const std::string &baseUrl);
void SetFailed(int code);
RequestMethod method_;
std::string postData_;
2023-07-14 15:24:34 +02:00
std::string userAgent_;
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_;
const char *acceptMime_ = "*/*";
std::string postMime_;
2019-09-28 11:40:10 -07:00
int resultCode_ = 0;
bool completed_ = false;
bool failed_ = false;
bool cancelled_ = false;
ProgressBarMode progressBarMode_;
bool joined_ = false;
};
2013-06-02 23:44:28 +02:00
using std::shared_ptr;
class Downloader {
public:
~Downloader() {
CancelAll();
}
2013-11-18 16:27:39 +01:00
std::shared_ptr<Download> StartDownload(const std::string &url, const Path &outfile, ProgressBarMode mode, const char *acceptMime = nullptr);
std::shared_ptr<Download> StartDownloadWithCallback(
const std::string &url,
2021-05-14 22:46:03 -07:00
const Path &outfile,
ProgressBarMode mode,
std::function<void(Download &)> callback,
2023-07-18 15:52:14 +02:00
const std::string &name = "",
const char *acceptMime = nullptr);
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.
ProgressBarMode mode,
2023-07-18 15:52:14 +02:00
std::function<void(Download &)> callback,
const std::string &name = "");
// Drops finished downloads from the list.
void Update();
void CancelAll();
void WaitForAll();
2023-07-14 15:24:34 +02:00
void SetUserAgent(const std::string &userAgent) {
userAgent_ = userAgent;
}
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_;
};
} // http