Skip to content

Commit de18ce5

Browse files
authored
[k2] add curl, part 3 (#1462)
* Made the semantics of Easy part is more similar with a reference implementation * Reorganized files hierarchy and better naming * Supported the whole list of Multi methods --------- Signed-off-by: Petr Shumilov <p.shumilov@vkteam.ru>
1 parent 6032c49 commit de18ce5

25 files changed

Lines changed: 1390 additions & 175 deletions

builtin-functions/kphp-light/stdlib/curl-functions.txt

Lines changed: 35 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -205,18 +205,6 @@ define('CURL_NETRC_REQUIRED', 2);
205205

206206
define('CURLOPT_RETURNTRANSFER', 1234567);
207207

208-
define('CURLMOPT_PIPELINING', 1000);
209-
define('CURLMOPT_MAXCONNECTS', 1001);
210-
define('CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE', 1002);
211-
define('CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE', 1003);
212-
define('CURLMOPT_MAX_HOST_CONNECTIONS', 1004);
213-
define('CURLMOPT_MAX_PIPELINE_LENGTH', 1005);
214-
define('CURLMOPT_MAX_TOTAL_CONNECTIONS', 1006);
215-
216-
define('CURLPIPE_NOTHING', 0);
217-
define('CURLPIPE_HTTP1', 1);
218-
define('CURLPIPE_MULTIPLEX', 2);
219-
220208
define('CURL_HTTP_VERSION_NONE', 0);
221209
define('CURL_HTTP_VERSION_1_0', 1);
222210
define('CURL_HTTP_VERSION_1_1', 2);
@@ -225,6 +213,15 @@ define('CURL_HTTP_VERSION_2', CURL_HTTP_VERSION_2_0);
225213
define('CURL_HTTP_VERSION_2TLS', 4);
226214
define('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE', 5);
227215

216+
define('UPLOAD_ERR_OK', 0);
217+
define('UPLOAD_ERR_INI_SIZE', 1);
218+
define('UPLOAD_ERR_FORM_SIZE', 2);
219+
define('UPLOAD_ERR_PARTIAL', 3);
220+
define('UPLOAD_ERR_NO_FILE', 4);
221+
define('UPLOAD_ERR_NO_TMP_DIR', 6);
222+
define('UPLOAD_ERR_CANT_WRITE', 7);
223+
define('UPLOAD_ERR_EXTENSION', 8);
224+
228225
define("CURLM_CALL_MULTI_PERFORM", -1);
229226
define("CURLM_OK", 0);
230227
define("CURLM_BAD_HANDLE", 1);
@@ -235,61 +232,53 @@ define("CURLM_BAD_SOCKET", 5);
235232
define("CURLM_UNKNOWN_OPTION", 6);
236233
define("CURLM_ADDED_ALREADY", 7);
237234

238-
define('UPLOAD_ERR_OK', 0);
239-
define('UPLOAD_ERR_INI_SIZE', 1);
240-
define('UPLOAD_ERR_FORM_SIZE', 2);
241-
define('UPLOAD_ERR_PARTIAL', 3);
242-
define('UPLOAD_ERR_NO_FILE', 4);
243-
define('UPLOAD_ERR_NO_TMP_DIR', 6);
244-
define('UPLOAD_ERR_CANT_WRITE', 7);
245-
define('UPLOAD_ERR_EXTENSION', 8);
235+
define('CURLMOPT_PIPELINING', 3);
236+
define('CURLMOPT_MAXCONNECTS', 6);
237+
// define('CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE', 30010);
238+
// define('CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE', 30009);
239+
define('CURLMOPT_MAX_HOST_CONNECTIONS', 7);
240+
define('CURLMOPT_MAX_PIPELINE_LENGTH', 8);
241+
define('CURLMOPT_MAX_TOTAL_CONNECTIONS', 13);
242+
243+
define('CURLPIPE_NOTHING', 0);
244+
define('CURLPIPE_HTTP1', 1);
245+
define('CURLPIPE_MULTIPLEX', 2);
246246

247-
// ===== SUPPORTED =====
247+
// ===== EASY API =====
248248
/** @kphp-extern-func-info interruptible */
249249
function curl_init ($url ::: string = "") ::: int;
250250
/** @kphp-extern-func-info interruptible */
251251
function curl_reset ($curl_handle ::: int) ::: void;
252-
253252
function curl_setopt ($curl_handle ::: int, $option ::: int, $value ::: mixed) ::: bool;
254253
function curl_setopt_array ($curl_handle ::: int, $options ::: array) ::: bool;
255-
256254
/** @kphp-extern-func-info interruptible */
257255
function curl_exec ($curl_handle ::: int) ::: mixed;
258-
259256
/** @kphp-extern-func-info interruptible */
260257
function curl_close ($curl_handle ::: int) ::: void;
261-
262258
/** @kphp-extern-func-info interruptible */
263259
function curl_exec_concurrently($curl_handle ::: int, $timeout ::: float = 1.0): string|false;
264-
265260
function curl_error ($curl_handle ::: int) ::: string;
266261
function curl_errno ($curl_handle ::: int) ::: int;
267-
268262
/** @kphp-extern-func-info interruptible */
269263
function curl_getinfo ($curl_handle ::: int, $option ::: int = 0) ::: mixed;
270264

271-
// ===== UNSUPPORTED =====
272-
273-
/** @kphp-extern-func-info stub generation-required */
265+
// ===== MULTI API =====
266+
/** @kphp-extern-func-info interruptible */
274267
function curl_multi_init () ::: int;
275-
/** @kphp-extern-func-info stub generation-required */
268+
/** @kphp-extern-func-info interruptible */
276269
function curl_multi_add_handle ($multi_handle ::: int, $curl_handle ::: int) ::: int|false;
277-
/** @kphp-extern-func-info stub generation-required */
278-
function curl_multi_getcontent ($curl_handle ::: int ) ::: string|false|null;
279-
/** @kphp-extern-func-info stub generation-required */
270+
/** @kphp-extern-func-info interruptible */
271+
function curl_multi_remove_handle ($multi_handle ::: int, $curl_handle ::: int) ::: int|false;
280272
function curl_multi_setopt ($multi_handle ::: int, $option ::: int, $value ::: int) ::: bool;
281-
/** @kphp-extern-func-info stub generation-required */
273+
/** @kphp-extern-func-info interruptible */
282274
function curl_multi_exec ($multi_handle ::: int, &$still_running ::: int) ::: int|false;
283-
/** @kphp-extern-func-info stub generation-required */
284-
function curl_multi_select ($multi_handle ::: int, $timeout ::: float = 1.0) ::: int|false;
285-
286-
/** @kphp-extern-func-info stub */
287-
function curl_multi_info_read ($multi_handle ::: int, &$msgs_in_queue ::: int = TODO) ::: int[]|false;
288-
/** @kphp-extern-func-info stub generation-required */
289-
function curl_multi_remove_handle ($multi_handle ::: int, $curl_handle ::: int) ::: int|false;
290-
/** @kphp-extern-func-info stub generation-required */
291-
function curl_multi_errno ($multi_handle ::: int) ::: int|false;
292-
/** @kphp-extern-func-info stub generation-required */
275+
/** @kphp-extern-func-info interruptible */
276+
function curl_multi_getcontent ($curl_handle ::: int ) ::: string|false|null;
277+
/** @kphp-extern-func-info interruptible */
293278
function curl_multi_close ($multi_handle ::: int) ::: void;
294-
/** @kphp-extern-func-info stub generation-required */
279+
function curl_multi_errno ($multi_handle ::: int) ::: int|false;
295280
function curl_multi_strerror ($errornum ::: int) ::: string|null;
281+
/** @kphp-extern-func-info interruptible */
282+
function curl_multi_select ($multi_handle ::: int, $timeout ::: float = 1.0) ::: int|false;
283+
/** @kphp-extern-func-info interruptible */
284+
function curl_multi_info_read ($multi_handle ::: int, &$msgs_in_queue ::: int = TODO) ::: int[]|false;

runtime-light/stdlib/curl/curl-context.h

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,21 @@
44

55
#pragma once
66

7+
#include <cstddef>
78
#include <cstdint>
89
#include <optional>
910

11+
#include "common/mixin/movable_only.h"
1012
#include "runtime-common/core/runtime-core.h"
1113
#include "runtime-light/stdlib/curl/defs.h"
1214
#include "runtime-light/stdlib/diagnostics/logs.h"
1315
#include "runtime-light/stdlib/web-transfer-lib/defs.h"
1416

1517
namespace kphp::web::curl {
1618

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

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

48+
inline auto set_errno(kphp::web::curl::CURLME code, std::string_view description) noexcept {
49+
set_errno(static_cast<int64_t>(code), description);
50+
}
51+
52+
inline auto set_errno(kphp::web::curl::CURLME code, std::optional<string> description = std::nullopt) noexcept {
53+
set_errno(static_cast<int64_t>(code), std::move(description));
54+
}
55+
4956
template<size_t N>
5057
inline auto bad_option_error(const char (&msg)[N]) noexcept {
5158
static_assert(N <= CURL_ERROR_SIZE, "too long error");
5259
kphp::log::warning("{}", msg);
5360
set_errno(CURLE::BAD_FUNCTION_ARGUMENT, {{msg, N}});
5461
}
62+
63+
inline auto errors_reset() noexcept {
64+
error_code = 0;
65+
error_description.fill(std::byte{0});
66+
}
67+
};
68+
69+
struct easy_context : curl_context {
70+
using curl_context::curl_context;
71+
72+
bool return_transfer{false};
73+
std::optional<string> private_data{std::nullopt};
74+
bool has_been_executed{false}; // Need for providing same as in PHP semantics of curl_getinfo(..., CURLINFO_EFFECTIVE_URL)
75+
76+
inline auto reset() noexcept {
77+
curl_context::errors_reset();
78+
return_transfer = false;
79+
private_data = std::nullopt;
80+
has_been_executed = false;
81+
}
82+
};
83+
84+
struct multi_context : curl_context {
85+
using curl_context::curl_context;
5586
};
5687

5788
} // namespace kphp::web::curl

