xref: /haiku/src/kits/network/libnetservices2/HttpFields.cpp (revision 7b1d966cf27635973aa04ebcad1c8b167003a692)
1a8003a70SNiels Sascha Reedijk /*
2a8003a70SNiels Sascha Reedijk  * Copyright 2022 Haiku Inc. All rights reserved.
3a8003a70SNiels Sascha Reedijk  * Distributed under the terms of the MIT License.
4a8003a70SNiels Sascha Reedijk  *
5a8003a70SNiels Sascha Reedijk  * Authors:
6a8003a70SNiels Sascha Reedijk  *		Niels Sascha Reedijk, niels.reedijk@gmail.com
7a8003a70SNiels Sascha Reedijk  */
8a8003a70SNiels Sascha Reedijk 
9a8003a70SNiels Sascha Reedijk #include <HttpFields.h>
10a8003a70SNiels Sascha Reedijk 
11a8003a70SNiels Sascha Reedijk #include <algorithm>
12a8003a70SNiels Sascha Reedijk #include <ctype.h>
13a8003a70SNiels Sascha Reedijk #include <utility>
14a8003a70SNiels Sascha Reedijk 
15cb67e348SNiels Sascha Reedijk #include "HttpPrivate.h"
16cb67e348SNiels Sascha Reedijk 
17a8003a70SNiels Sascha Reedijk using namespace BPrivate::Network;
18a8003a70SNiels Sascha Reedijk 
19a8003a70SNiels Sascha Reedijk 
20a8003a70SNiels Sascha Reedijk // #pragma mark -- utilities
21a8003a70SNiels Sascha Reedijk 
22a8003a70SNiels Sascha Reedijk 
23a8003a70SNiels Sascha Reedijk /*!
24a8003a70SNiels Sascha Reedijk 	\brief Validate whether the string is a valid HTTP header value
25a8003a70SNiels Sascha Reedijk 
26a8003a70SNiels Sascha Reedijk 	RFC 7230 section 3.2.6 determines that valid tokens for the header are:
27a8003a70SNiels Sascha Reedijk 	HTAB ('\t'), SP (32), all visible ASCII characters (33-126), and all characters that
28a8003a70SNiels Sascha Reedijk 	not control characters (in the case of a char, any value < 0)
29a8003a70SNiels Sascha Reedijk 
30a8003a70SNiels Sascha Reedijk 	\note When printing out the HTTP header, sometimes the string needs to be quoted and some
31a8003a70SNiels Sascha Reedijk 		characters need to be escaped. This function is not checking for whether the string can
32a8003a70SNiels Sascha Reedijk 		be transmitted as is.
33a8003a70SNiels Sascha Reedijk 
34a8003a70SNiels Sascha Reedijk 	\returns \c true if the string is valid, or \c false if it is not.
35a8003a70SNiels Sascha Reedijk */
36a8003a70SNiels Sascha Reedijk static inline bool
37a8003a70SNiels Sascha Reedijk validate_value_string(const std::string_view& string)
38a8003a70SNiels Sascha Reedijk {
39a8003a70SNiels Sascha Reedijk 	for (auto it = string.cbegin(); it < string.cend(); it++) {
40a8003a70SNiels Sascha Reedijk 		if ((*it >= 0 && *it < 32) || *it == 127 || *it == '\t')
41a8003a70SNiels Sascha Reedijk 			return false;
42a8003a70SNiels Sascha Reedijk 	}
43a8003a70SNiels Sascha Reedijk 	return true;
44a8003a70SNiels Sascha Reedijk }
45a8003a70SNiels Sascha Reedijk 
46a8003a70SNiels Sascha Reedijk 
478ccf8fb4SNiels Sascha Reedijk /*!
48a8003a70SNiels Sascha Reedijk 	\brief Case insensitively compare two string_views.
49a8003a70SNiels Sascha Reedijk 
50a8003a70SNiels Sascha Reedijk 	Inspired by https://stackoverflow.com/a/4119881
51a8003a70SNiels Sascha Reedijk */
52a8003a70SNiels Sascha Reedijk static inline bool
53a8003a70SNiels Sascha Reedijk iequals(const std::string_view& a, const std::string_view& b)
54a8003a70SNiels Sascha Reedijk {
55a8003a70SNiels Sascha Reedijk 	return std::equal(
56a8003a70SNiels Sascha Reedijk 		a.begin(), a.end(),
57a8003a70SNiels Sascha Reedijk 		b.begin(), b.end(),
58a8003a70SNiels Sascha Reedijk 		[](char a, char b) {
59a8003a70SNiels Sascha Reedijk 			return tolower(a) == tolower(b);
60a8003a70SNiels Sascha Reedijk 	});
61a8003a70SNiels Sascha Reedijk }
62a8003a70SNiels Sascha Reedijk 
63a8003a70SNiels Sascha Reedijk 
648ccf8fb4SNiels Sascha Reedijk /*!
658ccf8fb4SNiels Sascha Reedijk 	\brief Trim whitespace from the beginning and end of a string_view
668ccf8fb4SNiels Sascha Reedijk 
678ccf8fb4SNiels Sascha Reedijk 	Inspired by:
688ccf8fb4SNiels Sascha Reedijk 		https://terrislinenbach.medium.com/trimming-whitespace-from-a-string-view-6795e18b108f
698ccf8fb4SNiels Sascha Reedijk */
708ccf8fb4SNiels Sascha Reedijk static inline std::string_view
718ccf8fb4SNiels Sascha Reedijk trim(std::string_view in)
728ccf8fb4SNiels Sascha Reedijk {
738ccf8fb4SNiels Sascha Reedijk 	auto left = in.begin();
748ccf8fb4SNiels Sascha Reedijk 	for (;; ++left) {
758ccf8fb4SNiels Sascha Reedijk 		if (left == in.end())
768ccf8fb4SNiels Sascha Reedijk 			return std::string_view();
778ccf8fb4SNiels Sascha Reedijk 		if (!isspace(*left))
788ccf8fb4SNiels Sascha Reedijk 			break;
798ccf8fb4SNiels Sascha Reedijk 	}
808ccf8fb4SNiels Sascha Reedijk 
818ccf8fb4SNiels Sascha Reedijk 	auto right = in.end() - 1;
828ccf8fb4SNiels Sascha Reedijk 	for (; right > left && isspace(*right); --right);
838ccf8fb4SNiels Sascha Reedijk 
848ccf8fb4SNiels Sascha Reedijk 	return std::string_view(left, std::distance(left, right) + 1);
858ccf8fb4SNiels Sascha Reedijk }
868ccf8fb4SNiels Sascha Reedijk 
878ccf8fb4SNiels Sascha Reedijk 
88a8003a70SNiels Sascha Reedijk // #pragma mark -- BHttpFields::InvalidHeader
89a8003a70SNiels Sascha Reedijk 
90a8003a70SNiels Sascha Reedijk 
91a8003a70SNiels Sascha Reedijk BHttpFields::InvalidInput::InvalidInput(const char* origin, BString input)
92a8003a70SNiels Sascha Reedijk 	:
93a8003a70SNiels Sascha Reedijk 	BError(origin),
94a8003a70SNiels Sascha Reedijk 	input(std::move(input))
95a8003a70SNiels Sascha Reedijk {
96a8003a70SNiels Sascha Reedijk 
97a8003a70SNiels Sascha Reedijk }
98a8003a70SNiels Sascha Reedijk 
99a8003a70SNiels Sascha Reedijk 
100a8003a70SNiels Sascha Reedijk const char*
101a8003a70SNiels Sascha Reedijk BHttpFields::InvalidInput::Message() const noexcept
102a8003a70SNiels Sascha Reedijk {
103a8003a70SNiels Sascha Reedijk 	return "Invalid format or unsupported characters in input";
104a8003a70SNiels Sascha Reedijk }
105a8003a70SNiels Sascha Reedijk 
106a8003a70SNiels Sascha Reedijk 
107a8003a70SNiels Sascha Reedijk BString
108a8003a70SNiels Sascha Reedijk BHttpFields::InvalidInput::DebugMessage() const
109a8003a70SNiels Sascha Reedijk {
110a8003a70SNiels Sascha Reedijk 	BString output = BError::DebugMessage();
111a8003a70SNiels Sascha Reedijk 	output << "\t " << input << "\n";
112a8003a70SNiels Sascha Reedijk 	return output;
113a8003a70SNiels Sascha Reedijk }
114a8003a70SNiels Sascha Reedijk 
115a8003a70SNiels Sascha Reedijk 
116a8003a70SNiels Sascha Reedijk // #pragma mark -- BHttpFields::Name
117a8003a70SNiels Sascha Reedijk 
118a8003a70SNiels Sascha Reedijk 
1198ccf8fb4SNiels Sascha Reedijk BHttpFields::FieldName::FieldName() noexcept
1208ccf8fb4SNiels Sascha Reedijk 	: fName(std::string_view())
1218ccf8fb4SNiels Sascha Reedijk {
1228ccf8fb4SNiels Sascha Reedijk 
1238ccf8fb4SNiels Sascha Reedijk }
1248ccf8fb4SNiels Sascha Reedijk 
1258ccf8fb4SNiels Sascha Reedijk 
1268ccf8fb4SNiels Sascha Reedijk BHttpFields::FieldName::FieldName(const std::string_view& name) noexcept
127a8003a70SNiels Sascha Reedijk 	: fName(name)
128a8003a70SNiels Sascha Reedijk {
129a8003a70SNiels Sascha Reedijk 
130a8003a70SNiels Sascha Reedijk }
131a8003a70SNiels Sascha Reedijk 
132a8003a70SNiels Sascha Reedijk 
133a8003a70SNiels Sascha Reedijk /*!
1348ccf8fb4SNiels Sascha Reedijk 	\brief Copy constructor;
135a8003a70SNiels Sascha Reedijk */
1368ccf8fb4SNiels Sascha Reedijk BHttpFields::FieldName::FieldName(const FieldName& other) noexcept = default;
137a8003a70SNiels Sascha Reedijk 
138a8003a70SNiels Sascha Reedijk 
139a8003a70SNiels Sascha Reedijk /*!
140a8003a70SNiels Sascha Reedijk 	\brief Move constructor
141a8003a70SNiels Sascha Reedijk 
142a8003a70SNiels Sascha Reedijk 	Moving leaves the other object in the empty state. It is implemented to satisfy the internal
143a8003a70SNiels Sascha Reedijk 	requirements of BHttpFields and std::list<Field>. Once an object is moved from it must no
144a8003a70SNiels Sascha Reedijk 	longer be used as an entry in a BHttpFields object.
145a8003a70SNiels Sascha Reedijk */
146a8003a70SNiels Sascha Reedijk BHttpFields::FieldName::FieldName(FieldName&& other) noexcept
147a8003a70SNiels Sascha Reedijk 	: fName(std::move(other.fName))
148a8003a70SNiels Sascha Reedijk {
149a8003a70SNiels Sascha Reedijk 	other.fName = std::string_view();
150a8003a70SNiels Sascha Reedijk }
151a8003a70SNiels Sascha Reedijk 
152a8003a70SNiels Sascha Reedijk 
153a8003a70SNiels Sascha Reedijk /*!
1548ccf8fb4SNiels Sascha Reedijk 	\brief Copy assignment;
155a8003a70SNiels Sascha Reedijk */
156a8003a70SNiels Sascha Reedijk BHttpFields::FieldName&
1578ccf8fb4SNiels Sascha Reedijk BHttpFields::FieldName::operator=(const BHttpFields::FieldName& other) noexcept = default;
158a8003a70SNiels Sascha Reedijk 
159a8003a70SNiels Sascha Reedijk 
160a8003a70SNiels Sascha Reedijk /*!
161a8003a70SNiels Sascha Reedijk 	\brief Move assignment
162a8003a70SNiels Sascha Reedijk 
163a8003a70SNiels Sascha Reedijk 	Moving leaves the other object in the empty state. It is implemented to satisfy the internal
164a8003a70SNiels Sascha Reedijk 	requirements of BHttpFields and std::list<Field>. Once an object is moved from it must no
165a8003a70SNiels Sascha Reedijk 	longer be used as an entry in a BHttpFields object.
166a8003a70SNiels Sascha Reedijk */
167a8003a70SNiels Sascha Reedijk BHttpFields::FieldName&
168a8003a70SNiels Sascha Reedijk BHttpFields::FieldName::operator=(BHttpFields::FieldName&& other) noexcept
169a8003a70SNiels Sascha Reedijk {
170a8003a70SNiels Sascha Reedijk 	fName = std::move(other.fName);
171a8003a70SNiels Sascha Reedijk 	other.fName = std::string_view();
172a8003a70SNiels Sascha Reedijk 	return *this;
173a8003a70SNiels Sascha Reedijk }
174a8003a70SNiels Sascha Reedijk 
175a8003a70SNiels Sascha Reedijk 
176a8003a70SNiels Sascha Reedijk bool
177a8003a70SNiels Sascha Reedijk BHttpFields::FieldName::operator==(const BString& other) const noexcept
178a8003a70SNiels Sascha Reedijk {
1798ccf8fb4SNiels Sascha Reedijk 	return iequals(fName, std::string_view(other.String()));
180a8003a70SNiels Sascha Reedijk }
181a8003a70SNiels Sascha Reedijk 
182a8003a70SNiels Sascha Reedijk 
183a8003a70SNiels Sascha Reedijk bool
184a8003a70SNiels Sascha Reedijk BHttpFields::FieldName::operator==(const std::string_view& other) const noexcept
185a8003a70SNiels Sascha Reedijk {
1868ccf8fb4SNiels Sascha Reedijk 	return iequals(fName, other);
187a8003a70SNiels Sascha Reedijk }
188a8003a70SNiels Sascha Reedijk 
189a8003a70SNiels Sascha Reedijk 
190a8003a70SNiels Sascha Reedijk bool
191a8003a70SNiels Sascha Reedijk BHttpFields::FieldName::operator==(const BHttpFields::FieldName& other) const noexcept
192a8003a70SNiels Sascha Reedijk {
1938ccf8fb4SNiels Sascha Reedijk 	return iequals(fName, other.fName);
194a8003a70SNiels Sascha Reedijk }
195a8003a70SNiels Sascha Reedijk 
196a8003a70SNiels Sascha Reedijk 
197a8003a70SNiels Sascha Reedijk BHttpFields::FieldName::operator std::string_view() const
198a8003a70SNiels Sascha Reedijk {
1998ccf8fb4SNiels Sascha Reedijk 	return fName;
200a8003a70SNiels Sascha Reedijk }
201a8003a70SNiels Sascha Reedijk 
202a8003a70SNiels Sascha Reedijk 
203a8003a70SNiels Sascha Reedijk // #pragma mark -- BHttpFields::Field
204a8003a70SNiels Sascha Reedijk 
205a8003a70SNiels Sascha Reedijk 
206a8003a70SNiels Sascha Reedijk BHttpFields::Field::Field() noexcept
207a8003a70SNiels Sascha Reedijk 	: fName(std::string_view()), fValue(std::string_view())
208a8003a70SNiels Sascha Reedijk {
209a8003a70SNiels Sascha Reedijk 
210a8003a70SNiels Sascha Reedijk }
211a8003a70SNiels Sascha Reedijk 
212a8003a70SNiels Sascha Reedijk 
213a8003a70SNiels Sascha Reedijk BHttpFields::Field::Field(const std::string_view& name, const std::string_view& value)
214a8003a70SNiels Sascha Reedijk {
215cb67e348SNiels Sascha Reedijk 	if (name.length() == 0 || !validate_http_token_string(name))
2168ccf8fb4SNiels Sascha Reedijk 		throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, BString(name.data(), name.size()));
217a8003a70SNiels Sascha Reedijk 	if (value.length() == 0 || !validate_value_string(value))
218a8003a70SNiels Sascha Reedijk 		throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, BString(value.data(), value.length()));
219a8003a70SNiels Sascha Reedijk 
2208ccf8fb4SNiels Sascha Reedijk 	BString rawField(name.data(), name.size());
2218ccf8fb4SNiels Sascha Reedijk 	rawField << ": ";
2228ccf8fb4SNiels Sascha Reedijk 	rawField.Append(value.data(), value.size());
2238ccf8fb4SNiels Sascha Reedijk 
2248ccf8fb4SNiels Sascha Reedijk 	fName = std::string_view(rawField.String(), name.size());
2258ccf8fb4SNiels Sascha Reedijk 	fValue = std::string_view(rawField.String() + name.size() + 2, value.size());
2268ccf8fb4SNiels Sascha Reedijk 	fRawField = std::move(rawField);
227a8003a70SNiels Sascha Reedijk }
2288ccf8fb4SNiels Sascha Reedijk 
2298ccf8fb4SNiels Sascha Reedijk 
2308ccf8fb4SNiels Sascha Reedijk BHttpFields::Field::Field(BString& field)
2318ccf8fb4SNiels Sascha Reedijk {
2328ccf8fb4SNiels Sascha Reedijk 	// Check if the input contains a key, a separator and a value.
2338ccf8fb4SNiels Sascha Reedijk 	auto separatorIndex = field.FindFirst(':');
2348ccf8fb4SNiels Sascha Reedijk 	if (separatorIndex <= 0)
2358ccf8fb4SNiels Sascha Reedijk 		throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, field);
2368ccf8fb4SNiels Sascha Reedijk 
2378ccf8fb4SNiels Sascha Reedijk 	// Get the name and the value. Remove whitespace around the value.
2388ccf8fb4SNiels Sascha Reedijk 	auto name = std::string_view(field.String(), separatorIndex);
2398ccf8fb4SNiels Sascha Reedijk 	auto value = trim(std::string_view(field.String() + separatorIndex + 1));
2408ccf8fb4SNiels Sascha Reedijk 
2418ccf8fb4SNiels Sascha Reedijk 	if (name.length() == 0 || !validate_http_token_string(name))
2428ccf8fb4SNiels Sascha Reedijk 		throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, BString(name.data(), name.size()));
2438ccf8fb4SNiels Sascha Reedijk 	if (value.length() == 0 || !validate_value_string(value))
2448ccf8fb4SNiels Sascha Reedijk 		throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, BString(value.data(), value.length()));
2458ccf8fb4SNiels Sascha Reedijk 
2468ccf8fb4SNiels Sascha Reedijk 	fRawField = std::move(field);
2478ccf8fb4SNiels Sascha Reedijk 	fName = name;
2488ccf8fb4SNiels Sascha Reedijk 	fValue = value;
249a8003a70SNiels Sascha Reedijk }
250a8003a70SNiels Sascha Reedijk 
251a8003a70SNiels Sascha Reedijk 
252a8003a70SNiels Sascha Reedijk BHttpFields::Field::Field(const BHttpFields::Field& other)
253a8003a70SNiels Sascha Reedijk 	: fName(std::string_view()), fValue(std::string_view())
254a8003a70SNiels Sascha Reedijk {
2558ccf8fb4SNiels Sascha Reedijk 	if (other.IsEmpty()) {
2568ccf8fb4SNiels Sascha Reedijk 		fRawField = BString();
2578ccf8fb4SNiels Sascha Reedijk 		fName = std::string_view();
2588ccf8fb4SNiels Sascha Reedijk 		fValue = std::string_view();
2598ccf8fb4SNiels Sascha Reedijk 	} else {
2608ccf8fb4SNiels Sascha Reedijk 		fRawField = other.fRawField;
2618ccf8fb4SNiels Sascha Reedijk 		auto nameSize = other.Name().fName.size();
2628ccf8fb4SNiels Sascha Reedijk 		auto valueOffset = other.fValue.data() - other.fRawField.value().String();
2638ccf8fb4SNiels Sascha Reedijk 		fName = std::string_view((*fRawField).String(), nameSize);
2648ccf8fb4SNiels Sascha Reedijk 		fValue = std::string_view((*fRawField).String() + valueOffset, other.fValue.size());
265a8003a70SNiels Sascha Reedijk 	}
266a8003a70SNiels Sascha Reedijk }
267a8003a70SNiels Sascha Reedijk 
268a8003a70SNiels Sascha Reedijk 
269a8003a70SNiels Sascha Reedijk BHttpFields::Field::Field(BHttpFields::Field&& other) noexcept
2708ccf8fb4SNiels Sascha Reedijk 	: fRawField(std::move(other.fRawField)), fName(std::move(other.fName)), fValue(std::move(other.fValue))
271a8003a70SNiels Sascha Reedijk {
272a8003a70SNiels Sascha Reedijk 	other.fName.fName = std::string_view();
273a8003a70SNiels Sascha Reedijk 	other.fValue = std::string_view();
274a8003a70SNiels Sascha Reedijk }
275a8003a70SNiels Sascha Reedijk 
276a8003a70SNiels Sascha Reedijk 
277a8003a70SNiels Sascha Reedijk BHttpFields::Field&
278a8003a70SNiels Sascha Reedijk BHttpFields::Field::operator=(const BHttpFields::Field& other)
279a8003a70SNiels Sascha Reedijk {
280a8003a70SNiels Sascha Reedijk 	if (other.IsEmpty()) {
2818ccf8fb4SNiels Sascha Reedijk 		fRawField = BString();
282a8003a70SNiels Sascha Reedijk 		fName = std::string_view();
283a8003a70SNiels Sascha Reedijk 		fValue = std::string_view();
284a8003a70SNiels Sascha Reedijk 	} else {
2858ccf8fb4SNiels Sascha Reedijk 		fRawField = other.fRawField;
2868ccf8fb4SNiels Sascha Reedijk 		auto nameSize = other.Name().fName.size();
2878ccf8fb4SNiels Sascha Reedijk 		auto valueOffset = other.fValue.data() - other.fRawField.value().String();
2888ccf8fb4SNiels Sascha Reedijk 		fName = std::string_view((*fRawField).String(), nameSize);
2898ccf8fb4SNiels Sascha Reedijk 		fValue = std::string_view((*fRawField).String() + valueOffset, other.fValue.size());
290a8003a70SNiels Sascha Reedijk 	}
291a8003a70SNiels Sascha Reedijk 	return *this;
292a8003a70SNiels Sascha Reedijk }
293a8003a70SNiels Sascha Reedijk 
294a8003a70SNiels Sascha Reedijk 
295a8003a70SNiels Sascha Reedijk BHttpFields::Field&
296a8003a70SNiels Sascha Reedijk BHttpFields::Field::operator=(BHttpFields::Field&& other) noexcept
297a8003a70SNiels Sascha Reedijk {
2988ccf8fb4SNiels Sascha Reedijk 	fRawField = std::move(other.fRawField);
299a8003a70SNiels Sascha Reedijk 	fName = std::move(other.fName);
300a8003a70SNiels Sascha Reedijk 	other.fName.fName = std::string_view();
301a8003a70SNiels Sascha Reedijk 	fValue = std::move(other.fValue);
302a8003a70SNiels Sascha Reedijk 	fValue = std::string_view();
303a8003a70SNiels Sascha Reedijk 	return *this;
304a8003a70SNiels Sascha Reedijk }
305a8003a70SNiels Sascha Reedijk 
306a8003a70SNiels Sascha Reedijk 
307a8003a70SNiels Sascha Reedijk const BHttpFields::FieldName&
308a8003a70SNiels Sascha Reedijk BHttpFields::Field::Name() const noexcept
309a8003a70SNiels Sascha Reedijk {
310a8003a70SNiels Sascha Reedijk 	return fName;
311a8003a70SNiels Sascha Reedijk }
312a8003a70SNiels Sascha Reedijk 
313a8003a70SNiels Sascha Reedijk 
314a8003a70SNiels Sascha Reedijk std::string_view
315a8003a70SNiels Sascha Reedijk BHttpFields::Field::Value() const noexcept
316a8003a70SNiels Sascha Reedijk {
3178ccf8fb4SNiels Sascha Reedijk 	return fValue;
318a8003a70SNiels Sascha Reedijk }
3198ccf8fb4SNiels Sascha Reedijk 
3208ccf8fb4SNiels Sascha Reedijk 
3218ccf8fb4SNiels Sascha Reedijk std::string_view
3228ccf8fb4SNiels Sascha Reedijk BHttpFields::Field::RawField() const noexcept
3238ccf8fb4SNiels Sascha Reedijk {
3248ccf8fb4SNiels Sascha Reedijk 	if (fRawField)
3258ccf8fb4SNiels Sascha Reedijk 		return std::string_view((*fRawField).String(), (*fRawField).Length());
3268ccf8fb4SNiels Sascha Reedijk 	else
3278ccf8fb4SNiels Sascha Reedijk 		return std::string_view();
328a8003a70SNiels Sascha Reedijk }
329a8003a70SNiels Sascha Reedijk 
330a8003a70SNiels Sascha Reedijk 
331a8003a70SNiels Sascha Reedijk bool
332a8003a70SNiels Sascha Reedijk BHttpFields::Field::IsEmpty() const noexcept
333a8003a70SNiels Sascha Reedijk {
334a8003a70SNiels Sascha Reedijk 	// The object is either fully empty, or it has data, so we only have to check fValue.
3358ccf8fb4SNiels Sascha Reedijk 	return !fRawField.has_value();
336a8003a70SNiels Sascha Reedijk }
337a8003a70SNiels Sascha Reedijk 
338a8003a70SNiels Sascha Reedijk 
339a8003a70SNiels Sascha Reedijk // #pragma mark -- BHttpFields
340a8003a70SNiels Sascha Reedijk 
341a8003a70SNiels Sascha Reedijk 
342a8003a70SNiels Sascha Reedijk BHttpFields::BHttpFields()
343a8003a70SNiels Sascha Reedijk {
344a8003a70SNiels Sascha Reedijk 
345a8003a70SNiels Sascha Reedijk }
346a8003a70SNiels Sascha Reedijk 
347a8003a70SNiels Sascha Reedijk 
348a8003a70SNiels Sascha Reedijk BHttpFields::BHttpFields(std::initializer_list<BHttpFields::Field> fields)
349a8003a70SNiels Sascha Reedijk {
3508ccf8fb4SNiels Sascha Reedijk 	AddFields(fields);
351a8003a70SNiels Sascha Reedijk }
352a8003a70SNiels Sascha Reedijk 
353a8003a70SNiels Sascha Reedijk 
354a8003a70SNiels Sascha Reedijk BHttpFields::BHttpFields(const BHttpFields& other) = default;
355a8003a70SNiels Sascha Reedijk 
356a8003a70SNiels Sascha Reedijk 
357a8003a70SNiels Sascha Reedijk BHttpFields::BHttpFields(BHttpFields&& other)
358a8003a70SNiels Sascha Reedijk 	: fFields(std::move(other.fFields))
359a8003a70SNiels Sascha Reedijk {
360a8003a70SNiels Sascha Reedijk 	// Explicitly clear the other list, as the C++ standard does not specify that the other list
361a8003a70SNiels Sascha Reedijk 	// will be empty.
362a8003a70SNiels Sascha Reedijk 	other.fFields.clear();
363a8003a70SNiels Sascha Reedijk }
364a8003a70SNiels Sascha Reedijk 
365a8003a70SNiels Sascha Reedijk 
366a8003a70SNiels Sascha Reedijk BHttpFields::~BHttpFields() noexcept
367a8003a70SNiels Sascha Reedijk {
368a8003a70SNiels Sascha Reedijk 
369a8003a70SNiels Sascha Reedijk }
370a8003a70SNiels Sascha Reedijk 
371a8003a70SNiels Sascha Reedijk 
372a8003a70SNiels Sascha Reedijk BHttpFields&
373a8003a70SNiels Sascha Reedijk BHttpFields::operator=(const BHttpFields& other) = default;
374a8003a70SNiels Sascha Reedijk 
375a8003a70SNiels Sascha Reedijk 
376a8003a70SNiels Sascha Reedijk BHttpFields&
377a8003a70SNiels Sascha Reedijk BHttpFields::operator=(BHttpFields&& other) noexcept
378a8003a70SNiels Sascha Reedijk {
379a8003a70SNiels Sascha Reedijk 	fFields = std::move(other.fFields);
380a8003a70SNiels Sascha Reedijk 
381a8003a70SNiels Sascha Reedijk 	// Explicitly clear the other list, as the C++ standard does not specify that the other list
382a8003a70SNiels Sascha Reedijk 	// will be empty.
383a8003a70SNiels Sascha Reedijk 	other.fFields.clear();
384a8003a70SNiels Sascha Reedijk 	return *this;
385a8003a70SNiels Sascha Reedijk }
386a8003a70SNiels Sascha Reedijk 
387a8003a70SNiels Sascha Reedijk 
388a8003a70SNiels Sascha Reedijk const BHttpFields::Field&
389a8003a70SNiels Sascha Reedijk BHttpFields::operator[](size_t index) const
390a8003a70SNiels Sascha Reedijk {
391a8003a70SNiels Sascha Reedijk 	if (index >= fFields.size())
392a8003a70SNiels Sascha Reedijk 		throw BRuntimeError(__PRETTY_FUNCTION__, "Index out of bounds");
393a8003a70SNiels Sascha Reedijk 	auto it = fFields.cbegin();
394a8003a70SNiels Sascha Reedijk 	std::advance(it, index);
395a8003a70SNiels Sascha Reedijk 	return *it;
396a8003a70SNiels Sascha Reedijk }
397a8003a70SNiels Sascha Reedijk 
398a8003a70SNiels Sascha Reedijk 
399a8003a70SNiels Sascha Reedijk void
400a8003a70SNiels Sascha Reedijk BHttpFields::AddField(const std::string_view& name, const std::string_view& value)
401a8003a70SNiels Sascha Reedijk {
4028ccf8fb4SNiels Sascha Reedijk 	fFields.emplace_back(name, value);
4038ccf8fb4SNiels Sascha Reedijk }
4048ccf8fb4SNiels Sascha Reedijk 
4058ccf8fb4SNiels Sascha Reedijk 
4068ccf8fb4SNiels Sascha Reedijk void
4078ccf8fb4SNiels Sascha Reedijk BHttpFields::AddField(BString& field)
4088ccf8fb4SNiels Sascha Reedijk {
4098ccf8fb4SNiels Sascha Reedijk 	fFields.emplace_back(field);
410a8003a70SNiels Sascha Reedijk }
411a8003a70SNiels Sascha Reedijk 
412a8003a70SNiels Sascha Reedijk 
413a8003a70SNiels Sascha Reedijk void
414d9a4c607SNiels Sascha Reedijk BHttpFields::AddFields(std::initializer_list<Field> fields)
415d9a4c607SNiels Sascha Reedijk {
416d9a4c607SNiels Sascha Reedijk 	for (auto& field: fields) {
417d9a4c607SNiels Sascha Reedijk 		if (!field.IsEmpty())
4188ccf8fb4SNiels Sascha Reedijk 			fFields.push_back(std::move(field));
419d9a4c607SNiels Sascha Reedijk 	}
420d9a4c607SNiels Sascha Reedijk }
421d9a4c607SNiels Sascha Reedijk 
422d9a4c607SNiels Sascha Reedijk 
423d9a4c607SNiels Sascha Reedijk void
424a8003a70SNiels Sascha Reedijk BHttpFields::RemoveField(const std::string_view& name) noexcept
425a8003a70SNiels Sascha Reedijk {
426a8003a70SNiels Sascha Reedijk 	for(auto it = FindField(name); it != end(); it = FindField(name)) {
427a8003a70SNiels Sascha Reedijk 		fFields.erase(it);
428a8003a70SNiels Sascha Reedijk 	}
429a8003a70SNiels Sascha Reedijk }
430a8003a70SNiels Sascha Reedijk 
431a8003a70SNiels Sascha Reedijk 
432a8003a70SNiels Sascha Reedijk void
433a8003a70SNiels Sascha Reedijk BHttpFields::RemoveField(ConstIterator it) noexcept
434a8003a70SNiels Sascha Reedijk {
435a8003a70SNiels Sascha Reedijk 	fFields.erase(it);
436a8003a70SNiels Sascha Reedijk }
437a8003a70SNiels Sascha Reedijk 
438a8003a70SNiels Sascha Reedijk 
439a8003a70SNiels Sascha Reedijk void
440a8003a70SNiels Sascha Reedijk BHttpFields::MakeEmpty() noexcept
441a8003a70SNiels Sascha Reedijk {
442a8003a70SNiels Sascha Reedijk 	fFields.clear();
443a8003a70SNiels Sascha Reedijk }
444a8003a70SNiels Sascha Reedijk 
445a8003a70SNiels Sascha Reedijk 
446a8003a70SNiels Sascha Reedijk BHttpFields::ConstIterator
447a8003a70SNiels Sascha Reedijk BHttpFields::FindField(const std::string_view& name) const noexcept
448a8003a70SNiels Sascha Reedijk {
449a8003a70SNiels Sascha Reedijk 	for (auto it = fFields.cbegin(); it != fFields.cend(); it++) {
450a8003a70SNiels Sascha Reedijk 		if ((*it).Name() == name)
451a8003a70SNiels Sascha Reedijk 			return it;
452a8003a70SNiels Sascha Reedijk 	}
453a8003a70SNiels Sascha Reedijk 	return fFields.cend();
454a8003a70SNiels Sascha Reedijk }
455a8003a70SNiels Sascha Reedijk 
456a8003a70SNiels Sascha Reedijk 
457a8003a70SNiels Sascha Reedijk size_t
458a8003a70SNiels Sascha Reedijk BHttpFields::CountFields() const noexcept
459a8003a70SNiels Sascha Reedijk {
460a8003a70SNiels Sascha Reedijk 	return fFields.size();
461a8003a70SNiels Sascha Reedijk }
462a8003a70SNiels Sascha Reedijk 
463a8003a70SNiels Sascha Reedijk 
464*7b1d966cSNiels Sascha Reedijk size_t
465*7b1d966cSNiels Sascha Reedijk BHttpFields::CountFields(const std::string_view& name) const noexcept
466*7b1d966cSNiels Sascha Reedijk {
467*7b1d966cSNiels Sascha Reedijk 	size_t count = 0;
468*7b1d966cSNiels Sascha Reedijk 	for (auto it = fFields.cbegin(); it != fFields.cend(); it++) {
469*7b1d966cSNiels Sascha Reedijk 		if ((*it).Name() == name)
470*7b1d966cSNiels Sascha Reedijk 			count += 1;
471*7b1d966cSNiels Sascha Reedijk 	}
472*7b1d966cSNiels Sascha Reedijk 	return count;
473*7b1d966cSNiels Sascha Reedijk }
474*7b1d966cSNiels Sascha Reedijk 
475*7b1d966cSNiels Sascha Reedijk 
476a8003a70SNiels Sascha Reedijk BHttpFields::ConstIterator
477a8003a70SNiels Sascha Reedijk BHttpFields::begin() const noexcept
478a8003a70SNiels Sascha Reedijk {
479a8003a70SNiels Sascha Reedijk 	return fFields.cbegin();
480a8003a70SNiels Sascha Reedijk }
481a8003a70SNiels Sascha Reedijk 
482a8003a70SNiels Sascha Reedijk 
483a8003a70SNiels Sascha Reedijk BHttpFields::ConstIterator
484a8003a70SNiels Sascha Reedijk BHttpFields::end() const noexcept
485a8003a70SNiels Sascha Reedijk {
486a8003a70SNiels Sascha Reedijk 	return fFields.cend();
487a8003a70SNiels Sascha Reedijk }
488