Skip to content

Commit 69d855a

Browse files
committed
httpclient alpha (PR#11548)
1 parent 74120ed commit 69d855a

File tree

11 files changed

+1126
-128
lines changed

11 files changed

+1126
-128
lines changed

.github/workflows/MainDistributionPipeline.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ jobs:
1818
with:
1919
duckdb_version: main
2020
ci_tools_version: main
21-
extension_name: quack
21+
extension_name: http_client
2222

2323
duckdb-stable-build:
2424
name: Build extension binaries
2525
uses: duckdb/extension-ci-tools/.github/workflows/[email protected]
2626
with:
2727
duckdb_version: v1.1.1
2828
ci_tools_version: v1.1.1
29-
extension_name: quack
29+
extension_name: http_client
3030

3131
duckdb-stable-deploy:
3232
name: Deploy extension binaries
@@ -35,5 +35,5 @@ jobs:
3535
secrets: inherit
3636
with:
3737
duckdb_version: v1.1.1
38-
extension_name: quack
38+
extension_name: http_client
3939
deploy_latest: ${{ startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' }}

CMakeLists.txt

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.5)
22

33
# Set extension name here
4-
set(TARGET_NAME quack)
4+
set(TARGET_NAME http_client)
55

66
# DuckDB's extension distribution supports vcpkg. As such, dependencies can be added in ./vcpkg.json and then
77
# used in cmake with find_package. Feel free to remove or replace with other dependencies.
@@ -12,16 +12,30 @@ set(EXTENSION_NAME ${TARGET_NAME}_extension)
1212
set(LOADABLE_EXTENSION_NAME ${TARGET_NAME}_loadable_extension)
1313

1414
project(${TARGET_NAME})
15-
include_directories(src/include)
15+
include_directories(src/include duckdb/third_party/httplib)
1616

17-
set(EXTENSION_SOURCES src/quack_extension.cpp)
17+
set(EXTENSION_SOURCES src/http_client_extension.cpp)
18+
19+
if(MINGW)
20+
set(OPENSSL_USE_STATIC_LIBS TRUE)
21+
endif()
22+
23+
# Find OpenSSL before building extensions
24+
find_package(OpenSSL REQUIRED)
1825

1926
build_static_extension(${TARGET_NAME} ${EXTENSION_SOURCES})
2027
build_loadable_extension(${TARGET_NAME} " " ${EXTENSION_SOURCES})
2128

22-
# Link OpenSSL in both the static library as the loadable extension
23-
target_link_libraries(${EXTENSION_NAME} OpenSSL::SSL OpenSSL::Crypto)
24-
target_link_libraries(${LOADABLE_EXTENSION_NAME} OpenSSL::SSL OpenSSL::Crypto)
29+
include_directories(${OPENSSL_INCLUDE_DIR})
30+
target_link_libraries(${LOADABLE_EXTENSION_NAME} duckdb_mbedtls ${OPENSSL_LIBRARIES})
31+
target_link_libraries(${EXTENSION_NAME} duckdb_mbedtls ${OPENSSL_LIBRARIES})
32+
33+
if(MINGW)
34+
set(WIN_LIBS crypt32 ws2_32 wsock32)
35+
find_package(ZLIB)
36+
target_link_libraries(${LOADABLE_EXTENSION_NAME} ZLIB::ZLIB ${WIN_LIBS})
37+
target_link_libraries(${EXTENSION_NAME} ZLIB::ZLIB ${WIN_LIBS})
38+
endif()
2539

2640
install(
2741
TARGETS ${EXTENSION_NAME}

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
PROJ_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
22

33
# Configuration of extension
4-
EXT_NAME=quack
4+
EXT_NAME=http_client
55
EXT_CONFIG=${PROJ_DIR}extension_config.cmake
66

77
# Include the Makefile from extension-ci-tools
8-
include extension-ci-tools/makefiles/duckdb_extension.Makefile
8+
include extension-ci-tools/makefiles/duckdb_extension.Makefile

extension_config.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# This file is included by DuckDB's build system. It specifies which extension to load
22

33
# Extension from this repo
4-
duckdb_extension_load(quack
4+
duckdb_extension_load(http_client
55
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}
66
LOAD_TESTS
77
)
88

99
# Any extra extensions that should be built
10-
# e.g.: duckdb_extension_load(json)
10+
# duckdb_extension_load(httpfs)

src/http_client_extension.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#define DUCKDB_EXTENSION_MAIN
2+
#include "http_client_extension.hpp"
3+
#include "duckdb.hpp"
4+
#include "duckdb/function/scalar_function.hpp"
5+
#include "duckdb/main/extension_util.hpp"
6+
#include "duckdb/common/atomic.hpp"
7+
#include "duckdb/common/exception/http_exception.hpp"
8+
#include <duckdb/parser/parsed_data/create_scalar_function_info.hpp>
9+
10+
#define CPPHTTPLIB_OPENSSL_SUPPORT
11+
#include "httplib.hpp"
12+
13+
#include <string>
14+
#include <sstream>
15+
16+
namespace duckdb {
17+
18+
static void HTTPGetRequestFunction(DataChunk &args, ExpressionState &state, Vector &result) {
19+
D_ASSERT(args.data.size() == 1);
20+
21+
UnaryExecutor::Execute<string_t, string_t>(args.data[0], result, args.size(), [&](string_t input) {
22+
std::string url = input.GetString();
23+
duckdb_httplib_openssl::Client client(url);
24+
25+
auto res = client.Get("/");
26+
if (res) {
27+
if (res->status == 200) {
28+
return StringVector::AddString(result, res->body);
29+
} else {
30+
throw std::runtime_error("HTTP error: " + std::to_string(res->status));
31+
}
32+
} else {
33+
auto err = res.error();
34+
throw std::runtime_error("HTTP error: " + duckdb_httplib_openssl::to_string(err)); // Fully qualify to_string
35+
}
36+
});
37+
}
38+
39+
static void HTTPPostRequestFunction(DataChunk &args, ExpressionState &state, Vector &result) {
40+
D_ASSERT(args.data.size() == 3);
41+
42+
auto &url_vector = args.data[0];
43+
auto &headers_vector = args.data[1];
44+
auto &body_vector = args.data[2];
45+
46+
TernaryExecutor::Execute<string_t, string_t, string_t, string_t>(
47+
url_vector, headers_vector, body_vector, result, args.size(),
48+
[&](string_t url, string_t headers, string_t body) {
49+
std::string url_str = url.GetString();
50+
duckdb_httplib_openssl::Client client(url_str);
51+
52+
// HeaderMap header_map = {};
53+
54+
duckdb_httplib_openssl::Headers header_map; // Fully qualified httplib::Headers
55+
std::istringstream header_stream(headers.GetString());
56+
std::string header;
57+
while (std::getline(header_stream, header)) {
58+
size_t colon_pos = header.find(':');
59+
if (colon_pos != std::string::npos) {
60+
std::string key = header.substr(0, colon_pos);
61+
std::string value = header.substr(colon_pos + 1);
62+
// Trim leading and trailing whitespace
63+
key.erase(0, key.find_first_not_of(" \t"));
64+
key.erase(key.find_last_not_of(" \t") + 1);
65+
value.erase(0, value.find_first_not_of(" \t"));
66+
value.erase(value.find_last_not_of(" \t") + 1);
67+
header_map.emplace(key, value);
68+
}
69+
}
70+
71+
auto res = client.Post("/", header_map, body.GetString(), "application/json");
72+
if (res) {
73+
if (res->status == 200) {
74+
return StringVector::AddString(result, res->body);
75+
} else {
76+
throw std::runtime_error("HTTP error: " + std::to_string(res->status));
77+
}
78+
} else {
79+
auto err = res.error();
80+
throw std::runtime_error("HTTP error: " + duckdb_httplib_openssl::to_string(err)); // Fully qualify to_string
81+
}
82+
});
83+
}
84+
85+
static void LoadInternal(DatabaseInstance &instance) {
86+
ScalarFunctionSet http_get("http_get");
87+
http_get.AddFunction(ScalarFunction({LogicalType::VARCHAR}, LogicalType::VARCHAR, HTTPGetRequestFunction));
88+
ExtensionUtil::RegisterFunction(instance, http_get);
89+
90+
ScalarFunctionSet http_post("http_post");
91+
http_post.AddFunction(ScalarFunction(
92+
{LogicalType::VARCHAR, LogicalType::VARCHAR, LogicalType::VARCHAR},
93+
LogicalType::VARCHAR, HTTPPostRequestFunction));
94+
ExtensionUtil::RegisterFunction(instance, http_post);
95+
}
96+
97+
void HttpClientExtension::Load(DuckDB &db) {
98+
LoadInternal(*db.instance);
99+
}
100+
101+
std::string HttpClientExtension::Name() {
102+
return "http_client";
103+
}
104+
105+
std::string HttpClientExtension::Version() const {
106+
#ifdef EXT_VERSION_HTTPCLIENT
107+
return EXT_VERSION_HTTPCLIENT;
108+
#else
109+
return "";
110+
#endif
111+
}
112+
113+
114+
} // namespace duckdb
115+
116+
extern "C" {
117+
DUCKDB_EXTENSION_API void http_client_init(duckdb::DatabaseInstance &db) {
118+
duckdb::DuckDB db_wrapper(db);
119+
db_wrapper.LoadExtension<duckdb::HttpClientExtension>();
120+
}
121+
122+
DUCKDB_EXTENSION_API const char *http_client_version() {
123+
return duckdb::DuckDB::LibraryVersion();
124+
}
125+
}
126+
127+
#ifndef DUCKDB_EXTENSION_MAIN
128+
#error DUCKDB_EXTENSION_MAIN not defined
129+
#endif
130+

0 commit comments

Comments
 (0)