WPILibC++ 2024.3.2
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
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
40 bool spacePlus = true);
41
42// Parse a set of HTTP headers. Saves just the Content-Type and Content-Length
43// fields.
44// @param is Input stream
45// @param contentType If not null, Content-Type contents are saved here.
46// @param contentLength If not null, Content-Length contents are saved here.
47// @return False if error occurred in input stream
49 SmallVectorImpl<char>* contentLength);
50
51// Look for a MIME multi-part boundary. On return, the input stream will
52// be located at the character following the boundary (usually "\r\n").
53// @param is Input stream
54// @param boundary Boundary string to scan for (not including "--" prefix)
55// @param saveBuf If not null, all scanned characters up to but not including
56// the boundary are saved to this string
57// @return False if error occurred on input stream, true if boundary found.
59 std::string* saveBuf);
60
61/**
62 * Map for looking up elements of the query portion of a URI. Does not
63 * handle multiple elements with the same name. This is a reference type;
64 * it does not make a copy of the query string, so it is important that the
65 * query string has a lifetime at least as long as this object.
66 */
68 public:
69 /**
70 * Constructs an empty map (with no entries).
71 */
72 HttpQueryMap() = default;
73
74 /**
75 * Constructs from an escaped query string. Note: does not make a copy of
76 * the query string, so it must not be a temporary.
77 *
78 * @param query query string
79 */
81
82 /**
83 * Gets an element of the query string. Both the name and the returned
84 * value are unescaped strings.
85 *
86 * @param name name (unescaped)
87 * @param buf result buffer for value
88 * @return Optional unescaped value. Returns an empty optional if the
89 * name is not present in the query map.
90 */
91 std::optional<std::string_view> Get(std::string_view name,
92 SmallVectorImpl<char>& buf) const;
93
94 private:
96};
97
98class HttpPathRef;
99
100/**
101 * Class for HTTP path matching. A root path is represented as a single
102 * empty element, otherwise the path consists of each non-empty element
103 * between the '/' characters:
104 * - "" -> []
105 * - "/" -> [""]
106 * - "/foo" -> ["foo"]
107 * - "/foo/bar" -> ["foo", "bar"]
108 * - "/foo//bar/" -> ["foo", "bar"]
109 *
110 * All path elements are unescaped.
111 */
112class HttpPath {
113 public:
114 /**
115 * Constructs an empty HTTP path.
116 */
117 HttpPath() = default;
118
119 /**
120 * Constructs a HTTP path from an escaped path string. Makes a copy of the
121 * path, so it's safe to be a temporary.
122 */
124
125 /**
126 * Evaluates to true if the path is not empty.
127 */
128 explicit operator bool() const { return !empty(); }
129
130 /**
131 * Returns true if the path has no elements.
132 */
133 bool empty() const { return m_pathEnds.empty(); }
134
135 /**
136 * Returns number of elements in the path.
137 */
138 size_t size() const { return m_pathEnds.size(); }
139
140 /**
141 * Returns true if the path exactly matches the provided match list.
142 *
143 * @param match match list
144 * @return True if path equals match list
145 */
146 bool equals(std::initializer_list<std::string_view> match) const {
147 return equals(0, {match.begin(), match.end()});
148 }
149 bool equals(std::span<const std::string_view> match) const {
150 return equals(0, match);
151 }
152 bool equals(std::string_view match) const { return equals(0, {match}); }
153
154 /**
155 * Returns true if the elements of the path starting at the "start" element
156 * match the provided match list, and there are no additional elements.
157 *
158 * @param start element to start matching at
159 * @param match match list
160 * @return True if match
161 */
162 bool equals(size_t start,
163 std::initializer_list<std::string_view> match) const {
164 return equals(start, {match.begin(), match.end()});
165 }
166 bool equals(size_t start, std::span<const std::string_view> match) const {
167 if (m_pathEnds.size() != (start + match.size())) {
168 return false;
169 }
170 return startswith(start, match);
171 }
172 bool equals(size_t start, std::string_view match) const {
173 return equals(start, {match});
174 }
175
176 /**
177 * Returns true if the first elements of the path match the provided match
178 * list. The path may have additional elements.
179 *
180 * @param match match list
181 * @return True if path starts with match list
182 */
183 bool startswith(std::initializer_list<std::string_view> match) const {
184 return startswith(0, {match.begin(), match.end()});
185 }
186 bool startswith(std::span<const std::string_view> match) const {
187 return startswith(0, match);
188 }
189 bool startswith(std::string_view match) const {
190 return startswith(0, {match});
191 }
192
193 /**
194 * Returns true if the elements of the path starting at the "start" element
195 * match the provided match list. The path may have additional elements.
196 *
197 * @param start element to start matching at
198 * @param match match list
199 * @return True if path starting at the start element matches the match list
200 */
201 bool startswith(size_t start,
202 std::initializer_list<std::string_view> match) const {
203 return startswith(start, {match.begin(), match.end()});
204 }
205
206 bool startswith(size_t start, std::span<const std::string_view> match) const;
207
208 bool startswith(size_t start, std::string_view match) const {
209 return startswith(start, {match});
210 }
211
212 /**
213 * Gets a single element of the path.
214 */
216
217 /**
218 * Returns a path reference with the first N elements of the path removed.
219 */
220 HttpPathRef drop_front(size_t n) const;
221
222 private:
223 SmallString<128> m_pathBuf;
224 SmallVector<size_t, 16> m_pathEnds;
225};
226
227/**
228 * Proxy reference object for a portion of a HttpPath.
229 */
231 public:
232 HttpPathRef() = default;
233 /*implicit*/ HttpPathRef(const HttpPath& path, // NOLINT
234 size_t start = 0)
235 : m_path(&path), m_start(start) {}
236
237 explicit operator bool() const { return !empty(); }
238 bool empty() const { return m_path && m_path->size() == m_start; }
239 size_t size() const { return m_path ? m_path->size() - m_start : 0; }
240
241 bool equals(std::initializer_list<std::string_view> match) const {
242 return equals(0, {match.begin(), match.end()});
243 }
244 bool equals(std::span<const std::string_view> match) const {
245 return equals(0, match);
246 }
247 bool equals(std::string_view match) const { return equals(0, {match}); }
248
249 bool equals(size_t start,
250 std::initializer_list<std::string_view> match) const {
251 return equals(start, {match.begin(), match.end()});
252 }
253 bool equals(size_t start, std::span<const std::string_view> match) const {
254 return m_path ? m_path->equals(m_start + start, match) : false;
255 }
256 bool equals(size_t start, std::string_view match) const {
257 return equals(start, {match});
258 }
259
260 bool startswith(std::initializer_list<std::string_view> match) const {
261 return startswith(0, {match.begin(), match.end()});
262 }
263 bool startswith(std::span<const std::string_view> match) const {
264 return startswith(0, match);
265 }
266 bool startswith(std::string_view match) const {
267 return startswith(0, {match});
268 }
269
270 bool startswith(size_t start,
271 std::initializer_list<std::string_view> match) const {
272 return startswith(start, {match.begin(), match.end()});
273 }
274 bool startswith(size_t start, std::span<const std::string_view> match) const {
275 return m_path ? m_path->startswith(m_start + start, match) : false;
276 }
277 bool startswith(size_t start, std::string_view match) const {
278 return startswith(start, {match});
279 }
280
281 std::string_view operator[](size_t n) const {
282 return m_path ? m_path->operator[](m_start + n) : std::string_view{};
283 }
284 HttpPathRef drop_front(size_t n) const {
285 return m_path ? m_path->drop_front(m_start + n) : HttpPathRef{};
286 }
287
288 private:
289 const HttpPath* m_path = nullptr;
290 size_t m_start = 0;
291};
292
294 public:
295 HttpLocation() = default;
296 HttpLocation(std::string_view url_, bool* error, std::string* errorMsg);
297
298 std::string url; // retain copy
299 std::string user; // unescaped
300 std::string password; // unescaped
301 std::string host;
302 int port;
303 std::string path; // escaped, not including leading '/'
304 std::vector<std::pair<std::string, std::string>> params; // unescaped
305 std::string fragment;
306};
307
309 public:
310 HttpRequest() = default;
311
312 explicit HttpRequest(const HttpLocation& loc)
313 : host{loc.host}, port{loc.port} {
314 SetPath(loc.path, loc.params);
315 SetAuth(loc);
316 }
317
318 template <typename T>
319 HttpRequest(const HttpLocation& loc, const T& extraParams);
320
322 : host{loc.host}, port{loc.port}, path{path_} {
323 SetAuth(loc);
324 }
325
326 template <typename T>
327 HttpRequest(const HttpLocation& loc, std::string_view path_, const T& params)
328 : host{loc.host}, port{loc.port} {
329 SetPath(path_, params);
330 SetAuth(loc);
331 }
332
334 int port;
335 std::string auth;
337
338 private:
339 void SetAuth(const HttpLocation& loc);
340 template <typename T>
341 void SetPath(std::string_view path_, const T& params);
342
343 template <typename T>
344 static std::string_view GetFirst(const T& elem) {
345 return elem.first;
346 }
347 template <typename T>
348 static std::string_view GetFirst(const StringMapEntry<T>& elem) {
349 return elem.getKey();
350 }
351 template <typename T>
352 static std::string_view GetSecond(const T& elem) {
353 return elem.second;
354 }
355};
356
358 public:
359 HttpConnection(std::unique_ptr<wpi::NetworkStream> stream_, int timeout)
360 : stream{std::move(stream_)}, is{*stream, timeout}, os{*stream, true} {}
361
362 bool Handshake(const HttpRequest& request, std::string* warnMsg);
363
364 std::unique_ptr<wpi::NetworkStream> stream;
367
368 // Valid after Handshake() is successful
371
372 explicit operator bool() const { return stream && !is.has_error(); }
373};
374
376 public:
378 bool saveSkipped = false) {
379 Reset(saveSkipped);
380 SetBoundary(boundary);
381 }
382
383 // Change the boundary. This is only safe to do when IsDone() is true (or
384 // immediately after construction).
386
387 // Reset the scanner. This allows reuse of internal buffers.
388 void Reset(bool saveSkipped = false);
389
390 // Execute the scanner. Will automatically call Reset() on entry if IsDone()
391 // is true.
392 // @param in input data
393 // @return the input not consumed; empty if all input consumed
395
396 // Returns true when the boundary has been found.
397 bool IsDone() const { return m_state == kDone; }
398
399 // Get the skipped data. Will be empty if saveSkipped was false.
401 return m_saveSkipped ? std::string_view{m_buf} : std::string_view{};
402 }
403
404 private:
405 SmallString<64> m_boundaryWith, m_boundaryWithout;
406
407 // Internal state
408 enum State { kBoundary, kPadding, kDone };
409 State m_state;
410 size_t m_posWith, m_posWithout;
411 enum Dashes { kUnknown, kWith, kWithout };
412 Dashes m_dashes;
413
414 // Buffer
415 bool m_saveSkipped;
416 std::string m_buf;
417};
418
419} // namespace wpi
420
421#include "HttpUtil.inc"
422
423#endif // WPINET_HTTPUTIL_H_
This file defines the SmallString class.
This file defines the SmallVector class.
This file defines the StringMap class.
Definition: HttpUtil.h:357
SmallString< 64 > contentType
Definition: HttpUtil.h:369
SmallString< 64 > contentLength
Definition: HttpUtil.h:370
bool Handshake(const HttpRequest &request, std::string *warnMsg)
wpi::raw_socket_ostream os
Definition: HttpUtil.h:366
HttpConnection(std::unique_ptr< wpi::NetworkStream > stream_, int timeout)
Definition: HttpUtil.h:359
std::unique_ptr< wpi::NetworkStream > stream
Definition: HttpUtil.h:364
wpi::raw_socket_istream is
Definition: HttpUtil.h:365
Definition: HttpUtil.h:293
std::string user
Definition: HttpUtil.h:299
std::string url
Definition: HttpUtil.h:298
std::string password
Definition: HttpUtil.h:300
HttpLocation(std::string_view url_, bool *error, std::string *errorMsg)
int port
Definition: HttpUtil.h:302
std::string fragment
Definition: HttpUtil.h:305
HttpLocation()=default
std::string host
Definition: HttpUtil.h:301
std::vector< std::pair< std::string, std::string > > params
Definition: HttpUtil.h:304
std::string path
Definition: HttpUtil.h:303
Definition: HttpUtil.h:375
HttpMultipartScanner(std::string_view boundary, bool saveSkipped=false)
Definition: HttpUtil.h:377
void Reset(bool saveSkipped=false)
std::string_view GetSkipped() const
Definition: HttpUtil.h:400
std::string_view Execute(std::string_view in)
bool IsDone() const
Definition: HttpUtil.h:397
void SetBoundary(std::string_view boundary)
Class for HTTP path matching.
Definition: HttpUtil.h:112
bool empty() const
Returns true if the path has no elements.
Definition: HttpUtil.h:133
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:183
size_t size() const
Returns number of elements in the path.
Definition: HttpUtil.h:138
HttpPathRef drop_front(size_t n) const
Returns a path reference with the first N elements of the path removed.
Definition: HttpUtil.inc:14
bool equals(std::string_view match) const
Definition: HttpUtil.h:152
bool startswith(size_t start, std::string_view match) const
Definition: HttpUtil.h:208
bool equals(size_t start, std::span< const std::string_view > match) const
Definition: HttpUtil.h:166
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:162
bool startswith(std::string_view match) const
Definition: HttpUtil.h:189
bool equals(std::span< const std::string_view > match) const
Definition: HttpUtil.h:149
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:201
bool startswith(std::span< const std::string_view > match) const
Definition: HttpUtil.h:186
bool equals(size_t start, std::string_view match) const
Definition: HttpUtil.h:172
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:146
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:230
bool empty() const
Definition: HttpUtil.h:238
bool startswith(size_t start, std::initializer_list< std::string_view > match) const
Definition: HttpUtil.h:270
size_t size() const
Definition: HttpUtil.h:239
bool equals(size_t start, std::initializer_list< std::string_view > match) const
Definition: HttpUtil.h:249
bool equals(std::initializer_list< std::string_view > match) const
Definition: HttpUtil.h:241
std::string_view operator[](size_t n) const
Definition: HttpUtil.h:281
bool startswith(size_t start, std::string_view match) const
Definition: HttpUtil.h:277
bool equals(size_t start, std::span< const std::string_view > match) const
Definition: HttpUtil.h:253
bool startswith(std::initializer_list< std::string_view > match) const
Definition: HttpUtil.h:260
bool equals(size_t start, std::string_view match) const
Definition: HttpUtil.h:256
bool startswith(std::string_view match) const
Definition: HttpUtil.h:266
bool startswith(size_t start, std::span< const std::string_view > match) const
Definition: HttpUtil.h:274
HttpPathRef()=default
HttpPathRef drop_front(size_t n) const
Definition: HttpUtil.h:284
bool equals(std::span< const std::string_view > match) const
Definition: HttpUtil.h:244
bool equals(std::string_view match) const
Definition: HttpUtil.h:247
bool startswith(std::span< const std::string_view > match) const
Definition: HttpUtil.h:263
HttpPathRef(const HttpPath &path, size_t start=0)
Definition: HttpUtil.h:233
Map for looking up elements of the query portion of a URI.
Definition: HttpUtil.h:67
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:308
HttpRequest(const HttpLocation &loc, std::string_view path_)
Definition: HttpUtil.h:321
HttpRequest()=default
HttpRequest(const HttpLocation &loc)
Definition: HttpUtil.h:312
SmallString< 128 > path
Definition: HttpUtil.h:336
HttpRequest(const HttpLocation &loc, std::string_view path_, const T &params)
Definition: HttpUtil.h:327
int port
Definition: HttpUtil.h:334
SmallString< 128 > host
Definition: HttpUtil.h:333
std::string auth
Definition: HttpUtil.h:335
size_t size() const
Definition: SmallVector.h:98
bool empty() const
Definition: SmallVector.h:101
StringMapEntry - This is used to represent one value that is inserted into a StringMap.
Definition: StringMapEntry.h:106
std::string_view getKey() const
Definition: StringMapEntry.h:112
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
basic_string_view< char > string_view
Definition: core.h:501
constexpr auto in(type t, int set) -> bool
Definition: core.h:611
Definition: array.h:89
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)
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)