Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,19 @@ jobs:
- name: Build
run: cmake --build cmake-build-win32 --target easy_http --config Release --parallel

- name: Prepare artifact files
shell: bash
run: |
mkdir -p stage/scripting/include
cp out/bin/Release/easy_http_amxx.dll stage/
cp out/pdb/Release/easy_http_amxx.pdb stage/
cp amxx/scripting/include/easy_http.inc amxx/scripting/include/easy_http_json.inc stage/scripting/include/

- name: Upload Windows artifacts
uses: actions/upload-artifact@v4
with:
name: easy_http-windows-win32
path: |
out/bin/Release/**
out/pdb/Release/**
path: stage/**
if-no-files-found: error
retention-days: 14

Expand Down Expand Up @@ -107,10 +113,16 @@ jobs:
cmake --build . --target easy_http -j$(nproc)
'

- name: Prepare artifact files
run: |
mkdir -p stage/scripting/include
cp out/bin/Release/easy_http_amxx_i386.so stage/
cp amxx/scripting/include/easy_http.inc amxx/scripting/include/easy_http_json.inc stage/scripting/include/

- name: Upload Linux artifact
uses: actions/upload-artifact@v4
with:
name: easy_http-linux-i386
path: out/bin/Release/easy_http_amxx_i386.so
path: stage/**
if-no-files-found: error
retention-days: 14
6 changes: 3 additions & 3 deletions amxx/scripting/include/easy_http.inc
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,16 @@ native ezhttp_option_set_body(EzHttpOptions:options_id, const body[]);
/**
* Copies serialized string to the requests body.
*
* @note Needs to be freed using ezjson_free() native.
* @note If using ezjson_move(), the C++ module will consume the JSON handle and free it.
*
* @param options_id Options identifier created via ezhttp_create_options().
* @param json EzJSON handle.
* @param json EzJSONOwnership tag (use ezjson_move() or ezjson_copy()).
* @param pretty True to format pretty JSON string, false to not.
*
* @return True if serialization was successful, false otherwise.
* @error If passed handle is not a valid value. If passed options_id is not exists.
*/
native bool:ezhttp_option_set_body_from_json(EzHttpOptions:options_id, EzJSON:json, bool:pretty = false);
native bool:ezhttp_option_set_body_from_json(EzHttpOptions:options_id, EzJSONOwnership:json, bool:pretty = false);

/**
* Appends a body to the HTTP request.
Expand Down
26 changes: 26 additions & 0 deletions amxx/scripting/include/easy_http_json.inc
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,32 @@ native EzJSON:ezjson_deep_copy(const EzJSON:value);
*/
native bool:ezjson_free(&EzJSON:handle);

/**
* Transfers ownership of a JSON handle.
* The receiving native will consume and free the handle, invalidating it.
*
* @param handle EzJSON handle to move
*
* @return EzJSONOwnership tagged handle
*/
stock EzJSONOwnership:ezjson_move(EzJSON:handle)
{
return EzJSONOwnership:handle;
}

/**
* Copies a JSON handle and transfers ownership of the copy.
* The original handle remains untouched and valid.
*
* @param handle EzJSON handle to copy
*
* @return EzJSONOwnership tagged handle
*/
stock EzJSONOwnership:ezjson_copy(EzJSON:handle)
{
return EzJSONOwnership:ezjson_deep_copy(handle);
}

