XRootD
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"
25 #include "XrdClHttpOptionsCache.hh"
26 #include "XrdClHttpResponseInfo.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
38 typedef void CURL;
39 struct curl_slist;
40 
41 namespace XrdCl {
42 
43 class ResponseHandler;
44 class Log;
45 class URL;
46 
47 }
48 
49 namespace XrdClHttp {
50 
51 class CurlOperation;
52 
53 const uint64_t kLogXrdClHttp = 73173;
54 
55 bool HTTPStatusIsError(unsigned status);
56 
57 std::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.
61 std::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.
66 bool 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
78 std::string_view ltrim_view(const std::string_view &input_view);
79 
80 // Trim the left and right side of a string_view of whitespace
81 std::string_view trim_view(const std::string_view &input_view);
82 
83 // Apply the common XrdClHttp configuration to a curl handle.
84 void 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
88 CURL *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.
93 class HeaderParser {
94 public:
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 
157 private:
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 
192 public:
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 
223 private:
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()
const std::string & GetETag() const
void SetMultipartSeparator(const std::string_view &sep)
ResponseInfo::HeaderMap && MoveHeaders()
static void ParseDigest(const std::string &digest, XrdClHttp::ChecksumInfo &info)
const XrdClHttp::ChecksumInfo & GetChecksums() const
std::string GetStatusMessage() const
uint64_t GetOffset() const
bool IsMultipartByterange() const
int64_t GetContentLength() const
const std::string & MultipartSeparator() const
static bool Canonicalize(std::string &headerName)
const std::string & GetLocation() const
bool Parse(const std::string &headers)
VerbsCache::HttpVerbs GetAllowedVerbs() const
static std::string ChecksumTypeToDigestName(XrdClHttp::ChecksumType type)
static bool Base64Decode(std::string_view input, std::array< unsigned char, g_max_checksum_length > &output)
const std::string & GetCacheControl() 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)
std::string GetBearerToken(XrdCl::Log *logger=nullptr)
void ConfigureHandle(CURL *curl, bool verbose)
std::string_view trim_view(const std::string_view &input_view)
XrdSysError Log
Definition: XrdConfig.cc:113
const uint64_t kLogXrdClHttp