Skip to content

Commit 44f4ec2

Browse files
committed
Add curl_multi_getcontent support
Signed-off-by: Petr Shumilov <p.shumilov@vkteam.ru>
1 parent a7f83a2 commit 44f4ec2

6 files changed

Lines changed: 148 additions & 66 deletions

File tree

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,13 +281,14 @@ function curl_multi_setopt ($multi_handle ::: int, $option ::: int, $value ::: i
281281
/** @kphp-extern-func-info interruptible */
282282
function curl_multi_exec ($multi_handle ::: int, &$still_running ::: int) ::: int|false;
283283

284+
/** @kphp-extern-func-info interruptible */
285+
function curl_multi_getcontent ($curl_handle ::: int ) ::: string|false|null;
286+
284287
/** @kphp-extern-func-info interruptible */
285288
function curl_multi_close ($multi_handle ::: int) ::: void;
286289

287290
// ===== UNSUPPORTED =====
288291
/** @kphp-extern-func-info stub generation-required */
289-
function curl_multi_getcontent ($curl_handle ::: int ) ::: string|false|null;
290-
/** @kphp-extern-func-info stub generation-required */
291292
function curl_multi_select ($multi_handle ::: int, $timeout ::: float = 1.0) ::: int|false;
292293

293294
/** @kphp-extern-func-info stub */

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "runtime-light/stdlib/web-transfer-lib/defs.h"
2020
#include "runtime-light/stdlib/web-transfer-lib/details/web-property.h"
2121
#include "runtime-light/stdlib/web-transfer-lib/web-composite-transfer.h"
22+
#include "runtime-light/stdlib/web-transfer-lib/web-simple-transfer.h"
2223

2324
inline auto f$curl_multi_init() noexcept -> kphp::coro::task<kphp::web::curl::multi_type> {
2425
auto open_res{co_await kphp::forks::id_managed(kphp::web::composite_transfer_open(kphp::web::transfer_backend::CURL))};
@@ -122,7 +123,7 @@ inline auto f$curl_multi_setopt(kphp::web::curl::multi_type multi_id, int64_t op
122123
}
123124
}
124125

125-
inline auto f$curl_multi_exec(kphp::web::curl::multi_type multi_id, int64_t& still_running) noexcept -> kphp::coro::task<Optional<int64_t>>{
126+
inline auto f$curl_multi_exec(kphp::web::curl::multi_type multi_id, int64_t& still_running) noexcept -> kphp::coro::task<Optional<int64_t>> {
126127
auto& curl_state{CurlInstanceState::get()};
127128
if (!curl_state.multi_ctx.has(multi_id)) [[unlikely]] {
128129
co_return false;
@@ -139,6 +140,24 @@ inline auto f$curl_multi_exec(kphp::web::curl::multi_type multi_id, int64_t& sti
139140
co_return multi_ctx.error_code;
140141
}
141142

143+
inline auto f$curl_multi_getcontent(kphp::web::curl::easy_type easy_id) noexcept -> kphp::coro::task<Optional<string>> {
144+
auto& curl_state{CurlInstanceState::get()};
145+
if (!curl_state.easy_ctx.has(easy_id)) [[unlikely]] {
146+
co_return false;
147+
}
148+
auto& easy_ctx{curl_state.easy_ctx.get_or_init(easy_id)};
149+
if (easy_ctx.return_transfer) {
150+
auto res{co_await kphp::forks::id_managed(kphp::web::simple_transfer_get_response(kphp::web::simple_transfer{easy_id}))};
151+
if (!res.has_value()) [[unlikely]] {
152+
easy_ctx.set_errno(res.error().code, res.error().description);
153+
kphp::web::curl::print_error("Could not get response of curl easy handle", std::move(res.error()));
154+
co_return false;
155+
}
156+
co_return (*res).body;
157+
}
158+
co_return Optional<string>{};
159+
}
160+
142161
inline auto f$curl_multi_close(kphp::web::curl::multi_type multi_id) noexcept -> kphp::coro::task<void> {
143162
auto& curl_state{CurlInstanceState::get()};
144163
if (!curl_state.multi_ctx.has(multi_id)) [[unlikely]] {
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Compiler for PHP (aka KPHP)
2+
// Copyright (c) 2025 LLC «V Kontakte»
3+
// Distributed under the GPL v3 License, see LICENSE.notice.txt
4+
5+
#pragma once
6+
7+
#include <__expected/expected.h>
8+
#include <cstddef>
9+
#include <expected>
10+
#include <optional>
11+
#include <span>
12+
#include <utility>
13+
#include <variant>
14+
15+
#include "runtime-common/core/runtime-core.h"
16+
#include "runtime-light/coroutine/task.h"
17+
#include "runtime-light/stdlib/diagnostics/logs.h"
18+
#include "runtime-light/stdlib/web-transfer-lib/defs.h"
19+
#include "runtime-light/stdlib/web-transfer-lib/details/web-error.h"
20+
#include "runtime-light/stdlib/web-transfer-lib/web-state.h"
21+
#include "runtime-light/tl/tl-core.h"
22+
#include "runtime-light/tl/tl-types.h"
23+
24+
namespace kphp::web::details {
25+
26+
inline auto process_simple_response(std::span<const std::byte> request) noexcept -> kphp::coro::task<std::expected<response, error>> {
27+
auto& web_state{WebInstanceState::get()};
28+
29+
auto session{web_state.session_get_or_init()};
30+
if (!session.has_value()) [[unlikely]] {
31+
kphp::log::error("failed to start or get session with Web component");
32+
}
33+
34+
if ((*session).get() == nullptr) [[unlikely]] {
35+
kphp::log::error("session with Web components has been closed");
36+
}
37+
38+
kphp::stl::vector<std::byte, kphp::memory::script_allocator> ok_or_error_buffer{};
39+
response resp{};
40+
std::optional<error> err{};
41+
auto frame_num{0};
42+
43+
auto response_buffer_provider{[&frame_num, &ok_or_error_buffer, &resp](size_t size) noexcept -> std::span<std::byte> {
44+
switch (frame_num) {
45+
case 0:
46+
ok_or_error_buffer.resize(size);
47+
return {ok_or_error_buffer.data(), size};
48+
case 1:
49+
resp.headers = string{static_cast<string::size_type>(size), false};
50+
return {reinterpret_cast<std::byte*>(resp.headers.buffer()), size};
51+
case 2:
52+
resp.body = string{static_cast<string::size_type>(size), false};
53+
return {reinterpret_cast<std::byte*>(resp.body.buffer()), size};
54+
default:
55+
kphp::log::assertion(false);
56+
return {};
57+
}
58+
}};
59+
60+
const auto response_handler{[&frame_num, &err, &ok_or_error_buffer](
61+
[[maybe_unused]] std::span<std::byte> _) noexcept -> kphp::component::inter_component_session::client::response_readiness {
62+
switch (frame_num) {
63+
case 0: {
64+
frame_num += 1;
65+
tl::Either<tl::SimpleWebTransferPerformResultOk, tl::WebError> simple_web_transfer_perform_resp{};
66+
tl::fetcher tlf{ok_or_error_buffer};
67+
if (!simple_web_transfer_perform_resp.fetch(tlf)) [[unlikely]] {
68+
kphp::log::error("failed to parse response of Simple descriptor");
69+
}
70+
if (auto r{simple_web_transfer_perform_resp.value}; std::holds_alternative<tl::WebError>(r)) {
71+
err.emplace(details::process_error(std::get<tl::WebError>(r)));
72+
return kphp::component::inter_component_session::client::response_readiness::ready;
73+
}
74+
return kphp::component::inter_component_session::client::response_readiness::pending;
75+
}
76+
case 1:
77+
frame_num += 1;
78+
return kphp::component::inter_component_session::client::response_readiness::pending;
79+
case 2: // NOLINT
80+
return kphp::component::inter_component_session::client::response_readiness::ready;
81+
default:
82+
return kphp::component::inter_component_session::client::response_readiness::ready;
83+
}
84+
}};
85+
86+
if (auto res{co_await (*session).get()->client.query(request, std::move(response_buffer_provider), response_handler)}; !res) [[unlikely]] {
87+
kphp::log::error("failed to send request of Simple descriptor processing");
88+
}
89+
if (err.has_value()) [[unlikely]] {
90+
co_return std::unexpected{std::move(*err)};
91+
}
92+
co_return std::expected<response, error>{std::move(resp)};
93+
}
94+
95+
} // namespace kphp::web::details

runtime-light/stdlib/web-transfer-lib/web-composite-transfer.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,7 @@ inline auto composite_transfer_perform(composite_transfer ct) noexcept -> kphp::
237237
tl::compositeWebTransferConfig tl_composite_config{tl::vector<tl::webProperty>{std::move(tl_composite_props)}};
238238

239239
// Prepare `CompositeWebTransferPerform` method
240-
tl::CompositeWebTransferPerform tl_perform{
241-
.descriptor = tl::u64{ct.descriptor}, .config = std::move(tl_composite_config)};
240+
tl::CompositeWebTransferPerform tl_perform{.descriptor = tl::u64{ct.descriptor}, .config = std::move(tl_composite_config)};
242241
tl::storer tls{tl_perform.footprint()};
243242
tl_perform.store(tls);
244243

runtime-light/stdlib/web-transfer-lib/web-simple-transfer.h

Lines changed: 13 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "runtime-light/stdlib/diagnostics/logs.h"
1616
#include "runtime-light/stdlib/web-transfer-lib/defs.h"
1717
#include "runtime-light/stdlib/web-transfer-lib/details/web-error.h"
18+
#include "runtime-light/stdlib/web-transfer-lib/details/web-response.h"
1819
#include "runtime-light/stdlib/web-transfer-lib/web-composite-transfer.h"
1920
#include "runtime-light/stdlib/web-transfer-lib/web-state.h"
2021
#include "runtime-light/tl/tl-core.h"
@@ -81,15 +82,6 @@ inline auto simple_transfer_perform(simple_transfer st) noexcept -> kphp::coro::
8182
co_return std::unexpected{error{.code = WEB_INTERNAL_ERROR_CODE, .description = string{"unknown Simple transfer"}}};
8283
}
8384

84-
auto session{web_state.session_get_or_init()};
85-
if (!session.has_value()) [[unlikely]] {
86-
kphp::log::error("failed to start or get session with Web component");
87-
}
88-
89-
if ((*session).get() == nullptr) [[unlikely]] {
90-
kphp::log::error("session with Web components has been closed");
91-
}
92-
9385
kphp::stl::vector<tl::webProperty, kphp::memory::script_allocator> tl_props{};
9486
auto& props{simple2config[st.descriptor].properties};
9587
for (const auto& [id, val] : props) {
@@ -101,61 +93,21 @@ inline auto simple_transfer_perform(simple_transfer st) noexcept -> kphp::coro::
10193
tl::storer tls{tl_perform.footprint()};
10294
tl_perform.store(tls);
10395

104-
kphp::stl::vector<std::byte, kphp::memory::script_allocator> ok_or_error_buffer{};
105-
response resp{};
106-
std::optional<error> err{};
107-
auto frame_num{0};
108-
109-
auto response_buffer_provider{[&frame_num, &ok_or_error_buffer, &resp](size_t size) noexcept -> std::span<std::byte> {
110-
switch (frame_num) {
111-
case 0:
112-
ok_or_error_buffer.resize(size);
113-
return {ok_or_error_buffer.data(), size};
114-
case 1:
115-
resp.headers = string{static_cast<string::size_type>(size), false};
116-
return {reinterpret_cast<std::byte*>(resp.headers.buffer()), size};
117-
case 2:
118-
resp.body = string{static_cast<string::size_type>(size), false};
119-
return {reinterpret_cast<std::byte*>(resp.body.buffer()), size};
120-
default:
121-
kphp::log::assertion(false);
122-
return {};
123-
}
124-
}};
96+
co_return co_await details::process_simple_response(tls.view());
97+
}
12598

126-
const auto response_handler{[&frame_num, &err, &ok_or_error_buffer](
127-
[[maybe_unused]] std::span<std::byte> _) noexcept -> kphp::component::inter_component_session::client::response_readiness {
128-
switch (frame_num) {
129-
case 0: {
130-
frame_num += 1;
131-
tl::Either<tl::SimpleWebTransferPerformResultOk, tl::WebError> simple_web_transfer_perform_resp{};
132-
tl::fetcher tlf{ok_or_error_buffer};
133-
if (!simple_web_transfer_perform_resp.fetch(tlf)) [[unlikely]] {
134-
kphp::log::error("failed to parse response of Simple descriptor performing");
135-
}
136-
if (auto r{simple_web_transfer_perform_resp.value}; std::holds_alternative<tl::WebError>(r)) {
137-
err.emplace(details::process_error(std::get<tl::WebError>(r)));
138-
return kphp::component::inter_component_session::client::response_readiness::ready;
139-
}
140-
return kphp::component::inter_component_session::client::response_readiness::pending;
141-
}
142-
case 1:
143-
frame_num += 1;
144-
return kphp::component::inter_component_session::client::response_readiness::pending;
145-
case 2: // NOLINT
146-
return kphp::component::inter_component_session::client::response_readiness::ready;
147-
default:
148-
return kphp::component::inter_component_session::client::response_readiness::ready;
149-
}
150-
}};
99+
inline auto simple_transfer_get_response(simple_transfer st) noexcept -> kphp::coro::task<std::expected<response, error>> {
100+
auto& web_state{WebInstanceState::get()};
151101

152-
if (auto res{co_await (*session).get()->client.query(tls.view(), std::move(response_buffer_provider), response_handler)}; !res) [[unlikely]] {
153-
kphp::log::error("failed to send request of Simple descriptor performing");
154-
}
155-
if (err.has_value()) [[unlikely]] {
156-
co_return std::unexpected{std::move(*err)};
102+
if (!web_state.simple_transfer2config.contains(st.descriptor)) {
103+
co_return std::unexpected{error{.code = WEB_INTERNAL_ERROR_CODE, .description = string{"unknown Simple transfer"}}};
157104
}
158-
co_return std::expected<response, error>{std::move(resp)};
105+
106+
tl::SimpleWebTransferGetResponse web_transfer_get_resp{.descriptor = tl::u64{st.descriptor}};
107+
tl::storer tls{web_transfer_get_resp.footprint()};
108+
web_transfer_get_resp.store(tls);
109+
110+
co_return co_await details::process_simple_response(tls.view());
159111
}
160112

161113
inline auto simple_transfer_reset(simple_transfer st) noexcept -> kphp::coro::task<std::expected<void, error>> {

runtime-light/tl/tl-functions.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,22 @@ class SimpleWebTransferPerform final {
453453
}
454454
};
455455

456+
class SimpleWebTransferGetResponse final {
457+
static constexpr uint32_t SIMPLE_WEB_TRANSFER_GET_RESPONSE_MAGIC = 0xAADD'FF24;
458+
459+
public:
460+
tl::u64 descriptor;
461+
462+
void store(tl::storer& tls) const noexcept {
463+
tl::magic{.value = SIMPLE_WEB_TRANSFER_GET_RESPONSE_MAGIC}.store(tls);
464+
descriptor.store(tls);
465+
}
466+
467+
constexpr size_t footprint() const noexcept {
468+
return tl::magic{.value = SIMPLE_WEB_TRANSFER_GET_RESPONSE_MAGIC}.footprint() + descriptor.footprint();
469+
}
470+
};
471+
456472
class SimpleWebTransferClose final {
457473
static constexpr uint32_t SIMPLE_WEB_TRANSFER_CLOSE_MAGIC = 0x36F7'16BB;
458474

0 commit comments

Comments
 (0)