/**
* Gets string data.
*
Expand Down
3 changes: 1 addition & 2 deletions amxx_test/scripting/ez_http_test.sma
Original file line number Diff line number Diff line change
Expand Up @@ -622,8 +622,7 @@ START_ASYNC_TEST(test_post_body_json)
ezjson_object_set_string(json_root, "StringField", "TestValue");
ezjson_object_set_number(json_root, "NumberField", 21);

ezhttp_option_set_body_from_json(opt, json_root);
ezjson_free(json_root);
ezhttp_option_set_body_from_json(opt, ezjson_move(json_root));
ezhttp_option_set_header(opt, "Content-Type", "application/json");

EZHTTP_OPTION_SET_TEST_DATA(opt)
Expand Down
8 changes: 8 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ find_package(Threads REQUIRED)
find_package(HLSDK REQUIRED)
find_package(metamod REQUIRED)

if (UNIX)
find_package(OpenSSL REQUIRED)
endif()

if (AMXX_EASY_HTTP_BUILD_STATIC)
set(LIBRARY_BUILD_TYPE STATIC)
set(TARGET_LIBRARIES_SCOPE PUBLIC)
Expand Down Expand Up @@ -70,6 +74,10 @@ add_library(easy_http::easy_http ALIAS ${TARGET_NAME})
target_include_directories(${TARGET_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
if (UNIX)
target_include_directories(${TARGET_NAME} PRIVATE ${OPENSSL_INCLUDE_DIR})
target_link_libraries(${TARGET_NAME} PRIVATE OpenSSL::Crypto)
endif()

target_compile_definitions(${TARGET_NAME} PRIVATE
NOMINMAX
Expand Down
2 changes: 2 additions & 0 deletions src/EasyHttpModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,12 +259,14 @@ void EasyHttpModule::ShutdownWithoutCallbacks()

if (terminating_ez)
{
terminating_ez->CancelAllRequests();
terminating_ez->ForgetAllRequests();
forgotten_easy_http_.emplace_back(std::move(terminating_ez));
}

if (forgettable_ez)
{
forgettable_ez->CancelAllRequests();
forgettable_ez->ForgetAllRequests();
forgotten_easy_http_.emplace_back(std::move(forgettable_ez));
}
Expand Down
25 changes: 23 additions & 2 deletions src/easy_http/EasyHttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,12 @@ EasyHttp::~EasyHttp()

{
std::lock_guard lock_guard(pending_requests_mutex_);
std::lock_guard lock_guard_completed(completed_requests_mutex_);
stop_requested_ = true;
}

pending_requests_cv_.notify_all();
completed_requests_cv_.notify_all();

for (auto &worker_thread : worker_threads_)
{
Expand Down Expand Up @@ -205,9 +207,12 @@ void EasyHttp::WorkerLoop()
bool forgotten = pending_request.request_control->forgotten.load();
if (!forgotten)
{
std::lock_guard lock_guard(completed_requests_mutex_);
std::unique_lock lock_guard(completed_requests_mutex_);
completed_requests_cv_.wait(lock_guard, [this]()
{ return stop_requested_ || completed_requests_.size() < 1024; });

forgotten = pending_request.request_control->forgotten.load();
if (!forgotten)
if (!forgotten && !stop_requested_)
{
completed_requests_.push_back(CompletedRequest{
pending_request.request_control,
Expand All @@ -232,6 +237,7 @@ bool EasyHttp::TryPopCompletedRequest(CompletedRequest &completed_request)

completed_request = std::move(completed_requests_.front());
completed_requests_.pop_front();
completed_requests_cv_.notify_one();
return true;
}

Expand All @@ -253,6 +259,7 @@ void EasyHttp::DropCompletedRequestsWithoutCallbacks()
}

completed_requests_.clear();
completed_requests_cv_.notify_all();
}

for (auto &request_control : completed_request_controls)
Expand Down Expand Up @@ -374,6 +381,8 @@ void EasyHttp::SetSessionCommonOptions(cpr::Session &session, const std::shared_

if (options.connect_timeout)
session.SetConnectTimeout(*options.connect_timeout);

curl_easy_setopt(session.GetCurlHolder()->handle, CURLOPT_NOSIGNAL, 1L);
}

Response EasyHttp::SendRequest(const std::shared_ptr<RequestControl> &request_control, RequestMethod method, const cpr::Url &url, const RequestOptions &options)
Expand Down Expand Up @@ -537,6 +546,7 @@ Response EasyHttp::FtpUpload(cpr::Session &session, const std::shared_ptr<Reques
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, 0L);
curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
if (options.require_secure)
{
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
Expand All @@ -545,6 +555,9 @@ Response EasyHttp::FtpUpload(cpr::Session &session, const std::shared_ptr<Reques

CURLcode curl_result = curl_easy_perform(curl);
file.close();
curl_easy_setopt(curl, CURLOPT_READFUNCTION, nullptr);
curl_easy_setopt(curl, CURLOPT_READDATA, nullptr);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 0L);

Response response(session.Complete(curl_result));
if (request_control->canceled.load())
Expand Down Expand Up @@ -597,7 +610,9 @@ Response EasyHttp::FtpDownloadSingle(cpr::Session &session, const std::shared_pt

CURL *curl = session.GetCurlHolder()->handle;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_UPLOAD, 0L);
curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, 0L);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
if (options.require_secure)
{
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
Expand All @@ -606,6 +621,8 @@ Response EasyHttp::FtpDownloadSingle(cpr::Session &session, const std::shared_pt

CURLcode curl_result = curl_easy_perform(curl);
file.close();
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, nullptr);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, nullptr);

Response response(session.Complete(curl_result));
if (request_control->canceled.load())
Expand Down Expand Up @@ -647,11 +664,13 @@ Response EasyHttp::FtpDownloadWildcard(cpr::Session &session, const std::shared_

CURL *curl = session.GetCurlHolder()->handle;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_UPLOAD, 0L);
curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, 0L);
curl_easy_setopt(curl, CURLOPT_WILDCARDMATCH, 1L);
curl_easy_setopt(curl, CURLOPT_CHUNK_BGN_FUNCTION, OnFtpWildcardChunkBegin);
curl_easy_setopt(curl, CURLOPT_CHUNK_END_FUNCTION, OnFtpWildcardChunkEnd);
curl_easy_setopt(curl, CURLOPT_CHUNK_DATA, &context);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
if (options.require_secure)
{
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
Expand All @@ -664,6 +683,8 @@ Response EasyHttp::FtpDownloadWildcard(cpr::Session &session, const std::shared_
curl_easy_setopt(curl, CURLOPT_CHUNK_BGN_FUNCTION, nullptr);
curl_easy_setopt(curl, CURLOPT_CHUNK_END_FUNCTION, nullptr);
curl_easy_setopt(curl, CURLOPT_CHUNK_DATA, nullptr);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, nullptr);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, nullptr);

Response response(session.Complete(curl_result));
if (request_control->canceled.load())
Expand Down
1 change: 1 addition & 0 deletions src/easy_http/EasyHttp.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ namespace ezhttp
std::deque<PendingRequest> pending_requests_;

std::mutex completed_requests_mutex_;
std::condition_variable completed_requests_cv_;
std::deque<CompletedRequest> completed_requests_;

mutable std::mutex requests_mutex_;
Expand Down
16 changes: 15 additions & 1 deletion src/easy_http/UrlUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace ezhttp

CURLUcode rc;
char* host = nullptr;
char* scheme = nullptr;

rc = curl_url_set(curl_url_, CURLUPART_URL, url.c_str(), 0);
if (rc != CURLUE_OK)
Expand All @@ -19,7 +20,20 @@ namespace ezhttp
if (rc != CURLUE_OK)
return "";

return { host };
std::string result;
rc = curl_url_get(curl_url_, CURLUPART_SCHEME, &scheme, 0);
if (rc == CURLUE_OK && scheme)
{
result = std::string(scheme) + "://" + host;
curl_free(scheme);
}
else
{
result = host;
}

curl_free(host);
return result;
}

void UrlUtils::InitializeIfNeeded()
Expand Down
Loading