audio_trans/lib/inc/drogon/HttpClient.h

359 lines
12 KiB
C
Raw Permalink Normal View History

2025-04-07 11:31:21 +08:00
/**
*
* @file HttpClient.h
*
* @author An Tao
*
* Copyright 2018, An Tao. All rights reserved.
* https://github.com/an-tao/drogon
* Use of this source code is governed by the MIT license
* that can be found in the License file.
*
* Drogon
*
*/
#pragma once
#include <drogon/exports.h>
#include <drogon/HttpTypes.h>
#include <drogon/drogon_callbacks.h>
#include <drogon/HttpResponse.h>
#include <drogon/HttpRequest.h>
#include <trantor/utils/NonCopyable.h>
#include <trantor/net/EventLoop.h>
#include <functional>
#include <memory>
#include <future>
#include "drogon/HttpBinder.h"
#ifdef __cpp_impl_coroutine
#include <drogon/utils/coroutine.h>
#endif
namespace drogon
{
class HttpClient;
using HttpClientPtr = std::shared_ptr<HttpClient>;
#ifdef __cpp_impl_coroutine
namespace internal
{
struct HttpRespAwaiter : public CallbackAwaiter<HttpResponsePtr>
{
HttpRespAwaiter(HttpClient *client, HttpRequestPtr req, double timeout)
: client_(client), req_(std::move(req)), timeout_(timeout)
{
}
void await_suspend(std::coroutine_handle<> handle);
private:
HttpClient *client_;
HttpRequestPtr req_;
double timeout_;
};
} // namespace internal
#endif
/// Asynchronous http client
/**
* HttpClient implementation object uses the HttpAppFramework's event loop by
* default, so you should call app().run() to make the client work.
* Each HttpClient object establishes a persistent connection with the server.
* If the connection is broken, the client attempts to reconnect
* when calling the sendRequest method.
*
* Using the static mathod newHttpClient(...) to get shared_ptr of the object
* implementing the class, the shared_ptr is retained in the framework until all
* response callbacks are invoked without fear of accidental deconstruction.
*
*/
class DROGON_EXPORT HttpClient : public trantor::NonCopyable
{
public:
/**
* @brief Send a request asynchronously to the server
*
* @param req The request sent to the server.
* @param callback The callback is called when the response is received from
* the server.
* @param timeout In seconds. If the response is not received within the
* timeout, the callback is called with `ReqResult::Timeout` and an empty
* response. The zero value by default disables the timeout.
*
* @note
* The request object is altered(some headers are added to it) before it is
* sent, so calling this method with a same request object in different
* thread is dangerous.
* Please be careful when using timeout on an non-idempotent request.
*/
virtual void sendRequest(const HttpRequestPtr &req,
const HttpReqCallback &callback,
double timeout = 0) = 0;
/**
* @brief Send a request asynchronously to the server
*
* @param req The request sent to the server.
* @param callback The callback is called when the response is received from
* the server.
* @param timeout In seconds. If the response is not received within
* the timeout, the callback is called with `ReqResult::Timeout` and an
* empty response. The zero value by default disables the timeout.
*
* @note
* The request object is altered(some headers are added to it) before it is
* sent, so calling this method with a same request object in different
* thread is dangerous.
* Please be careful when using timeout on an non-idempotent request.
*/
virtual void sendRequest(const HttpRequestPtr &req,
HttpReqCallback &&callback,
double timeout = 0) = 0;
/**
* @brief Send a request synchronously to the server and return the
* response.
*
* @param req
* @param timeout In seconds. If the response is not received within the
* timeout, the `ReqResult::Timeout` and an empty response is returned. The
* zero value by default disables the timeout.
*
* @return std::pair<ReqResult, HttpResponsePtr>
* @note Never call this function in the event loop thread of the
* client (partially in the callback function of the asynchronous
* sendRequest method), otherwise the thread will be blocked forever.
* Please be careful when using timeout on an non-idempotent request.
*/
std::pair<ReqResult, HttpResponsePtr> sendRequest(const HttpRequestPtr &req,
double timeout = 0)
{
assert(!getLoop()->isInLoopThread() &&
"Deadlock detected! Calling a sync API from the same loop as "
"the HTTP client processes on will deadlock the event loop");
std::promise<std::pair<ReqResult, HttpResponsePtr>> prom;
auto f = prom.get_future();
sendRequest(
req,
[&prom](ReqResult r, const HttpResponsePtr &resp) {
prom.set_value({r, resp});
},
timeout);
return f.get();
}
#ifdef __cpp_impl_coroutine
/**
* @brief Send a request via coroutines to the server and return an
* awaiter what could be `co_await`-ed to retrieve the response
* (HttpResponsePtr)
*
* @param req
* @param timeout In seconds. If the response is not received within the
* timeout, A `std::runtime_error` with the message "Timeout" is thrown.
* The zero value by default disables the timeout.
*
* @return internal::HttpRespAwaiter. Await on it to get the response
*/
internal::HttpRespAwaiter sendRequestCoro(HttpRequestPtr req,
double timeout = 0)
{
return internal::HttpRespAwaiter(this, std::move(req), timeout);
}
#endif
/// Set the pipelining depth, which is the number of requests that are not
/// responding.
/**
* If this method is not called, the default depth value is 0 which means
* the pipelining is disabled. For details about pipelining, see
* rfc2616-8.1.2.2
*/
virtual void setPipeliningDepth(size_t depth) = 0;
/// Enable cookies for the client
/**
* @param flag if the parameter is true, all requests sent by the client
* carry the cookies set by the server side. Cookies are disabled by
* default.
*/
virtual void enableCookies(bool flag = true) = 0;
/// Add a cookie to the client
/**
* @note
* These methods are independent of the enableCookies() method. Whether the
* enableCookies() is called with true or false, the cookies added by these
* methods will be sent to the server.
*/
virtual void addCookie(const std::string &key,
const std::string &value) = 0;
/// Add a cookie to the client
/**
* @note
* These methods are independent of the enableCookies() method. Whether the
* enableCookies() is called with true or false, the cookies added by these
* methods will be sent to the server.
*/
virtual void addCookie(const Cookie &cookie) = 0;
/**
* @brief Set the user_agent header, the default value is 'DrogonClient' if
* this method is not used.
*
* @param userAgent The user_agent value, if it is empty, the user_agent
* header is not sent to the server.
*/
virtual void setUserAgent(const std::string &userAgent) = 0;
/**
* @brief Creaet a new HTTP client which use ip and port to connect to
* server
*
* @param ip The ip address of the HTTP server
* @param port The port of the HTTP server
* @param useSSL if the parameter is set to true, the client connects to the
* server using HTTPS.
* @param loop If the loop parameter is set to nullptr, the client uses the
* HttpAppFramework's event loop, otherwise it runs in the loop identified
* by the parameter.
* @param useOldTLS If the parameter is set to true, the TLS1.0/1.1 are
* eanbled for HTTPS.
* @param validateCert If the parameter is set to true, the client validates
* the server certificate when SSL handshaking.
* @return HttpClientPtr The smart pointer to the new client object.
* @note: The ip parameter support for both ipv4 and ipv6 address
*/
static HttpClientPtr newHttpClient(const std::string &ip,
uint16_t port,
bool useSSL = false,
trantor::EventLoop *loop = nullptr,
bool useOldTLS = false,
bool validateCert = true);
/// Get the event loop of the client;
virtual trantor::EventLoop *getLoop() = 0;
/// Get the number of bytes sent or received
virtual size_t bytesSent() const = 0;
virtual size_t bytesReceived() const = 0;
virtual std::string host() const = 0;
std::string getHost() const
{
return host();
}
virtual uint16_t port() const = 0;
uint16_t getPort() const
{
return port();
}
virtual bool secure() const = 0;
bool onDefaultPort() const
{
if (secure())
return port() == 443;
return port() == 80;
}
/**
* @brief Set the client certificate used by the HTTP connection
*
* @param cert Path to the certificate
* @param key Path to the certificate's private key
* @note this method has no effect if the HTTP client is communicating via
* unencrypted HTTP
*/
virtual void setCertPath(const std::string &cert,
const std::string &key) = 0;
/**
* @brief Supplies command style options for `SSL_CONF_cmd`
*
* @param sslConfCmds options for SSL_CONF_cmd
* @note this method has no effect if the HTTP client is communicating via
* unencrypted HTTP
* @code
* addSSLConfigs({{"-dhparam", "/path/to/dhparam"}, {"-strict", ""}});
* @endcode
*/
virtual void addSSLConfigs(
const std::vector<std::pair<std::string, std::string>>
&sslConfCmds) = 0;
/// Create a Http client using the hostString to connect to server
/**
*
* @param hostString this parameter must be prefixed by 'http://' or
* 'https://'.
*
* Examples for hostString:
* @code
https://www.baidu.com
http://www.baidu.com
https://127.0.0.1:8080/
http://127.0.0.1
http://[::1]:8080/ //IPv6 address must be enclosed in [], rfc2732
@endcode
*
* @param loop If the loop parameter is set to nullptr, the client uses the
* HttpAppFramework's event loop, otherwise it runs in the loop identified
* by the parameter.
*
* @param useOldTLS If the parameter is set to true, the TLS1.0/1.1 are
* enabled for HTTPS.
* @note
*
* @param validateCert If the parameter is set to true, the client validates
* the server certificate when SSL handshaking.
*
* @note Don't add path and parameters in hostString, the request path and
* parameters should be set in HttpRequestPtr when calling the sendRequest()
* method.
*
*/
static HttpClientPtr newHttpClient(const std::string &hostString,
trantor::EventLoop *loop = nullptr,
bool useOldTLS = false,
bool validateCert = true);
virtual ~HttpClient()
{
}
protected:
HttpClient() = default;
};
#ifdef __cpp_impl_coroutine
inline void internal::HttpRespAwaiter::await_suspend(
std::coroutine_handle<> handle)
{
assert(client_ != nullptr);
assert(req_ != nullptr);
client_->sendRequest(
req_,
[handle = std::move(handle), this](ReqResult result,
const HttpResponsePtr &resp) {
if (result == ReqResult::Ok)
setValue(resp);
else
{
std::stringstream ss;
ss << result;
setException(
std::make_exception_ptr(std::runtime_error(ss.str())));
}
handle.resume();
},
timeout_);
}
#endif
} // namespace drogon