runtime-light/stdlib/curl/curl-easy-functions.h

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ inline auto f$curl_init(string url = string{""}) noexcept -> kphp::coro::task<kp
2626
co_return 0;
2727
}
2828
const auto st{kphp::web::simple_transfer{*open_res}};
29+
auto& easy_ctx{CurlInstanceState::get().easy_ctx.get_or_init(st.descriptor)};
2930
auto setopt_res{
3031
kphp::web::set_transfer_property(st, static_cast<kphp::web::property_id>(kphp::web::curl::CURLOPT::URL), kphp::web::property_value::as_string(url))};
3132
if (!setopt_res.has_value()) [[unlikely]] {
32-
auto& easy_ctx{CurlInstanceState::get().easy_ctx.get_or_init(st.descriptor)};
3333
easy_ctx.set_errno(setopt_res.error().code, setopt_res.error().description);
3434
kphp::web::curl::print_error("could not set URL for a new curl easy handle", std::move(setopt_res.error()));
3535
co_return 0;
@@ -38,7 +38,11 @@ inline auto f$curl_init(string url = string{""}) noexcept -> kphp::coro::task<kp
3838
}
3939

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

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

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

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

426438
inline auto f$curl_reset(kphp::web::curl::easy_type easy_id) noexcept -> kphp::coro::task<void> {
427-
auto& easy_ctx{CurlInstanceState::get().easy_ctx.get_or_init(easy_id)};
439+
auto& curl_state{CurlInstanceState::get()};
440+
if (!curl_state.easy_ctx.has(easy_id)) {
441+
co_return;
442+
}
443+
auto& easy_ctx{curl_state.easy_ctx.get_or_init(easy_id)};
428444
auto res{co_await kphp::forks::id_managed(kphp::web::simple_transfer_reset(kphp::web::simple_transfer{easy_id}))};
429445
if (!res.has_value()) [[unlikely]] {
430446
easy_ctx.set_errno(res.error().code, res.error().description);
431447
kphp::web::curl::print_error("could not reset curl easy handle", std::move(res.error()));
432448
co_return;
433449
}
434-
easy_ctx.return_transfer = false;
435-
easy_ctx.private_data = std::nullopt;
450+
easy_ctx.reset();
436451
}
437452

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

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

