Skip to content

Commit 6974124

Browse files
committed
add HttpResponse
1 parent 63047b9 commit 6974124

File tree

7 files changed

+109
-94
lines changed

7 files changed

+109
-94
lines changed

src/iceberg/catalog/rest/config.cc

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,26 +34,19 @@ std::unique_ptr<RestCatalogConfig> RestCatalogConfig::FromMap(
3434
return rest_catalog_config;
3535
}
3636

37-
Result<cpr::Header> RestCatalogConfig::GetExtraHeaders() const {
38-
cpr::Header headers;
39-
40-
headers[std::string(kHeaderContentType)] = std::string(kMimeTypeApplicationJson);
41-
headers[std::string(kHeaderUserAgent)] = std::string(kUserAgent);
42-
headers[std::string(kHeaderXClientVersion)] = std::string(kClientVersion);
37+
std::unordered_map<std::string, std::string> RestCatalogConfig::GetExtraHeaders() const {
38+
std::unordered_map<std::string, std::string> headers;
39+
headers[kHeaderContentType] = kMimeTypeApplicationJson;
40+
headers[kHeaderUserAgent] = kUserAgent;
4341

4442
constexpr std::string_view prefix = "header.";
4543
for (const auto& [key, value] : configs_) {
4644
if (key.starts_with(prefix)) {
47-
std::string_view header_name = std::string_view(key).substr(prefix.length());
48-
49-
if (header_name.empty()) {
50-
return InvalidArgument("Header name cannot be empty after '{}' prefix", prefix);
51-
}
52-
53-
if (value.empty()) {
54-
return InvalidArgument("Header value for '{}' cannot be empty", header_name);
45+
std::string header_name = key.substr(prefix.length());
46+
if (header_name.empty() || value.empty()) {
47+
continue;
5548
}
56-
headers[std::string(header_name)] = value;
49+
headers[header_name] = value;
5750
}
5851
}
5952
return headers;

src/iceberg/catalog/rest/config.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@
2121

2222
#include <memory>
2323
#include <string>
24+
#include <string_view>
2425
#include <unordered_map>
2526

2627
#include <cpr/cprtypes.h>
2728

2829
#include "iceberg/catalog/rest/iceberg_rest_export.h"
29-
#include "iceberg/result.h"
3030
#include "iceberg/util/config.h"
3131

3232
/// \file iceberg/catalog/rest/config.h
@@ -61,9 +61,8 @@ class ICEBERG_REST_EXPORT RestCatalogConfig : public ConfigBase<RestCatalogConfi
6161
///
6262
/// This includes default headers like Content-Type, User-Agent, X-Client-Version and
6363
/// any custom headers prefixed with "header." in the properties.
64-
/// \return A Result containing cpr::Header object, or an error if names/values are
65-
/// invalid.
66-
Result<cpr::Header> GetExtraHeaders() const;
64+
/// \return A map of header names to values.
65+
std::unordered_map<std::string, std::string> GetExtraHeaders() const;
6766
};
6867

6968
} // namespace iceberg::rest

src/iceberg/catalog/rest/constant.h

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
#pragma once
2121

22-
#include <string_view>
22+
#include <string>
2323

2424
#include "iceberg/version.h"
2525

@@ -28,17 +28,16 @@
2828

2929
namespace iceberg::rest {
3030

31-
constexpr std::string_view kHeaderContentType = "Content-Type";
32-
constexpr std::string_view kHeaderAccept = "Accept";
33-
constexpr std::string_view kHeaderXClientVersion = "X-Client-Version";
34-
constexpr std::string_view kHeaderUserAgent = "User-Agent";
31+
inline const std::string kHeaderContentType = "Content-Type";
32+
inline const std::string kHeaderAccept = "Accept";
33+
inline const std::string kHeaderXClientVersion = "X-Client-Version";
34+
inline const std::string kHeaderUserAgent = "User-Agent";
3535

36-
constexpr std::string_view kMimeTypeApplicationJson = "application/json";
37-
constexpr std::string_view kClientVersion = "0.14.1";
38-
constexpr std::string_view kUserAgentPrefix = "iceberg-cpp/";
39-
constexpr std::string_view kUserAgent = "iceberg-cpp/" ICEBERG_VERSION_STRING;
36+
inline const std::string kMimeTypeApplicationJson = "application/json";
37+
inline const std::string kUserAgentPrefix = "iceberg-cpp/";
38+
inline const std::string kUserAgent = "iceberg-cpp/" ICEBERG_VERSION_STRING;
4039

41-
constexpr std::string_view kQueryParamParent = "parent";
42-
constexpr std::string_view kQueryParamPageToken = "page_token";
40+
inline const std::string kQueryParamParent = "parent";
41+
inline const std::string kQueryParamPageToken = "page_token";
4342

4443
} // namespace iceberg::rest

src/iceberg/catalog/rest/http_client_interal.h

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <nlohmann/json.hpp>
2828

2929
#include "iceberg/catalog/rest/config.h"
30+
#include "iceberg/catalog/rest/http_response.h"
3031
#include "iceberg/catalog/rest/iceberg_rest_export.h"
3132
#include "iceberg/result.h"
3233

@@ -36,17 +37,9 @@
3637
namespace iceberg::rest {
3738

3839
/// \brief HTTP client for making requests to Iceberg REST Catalog API.
39-
///
40-
/// This class wraps the CPR library and provides a type-safe interface for making
41-
/// HTTP requests. It handles authentication, headers, and response parsing.
4240
class ICEBERG_REST_EXPORT HttpClient {
4341
public:
44-
/// \brief Factory function to create and initialize an HttpClient.
45-
/// This is the preferred way to construct an HttpClient, as it can handle
46-
/// potential errors during configuration parsing (e.g., invalid headers).
47-
/// \param config The catalog configuration.
48-
/// \return A Result containing a unique_ptr to the HttpClient, or an Error.
49-
static Result<std::unique_ptr<HttpClient>> Make(const RestCatalogConfig& config);
42+
explicit HttpClient(const RestCatalogConfig&);
5043

5144
HttpClient(const HttpClient&) = delete;
5245
HttpClient& operator=(const HttpClient&) = delete;
@@ -55,36 +48,42 @@ class ICEBERG_REST_EXPORT HttpClient {
5548

5649
/// \brief Sends a GET request.
5750
/// \param target The target path relative to the base URL (e.g., "/v1/namespaces").
58-
Result<cpr::Response> Get(const std::string& target, const cpr::Parameters& params = {},
59-
const cpr::Header& headers = {});
51+
Result<HttpResponse> Get(const std::string& target, const cpr::Parameters& params = {},
52+
const cpr::Header& headers = {});
6053

6154
/// \brief Sends a POST request.
6255
/// \param target The target path relative to the base URL (e.g., "/v1/namespaces").
63-
Result<cpr::Response> Post(const std::string& target, const cpr::Body& body,
64-
const cpr::Parameters& params = {},
65-
const cpr::Header& headers = {});
56+
Result<HttpResponse> Post(const std::string& target, const cpr::Body& body,
57+
const cpr::Parameters& params = {},
58+
const cpr::Header& headers = {});
6659

6760
/// \brief Sends a HEAD request.
6861
/// \param target The target path relative to the base URL (e.g., "/v1/namespaces").
69-
Result<cpr::Response> Head(const std::string& target,
70-
const cpr::Parameters& params = {},
71-
const cpr::Header& headers = {});
62+
Result<HttpResponse> Head(const std::string& target, const cpr::Parameters& params = {},
63+
const cpr::Header& headers = {});
7264

7365
/// \brief Sends a DELETE request.
7466
/// \param target The target path relative to the base URL (e.g., "/v
75-
Result<cpr::Response> Delete(const std::string& target,
76-
const cpr::Parameters& params = {},
77-
const cpr::Header& headers = {});
67+
Result<HttpResponse> Delete(const std::string& target,
68+
const cpr::Parameters& params = {},
69+
const cpr::Header& headers = {});
7870

7971
private:
80-
/// \brief Private constructor. Use the static Create() factory function instead.
81-
explicit HttpClient(cpr::Header session_headers);
82-
8372
/// \brief Internal helper to execute a request.
8473
template <typename Func>
85-
Result<cpr::Response> Execute(const std::string& target, const cpr::Parameters& params,
86-
const cpr::Header& request_headers,
87-
Func&& perform_request);
74+
Result<HttpResponse> Execute(const std::string& target, const cpr::Parameters& params,
75+
const cpr::Header& request_headers,
76+
Func&& perform_request) {
77+
cpr::Header combined_headers = default_headers_;
78+
combined_headers.insert(request_headers.begin(), request_headers.end());
79+
80+
session_->SetUrl(cpr::Url{target});
81+
session_->SetParameters(params);
82+
session_->SetHeader(combined_headers);
83+
84+
cpr::Response response = perform_request(*session_);
85+
return HttpResponse{std::move(response)};
86+
}
8887

8988
cpr::Header default_headers_;
9089
std::unique_ptr<cpr::Session> session_;

src/iceberg/catalog/rest/http_client_internal.cc

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,65 +20,44 @@
2020
#include <nlohmann/json.hpp>
2121

2222
#include "cpr/body.h"
23+
#include "cpr/cprtypes.h"
2324
#include "iceberg/catalog/rest/config.h"
2425
#include "iceberg/catalog/rest/http_client_interal.h"
25-
#include "iceberg/util/macros.h"
2626

2727
namespace iceberg::rest {
2828

29-
Result<std::unique_ptr<HttpClient>> HttpClient::Make(const RestCatalogConfig& config) {
30-
ICEBERG_ASSIGN_OR_RAISE(auto session_headers, config.GetExtraHeaders());
31-
return std::unique_ptr<HttpClient>(new HttpClient(std::move(session_headers)));
32-
}
33-
34-
HttpClient::HttpClient(cpr::Header session_headers)
35-
: default_headers_(std::move(session_headers)),
36-
session_(std::make_unique<cpr::Session>()) {}
29+
HttpClient::HttpClient(const RestCatalogConfig& config)
30+
: default_headers_{config.GetExtraHeaders().begin(), config.GetExtraHeaders().end()},
31+
session_{std::make_unique<cpr::Session>()} {}
3732

38-
Result<cpr::Response> HttpClient::Get(const std::string& target,
39-
const cpr::Parameters& params,
40-
const cpr::Header& headers) {
33+
Result<HttpResponse> HttpClient::Get(const std::string& target,
34+
const cpr::Parameters& params,
35+
const cpr::Header& headers) {
4136
return Execute(target, params, headers,
4237
[&](cpr::Session& session) { return session.Get(); });
4338
}
4439

45-
Result<cpr::Response> HttpClient::Post(const std::string& target, const cpr::Body& body,
46-
const cpr::Parameters& params,
47-
const cpr::Header& headers) {
40+
Result<HttpResponse> HttpClient::Post(const std::string& target, const cpr::Body& body,
41+
const cpr::Parameters& params,
42+
const cpr::Header& headers) {
4843
return Execute(target, params, headers, [&](cpr::Session& session) {
4944
session.SetBody(body);
5045
return session.Post();
5146
});
5247
}
5348

54-
Result<cpr::Response> HttpClient::Head(const std::string& target,
55-
const cpr::Parameters& params,
56-
const cpr::Header& headers) {
49+
Result<HttpResponse> HttpClient::Head(const std::string& target,
50+
const cpr::Parameters& params,
51+
const cpr::Header& headers) {
5752
return Execute(target, params, headers,
5853
[&](cpr::Session& session) { return session.Head(); });
5954
}
6055

61-
Result<cpr::Response> HttpClient::Delete(const std::string& target,
62-
const cpr::Parameters& params,
63-
const cpr::Header& headers) {
56+
Result<HttpResponse> HttpClient::Delete(const std::string& target,
57+
const cpr::Parameters& params,
58+
const cpr::Header& headers) {
6459
return Execute(target, params, headers,
6560
[&](cpr::Session& session) { return session.Delete(); });
6661
}
6762

68-
template <typename Func>
69-
Result<cpr::Response> HttpClient::Execute(const std::string& target,
70-
const cpr::Parameters& params,
71-
const cpr::Header& request_headers,
72-
Func&& perform_request) {
73-
cpr::Header combined_headers = default_headers_;
74-
combined_headers.insert(request_headers.begin(), request_headers.end());
75-
76-
session_->SetUrl(cpr::Url{target});
77-
session_->SetParameters(params);
78-
session_->SetHeader(combined_headers);
79-
80-
cpr::Response response = perform_request(*session_);
81-
return response;
82-
}
83-
8463
} // namespace iceberg::rest
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#pragma once
21+
22+
#include <string>
23+
24+
#include "cpr/response.h"
25+
#include "iceberg/catalog/rest/iceberg_rest_export.h"
26+
27+
/// \file iceberg/catalog/rest/http_response.h
28+
/// \brief A simple wrapper for cpr::Response. This class encapsulates the details of the
29+
/// underlying cpr library's response, providing a consistent interface that is
30+
/// independent of the specific network library used.
31+
32+
class ICEBERG_REST_EXPORT HttpResponse {
33+
public:
34+
explicit HttpResponse(cpr::Response response) : response_(std::move(response)) {}
35+
36+
/// \brief Get the HTTP status code of the response.
37+
/// \return The HTTP status code.
38+
int32_t status_code() const { return response_.status_code; }
39+
40+
/// \brief Get the body of the response as a string.
41+
/// \return The response body.
42+
const std::string& body() const { return response_.text; }
43+
44+
private:
45+
cpr::Response response_;
46+
};

src/iceberg/catalog/rest/meson.build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ install_headers(
6161
'json_internal.h',
6262
'types.h',
6363
'util.h',
64-
'resource_path.h',
64+
'resource_paths.h',
6565
],
6666
subdir: 'iceberg/catalog/rest',
6767
)

0 commit comments

Comments
 (0)