Skip to content
Merged
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
81 changes: 35 additions & 46 deletions builtin-functions/kphp-light/stdlib/curl-functions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -205,18 +205,6 @@ define('CURL_NETRC_REQUIRED', 2);

define('CURLOPT_RETURNTRANSFER', 1234567);

define('CURLMOPT_PIPELINING', 1000);
define('CURLMOPT_MAXCONNECTS', 1001);
define('CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE', 1002);
define('CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE', 1003);
define('CURLMOPT_MAX_HOST_CONNECTIONS', 1004);
define('CURLMOPT_MAX_PIPELINE_LENGTH', 1005);
define('CURLMOPT_MAX_TOTAL_CONNECTIONS', 1006);

define('CURLPIPE_NOTHING', 0);
define('CURLPIPE_HTTP1', 1);
define('CURLPIPE_MULTIPLEX', 2);

define('CURL_HTTP_VERSION_NONE', 0);
define('CURL_HTTP_VERSION_1_0', 1);
define('CURL_HTTP_VERSION_1_1', 2);
Expand All @@ -225,6 +213,15 @@ define('CURL_HTTP_VERSION_2', CURL_HTTP_VERSION_2_0);
define('CURL_HTTP_VERSION_2TLS', 4);
define('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE', 5);

define('UPLOAD_ERR_OK', 0);
define('UPLOAD_ERR_INI_SIZE', 1);
define('UPLOAD_ERR_FORM_SIZE', 2);
define('UPLOAD_ERR_PARTIAL', 3);
define('UPLOAD_ERR_NO_FILE', 4);
define('UPLOAD_ERR_NO_TMP_DIR', 6);
define('UPLOAD_ERR_CANT_WRITE', 7);
define('UPLOAD_ERR_EXTENSION', 8);

define("CURLM_CALL_MULTI_PERFORM", -1);
define("CURLM_OK", 0);
define("CURLM_BAD_HANDLE", 1);
Expand All @@ -235,61 +232,53 @@ define("CURLM_BAD_SOCKET", 5);
define("CURLM_UNKNOWN_OPTION", 6);
define("CURLM_ADDED_ALREADY", 7);

define('UPLOAD_ERR_OK', 0);
define('UPLOAD_ERR_INI_SIZE', 1);
define('UPLOAD_ERR_FORM_SIZE', 2);
define('UPLOAD_ERR_PARTIAL', 3);
define('UPLOAD_ERR_NO_FILE', 4);
define('UPLOAD_ERR_NO_TMP_DIR', 6);
define('UPLOAD_ERR_CANT_WRITE', 7);
define('UPLOAD_ERR_EXTENSION', 8);
define('CURLMOPT_PIPELINING', 3);
define('CURLMOPT_MAXCONNECTS', 6);
// define('CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE', 30010);
// define('CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE', 30009);
define('CURLMOPT_MAX_HOST_CONNECTIONS', 7);
define('CURLMOPT_MAX_PIPELINE_LENGTH', 8);
define('CURLMOPT_MAX_TOTAL_CONNECTIONS', 13);

define('CURLPIPE_NOTHING', 0);
define('CURLPIPE_HTTP1', 1);
define('CURLPIPE_MULTIPLEX', 2);

// ===== SUPPORTED =====
// ===== EASY API =====
/** @kphp-extern-func-info interruptible */
function curl_init ($url ::: string = "") ::: int;
/** @kphp-extern-func-info interruptible */
function curl_reset ($curl_handle ::: int) ::: void;

function curl_setopt ($curl_handle ::: int, $option ::: int, $value ::: mixed) ::: bool;
function curl_setopt_array ($curl_handle ::: int, $options ::: array) ::: bool;

/** @kphp-extern-func-info interruptible */
function curl_exec ($curl_handle ::: int) ::: mixed;

/** @kphp-extern-func-info interruptible */
function curl_close ($curl_handle ::: int) ::: void;

/** @kphp-extern-func-info interruptible */
function curl_exec_concurrently($curl_handle ::: int, $timeout ::: float = 1.0): string|false;