469492
inline auto f$curl_errno(kphp::web::curl::easy_type easy_id) noexcept -> int64_t {
470-
auto& easy_ctx{CurlInstanceState::get().easy_ctx.get_or_init(easy_id)};
493+
auto& curl_state{CurlInstanceState::get()};
494+
if (!curl_state.easy_ctx.has(easy_id)) {
495+
return 0;
496+
}
497+
auto& easy_ctx{curl_state.easy_ctx.get_or_init(easy_id)};
471498
return easy_ctx.error_code;
472499
}
473500

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

495528
if (!easy_ctx.has_been_executed) {
496529
const auto url_opt_id{static_cast<kphp::web::property_id>(kphp::web::curl::CURLOPT::URL)};
497-
const auto url{co_await kphp::forks::id_managed(kphp::web::get_transfer_properties(kphp::web::simple_transfer{easy_id}, url_opt_id))};
530+
const auto url{co_await kphp::forks::id_managed(
531+
kphp::web::get_transfer_properties(kphp::web::simple_transfer{easy_id}, url_opt_id, kphp::web::get_properties_policy::cached))};
498532
if (url.has_value()) {
499533
const auto& v{(*url).find(url_opt_id)};
500534
kphp::log::assertion(v != (*url).end());
@@ -538,7 +572,8 @@ inline auto f$curl_getinfo(kphp::web::curl::easy_type easy_id, int64_t option =
538572
case kphp::web::curl::CURLINFO::EFFECTIVE_URL:
539573
if (!easy_ctx.has_been_executed) {
540574
const auto url_opt_id{static_cast<kphp::web::property_id>(kphp::web::curl::CURLOPT::URL)};
541-
const auto url{co_await kphp::forks::id_managed(kphp::web::get_transfer_properties(kphp::web::simple_transfer{easy_id}, url_opt_id))};
575+
const auto url{co_await kphp::forks::id_managed(
576+
kphp::web::get_transfer_properties(kphp::web::simple_transfer{easy_id}, url_opt_id, kphp::web::get_properties_policy::cached))};
542577
if (url.has_value()) {
543578
co_return (*url).find(url_opt_id)->second.to_mixed();
544579
}
@@ -570,8 +605,10 @@ inline auto f$curl_getinfo(kphp::web::curl::easy_type easy_id, int64_t option =
570605
case kphp::web::curl::CURLINFO::CONDITION_UNMET:
571606
case kphp::web::curl::CURLINFO::NUM_CONNECTS:
572607
case kphp::web::curl::CURLINFO::HEADER_OUT: {
573-
auto res{co_await kphp::forks::id_managed(kphp::web::get_transfer_properties(kphp::web::simple_transfer{easy_id}, option))};
608+
auto res{co_await kphp::forks::id_managed(
609+
kphp::web::get_transfer_properties(kphp::web::simple_transfer{easy_id}, option, kphp::web::get_properties_policy::load))};
574610
if (!res.has_value()) [[unlikely]] {
611+
easy_ctx.set_errno(res.error().code, res.error().description);
575612
kphp::web::curl::print_error("could not get a specific info of easy handle", std::move(res.error()));
576613
co_return 0;
577614
}

0 commit comments

Comments
 (0)