/* * HTTP.h * ------ * Purpose: Simple HTTP client interface. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include #include #include OPENMPT_NAMESPACE_BEGIN struct URI { mpt::ustring scheme; mpt::ustring username; mpt::ustring password; mpt::ustring host; mpt::ustring port; mpt::ustring path; mpt::ustring query; mpt::ustring fragment; }; class bad_uri : public std::runtime_error { public: bad_uri(const std::string &msg) : std::runtime_error(msg) { } }; URI ParseURI(mpt::ustring str); namespace HTTP { class exception : public std::runtime_error { private: mpt::ustring message; public: exception(const mpt::ustring &m); mpt::ustring GetMessage() const; }; class status_exception : public std::runtime_error { public: status_exception(uint64 status) : std::runtime_error(MPT_AFORMAT("HTTP status {}")(status)) { return; } }; class Abort : public exception { public: Abort() : exception(U_("Operation aborted.")) { return; } }; struct NativeHandle; class Handle { private: std::unique_ptr handle; public: Handle(); Handle(const Handle &) = delete; Handle & operator=(const Handle &) = delete; explicit operator bool() const; bool operator!() const; Handle(NativeHandle h); Handle & operator=(NativeHandle h); operator NativeHandle (); ~Handle(); }; class InternetSession { private: Handle internet; public: InternetSession(mpt::ustring userAgent); operator NativeHandle (); template auto operator()(const TRequest &request) -> decltype(request(*this)) { return request(*this); } template auto Request(const TRequest &request) -> decltype(request(*this)) { return request(*this); } }; enum class Protocol { HTTP, HTTPS, }; enum class Port : uint16 { Default = 0, HTTP = 80, HTTPS = 443, }; enum class Method { Get, Head, Post, Put, Delete, Trace, Options, Connect, Patch, }; using Query = std::vector>; namespace MimeType { inline std::string Text() { return "text/plain"; } inline std::string JSON() { return "application/json"; } inline std::string Binary() { return "application/octet-stream"; } } using AcceptMimeTypes = std::vector; namespace MimeTypes { inline AcceptMimeTypes Text() { return {"text/*"}; } inline AcceptMimeTypes JSON() { return {MimeType::JSON()}; } inline AcceptMimeTypes Binary() { return {MimeType::Binary()}; } } using Headers = std::vector>; enum Flags { None = 0x00u, NoCache = 0x01u, AutoRedirect = 0x02u, }; struct Result { uint64 Status = 0; std::optional ContentLength; std::vector Data; void CheckStatus(uint64 expected) const { if(Status != expected) { throw status_exception(Status); } } }; enum class Progress { Start = 1, ConnectionEstablished = 2, RequestOpened = 3, RequestSent = 4, ResponseReceived = 5, TransferBegin = 6, TransferRunning = 7, TransferDone = 8, }; struct Request { Protocol protocol = Protocol::HTTPS; mpt::ustring host; Port port = Port::Default; mpt::ustring username; mpt::ustring password; Method method = Method::Get; mpt::ustring path = U_("/"); Query query; mpt::ustring referrer; AcceptMimeTypes acceptMimeTypes; Flags flags = None; Headers headers; std::string dataMimeType; mpt::const_byte_span data; std::ostream *outputStream = nullptr; std::function)> progressCallback = nullptr; Request &SetURI(const URI &uri); #if defined(MPT_BUILD_RETRO) Request &InsecureTLSDowngradeWindowsXP(); #endif // MPT_BUILD_RETRO Result operator()(InternetSession &internet) const; private: void progress(Progress progress, uint64 transferred, std::optional expectedSize) const; }; Result SimpleGet(InternetSession &internet, Protocol protocol, const mpt::ustring &host, const mpt::ustring &path); } // namespace HTTP OPENMPT_NAMESPACE_END