WPILibC++ 2025.2.1
Loading...
Searching...
No Matches
HttpUtil.h
Go to the documentation of this file.
1// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#ifndef WPINET_HTTPUTIL_H_
6#define WPINET_HTTPUTIL_H_
7
8#include <initializer_list>
9#include <memory>
10#include <optional>
11#include <span>
12#include <string>
13#include <string_view>
14#include <utility>
15#include <vector>
16
17#include <wpi/SmallString.h>
18#include <wpi/SmallVector.h>
19#include <wpi/StringMap.h>
20#include <wpi/raw_istream.h>
21
25
26namespace wpi {
27
28// Unescape a %xx-encoded URI.
29// @param buf Buffer for output
30// @param error Set to true if an error occurred
31// @return Escaped string
32std::string_view UnescapeURI(std::string_view str, SmallVectorImpl<char>& buf,
33 bool* error);
34
35// Escape a string with %xx-encoding.
36// @param buf Buffer for output
37// @param spacePlus If true, encodes spaces to '+' rather than "%20"
38// @return Escaped string
39std::string_view EscapeURI(std::string_view str, SmallVectorImpl<char>& buf,
40 bool spacePlus = true);
41
42// Escape a string for HTML output.
43// @param buf Buffer for output
44// @return Escaped string
45std::string_view EscapeHTML(std::string_view str, SmallVectorImpl<char>& buf);
46
47// Parse a set of HTTP headers. Saves just the Content-Type and Content-Length
48// fields.
49// @param is Input stream
50// @param contentType If not null, Content-Type contents are saved here.
51// @param contentLength If not null, Content-Length contents are saved here.
52// @return False if error occurred in input stream
54 SmallVectorImpl<char>* contentLength);
55
56// Look for a MIME multi-part boundary. On return, the input stream will
57// be located at the character following the boundary (usually "\r\n").
58// @param is Input stream
59// @param boundary Boundary string to scan for (not including "--" prefix)
60// @param saveBuf If not null, all scanned characters up to but not including
61// the boundary are saved to this string
62// @return False if error occurred on input stream, true if boundary found.
63bool FindMultipartBoundary(wpi::raw_istream& is, std::string_view boundary,
64 std::string* saveBuf);
65
66/**
67 * Map for looking up elements of the query portion of a URI. Does not
68 * handle multiple elements with the same name. This is a reference type;
69 * it does not make a copy of the query string, so it is important that the
70 * query string has a lifetime at least as long as this object.
71 */
73 public:
74 /**
75 * Constructs an empty map (with no entries).
76 */
77 HttpQueryMap() = default;
78
79 /**
80 * Constructs from an escaped query string. Note: does not make a copy of
81 * the query string, so it must not be a temporary.
82 *
83 * @param query query string
84 */
85 explicit HttpQueryMap(std::string_view query);
86
87 /**
88 * Gets an element of the query string. Both the name and the returned
89 * value are unescaped strings.
90 *
91 * @param name name (unescaped)
92 * @param buf result buffer for value
93 * @return Optional unescaped value. Returns an empty optional if the
94 * name is not present in the query map.
95 */
96 std::optional<std::string_view> Get(std::string_view name,
97 SmallVectorImpl<char>& buf) const;
98
99 private:
101};
102
103class HttpPathRef;
104
105/**
106 * Class for HTTP path matching. A root path is represented as a single
107 * empty element, otherwise the path consists of each non-empty element
108 * between the '/' characters:
109 * - "" -> []
110 * - "/" -> [""]
111 * - "/foo" -> ["foo"]
112 * - "/foo/bar" -> ["foo", "bar"]
113 * - "/foo//bar/" -> ["foo", "bar"]
114 *
115 * All path elements are unescaped.
116 */
117class HttpPath {
118 public:
119 /**
120 * Constructs an empty HTTP path.
121 */
122 HttpPath() = default;
123
124 /**
125 * Constructs a HTTP path from an escaped path string. Makes a copy of the
126 * path, so it's safe to be a temporary.
127 */
128 explicit HttpPath(std::string_view path);
129
130 /**
131 * Evaluates to true if the path is not empty.
132 */
133 explicit operator bool() const { return !empty(); }
134
135 /**
136 * Returns true if the path has no elements.
137 */
138 bool empty() const { return m_pathEnds.empty(); }
139
140 /**
141 * Returns number of elements in the path.
142 */
143 size_t size() const { return m_pathEnds.size(); }
144
145 /**
146 * Returns true if the path exactly matches the provided match list.
147 *
148 * @param match match list
149 * @return True if path equals match list
150 */
151 bool equals(std::initializer_list<std::string_view> match) const {
152 return equals(0, {match.begin(), match.end()});
153 }
154 bool equals(std::span<const std::string_view> match) const {
155 return equals(0, match);
156 }
157 bool equals(std::string_view match) const { return equals(0, {match}); }
158
159 /**
160 * Returns true if the elements of the path starting at the "start" element
161 * match the provided match list, and there are no additional elements.
162 *
163 * @param start element to start matching at
164 * @param match match list
165 * @return True if match
166 */
167 bool equals(size_t start,
168 std::initializer_list<std::string_view> match) const {
169 return equals(start, {match.begin(), match.end()});
170 }
171 bool equals(size_t start, std::span<const std::string_view> match) const {
172 if (m_pathEnds.size() != (start + match.size())) {
173 return false;
174 }
175 return startswith(start, match);
176 }
177 bool equals(size_t start, std::string_view match) const {
178 return equals(start, {match});
179 }
180
181 /**
182 * Returns true if the first elements of the path match the provided match
183 * list. The path may have additional elements.
184 *
185 * @param match match list
186 * @return True if path starts with match list
187 */
188 bool startswith(std::initializer_list<std::string_view> match) const {
189 return startswith(0, {match.begin(), match.end()});
190 }
191 bool startswith(std::span<const std::string_view> match) const {
192 return startswith(0, match);
193 }
194 bool startswith(std::string_view match) const {
195 return startswith(0, {match});
196 }
197
198 /**
199 * Returns true if the elements of the path starting at the "start" element
200 * match the provided match list. The path may have additional elements.
201 *
202 * @param start element to start matching at
203 * @param match match list
204 * @return True if path starting at the start element matches the match list
205 */
206 bool startswith(size_t start,
207 std::initializer_list<std::string_view> match) const {
208 return startswith(start, {match.begin(), match.end()});
209 }
210
211 bool startswith(size_t start, std::span<const std::string_view> match) const;
212
213 bool startswith(size_t start, std::string_view match) const {
214 return startswith(start, {match});
215 }
216
217 /**
218 * Gets a single element of the path.
219 */
220 std::string_view operator[](size_t n) const;
221
222 /**
223 * Returns a path reference with the first N elements of the path removed.
224 */
225 HttpPathRef drop_front(size_t n) const;
226
227 private:
228 SmallString<128> m_pathBuf;
229 SmallVector<size_t, 16> m_pathEnds;
230};
231
232/**
233 * Proxy reference object for a portion of a HttpPath.
234 */
236 public:
237 HttpPathRef() = default;
238 /*implicit*/ HttpPathRef(const HttpPath& path, // NOLINT
239 size_t start = 0)
240 : m_path(&path), m_start(start) {}
241
242 explicit operator bool() const { return !empty(); }
243 bool empty() const { return m_path && m_path->size() == m_start; }
244 size_t size() const { return m_path ? m_path->size() - m_start : 0; }
245
246 bool equals(std::initializer_list<std::string_view> match) const {
247 return equals(0, {match.begin(), match.end()});
248 }
249 bool equals(std::span<const std::string_view> match) const {
250 return equals(0, match);
251 }
252 bool equals(std::string_view match) const { return equals(0, {match}); }
253
254 bool equals(size_t start,
255 std::initializer_list<std::string_view> match) const {
256 return equals(start, {match.begin(), match.end()});
257 }
258 bool equals(size_t start, std::span<const std::string_view> match) const {
259 return m_path ? m_path->equals(m_start + start, match) : false;
260 }
261 bool equals(size_t start, std::string_view match) const {
262 return equals(start, {match});
263 }
264
265 bool startswith(std::initializer_list<std::string_view> match) const {
266 return startswith(0, {match.begin(), match.end()});
267 }
268 bool startswith(std::span<const std::string_view> match) const {
269 return startswith(0, match);
270 }
271 bool startswith(std::string_view match) const {
272 return startswith(0, {match});
273 }
274
275 bool startswith(size_t start,
276 std::initializer_list<std::string_view> match) const {
277 return startswith(start, {match.begin(), match.end()});
278 }
279 bool startswith(size_t start, std::span<const std::string_view> match) const {
280 return m_path ? m_path->startswith(m_start + start, match) : false;
281 }
282 bool startswith(size_t start, std::string_view match) const {
283 return startswith(start, {match});
284 }
285
286 std::string_view operator[](size_t n) const {
287 return m_path ? m_path->operator[](m_start + n) : std::string_view{};
288 }
289 HttpPathRef drop_front(size_t n) const {
290 return m_path ? m_path->drop_front(m_start + n) : HttpPathRef{};
291 }
292
293 private:
294 const HttpPath* m_path = nullptr;
295 size_t m_start = 0;
296};
297
299 public:
300 HttpLocation() = default;
301 HttpLocation(std::string_view url_, bool* error, std::string* errorMsg);
302
303 std::string url; // retain copy
304 std::string user; // unescaped
305 std::string password; // unescaped
306 std::string host;
307 int port;
308 std::string path; // escaped, not including leading '/'
309 std::vector<std::pair<std::string, std::string>> params; // unescaped
310 std::string fragment;
311};
312
314 public:
315 HttpRequest() = default;
316
317 explicit HttpRequest(const HttpLocation& loc)
318 : host{loc.host}, port{loc.port} {
319 SetPath(loc.path, loc.params);
320 SetAuth(loc);
321 }
322
323 template <typename T>
324 HttpRequest(const HttpLocation& loc, const T& extraParams)
325 : host{loc.host}, port{loc.port} {
327 for (const auto& p : loc.params) {
328 params.emplace_back(std::pair{GetFirst(p), GetSecond(p)});
329 }
330 for (const auto& p : extraParams) {
331 params.emplace_back(std::pair{GetFirst(p), GetSecond(p)});
332 }
333 SetPath(loc.path, params);
334 SetAuth(loc);
335 }
336
337 HttpRequest(const HttpLocation& loc, std::string_view path_)
338 : host{loc.host}, port{loc.port}, path{path_} {
339 SetAuth(loc);
340 }
341
342 template <typename T>
343 HttpRequest(const HttpLocation& loc, std::string_view path_, const T& params)
344 : host{loc.host}, port{loc.port} {
345 SetPath(path_, params);
346 SetAuth(loc);
347 }
348
350 int port;
351 std::string auth;
353
354 private:
355 void SetAuth(const HttpLocation& loc);
356
357 template <typename T>
358 void SetPath(std::string_view path_, const T& params) {
359 // Build location including query string
361 pathOs << path_;
362 bool first = true;
363 for (const auto& param : params) {
364 if (first) {
365 pathOs << '?';
366 first = false;
367 } else {
368 pathOs << '&';
369 }
370 SmallString<64> escapeBuf;
371 pathOs << EscapeURI(GetFirst(param), escapeBuf, false);
372 if (!GetSecond(param).empty()) {
373 pathOs << '=' << EscapeURI(GetSecond(param), escapeBuf, false);
374 }
375 }
376 }
377
378 template <typename T>
379 static std::string_view GetFirst(const T& elem) {
380 return elem.first;
381 }
382 template <typename T>
383 static std::string_view GetFirst(const std::pair<std::string, T>& elem) {
384 return elem.first;
385 }
386 template <typename T>
387 static std::string_view GetSecond(const T& elem) {
388 return elem.second;
389 }
390};
391
393 public:
394 HttpConnection(std::unique_ptr<wpi::NetworkStream> stream_, int timeout)
395 : stream{std::move(stream_)}, is{*stream, timeout}, os{*stream, true} {}
396
397 bool Handshake(const HttpRequest& request, std::string* warnMsg);
398
399 std::unique_ptr<wpi::NetworkStream> stream;
402
403 // Valid after Handshake() is successful
406
407 explicit operator bool() const { return stream && !is.has_error(); }
408};
409
411 public:
412 explicit HttpMultipartScanner(std::string_view boundary,
413 bool saveSkipped = false) {
414 Reset(saveSkipped);
415 SetBoundary(boundary);
416 }
417
418 // Change the boundary. This is only safe to do when IsDone() is true (or
419 // immediately after construction).
420 void SetBoundary(std::string_view boundary);
421
422 // Reset the scanner. This allows reuse of internal buffers.
423 void Reset(bool saveSkipped = false);
424
425 // Execute the scanner. Will automatically call Reset() on entry if IsDone()
426 // is true.
427 // @param in input data
428 // @return the input not consumed; empty if all input consumed
429 std::string_view Execute(std::string_view in);
430
431 // Returns true when the boundary has been found.
432 bool IsDone() const { return m_state == kDone; }
433
434 // Get the skipped data. Will be empty if saveSkipped was false.
435 std::string_view GetSkipped() const {
436 return m_saveSkipped ? std::string_view{m_buf} : std::string_view{};
437 }
438
439 private:
440 SmallString<64> m_boundaryWith, m_boundaryWithout;
441
442 // Internal state
443 enum State { kBoundary, kPadding, kDone };
444 State m_state;
445 size_t m_posWith, m_posWithout;
446 enum Dashes { kUnknown, kWith, kWithout };
447 Dashes m_dashes;
448
449 // Buffer
450 bool m_saveSkipped;
451 std::string m_buf;
452};
453
454inline HttpPathRef HttpPath::drop_front(size_t n) const {
455 return HttpPathRef(*this, n);
456}
457
458} // namespace wpi
459
460#endif // WPINET_HTTPUTIL_H_
This file defines the SmallString class.
This file defines the SmallVector class.
Definition HttpUtil.h:392
SmallString< 64 > contentType
Definition HttpUtil.h:404
SmallString< 64 > contentLength
Definition HttpUtil.h:405
bool Handshake(const HttpRequest &request, std::string *warnMsg)
wpi::raw_socket_ostream os
Definition HttpUtil.h:401
HttpConnection(std::unique_ptr< wpi::NetworkStream > stream_, int timeout)
Definition HttpUtil.h:394
std::unique_ptr< wpi::NetworkStream > stream
Definition HttpUtil.h:399
wpi::raw_socket_istream is
Definition HttpUtil.h:400
Definition HttpUtil.h:298
std::string user
Definition HttpUtil.h:304
std::string url
Definition HttpUtil.h:303
std::string password
Definition HttpUtil.h:305
HttpLocation(std::string_view url_, bool *error, std::string *errorMsg)
int port
Definition HttpUtil.h:307
std::string fragment
Definition HttpUtil.h:310
HttpLocation()=default
std::string host
Definition HttpUtil.h:306
std::vector< std::pair< std::string, std::string > > params
Definition HttpUtil.h:309
std::string path
Definition HttpUtil.h:308
Definition HttpUtil.h:410
HttpMultipartScanner(std::string_view boundary, bool saveSkipped=false)
Definition HttpUtil.h:412
void Reset(bool saveSkipped=false)
std::string_view GetSkipped() const
Definition HttpUtil.h:435
std::string_view Execute(std::string_view in)
bool IsDone() const
Definition HttpUtil.h:432
void SetBoundary(std::string_view boundary)
Class for HTTP path matching.
Definition HttpUtil.h:117
bool empty() const
Returns true if the path has no elements.
Definition HttpUtil.h:138
bool startswith(std::initializer_list< std::string_view > match) const
Returns true if the first elements of the path match the provided match list.
Definition HttpUtil.h:188
size_t size() const
Returns number of elements in the path.
Definition HttpUtil.h:143
HttpPathRef drop_front(size_t n) const
Returns a path reference with the first N elements of the path removed.
Definition HttpUtil.h:454
bool equals(std::string_view match) const
Definition HttpUtil.h:157
bool startswith(size_t start, std::string_view match) const
Definition HttpUtil.h:213
bool equals(size_t start, std::span< const std::string_view > match) const
Definition HttpUtil.h:171
bool equals(size_t start, std::initializer_list< std::string_view > match) const
Returns true if the elements of the path starting at the "start" element match the provided match lis...
Definition HttpUtil.h:167
bool startswith(std::string_view match) const
Definition HttpUtil.h:194
bool equals(std::span< const std::string_view > match) const
Definition HttpUtil.h:154
bool startswith(size_t start, std::initializer_list< std::string_view > match) const
Returns true if the elements of the path starting at the "start" element match the provided match lis...
Definition HttpUtil.h:206
bool startswith(std::span< const std::string_view > match) const
Definition HttpUtil.h:191
bool equals(size_t start, std::string_view match) const
Definition HttpUtil.h:177
bool startswith(size_t start, std::span< const std::string_view > match) const
HttpPath()=default
Constructs an empty HTTP path.
HttpPath(std::string_view path)
Constructs a HTTP path from an escaped path string.
bool equals(std::initializer_list< std::string_view > match) const
Returns true if the path exactly matches the provided match list.
Definition HttpUtil.h:151
std::string_view operator[](size_t n) const
Gets a single element of the path.
Proxy reference object for a portion of a HttpPath.
Definition HttpUtil.h:235
bool empty() const
Definition HttpUtil.h:243
bool startswith(size_t start, std::initializer_list< std::string_view > match) const
Definition HttpUtil.h:275
size_t size() const
Definition HttpUtil.h:244
bool equals(size_t start, std::initializer_list< std::string_view > match) const
Definition HttpUtil.h:254
bool equals(std::initializer_list< std::string_view > match) const
Definition HttpUtil.h:246
std::string_view operator[](size_t n) const
Definition HttpUtil.h:286
bool startswith(size_t start, std::string_view match) const
Definition HttpUtil.h:282
bool equals(size_t start, std::span< const std::string_view > match) const
Definition HttpUtil.h:258
bool startswith(std::initializer_list< std::string_view > match) const
Definition HttpUtil.h:265
bool equals(size_t start, std::string_view match) const
Definition HttpUtil.h:261
bool startswith(std::string_view match) const
Definition HttpUtil.h:271
bool startswith(size_t start, std::span< const std::string_view > match) const
Definition HttpUtil.h:279
HttpPathRef()=default
HttpPathRef drop_front(size_t n) const
Definition HttpUtil.h:289
bool equals(std::span< const std::string_view > match) const
Definition HttpUtil.h:249
bool equals(std::string_view match) const
Definition HttpUtil.h:252
bool startswith(std::span< const std::string_view > match) const
Definition HttpUtil.h:268
HttpPathRef(const HttpPath &path, size_t start=0)
Definition HttpUtil.h:238
Map for looking up elements of the query portion of a URI.
Definition HttpUtil.h:72
HttpQueryMap()=default
Constructs an empty map (with no entries).
std::optional< std::string_view > Get(std::string_view name, SmallVectorImpl< char > &buf) const
Gets an element of the query string.
HttpQueryMap(std::string_view query)
Constructs from an escaped query string.
Definition HttpUtil.h:313
HttpRequest(const HttpLocation &loc, std::string_view path_)
Definition HttpUtil.h:337
HttpRequest()=default
HttpRequest(const HttpLocation &loc)
Definition HttpUtil.h:317
SmallString< 128 > path
Definition HttpUtil.h:352
HttpRequest(const HttpLocation &loc, std::string_view path_, const T &params)
Definition HttpUtil.h:343
int port
Definition HttpUtil.h:350
HttpRequest(const HttpLocation &loc, const T &extraParams)
Definition HttpUtil.h:324
SmallString< 128 > host
Definition HttpUtil.h:349
std::string auth
Definition HttpUtil.h:351
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:27
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition SmallVector.h:1212
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition sha1.h:30
reference emplace_back(ArgTypes &&... Args)
Definition SmallVector.h:953
size_t size() const
Definition SmallVector.h:99
bool empty() const
Definition SmallVector.h:102
StringMap is a sorted associative container that contains key-value pairs with unique string keys.
Definition StringMap.h:26
Definition raw_istream.h:22
bool has_error() const
Definition raw_istream.h:114
Definition raw_socket_istream.h:14
Definition raw_socket_ostream.h:14
A raw_ostream that writes to an SmallVector or SmallString.
Definition raw_ostream.h:619
Implement std::hash so that hash_code can be used in STL containers.
Definition PointerIntPair.h:280
Foonathan namespace.
Definition ntcore_cpp.h:26
bool ParseHttpHeaders(raw_istream &is, SmallVectorImpl< char > *contentType, SmallVectorImpl< char > *contentLength)
std::string_view UnescapeURI(std::string_view str, SmallVectorImpl< char > &buf, bool *error)
std::string_view EscapeHTML(std::string_view str, SmallVectorImpl< char > &buf)
bool FindMultipartBoundary(wpi::raw_istream &is, std::string_view boundary, std::string *saveBuf)
std::string_view EscapeURI(std::string_view str, SmallVectorImpl< char > &buf, bool spacePlus=true)