From 81cb6a08461f2fc9e8a1f6fd0911a4d49e3e274e Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 27 Jan 2024 18:46:03 +0800 Subject: [PATCH] Add stream and cookie support for UtilHttpClient. --- .../UtilHTTPClient_SteamAPI.cpp | 272 ++++++++++++++++-- Plugins/SCModelDownloader/SCModelDatabase.cpp | 2 +- include/Interface/IUtilHTTPClient.h | 17 +- 3 files changed, 269 insertions(+), 22 deletions(-) diff --git a/PluginLibs/UtilHTTPClient_SteamAPI/UtilHTTPClient_SteamAPI.cpp b/PluginLibs/UtilHTTPClient_SteamAPI/UtilHTTPClient_SteamAPI.cpp index a534e6bc..9679ede2 100644 --- a/PluginLibs/UtilHTTPClient_SteamAPI/UtilHTTPClient_SteamAPI.cpp +++ b/PluginLibs/UtilHTTPClient_SteamAPI/UtilHTTPClient_SteamAPI.cpp @@ -108,6 +108,18 @@ class CUtilHTTPPayload : public IUtilHTTPPayload std::string m_payload; public: + + bool ReadStreamFromRequestHandle(HTTPRequestHandle handle, uint32_t offset, uint32_t size) + { + m_payload.resize(size); + if (SteamHTTP()->GetHTTPStreamingResponseBodyData(handle, offset, (uint8*)m_payload.data(), size)) + { + return true; + } + + return false; + } + bool ReadFromRequestHandle(HTTPRequestHandle handle) { uint32_t responseSize = 0; @@ -139,19 +151,44 @@ class CUtilHTTPResponse : public IUtilHTTPResponse private: bool m_bResponseCompleted{}; bool m_bResponseError{}; + bool m_bIsHeaderReceived{}; + bool m_bIsStream{}; int m_iResponseStatusCode{}; CUtilHTTPPayload* m_pResponsePayload{}; + + HTTPRequestHandle m_RequestHandle{ INVALID_HTTPREQUEST_HANDLE }; public: - void OnSteamHTTPCompleted(HTTPRequestHandle RequestHandle, HTTPRequestCompleted_t* pResult, bool bHasError) + void OnSteamHTTPRecvHeader(HTTPRequestHeadersReceived_t* pResult, bool bHasError) + { + m_bIsHeaderReceived = true; + m_bIsStream = true; + + m_RequestHandle = pResult->m_hRequest; + } + + void OnSteamHTTPRecvData(HTTPRequestDataReceived_t* pResult, bool bHasError) + { + m_RequestHandle = pResult->m_hRequest; + + if (m_bIsStream && !bHasError) + { + m_pResponsePayload->ReadStreamFromRequestHandle(pResult->m_hRequest, pResult->m_cOffset, pResult->m_cBytesReceived); + } + } + + void OnSteamHTTPCompleted(HTTPRequestCompleted_t* pResult, bool bHasError) { + m_RequestHandle = pResult->m_hRequest; + + m_bIsHeaderReceived = true; m_bResponseCompleted = true; m_bResponseError = bHasError; m_iResponseStatusCode = (int)pResult->m_eStatusCode; - if (pResult->m_bRequestSuccessful && !bHasError) + if (pResult->m_bRequestSuccessful && !bHasError && !m_bIsStream) { - m_pResponsePayload->ReadFromRequestHandle(RequestHandle); + m_pResponsePayload->ReadFromRequestHandle(pResult->m_hRequest); } } @@ -170,6 +207,16 @@ class CUtilHTTPResponse : public IUtilHTTPResponse } } + bool GetHeaderSize(const char* name, size_t *buflen) override + { + return SteamHTTP()->GetHTTPResponseHeaderSize(m_RequestHandle, name, buflen); + } + + bool GetHeader(const char* name, char* buf, size_t buflen) override + { + return SteamHTTP()->GetHTTPResponseHeaderValue(m_RequestHandle, name, (uint8 *)buf, buflen); + } + bool IsResponseCompleted() const override { return m_bResponseCompleted; @@ -180,6 +227,16 @@ class CUtilHTTPResponse : public IUtilHTTPResponse return m_bResponseError; } + bool IsHeaderReceived() const override + { + return m_bIsHeaderReceived; + } + + bool IsStream() const override + { + return m_bIsStream; + } + int GetStatusCode() const override { return m_iResponseStatusCode; @@ -195,7 +252,7 @@ class CUtilHTTPRequest : public IUtilHTTPRequest { protected: HTTPRequestHandle m_RequestHandle{}; - CCallResult m_CallResult{}; + CCallResult m_CompleteCallResult{}; bool m_bRequesting{}; bool m_bResponding{}; bool m_bRequestSuccessful{}; @@ -239,9 +296,9 @@ class CUtilHTTPRequest : public IUtilHTTPRequest ~CUtilHTTPRequest() { - if (m_CallResult.IsActive()) + if (m_CompleteCallResult.IsActive()) { - m_CallResult.Cancel(); + m_CompleteCallResult.Cancel(); } if (m_RequestHandle != INVALID_HTTPREQUEST_HANDLE) @@ -257,33 +314,51 @@ class CUtilHTTPRequest : public IUtilHTTPRequest } } - //TODO stream read? - virtual void OnSteamHTTPCompleted(HTTPRequestCompleted_t* pResult, bool bHasError) + virtual void OnRespondStart() { - m_bRequesting = false; - m_bResponding = true; + if (!m_bResponding) + { + m_bRequesting = false; + m_bResponding = true; - if (m_Callbacks) + if (m_Callbacks) + { + m_Callbacks->OnUpdateState(UtilHTTPRequestState::Responding); + } + } + } + + virtual void OnRespondFinish() + { + if (m_bResponding) { - m_Callbacks->OnUpdateState(UtilHTTPRequestState::Responding); + m_bFinished = true; + m_bResponding = false; + + if (m_Callbacks) + { + m_Callbacks->OnUpdateState(UtilHTTPRequestState::Finished); + } } + } + + virtual void OnSteamHTTPCompleted(HTTPRequestCompleted_t* pResult, bool bHasError) + { + OnRespondStart(); m_bRequestSuccessful = pResult->m_bRequestSuccessful; - m_pResponse->OnSteamHTTPCompleted(m_RequestHandle, pResult, bHasError); + m_pResponse->OnSteamHTTPCompleted(pResult, bHasError); if (m_Callbacks) { - m_Callbacks->OnResponse(this, m_pResponse); + m_Callbacks->OnResponseComplete(this, m_pResponse); } m_bFinished = true; m_bResponding = false; - if (m_Callbacks) - { - m_Callbacks->OnUpdateState(UtilHTTPRequestState::Finished); - } + OnRespondFinish(); } public: @@ -298,7 +373,7 @@ class CUtilHTTPRequest : public IUtilHTTPRequest SteamAPICall_t SteamApiCall; if (SteamHTTP()->SendHTTPRequest(m_RequestHandle, &SteamApiCall)) { - m_CallResult.Set(SteamApiCall, this, &CUtilHTTPRequest::OnSteamHTTPCompleted); + m_CompleteCallResult.Set(SteamApiCall, this, &CUtilHTTPRequest::OnSteamHTTPCompleted); } m_bRequesting = true; @@ -467,6 +542,11 @@ class CUtilHTTPSyncRequest : public CUtilHTTPRequest return false; } + bool IsStream() const override + { + return false; + } + void WaitForResponse() override { if (m_hResponseEvent) @@ -525,6 +605,11 @@ class CUtilHTTPAsyncRequest : public CUtilHTTPRequest return true; } + bool IsStream() const override + { + return false; + } + void WaitForResponse() override { @@ -546,6 +631,135 @@ class CUtilHTTPAsyncRequest : public CUtilHTTPRequest } }; +class CUtilHTTPAsyncStreamRequest : public CUtilHTTPAsyncRequest +{ +private: + CCallResult m_StreamCompleteCallResult{}; + CCallResult m_RecvHeaderCallResult{}; + CCallResult m_RecvDataCallResult{}; + + IUtilHTTPStreamCallbacks* m_StreamCallbacks{}; + +public: + CUtilHTTPAsyncStreamRequest( + const UtilHTTPMethod method, + const std::string& host, + unsigned short port, + bool secure, + const std::string& target, + IUtilHTTPStreamCallbacks* callbacks, + HTTPCookieContainerHandle hCookieHandle) : + CUtilHTTPAsyncRequest(method, host, port, secure, target, nullptr, hCookieHandle), m_StreamCallbacks(callbacks) + { + + } + + ~CUtilHTTPAsyncStreamRequest() + { + if (m_StreamCompleteCallResult.IsActive()) + { + m_StreamCompleteCallResult.Cancel(); + } + if (m_RecvHeaderCallResult.IsActive()) + { + m_RecvHeaderCallResult.Cancel(); + } + if (m_RecvDataCallResult.IsActive()) + { + m_RecvDataCallResult.Cancel(); + } + } + + bool IsStream() const override + { + return true; + } + + virtual void OnSteamHTTPRecvHeader(HTTPRequestHeadersReceived_t* pResult, bool bHasError) + { + OnRespondStart(); + + m_pResponse->OnSteamHTTPRecvHeader(pResult, bHasError); + + if (m_StreamCallbacks) + { + m_StreamCallbacks->OnReceiveHeader(this, m_pResponse); + } + } + + virtual void OnSteamHTTPRecvData(HTTPRequestDataReceived_t* pResult, bool bHasError) + { + m_pResponse->OnSteamHTTPRecvData(pResult, bHasError); + + if (m_StreamCallbacks) + { + m_StreamCallbacks->OnReceiveData(this, m_pResponse); + } + } + + void OnSteamHTTPCompleted(HTTPRequestCompleted_t* pResult, bool bHasError) override + { + OnRespondStart(); + + m_bRequestSuccessful = pResult->m_bRequestSuccessful; + + m_pResponse->OnSteamHTTPCompleted(pResult, bHasError); + + if (m_StreamCallbacks) + { + m_StreamCallbacks->OnResponseComplete(this, m_pResponse); + } + + OnRespondFinish(); + } + + void OnRespondStart() override + { + if (!m_bResponding) + { + m_bRequesting = false; + m_bResponding = true; + + if (m_StreamCallbacks) + { + m_StreamCallbacks->OnUpdateState(UtilHTTPRequestState::Responding); + } + } + } + + void OnRespondFinish() override + { + if (m_bResponding) + { + m_bFinished = true; + m_bResponding = false; + + if (m_StreamCallbacks) + { + m_StreamCallbacks->OnUpdateState(UtilHTTPRequestState::Finished); + } + } + } + + void SendAsyncRequest() override + { + SteamAPICall_t SteamApiCall; + if (SteamHTTP()->SendHTTPRequestAndStreamResponse(m_RequestHandle, &SteamApiCall)) + { + m_StreamCompleteCallResult.Set(SteamApiCall, this, &CUtilHTTPAsyncStreamRequest::OnSteamHTTPCompleted); + m_RecvHeaderCallResult.Set(SteamApiCall, this, &CUtilHTTPAsyncStreamRequest::OnSteamHTTPRecvHeader); + m_RecvDataCallResult.Set(SteamApiCall, this, &CUtilHTTPAsyncStreamRequest::OnSteamHTTPRecvData); + } + + m_bRequesting = true; + + if (m_StreamCallbacks) + { + m_StreamCallbacks->OnUpdateState(UtilHTTPRequestState::Requesting); + } + } +}; + class CUtilHTTPClient : public IUtilHTTPClient { private: @@ -768,6 +982,26 @@ class CUtilHTTPClient : public IUtilHTTPClient return false; } + + IUtilHTTPRequest* CreateAsyncStreamRequestEx(const char* host, unsigned short port_us, const char* target, bool secure, const UtilHTTPMethod method, IUtilHTTPStreamCallbacks* callback) + { + return new CUtilHTTPAsyncStreamRequest(method, host, port_us, secure, target, callback, m_CookieHandle); + } + + IUtilHTTPRequest* CreateAsyncStreamRequest(const char* url, const UtilHTTPMethod method, IUtilHTTPStreamCallbacks* callbacks) override + { + CURLParsedResult result; + + if (!ParseUrlEx(url, &result)) + return NULL; + + auto RequestInstance = CreateAsyncStreamRequestEx(result.GetHost(), result.GetPort(), result.GetTarget(), result.IsSecure(), method, callbacks); + + AddToRequestPool(RequestInstance); + + return RequestInstance; + } + }; EXPOSE_INTERFACE(CUtilHTTPClient, IUtilHTTPClient, UTIL_HTTPCLIENT_STEAMAPI_INTERFACE_VERSION); \ No newline at end of file diff --git a/Plugins/SCModelDownloader/SCModelDatabase.cpp b/Plugins/SCModelDownloader/SCModelDatabase.cpp index e6ca2574..2f76094c 100644 --- a/Plugins/SCModelDownloader/SCModelDatabase.cpp +++ b/Plugins/SCModelDownloader/SCModelDatabase.cpp @@ -66,7 +66,7 @@ class CUtilHTTPCallbacks : public IUtilHTTPCallbacks delete this; } - void OnResponse(IUtilHTTPRequest* RequestInstance, IUtilHTTPResponse* ResponseInstance) override + void OnResponseComplete(IUtilHTTPRequest* RequestInstance, IUtilHTTPResponse* ResponseInstance) override { if (!RequestInstance->IsRequestSuccessful()) { diff --git a/include/Interface/IUtilHTTPClient.h b/include/Interface/IUtilHTTPClient.h index d9ebb892..3773d0f5 100644 --- a/include/Interface/IUtilHTTPClient.h +++ b/include/Interface/IUtilHTTPClient.h @@ -65,8 +65,12 @@ class IUtilHTTPResponse : public IBaseInterface public: virtual int GetStatusCode() const = 0; virtual IUtilHTTPPayload* GetPayload() const = 0; + virtual bool GetHeaderSize(const char* name, size_t *buflen) = 0; + virtual bool GetHeader(const char *name, char *buf, size_t buflen) = 0; virtual bool IsResponseCompleted() const = 0; virtual bool IsResponseError() const = 0; + virtual bool IsHeaderReceived() const = 0; + virtual bool IsStream() const = 0; }; class IUtilHTTPRequest : public IBaseInterface @@ -83,6 +87,7 @@ class IUtilHTTPRequest : public IBaseInterface virtual bool IsRequestSuccessful() const = 0; virtual bool IsFinished() const = 0; virtual bool IsAsync() const = 0; + virtual bool IsStream() const = 0; virtual UtilHTTPRequestState GetRequestState() const = 0; //Only available for SyncRequest @@ -99,10 +104,17 @@ class IUtilHTTPCallbacks : public IBaseInterface { public: virtual void Destroy() = 0; - virtual void OnResponse(IUtilHTTPRequest* RequestInstance, IUtilHTTPResponse *ResponseInstance) = 0; + virtual void OnResponseComplete(IUtilHTTPRequest* RequestInstance, IUtilHTTPResponse *ResponseInstance) = 0; virtual void OnUpdateState(UtilHTTPRequestState NewState) = 0; }; +class IUtilHTTPStreamCallbacks : public IUtilHTTPCallbacks +{ +public: + virtual void OnReceiveHeader(IUtilHTTPRequest* RequestInstance, IUtilHTTPResponse* ResponseInstance) = 0; + virtual void OnReceiveData(IUtilHTTPRequest* RequestInstance, IUtilHTTPResponse* ResponseInstance) = 0; +}; + class IURLParsedResult : public IBaseInterface { public: @@ -148,8 +160,9 @@ class IUtilHTTPClient : public IBaseInterface virtual IUtilHTTPRequest* GetRequestById(UtilHTTPRequestId_t id) = 0; virtual bool DestroyRequestById(UtilHTTPRequestId_t id) = 0; virtual bool SetCookie(const char *host, const char *url, const char* cookie) = 0; + virtual IUtilHTTPRequest* CreateAsyncStreamRequest(const char* url, const UtilHTTPMethod method, IUtilHTTPStreamCallbacks* callbacks) = 0; }; IUtilHTTPClient* UtilHTTPClient(); -#define UTIL_HTTPCLIENT_STEAMAPI_INTERFACE_VERSION "UtilHTTPClient_SteamAPI_002" \ No newline at end of file +#define UTIL_HTTPCLIENT_STEAMAPI_INTERFACE_VERSION "UtilHTTPClient_SteamAPI_003" \ No newline at end of file