function curl_error ($curl_handle ::: int) ::: string;
function curl_errno ($curl_handle ::: int) ::: int;

/** @kphp-extern-func-info interruptible */
function curl_getinfo ($curl_handle ::: int, $option ::: int = 0) ::: mixed;

// ===== UNSUPPORTED =====

/** @kphp-extern-func-info stub generation-required */
// ===== MULTI API =====
/** @kphp-extern-func-info interruptible */
function curl_multi_init () ::: int;
/** @kphp-extern-func-info stub generation-required */
/** @kphp-extern-func-info interruptible */
function curl_multi_add_handle ($multi_handle ::: int, $curl_handle ::: int) ::: int|false;
/** @kphp-extern-func-info stub generation-required */
function curl_multi_getcontent ($curl_handle ::: int ) ::: string|false|null;
/** @kphp-extern-func-info stub generation-required */
/** @kphp-extern-func-info interruptible */
function curl_multi_remove_handle ($multi_handle ::: int, $curl_handle ::: int) ::: int|false;
function curl_multi_setopt ($multi_handle ::: int, $option ::: int, $value ::: int) ::: bool;
/** @kphp-extern-func-info stub generation-required */
/** @kphp-extern-func-info interruptible */
function curl_multi_exec ($multi_handle ::: int, &$still_running ::: int) ::: int|false;
/** @kphp-extern-func-info stub generation-required */
function curl_multi_select ($multi_handle ::: int, $timeout ::: float = 1.0) ::: int|false;

/** @kphp-extern-func-info stub */
function curl_multi_info_read ($multi_handle ::: int, &$msgs_in_queue ::: int = TODO) ::: int[]|false;
/** @kphp-extern-func-info stub generation-required */
function curl_multi_remove_handle ($multi_handle ::: int, $curl_handle ::: int) ::: int|false;
/** @kphp-extern-func-info stub generation-required */
function curl_multi_errno ($multi_handle ::: int) ::: int|false;
/** @kphp-extern-func-info stub generation-required */
/** @kphp-extern-func-info interruptible */
function curl_multi_getcontent ($curl_handle ::: int ) ::: string|false|null;
/** @kphp-extern-func-info interruptible */
function curl_multi_close ($multi_handle ::: int) ::: void;
/** @kphp-extern-func-info stub generation-required */
function curl_multi_errno ($multi_handle ::: int) ::: int|false;
function curl_multi_strerror ($errornum ::: int) ::: string|null;
/** @kphp-extern-func-info interruptible */
function curl_multi_select ($multi_handle ::: int, $timeout ::: float = 1.0) ::: int|false;
/** @kphp-extern-func-info interruptible */
function curl_multi_info_read ($multi_handle ::: int, &$msgs_in_queue ::: int = TODO) ::: int[]|false;
39 changes: 35 additions & 4 deletions runtime-light/stdlib/curl/curl-context.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@

#pragma once

#include <cstddef>
#include <cstdint>
#include <optional>

#include "common/mixin/movable_only.h"
#include "runtime-common/core/runtime-core.h"
#include "runtime-light/stdlib/curl/defs.h"
#include "runtime-light/stdlib/diagnostics/logs.h"
#include "runtime-light/stdlib/web-transfer-lib/defs.h"

