XRootD
Loading...
Searching...
No Matches
XrdClHttpUtil.hh
Go to the documentation of this file.
1/******************************************************************************/
2/* Copyright (C) 2025, Pelican Project, Morgridge Institute for Research */
3/* */
4/* This file is part of the XrdClHttp client plugin for XRootD. */
5/* */
6/* XRootD is free software: you can redistribute it and/or modify it under */
7/* the terms of the GNU Lesser General Public License as published by the */
8/* Free Software Foundation, either version 3 of the License, or (at your */
9/* option) any later version. */
10/* */
11/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
12/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
13/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
14/* License for more details. */
15/* */
16/* The copyright holder's institutional names and contributor's names may not */
17/* be used to endorse or promote products derived from this software without */
18/* specific prior written permission of the institution or contributor. */
19/******************************************************************************/
20
21#ifndef XRDCLHTTPUTIL_HH
22#define XRDCLHTTPUTIL_HH
23
24#include "XrdClHttpChecksum.hh"
27
28#include <chrono>
29#include <condition_variable>
30#include <deque>
31#include <memory>
32#include <mutex>
33#include <string>
34#include <unordered_map>
35#include <vector>
36
37// Forward dec'ls
38typedef void CURL;
39struct curl_slist;
40
41namespace XrdCl {
42
43class ResponseHandler;
44class Log;
45class URL;
46
47}
48
49namespace XrdClHttp {
50
51class CurlOperation;
52
53const uint64_t kLogXrdClHttp = 73173;
54
55bool HTTPStatusIsError(unsigned status);
56
57std::pair<uint16_t, uint32_t> HTTPStatusConvert(unsigned status);
58
59// Read a bearer token from the standard XrdCl environment options, falling
60// back to the corresponding process environment variables.
61std::string GetBearerToken(XrdCl::Log *logger = nullptr);
62
63// Return whether bearer-token authentication is the first available method
64// requested through XrdSecPROTOCOL. With no explicit preference, use a token
65// whenever one is available.
66bool ShouldUseBearerToken(const std::string &protocols,
67 bool hasX509Credential,
68 bool hasBearerToken);
69
70// Add a bearer-token Authorization header through the common XrdClHttp
71// authentication path when it is the preferred available credential.
73 const XrdCl::URL &url,
74 std::vector<std::pair<std::string, std::string>> &headers,
75 XrdCl::Log *logger = nullptr);
76
77// Trim the left side of a string_view for space
78std::string_view ltrim_view(const std::string_view &input_view);
79
80// Trim the left and right side of a string_view of whitespace
81std::string_view trim_view(const std::string_view &input_view);
82
83// Apply the common XrdClHttp configuration to a curl handle.
84void ConfigureHandle(CURL *curl, bool verbose);
85
86// Returns a newly-created curl handle (no internal caching) with the
87// various configurations needed to be used by XrdClHttp
88CURL *GetHandle(bool verbose);
89
90// Parser for headers as emitted by libcurl.
91//
92// Records specific headers known to be used by the project but ignores others.
94public:
96
97 bool Parse(const std::string &headers);
98
99 int64_t GetContentLength() const {return m_content_length;}
100
101 uint64_t GetOffset() const {return m_response_offset;}
102
103 static bool Canonicalize(std::string &headerName);
104
105 bool HeadersDone() const {return m_recv_all_headers;}
106
107 // Move the received headers to the caller.
108 //
109 // Only invoke once HeadersDone() returns true.
110 ResponseInfo::HeaderMap && MoveHeaders() {return std::move(m_headers);}
111
112 int GetStatusCode() const {return m_status_code;}
113
114 // Setter for the status code
115 // Intended for use in unit tests.
116 void SetStatusCode(int sc) {m_status_code = sc;}
117
118 // Return whether the server response specified this is a multipart range.
119 bool IsMultipartByterange() const {return m_multipart_byteranges;}
120
121 // Return the separator specified in the server response with the
122 // `--` prefix included..
123 const std::string &MultipartSeparator() const {return m_multipart_sep;}
124
125 // Set the separator used for multipart messages; a `--` prefix
126 // will be added to the Getter.
127 void SetMultipartSeparator(const std::string_view &sep) {
128 m_multipart_sep = "--" + std::string(sep);
129 m_multipart_byteranges = true;
130 }
131
133 {
134 return VerbsCache::HttpVerbs(m_allow_verbs);
135 }
136
137 std::string GetStatusMessage() const {return m_resp_message;}
138
139 const std::string &GetLocation() const {return m_location;}
140 const std::string &GetETag() const {return m_etag;}
141 const std::string &GetCacheControl() const {return m_cache_control;}
142
143 // Returns a reference to the checksums parsed from the headers.
144 const XrdClHttp::ChecksumInfo &GetChecksums() const {return m_checksums;}
145
146 // Parse a RFC 3230 header, updating the checksum info structure.
147 static void ParseDigest(const std::string &digest, XrdClHttp::ChecksumInfo &info);
148
149 // Decode a base64-encoded string into a binary buffer.
150 static bool Base64Decode(
151 std::string_view input,
152 std::array<unsigned char, g_max_checksum_length> &output);
153
154 // Convert a checksum type to a RFC 3230 digest name.
155 static std::string ChecksumTypeToDigestName(XrdClHttp::ChecksumType type);
156
157private:
158
159 static bool validHeaderByte(unsigned char c);
160
161 int64_t m_content_length{-1};
162 uint64_t m_response_offset{0};
163
164 XrdClHttp::ChecksumInfo m_checksums;
165
166 bool m_recv_all_headers{false};
167 bool m_recv_status_line{false};
168 bool m_multipart_byteranges{false};
169
170 int m_status_code{-1};
171 std::string m_resp_protocol;
172 std::string m_resp_message;
173 std::string m_location;
174 std::string m_multipart_sep;
175 std::string m_etag;
176 std::string m_cache_control;
177
178 ResponseInfo::HeaderMap m_headers;
179
181};
182
192public:
193 HandlerQueue(unsigned max_pending_ops);
194
195 void Produce(std::shared_ptr<CurlOperation> handler);
196
197 std::shared_ptr<CurlOperation> Consume(std::chrono::steady_clock::duration);
198 std::shared_ptr<CurlOperation> TryConsume();
199
200 int PollFD() const {return m_read_fd;}
201
202 CURL *GetHandle();
203 void RecycleHandle(CURL *);
204
205 // Check all the operations in queue to see if any have expired.
206 //
207 // Each curl operation has a header timeout; if no headers have been received
208 // by the time the timeout expires, the operation is considered to have
209 // expired. This function checks all operations in the queue and
210 // removes any that have expired.
211 void Expire();
212
213 void Shutdown();
214 // Cleanup all idle handles in current thread.
215 void ReleaseHandles();
216
217 // Returns the class default number of pending operations.
218 static unsigned GetDefaultMaxPendingOps() {return m_default_max_pending_ops;}
219
220 // Returns a summary of the queue's performance statistics.
221 static std::string GetMonitoringJson();
222
223private:
224 bool m_shutdown{false};
225 std::deque<std::shared_ptr<CurlOperation>> m_ops;
226 static std::atomic<uint64_t> m_ops_consumed; // Count of operations consumed from the queue.
227 static std::atomic<uint64_t> m_ops_produced; // Count of operations added to the queue.
228 static std::atomic<uint64_t> m_ops_rejected; // Count of operations rejected by the queue.
229 thread_local static std::vector<CURL*> m_handles;
230 std::condition_variable m_consumer_cv;
231 std::condition_variable m_producer_cv;
232 std::mutex m_mutex;
233 const static unsigned m_default_max_pending_ops{50};
234 const unsigned m_max_pending_ops{50};
235 int m_read_fd{-1};
236 int m_write_fd{-1};
237};
238
239}
240
241#endif // XRDCLHTTPUTIL_HH
void CURL
std::shared_ptr< CurlOperation > Consume(std::chrono::steady_clock::duration)
HandlerQueue(unsigned max_pending_ops)
void Produce(std::shared_ptr< CurlOperation > handler)
static std::string GetMonitoringJson()
static unsigned GetDefaultMaxPendingOps()
std::shared_ptr< CurlOperation > TryConsume()
void SetMultipartSeparator(const std::string_view &sep)
static void ParseDigest(const std::string &digest, XrdClHttp::ChecksumInfo &info)
std::string GetStatusMessage() const
const std::string & GetCacheControl() const
uint64_t GetOffset() const
bool IsMultipartByterange() const
const XrdClHttp::ChecksumInfo & GetChecksums() const
int64_t GetContentLength() const
ResponseInfo::HeaderMap && MoveHeaders()
static bool Canonicalize(std::string &headerName)
const std::string & MultipartSeparator() const
bool Parse(const std::string &headers)
VerbsCache::HttpVerbs GetAllowedVerbs() const
static std::string ChecksumTypeToDigestName(XrdClHttp::ChecksumType type)
const std::string & GetLocation() const
static bool Base64Decode(std::string_view input, std::array< unsigned char, g_max_checksum_length > &output)
const std::string & GetETag() const
std::unordered_map< std::string, HeaderValues > HeaderMap
Handle diagnostics.
Definition XrdClLog.hh:101
URL representation.
Definition XrdClURL.hh:31
std::pair< uint16_t, uint32_t > HTTPStatusConvert(unsigned status)
void InjectBearerToken(const XrdCl::URL &url, std::vector< std::pair< std::string, std::string > > &headers, XrdCl::Log *logger=nullptr)
CURL * GetHandle(bool verbose)
bool HTTPStatusIsError(unsigned status)
bool ShouldUseBearerToken(const std::string &protocols, bool hasX509Credential, bool hasBearerToken)
std::string_view ltrim_view(const std::string_view &input_view)
const uint64_t kLogXrdClHttp
std::string GetBearerToken(XrdCl::Log *logger=nullptr)
void ConfigureHandle(CURL *curl, bool verbose)
std::string_view trim_view(const std::string_view &input_view)