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 
46 }
47 
48 namespace XrdClHttp {
49 
50 class CurlOperation;
51 
52 const uint64_t kLogXrdClHttp = 73173;
53 
54 bool HTTPStatusIsError(unsigned status);
55 
56 std::pair<uint16_t, uint32_t> HTTPStatusConvert(unsigned status);
57 
58 // Trim the left side of a string_view for space
59 std::string_view ltrim_view(const std::string_view &input_view);
60 
61 // Trim the left and right side of a string_view of whitespace
62 std::string_view trim_view(const std::string_view &input_view);
63 
64 // Returns a newly-created curl handle (no internal caching) with the
65 // various configurations needed to be used by XrdClHttp
66 CURL *GetHandle(bool verbose);
67 
68 // Parser for headers as emitted by libcurl.
69 //
70 // Records specific headers known to be used by the project but ignores others.
71 class HeaderParser {
72 public:
74 
75  bool Parse(const std::string &headers);
76 
77  int64_t GetContentLength() const {return m_content_length;}
78 
79  uint64_t GetOffset() const {return m_response_offset;}
80 
81  static bool Canonicalize(std::string &headerName);
82 
83  bool HeadersDone() const {return m_recv_all_headers;}
84 
85  // Move the received headers to the caller.
86  //
87  // Only invoke once HeadersDone() returns true.
88  ResponseInfo::HeaderMap && MoveHeaders() {return std::move(m_headers);}
89 
90  int GetStatusCode() const {return m_status_code;}
91 
92  // Setter for the status code
93  // Intended for use in unit tests.
94  void SetStatusCode(int sc) {m_status_code = sc;}
95 
96  // Return whether the server response specified this is a multipart range.
97  bool IsMultipartByterange() const {return m_multipart_byteranges;}
98 
99  // Return the separator specified in the server response with the
100  // `--` prefix included..
101  const std::string &MultipartSeparator() const {return m_multipart_sep;}
102 
103  // Set the separator used for multipart messages; a `--` prefix
104  // will be added to the Getter.
105  void SetMultipartSeparator(const std::string_view &sep) {
106  m_multipart_sep = "--" + std::string(sep);
107  m_multipart_byteranges = true;
108  }
109 
111  {
112  return VerbsCache::HttpVerbs(m_allow_verbs);
113  }
114 
115  std::string GetStatusMessage() const {return m_resp_message;}
116 
117  const std::string &GetLocation() const {return m_location;}
118  const std::string &GetETag() const {return m_etag;}
119  const std::string &GetCacheControl() const {return m_cache_control;}
120 
121  // Returns a reference to the checksums parsed from the headers.
122  const XrdClHttp::ChecksumInfo &GetChecksums() const {return m_checksums;}
123 
124  // Parse a RFC 3230 header, updating the checksum info structure.
125  static void ParseDigest(const std::string &digest, XrdClHttp::ChecksumInfo &info);
126 
127  // Decode a base64-encoded string into a binary buffer.
128  static bool Base64Decode(std::string_view input, std::array<unsigned char, 32> &output);
129 
130  // Convert a checksum type to a RFC 3230 digest name.
131  static std::string ChecksumTypeToDigestName(XrdClHttp::ChecksumType type);
132 
133 private:
134 
135  static bool validHeaderByte(unsigned char c);
136 
137  int64_t m_content_length{-1};
138  uint64_t m_response_offset{0};
139 
140  XrdClHttp::ChecksumInfo m_checksums;
141 
142  bool m_recv_all_headers{false};
143  bool m_recv_status_line{false};
144  bool m_multipart_byteranges{false};
145 
146  int m_status_code{-1};
147  std::string m_resp_protocol;
148  std::string m_resp_message;
149  std::string m_location;
150  std::string m_multipart_sep;
151  std::string m_etag;
152  std::string m_cache_control;
153 
154  ResponseInfo::HeaderMap m_headers;
155 
157 };
158 
168 public:
169  HandlerQueue(unsigned max_pending_ops);
170 
171  void Produce(std::shared_ptr<CurlOperation> handler);
172 
173  std::shared_ptr<CurlOperation> Consume(std::chrono::steady_clock::duration);
174  std::shared_ptr<CurlOperation> TryConsume();
175 
176  int PollFD() const {return m_read_fd;}
177 
178  CURL *GetHandle();
179  void RecycleHandle(CURL *);
180 
181  // Check all the operations in queue to see if any have expired.
182  //
183  // Each curl operation has a header timeout; if no headers have been received
184  // by the time the timeout expires, the operation is considered to have
185  // expired. This function checks all operations in the queue and
186  // removes any that have expired.
187  void Expire();
188 
189  void Shutdown();
190  // Cleanup all idle handles in current thread.
191  void ReleaseHandles();
192 
193  // Returns the class default number of pending operations.
194  static unsigned GetDefaultMaxPendingOps() {return m_default_max_pending_ops;}
195 
196  // Returns a summary of the queue's performance statistics.
197  static std::string GetMonitoringJson();
198 
199 private:
200  bool m_shutdown{false};
201  std::deque<std::shared_ptr<CurlOperation>> m_ops;
202  static std::atomic<uint64_t> m_ops_consumed; // Count of operations consumed from the queue.
203  static std::atomic<uint64_t> m_ops_produced; // Count of operations added to the queue.
204  static std::atomic<uint64_t> m_ops_rejected; // Count of operations rejected by the queue.
205  thread_local static std::vector<CURL*> m_handles;
206  std::condition_variable m_consumer_cv;
207  std::condition_variable m_producer_cv;
208  std::mutex m_mutex;
209  const static unsigned m_default_max_pending_ops{50};
210  const unsigned m_max_pending_ops{50};
211  int m_read_fd{-1};
212  int m_write_fd{-1};
213 };
214 
215 }
216 
217 #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)
static bool Base64Decode(std::string_view input, std::array< unsigned char, 32 > &output)
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)
void SetStatusCode(int sc)
const std::string & GetCacheControl() const
std::unordered_map< std::string, HeaderValues > HeaderMap
std::pair< uint16_t, uint32_t > HTTPStatusConvert(unsigned status)
CURL * GetHandle(bool verbose)
bool HTTPStatusIsError(unsigned status)
std::string_view ltrim_view(const std::string_view &input_view)
std::string_view trim_view(const std::string_view &input_view)
XrdSysError Log
Definition: XrdConfig.cc:113
const uint64_t kLogXrdClHttp