namespace kphp::web::curl {

struct easy_context {
bool return_transfer{false};
std::optional<string> private_data{std::nullopt};
struct curl_context : vk::movable_only {
int64_t error_code{0};
std::array<std::byte, kphp::web::curl::CURL_ERROR_SIZE> error_description{std::byte{0}};
bool has_been_executed{false}; // Need for providing same as in PHP semantics of curl_getinfo(..., CURLINFO_EFFECTIVE_URL)

inline auto set_errno(int64_t code, std::string_view description) noexcept {
// If Web Transfer Lib specific error
Expand All @@ -46,12 +45,44 @@ struct easy_context {
set_errno(static_cast<int64_t>(code), std::move(description));
}

inline auto set_errno(kphp::web::curl::CURLME code, std::string_view description) noexcept {
set_errno(static_cast<int64_t>(code), description);
}

inline auto set_errno(kphp::web::curl::CURLME code, std::optional<string> description = std::nullopt) noexcept {
set_errno(static_cast<int64_t>(code), std::move(description));
}

template<size_t N>
inline auto bad_option_error(const char (&msg)[N]) noexcept {
static_assert(N <= CURL_ERROR_SIZE, "too long error");
kphp::log::warning("{}", msg);
set_errno(CURLE::BAD_FUNCTION_ARGUMENT, {{msg, N}});
}

inline auto errors_reset() noexcept {
error_code = 0;
error_description.fill(std::byte{0});
}
};

struct easy_context : curl_context {
using curl_context::curl_context;

bool return_transfer{false};
std::optional<string> private_data{std::nullopt};
bool has_been_executed{false}; // Need for providing same as in PHP semantics of curl_getinfo(..., CURLINFO_EFFECTIVE_URL)

inline auto reset() noexcept {
curl_context::errors_reset();
return_transfer = false;
private_data = std::nullopt;
has_been_executed = false;
}
};

struct multi_context : curl_context {
using curl_context::curl_context;
};

} // namespace kphp::web::curl
67 changes: 52 additions & 15 deletions runtime-light/stdlib/curl/curl-easy-functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ inline auto f$curl_init(string url = string{""}) noexcept -> kphp::coro::task<kp
co_return 0;
}
const auto st{kphp::web::simple_transfer{*open_res}};
auto& easy_ctx{CurlInstanceState::get().easy_ctx.get_or_init(st.descriptor)};
auto setopt_res{
kphp::web::set_transfer_property(st, static_cast<kphp::web::property_id>(kphp::web::curl::CURLOPT::URL), kphp::web::property_value::as_string(url))};
if (!setopt_res.has_value()) [[unlikely]] {
auto& easy_ctx{CurlInstanceState::get().easy_ctx.get_or_init(st.descriptor)};
easy_ctx.set_errno(setopt_res.error().code, setopt_res.error().description);
kphp::web::curl::print_error("could not set URL for a new curl easy handle", std::move(setopt_res.error()));
co_return 0;
Expand All @@ -38,7 +38,11 @@ inline auto f$curl_init(string url = string{""}) noexcept -> kphp::coro::task<kp
}

inline auto f$curl_setopt(kphp::web::curl::easy_type easy_id, int64_t option, const mixed& value) noexcept -> bool { // NOLINT
auto& easy_ctx{CurlInstanceState::get().easy_ctx.get_or_init(easy_id)};
auto& curl_state{CurlInstanceState::get()};
if (!curl_state.easy_ctx.has(easy_id)) {
return false;
}
auto& easy_ctx{curl_state.easy_ctx.get_or_init(easy_id)};
auto st{kphp::web::simple_transfer{.descriptor = easy_id}};

if (option == static_cast<int64_t>(kphp::web::curl::CURLINFO::HEADER_OUT)) {
Expand Down Expand Up @@ -399,8 +403,12 @@ inline auto f$curl_setopt_array(kphp::web::curl::easy_type easy_id, const array<
}

inline auto f$curl_exec(kphp::web::curl::easy_type easy_id) noexcept -> kphp::coro::task<mixed> {
auto& curl_state{CurlInstanceState::get()};
if (!curl_state.easy_ctx.has(easy_id)) {
co_return false;
}
auto res{co_await kphp::forks::id_managed(kphp::web::simple_transfer_perform(kphp::web::simple_transfer{easy_id}))};
auto& easy_ctx{CurlInstanceState::get().easy_ctx.get_or_init(easy_id)};
auto& easy_ctx{curl_state.easy_ctx.get_or_init(easy_id)};
easy_ctx.has_been_executed = true;
if (!res.has_value()) [[unlikely]] {
easy_ctx.set_errno(res.error().code, res.error().description);
Expand All @@ -415,7 +423,11 @@ inline auto f$curl_exec(kphp::web::curl::easy_type easy_id) noexcept -> kphp::co
}

inline auto f$curl_close(kphp::web::curl::easy_type easy_id) noexcept -> kphp::coro::task<void> {
auto& easy_ctx{CurlInstanceState::get().easy_ctx.get_or_init(easy_id)};
auto& curl_state{CurlInstanceState::get()};
if (!curl_state.easy_ctx.has(easy_id)) {
co_return;
}
auto& easy_ctx{curl_state.easy_ctx.get_or_init(easy_id)};
auto res{co_await kphp::forks::id_managed(kphp::web::simple_transfer_close(kphp::web::simple_transfer{easy_id}))};
if (!res.has_value()) [[unlikely]] {
easy_ctx.set_errno(res.error().code, res.error().description);
Expand All @@ -424,19 +436,26 @@ inline auto f$curl_close(kphp::web::curl::easy_type easy_id) noexcept -> kphp::c
}

inline auto f$curl_reset(kphp::web::curl::easy_type easy_id) noexcept -> kphp::coro::task<void> {
auto& easy_ctx{CurlInstanceState::get().easy_ctx.get_or_init(easy_id)};
auto& curl_state{CurlInstanceState::get()};
if (!curl_state.easy_ctx.has(easy_id)) {
co_return;
}
auto& easy_ctx{curl_state.easy_ctx.get_or_init(easy_id)};
auto res{co_await kphp::forks::id_managed(kphp::web::simple_transfer_reset(kphp::web::simple_transfer{easy_id}))};
if (!res.has_value()) [[unlikely]] {
easy_ctx.set_errno(res.error().code, res.error().description);
kphp::web::curl::print_error("could not reset curl easy handle", std::move(res.error()));
co_return;
}
easy_ctx.return_transfer = false;
easy_ctx.private_data = std::nullopt;
easy_ctx.reset();
}

inline auto f$curl_exec_concurrently(kphp::web::curl::easy_type easy_id, double timeout_sec = 1.0) noexcept -> kphp::coro::task<Optional<string>> {
auto& easy_ctx{CurlInstanceState::get().easy_ctx.get_or_init(easy_id)};
auto& curl_state{CurlInstanceState::get()};
if (!curl_state.easy_ctx.has(easy_id)) {
co_return false;
}
auto& easy_ctx{curl_state.easy_ctx.get_or_init(easy_id)};
auto sched_res{co_await kphp::coro::io_scheduler::get().schedule(
kphp::forks::id_managed(kphp::web::simple_transfer_perform(kphp::web::simple_transfer{easy_id})),
kphp::web::curl::details::normalize_timeout(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::duration<double>{timeout_sec})))};
Expand All @@ -457,7 +476,11 @@ inline auto f$curl_exec_concurrently(kphp::web::curl::easy_type easy_id, double
}

inline auto f$curl_error(kphp::web::curl::easy_type easy_id) noexcept -> string {
auto& easy_ctx{CurlInstanceState::get().easy_ctx.get_or_init(easy_id)};
auto& curl_state{CurlInstanceState::get()};
if (!curl_state.easy_ctx.has(easy_id)) {
return {};
}
auto& easy_ctx{curl_state.easy_ctx.get_or_init(easy_id)};
if (easy_ctx.error_code != static_cast<int64_t>(kphp::web::curl::CURLE::OK)) [[likely]] {
const auto* const desc_data{reinterpret_cast<const char*>(easy_ctx.error_description.data())};
const auto desc_size{static_cast<string::size_type>(easy_ctx.error_description.size())};
Expand All @@ -467,16 +490,26 @@ inline auto f$curl_error(kphp::web::curl::easy_type easy_id) noexcept -> string
}

inline auto f$curl_errno(kphp::web::curl::easy_type easy_id) noexcept -> int64_t {
auto& easy_ctx{CurlInstanceState::get().easy_ctx.get_or_init(easy_id)};
auto& curl_state{CurlInstanceState::get()};
if (!curl_state.easy_ctx.has(easy_id)) {
return 0;
}
auto& easy_ctx{curl_state.easy_ctx.get_or_init(easy_id)};
return easy_ctx.error_code;
}

inline auto f$curl_getinfo(kphp::web::curl::easy_type easy_id, int64_t option = 0) noexcept -> kphp::coro::task<mixed> {
auto& easy_ctx{CurlInstanceState::get().easy_ctx.get_or_init(easy_id)};
auto& curl_state{CurlInstanceState::get()};
if (!curl_state.easy_ctx.has(easy_id)) {
co_return false;
}
auto& easy_ctx{curl_state.easy_ctx.get_or_init(easy_id)};
switch (static_cast<kphp::web::curl::CURLINFO>(option)) {
case kphp::web::curl::CURLINFO::NONE: {
auto res{co_await kphp::forks::id_managed(kphp::web::get_transfer_properties(kphp::web::simple_transfer{easy_id}, std::nullopt))};
auto res{co_await kphp::forks::id_managed(
kphp::web::get_transfer_properties(kphp::web::simple_transfer{easy_id}, std::nullopt, kphp::web::get_properties_policy::load))};
if (!res.has_value()) [[unlikely]] {
easy_ctx.set_errno(res.error().code, res.error().description);
kphp::web::curl::print_error("could not get all info options of easy handle", std::move(res.error()));
co_return false;
}
Expand All @@ -494,7 +527,8 @@ inline auto f$curl_getinfo(kphp::web::curl::easy_type easy_id, int64_t option =

if (!easy_ctx.has_been_executed) {
const auto url_opt_id{static_cast<kphp::web::property_id>(kphp::web::curl::CURLOPT::URL)};
const auto url{co_await kphp::forks::id_managed(kphp::web::get_transfer_properties(kphp::web::simple_transfer{easy_id}, url_opt_id))};
const auto url{co_await kphp::forks::id_managed(
kphp::web::get_transfer_properties(kphp::web::simple_transfer{easy_id}, url_opt_id, kphp::web::get_properties_policy::cached))};
if (url.has_value()) {
const auto& v{(*url).find(url_opt_id)};
kphp::log::assertion(v != (*url).end());
Expand Down Expand Up @@ -538,7 +572,8 @@ inline auto f$curl_getinfo(kphp::web::curl::easy_type easy_id, int64_t option =
case kphp::web::curl::CURLINFO::EFFECTIVE_URL:
if (!easy_ctx.has_been_executed) {
const auto url_opt_id{static_cast<kphp::web::property_id>(kphp::web::curl::CURLOPT::URL)};
const auto url{co_await kphp::forks::id_managed(kphp::web::get_transfer_properties(kphp::web::simple_transfer{easy_id}, url_opt_id))};
const auto url{co_await kphp::forks::id_managed(
kphp::web::get_transfer_properties(kphp::web::simple_transfer{easy_id}, url_opt_id, kphp::web::get_properties_policy::cached))};
if (url.has_value()) {
co_return (*url).find(url_opt_id)->second.to_mixed();
}
Expand Down Expand Up @@ -570,8 +605,10 @@ inline auto f$curl_getinfo(kphp::web::curl::easy_type easy_id, int64_t option =
case kphp::web::curl::CURLINFO::CONDITION_UNMET:
case kphp::web::curl::CURLINFO::NUM_CONNECTS:
case kphp::web::curl::CURLINFO::HEADER_OUT: {
auto res{co_await kphp::forks::id_managed(kphp::web::get_transfer_properties(kphp::web::simple_transfer{easy_id}, option))};
auto res{co_await kphp::forks::id_managed(
kphp::web::get_transfer_properties(kphp::web::simple_transfer{easy_id}, option, kphp::web::get_properties_policy::load))};
if (!res.has_value()) [[unlikely]] {
easy_ctx.set_errno(res.error().code, res.error().description);
kphp::web::curl::print_error("could not get a specific info of easy handle", std::move(res.error()));
co_return 0;
}
Expand Down
Loading
Loading