1515
1616namespace duckdb {
1717
18+ // Helper function to parse URL and setup client
19+ static std::pair<duckdb_httplib_openssl::Client, std::string> SetupHttpClient (const std::string &url) {
20+ std::string scheme, domain, path;
21+ size_t pos = url.find (" ://" );
22+ std::string mod_url = url;
23+ if (pos != std::string::npos) {
24+ scheme = mod_url.substr (0 , pos);
25+ mod_url.erase (0 , pos + 3 );
26+ }
27+
28+ pos = mod_url.find (" /" );
29+ if (pos != std::string::npos) {
30+ domain = mod_url.substr (0 , pos);
31+ path = mod_url.substr (pos);
32+ } else {
33+ domain = mod_url;
34+ path = " /" ;
35+ }
36+
37+ // Create client and set a reasonable timeout (e.g., 10 seconds)
38+ duckdb_httplib_openssl::Client client (domain.c_str ());
39+ client.set_read_timeout (10 , 0 ); // 10 seconds
40+ client.set_follow_location (true ); // Follow redirects
41+
42+ return std::make_pair (std::move (client), path);
43+ }
44+
45+ static void HandleHttpError (const duckdb_httplib_openssl::Result &res, const std::string &request_type) {
46+ std::string err_message = " HTTP " + request_type + " request failed. " ;
47+
48+ switch (res.error ()) {
49+ case duckdb_httplib_openssl::Error::Connection:
50+ err_message += " Connection error." ;
51+ break ;
52+ case duckdb_httplib_openssl::Error::BindIPAddress:
53+ err_message += " Failed to bind IP address." ;
54+ break ;
55+ case duckdb_httplib_openssl::Error::Read:
56+ err_message += " Error reading response." ;
57+ break ;
58+ case duckdb_httplib_openssl::Error::Write:
59+ err_message += " Error writing request." ;
60+ break ;
61+ case duckdb_httplib_openssl::Error::ExceedRedirectCount:
62+ err_message += " Too many redirects." ;
63+ break ;
64+ case duckdb_httplib_openssl::Error::Canceled:
65+ err_message += " Request was canceled." ;
66+ break ;
67+ case duckdb_httplib_openssl::Error::SSLConnection:
68+ err_message += " SSL connection failed." ;
69+ break ;
70+ case duckdb_httplib_openssl::Error::SSLLoadingCerts:
71+ err_message += " Failed to load SSL certificates." ;
72+ break ;
73+ case duckdb_httplib_openssl::Error::SSLServerVerification:
74+ err_message += " SSL server verification failed." ;
75+ break ;
76+ case duckdb_httplib_openssl::Error::UnsupportedMultipartBoundaryChars:
77+ err_message += " Unsupported characters in multipart boundary." ;
78+ break ;
79+ case duckdb_httplib_openssl::Error::Compression:
80+ err_message += " Error during compression." ;
81+ break ;
82+ default :
83+ err_message += " Unknown error." ;
84+ break ;
85+ }
86+ throw std::runtime_error (err_message);
87+ }
88+
89+
1890static void HTTPGetRequestFunction (DataChunk &args, ExpressionState &state, Vector &result) {
1991 D_ASSERT (args.data .size () == 1 );
2092
2193 UnaryExecutor::Execute<string_t , string_t >(args.data [0 ], result, args.size (), [&](string_t input) {
2294 std::string url = input.GetString ();
2395
24- // Parse the URL to extract the domain and path
25- std::string scheme, domain, path;
26- size_t pos = url.find (" ://" );
27- if (pos != std::string::npos) {
28- scheme = url.substr (0 , pos);
29- url.erase (0 , pos + 3 );
30- }
31-
32- pos = url.find (" /" );
33- if (pos != std::string::npos) {
34- domain = url.substr (0 , pos);
35- path = url.substr (pos);
36- } else {
37- domain = url;
38- path = " /" ;
39- }
40-
41- // Create client and set a reasonable timeout (e.g., 10 seconds)
42- duckdb_httplib_openssl::Client client (domain.c_str ());
43- client.set_read_timeout (10 , 0 ); // 10 seconds
44-
45- // Follow redirects
46- client.set_follow_location (true );
96+ // Use helper to setup client and parse URL
97+ auto client_and_path = SetupHttpClient (url);
98+ auto &client = client_and_path.first ;
99+ auto &path = client_and_path.second ;
47100
48101 // Make the GET request
49102 auto res = client.Get (path.c_str ());
50103 if (res) {
51104 if (res->status == 200 ) {
52105 return StringVector::AddString (result, res->body );
53106 } else {
54- throw std::runtime_error (" HTTP error: " + std::to_string (res->status ) + " - " + res->reason );
107+ throw std::runtime_error (" HTTP GET error: " + std::to_string (res->status ) + " - " + res->reason );
55108 }
56109 } else {
57- // Handle the error case
58- std::string err_message = " HTTP request failed. " ;
59-
60- // Convert httplib error codes to a descriptive message
61- switch (res.error ()) {
62- case duckdb_httplib_openssl::Error::Connection:
63- err_message += " Connection error." ;
64- break ;
65- case duckdb_httplib_openssl::Error::BindIPAddress:
66- err_message += " Failed to bind IP address." ;
67- break ;
68- case duckdb_httplib_openssl::Error::Read:
69- err_message += " Error reading response." ;
70- break ;
71- case duckdb_httplib_openssl::Error::Write:
72- err_message += " Error writing request." ;
73- break ;
74- case duckdb_httplib_openssl::Error::ExceedRedirectCount:
75- err_message += " Too many redirects." ;
76- break ;
77- case duckdb_httplib_openssl::Error::Canceled:
78- err_message += " Request was canceled." ;
79- break ;
80- case duckdb_httplib_openssl::Error::SSLConnection:
81- err_message += " SSL connection failed." ;
82- break ;
83- case duckdb_httplib_openssl::Error::SSLLoadingCerts:
84- err_message += " Failed to load SSL certificates." ;
85- break ;
86- case duckdb_httplib_openssl::Error::SSLServerVerification:
87- err_message += " SSL server verification failed." ;
88- break ;
89- case duckdb_httplib_openssl::Error::UnsupportedMultipartBoundaryChars:
90- err_message += " Unsupported characters in multipart boundary." ;
91- break ;
92- case duckdb_httplib_openssl::Error::Compression:
93- err_message += " Error during compression." ;
94- break ;
95- default :
96- err_message += " Unknown error." ;
97- break ;
98- }
99- throw std::runtime_error (err_message);
110+ // Handle errors
111+ HandleHttpError (res, " GET" );
100112 }
113+ // Ensure a return value in case of an error
114+ return string_t ();
101115 });
102116}
103117
104118static void HTTPPostRequestFunction (DataChunk &args, ExpressionState &state, Vector &result) {
105119 D_ASSERT (args.data .size () == 3 );
106120
107121 auto &url_vector = args.data [0 ];
108- auto &headers_vector = args.data [1 ]; // Already passed as a serialized string
109- auto &body_vector = args.data [2 ]; // Already passed as a JSON string
122+ auto &headers_vector = args.data [1 ];
123+ auto &body_vector = args.data [2 ];
110124
111- // Use TernaryExecutor instead of UnaryExecutor
112125 TernaryExecutor::Execute<string_t , string_t , string_t , string_t >(
113126 url_vector, headers_vector, body_vector, result, args.size (),
114- [&](string_t url, string_t headers_varchar , string_t body_varchar ) {
127+ [&](string_t url, string_t headers , string_t body ) {
115128 std::string url_str = url.GetString ();
116129
117- // Parse the URL to extract the domain and path
118- std::string scheme, domain, path;
119- size_t pos = url_str.find (" ://" );
120- if (pos != std::string::npos) {
121- scheme = url_str.substr (0 , pos);
122- url_str.erase (0 , pos + 3 );
123- }
124-
125- pos = url_str.find (" /" );
126- if (pos != std::string::npos) {
127- domain = url_str.substr (0 , pos);
128- path = url_str.substr (pos);
129- } else {
130- domain = url_str;
131- path = " /" ;
132- }
133-
134- // Create the client and set a timeout
135- duckdb_httplib_openssl::Client client (domain.c_str ());
136- client.set_read_timeout (10 , 0 ); // 10 seconds
137- // Follow redirects
138- client.set_follow_location (true );
139-
140- // Follow redirects for POST as well
141- client.set_follow_location (true );
130+ // Use helper to setup client and parse URL
131+ auto client_and_path = SetupHttpClient (url_str);
132+ auto &client = client_and_path.first ;
133+ auto &path = client_and_path.second ;
142134
143- // Deserialize the header string into a header map
135+ // Prepare headers
144136 duckdb_httplib_openssl::Headers header_map;
145- std::istringstream header_stream (headers_varchar .GetString ());
137+ std::istringstream header_stream (headers .GetString ());
146138 std::string header;
147139 while (std::getline (header_stream, header)) {
148140 size_t colon_pos = header.find (' :' );
149141 if (colon_pos != std::string::npos) {
150142 std::string key = header.substr (0 , colon_pos);
151143 std::string value = header.substr (colon_pos + 1 );
152- // Trim leading/ trailing whitespace
144+ // Trim leading and trailing whitespace
153145 key.erase (0 , key.find_first_not_of (" \t " ));
154146 key.erase (key.find_last_not_of (" \t " ) + 1 );
155147 value.erase (0 , value.find_first_not_of (" \t " ));
@@ -158,60 +150,20 @@ static void HTTPPostRequestFunction(DataChunk &args, ExpressionState &state, Vec
158150 }
159151 }
160152
161- // Prepare the POST body (it is passed as a string)
162- std::string body_str = body_varchar.GetString ();
163-
164- // Make the POST request
165- auto res = client.Post (path.c_str (), header_map, body_str, " application/json" );
153+ // Make the POST request with headers and body
154+ auto res = client.Post (path.c_str (), header_map, body.GetString (), " application/json" );
166155 if (res) {
167156 if (res->status == 200 ) {
168157 return StringVector::AddString (result, res->body );
169158 } else {
170- throw std::runtime_error (" HTTP error: " + std::to_string (res->status ) + " - " + res->reason );
159+ throw std::runtime_error (" HTTP POST error: " + std::to_string (res->status ) + " - " + res->reason );
171160 }
172161 } else {
173- // Handle the error case
174- std::string err_message = " HTTP POST request failed. " ;
175- switch (res.error ()) {
176- case duckdb_httplib_openssl::Error::Connection:
177- err_message += " Connection error." ;
178- break ;
179- case duckdb_httplib_openssl::Error::BindIPAddress:
180- err_message += " Failed to bind IP address." ;
181- break ;
182- case duckdb_httplib_openssl::Error::Read:
183- err_message += " Error reading response." ;
184- break ;
185- case duckdb_httplib_openssl::Error::Write:
186- err_message += " Error writing request." ;
187- break ;
188- case duckdb_httplib_openssl::Error::ExceedRedirectCount:
189- err_message += " Too many redirects." ;
190- break ;
191- case duckdb_httplib_openssl::Error::Canceled:
192- err_message += " Request was canceled." ;
193- break ;
194- case duckdb_httplib_openssl::Error::SSLConnection:
195- err_message += " SSL connection failed." ;
196- break ;
197- case duckdb_httplib_openssl::Error::SSLLoadingCerts:
198- err_message += " Failed to load SSL certificates." ;
199- break ;
200- case duckdb_httplib_openssl::Error::SSLServerVerification:
201- err_message += " SSL server verification failed." ;
202- break ;
203- case duckdb_httplib_openssl::Error::UnsupportedMultipartBoundaryChars:
204- err_message += " Unsupported characters in multipart boundary." ;
205- break ;
206- case duckdb_httplib_openssl::Error::Compression:
207- err_message += " Error during compression." ;
208- break ;
209- default :
210- err_message += " Unknown error." ;
211- break ;
212- }
213- throw std::runtime_error (err_message);
162+ // Handle errors
163+ HandleHttpError (res, " POST" );
214164 }
165+ // Ensure a return value in case of an error
166+ return string_t ();
215167 });
216168}
217169
0 commit comments