xref: /haiku/src/kits/network/libnetservices2/HttpFields.cpp (revision 71e29bbeea760848317843508c711f2a0b446fbb)
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
validate_value_string(const std::string_view & string)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
iequals(const std::string_view & a,const std::string_view & b)53a8003a70SNiels Sascha Reedijk iequals(const std::string_view& a, const std::string_view& b)
54a8003a70SNiels Sascha Reedijk {
55*71e29bbeSNiels Sascha Reedijk 	return std::equal(a.begin(), a.end(), b.begin(), b.end(),
56*71e29bbeSNiels Sascha Reedijk 		[](char a, char b) { return tolower(a) == tolower(b); });
57a8003a70SNiels Sascha Reedijk }
58a8003a70SNiels Sascha Reedijk 
59a8003a70SNiels Sascha Reedijk 
608ccf8fb4SNiels Sascha Reedijk /*!
618ccf8fb4SNiels Sascha Reedijk 	\brief Trim whitespace from the beginning and end of a string_view
628ccf8fb4SNiels Sascha Reedijk 
638ccf8fb4SNiels Sascha Reedijk 	Inspired by:
648ccf8fb4SNiels Sascha Reedijk 		https://terrislinenbach.medium.com/trimming-whitespace-from-a-string-view-6795e18b108f
658ccf8fb4SNiels Sascha Reedijk */
668ccf8fb4SNiels Sascha Reedijk static inline std::string_view
trim(std::string_view in)678ccf8fb4SNiels Sascha Reedijk trim(std::string_view in)
688ccf8fb4SNiels Sascha Reedijk {
698ccf8fb4SNiels Sascha Reedijk 	auto left = in.begin();
708ccf8fb4SNiels Sascha Reedijk 	for (;; ++left) {
718ccf8fb4SNiels Sascha Reedijk 		if (left == in.end())
728ccf8fb4SNiels Sascha Reedijk 			return std::string_view();
738ccf8fb4SNiels Sascha Reedijk 		if (!isspace(*left))
748ccf8fb4SNiels Sascha Reedijk 			break;
758ccf8fb4SNiels Sascha Reedijk 	}
768ccf8fb4SNiels Sascha Reedijk 
778ccf8fb4SNiels Sascha Reedijk 	auto right = in.end() - 1;
78*71e29bbeSNiels Sascha Reedijk 	for (; right > left && isspace(*right); --right)
79*71e29bbeSNiels Sascha Reedijk 		;
808ccf8fb4SNiels Sascha Reedijk 
818ccf8fb4SNiels Sascha Reedijk 	return std::string_view(left, std::distance(left, right) + 1);
828ccf8fb4SNiels Sascha Reedijk }
838ccf8fb4SNiels Sascha Reedijk 
848ccf8fb4SNiels Sascha Reedijk 
85a8003a70SNiels Sascha Reedijk // #pragma mark -- BHttpFields::InvalidHeader
86a8003a70SNiels Sascha Reedijk 
87a8003a70SNiels Sascha Reedijk 
InvalidInput(const char * origin,BString input)88a8003a70SNiels Sascha Reedijk BHttpFields::InvalidInput::InvalidInput(const char* origin, BString input)
89a8003a70SNiels Sascha Reedijk 	:
90a8003a70SNiels Sascha Reedijk 	BError(origin),
91a8003a70SNiels Sascha Reedijk 	input(std::move(input))
92a8003a70SNiels Sascha Reedijk {
93a8003a70SNiels Sascha Reedijk }
94a8003a70SNiels Sascha Reedijk 
95a8003a70SNiels Sascha Reedijk 
96a8003a70SNiels Sascha Reedijk const char*
Message() const97a8003a70SNiels Sascha Reedijk BHttpFields::InvalidInput::Message() const noexcept
98a8003a70SNiels Sascha Reedijk {
99a8003a70SNiels Sascha Reedijk 	return "Invalid format or unsupported characters in input";
100a8003a70SNiels Sascha Reedijk }
101a8003a70SNiels Sascha Reedijk 
102a8003a70SNiels Sascha Reedijk 
103a8003a70SNiels Sascha Reedijk BString
DebugMessage() const104a8003a70SNiels Sascha Reedijk BHttpFields::InvalidInput::DebugMessage() const
105a8003a70SNiels Sascha Reedijk {
106a8003a70SNiels Sascha Reedijk 	BString output = BError::DebugMessage();
107a8003a70SNiels Sascha Reedijk 	output << "\t " << input << "\n";
108a8003a70SNiels Sascha Reedijk 	return output;
109a8003a70SNiels Sascha Reedijk }
110a8003a70SNiels Sascha Reedijk 
111a8003a70SNiels Sascha Reedijk 
112a8003a70SNiels Sascha Reedijk // #pragma mark -- BHttpFields::Name
113a8003a70SNiels Sascha Reedijk 
114a8003a70SNiels Sascha Reedijk 
FieldName()1158ccf8fb4SNiels Sascha Reedijk BHttpFields::FieldName::FieldName() noexcept
116*71e29bbeSNiels Sascha Reedijk 	:
117*71e29bbeSNiels Sascha Reedijk 	fName(std::string_view())
1188ccf8fb4SNiels Sascha Reedijk {
1198ccf8fb4SNiels Sascha Reedijk }
1208ccf8fb4SNiels Sascha Reedijk 
1218ccf8fb4SNiels Sascha Reedijk 
FieldName(const std::string_view & name)1228ccf8fb4SNiels Sascha Reedijk BHttpFields::FieldName::FieldName(const std::string_view& name) noexcept
123*71e29bbeSNiels Sascha Reedijk 	:
124*71e29bbeSNiels Sascha Reedijk 	fName(name)
125a8003a70SNiels Sascha Reedijk {
126a8003a70SNiels Sascha Reedijk }
127a8003a70SNiels Sascha Reedijk 
128a8003a70SNiels Sascha Reedijk 
129a8003a70SNiels Sascha Reedijk /*!
1308ccf8fb4SNiels Sascha Reedijk 	\brief Copy constructor;
131a8003a70SNiels Sascha Reedijk */
1328ccf8fb4SNiels Sascha Reedijk BHttpFields::FieldName::FieldName(const FieldName& other) noexcept = default;
133a8003a70SNiels Sascha Reedijk 
134a8003a70SNiels Sascha Reedijk 
135a8003a70SNiels Sascha Reedijk /*!
136a8003a70SNiels Sascha Reedijk 	\brief Move constructor
137a8003a70SNiels Sascha Reedijk 
138a8003a70SNiels Sascha Reedijk 	Moving leaves the other object in the empty state. It is implemented to satisfy the internal
139a8003a70SNiels Sascha Reedijk 	requirements of BHttpFields and std::list<Field>. Once an object is moved from it must no
140a8003a70SNiels Sascha Reedijk 	longer be used as an entry in a BHttpFields object.
141a8003a70SNiels Sascha Reedijk */
FieldName(FieldName && other)142a8003a70SNiels Sascha Reedijk BHttpFields::FieldName::FieldName(FieldName&& other) noexcept
143*71e29bbeSNiels Sascha Reedijk 	:
144*71e29bbeSNiels Sascha Reedijk 	fName(std::move(other.fName))
145a8003a70SNiels Sascha Reedijk {
146a8003a70SNiels Sascha Reedijk 	other.fName = std::string_view();
147a8003a70SNiels Sascha Reedijk }
148a8003a70SNiels Sascha Reedijk 
149a8003a70SNiels Sascha Reedijk 
150a8003a70SNiels Sascha Reedijk /*!
1518ccf8fb4SNiels Sascha Reedijk 	\brief Copy assignment;
152a8003a70SNiels Sascha Reedijk */
153*71e29bbeSNiels Sascha Reedijk BHttpFields::FieldName& BHttpFields::FieldName::operator=(
154*71e29bbeSNiels Sascha Reedijk 	const BHttpFields::FieldName& other) noexcept = default;
155a8003a70SNiels Sascha Reedijk 
156a8003a70SNiels Sascha Reedijk 
157a8003a70SNiels Sascha Reedijk /*!
158a8003a70SNiels Sascha Reedijk 	\brief Move assignment
159a8003a70SNiels Sascha Reedijk 
160a8003a70SNiels Sascha Reedijk 	Moving leaves the other object in the empty state. It is implemented to satisfy the internal
161a8003a70SNiels Sascha Reedijk 	requirements of BHttpFields and std::list<Field>. Once an object is moved from it must no
162a8003a70SNiels Sascha Reedijk 	longer be used as an entry in a BHttpFields object.
163a8003a70SNiels Sascha Reedijk */
164a8003a70SNiels Sascha Reedijk BHttpFields::FieldName&
operator =(BHttpFields::FieldName && other)165a8003a70SNiels Sascha Reedijk BHttpFields::FieldName::operator=(BHttpFields::FieldName&& other) noexcept
166a8003a70SNiels Sascha Reedijk {
167a8003a70SNiels Sascha Reedijk 	fName = std::move(other.fName);
168a8003a70SNiels Sascha Reedijk 	other.fName = std::string_view();
169a8003a70SNiels Sascha Reedijk 	return *this;
170a8003a70SNiels Sascha Reedijk }
171a8003a70SNiels Sascha Reedijk 
172a8003a70SNiels Sascha Reedijk 
173a8003a70SNiels Sascha Reedijk bool
operator ==(const BString & other) const174a8003a70SNiels Sascha Reedijk BHttpFields::FieldName::operator==(const BString& other) const noexcept
175a8003a70SNiels Sascha Reedijk {
1768ccf8fb4SNiels Sascha Reedijk 	return iequals(fName, std::string_view(other.String()));
177a8003a70SNiels Sascha Reedijk }
178a8003a70SNiels Sascha Reedijk 
179a8003a70SNiels Sascha Reedijk 
180a8003a70SNiels Sascha Reedijk bool
operator ==(const std::string_view & other) const181a8003a70SNiels Sascha Reedijk BHttpFields::FieldName::operator==(const std::string_view& other) const noexcept
182a8003a70SNiels Sascha Reedijk {
1838ccf8fb4SNiels Sascha Reedijk 	return iequals(fName, other);
184a8003a70SNiels Sascha Reedijk }
185a8003a70SNiels Sascha Reedijk 
186a8003a70SNiels Sascha Reedijk 
187a8003a70SNiels Sascha Reedijk bool
operator ==(const BHttpFields::FieldName & other) const188a8003a70SNiels Sascha Reedijk BHttpFields::FieldName::operator==(const BHttpFields::FieldName& other) const noexcept
189a8003a70SNiels Sascha Reedijk {
1908ccf8fb4SNiels Sascha Reedijk 	return iequals(fName, other.fName);
191a8003a70SNiels Sascha Reedijk }
192a8003a70SNiels Sascha Reedijk 
193a8003a70SNiels Sascha Reedijk 
operator std::string_view() const194a8003a70SNiels Sascha Reedijk BHttpFields::FieldName::operator std::string_view() const
195a8003a70SNiels Sascha Reedijk {
1968ccf8fb4SNiels Sascha Reedijk 	return fName;
197a8003a70SNiels Sascha Reedijk }
198a8003a70SNiels Sascha Reedijk 
199a8003a70SNiels Sascha Reedijk 
200a8003a70SNiels Sascha Reedijk // #pragma mark -- BHttpFields::Field
201a8003a70SNiels Sascha Reedijk 
202a8003a70SNiels Sascha Reedijk 
Field()203a8003a70SNiels Sascha Reedijk BHttpFields::Field::Field() noexcept
204*71e29bbeSNiels Sascha Reedijk 	:
205*71e29bbeSNiels Sascha Reedijk 	fName(std::string_view()),
206*71e29bbeSNiels Sascha Reedijk 	fValue(std::string_view())
207a8003a70SNiels Sascha Reedijk {
208a8003a70SNiels Sascha Reedijk }
209a8003a70SNiels Sascha Reedijk 
210a8003a70SNiels Sascha Reedijk 
Field(const std::string_view & name,const std::string_view & value)211a8003a70SNiels Sascha Reedijk BHttpFields::Field::Field(const std::string_view& name, const std::string_view& value)
212a8003a70SNiels Sascha Reedijk {
213cb67e348SNiels Sascha Reedijk 	if (name.length() == 0 || !validate_http_token_string(name))
2148ccf8fb4SNiels Sascha Reedijk 		throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, BString(name.data(), name.size()));
215a8003a70SNiels Sascha Reedijk 	if (value.length() == 0 || !validate_value_string(value))
216a8003a70SNiels Sascha Reedijk 		throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, BString(value.data(), value.length()));
217a8003a70SNiels Sascha Reedijk 
2188ccf8fb4SNiels Sascha Reedijk 	BString rawField(name.data(), name.size());
2198ccf8fb4SNiels Sascha Reedijk 	rawField << ": ";
2208ccf8fb4SNiels Sascha Reedijk 	rawField.Append(value.data(), value.size());
2218ccf8fb4SNiels Sascha Reedijk 
2228ccf8fb4SNiels Sascha Reedijk 	fName = std::string_view(rawField.String(), name.size());
2238ccf8fb4SNiels Sascha Reedijk 	fValue = std::string_view(rawField.String() + name.size() + 2, value.size());
2248ccf8fb4SNiels Sascha Reedijk 	fRawField = std::move(rawField);
225a8003a70SNiels Sascha Reedijk }
2268ccf8fb4SNiels Sascha Reedijk 
2278ccf8fb4SNiels Sascha Reedijk 
Field(BString & field)2288ccf8fb4SNiels Sascha Reedijk BHttpFields::Field::Field(BString& field)
2298ccf8fb4SNiels Sascha Reedijk {
2308ccf8fb4SNiels Sascha Reedijk 	// Check if the input contains a key, a separator and a value.
2318ccf8fb4SNiels Sascha Reedijk 	auto separatorIndex = field.FindFirst(':');
2328ccf8fb4SNiels Sascha Reedijk 	if (separatorIndex <= 0)
2338ccf8fb4SNiels Sascha Reedijk 		throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, field);
2348ccf8fb4SNiels Sascha Reedijk 
2358ccf8fb4SNiels Sascha Reedijk 	// Get the name and the value. Remove whitespace around the value.
2368ccf8fb4SNiels Sascha Reedijk 	auto name = std::string_view(field.String(), separatorIndex);
2378ccf8fb4SNiels Sascha Reedijk 	auto value = trim(std::string_view(field.String() + separatorIndex + 1));
2388ccf8fb4SNiels Sascha Reedijk 
2398ccf8fb4SNiels Sascha Reedijk 	if (name.length() == 0 || !validate_http_token_string(name))
2408ccf8fb4SNiels Sascha Reedijk 		throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, BString(name.data(), name.size()));
2418ccf8fb4SNiels Sascha Reedijk 	if (value.length() == 0 || !validate_value_string(value))
2428ccf8fb4SNiels Sascha Reedijk 		throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, BString(value.data(), value.length()));
2438ccf8fb4SNiels Sascha Reedijk 
2448ccf8fb4SNiels Sascha Reedijk 	fRawField = std::move(field);
2458ccf8fb4SNiels Sascha Reedijk 	fName = name;
2468ccf8fb4SNiels Sascha Reedijk 	fValue = value;
247a8003a70SNiels Sascha Reedijk }
248a8003a70SNiels Sascha Reedijk 
249a8003a70SNiels Sascha Reedijk 
Field(const BHttpFields::Field & other)250a8003a70SNiels Sascha Reedijk BHttpFields::Field::Field(const BHttpFields::Field& other)
251*71e29bbeSNiels Sascha Reedijk 	:
252*71e29bbeSNiels Sascha Reedijk 	fName(std::string_view()),
253*71e29bbeSNiels Sascha Reedijk 	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 
Field(BHttpFields::Field && other)269a8003a70SNiels Sascha Reedijk BHttpFields::Field::Field(BHttpFields::Field&& other) noexcept
270*71e29bbeSNiels Sascha Reedijk 	:
271*71e29bbeSNiels Sascha Reedijk 	fRawField(std::move(other.fRawField)),
272*71e29bbeSNiels Sascha Reedijk 	fName(std::move(other.fName)),
273*71e29bbeSNiels Sascha Reedijk 	fValue(std::move(other.fValue))
274a8003a70SNiels Sascha Reedijk {
275a8003a70SNiels Sascha Reedijk 	other.fName.fName = std::string_view();
276a8003a70SNiels Sascha Reedijk 	other.fValue = std::string_view();
277a8003a70SNiels Sascha Reedijk }
278a8003a70SNiels Sascha Reedijk 
279a8003a70SNiels Sascha Reedijk 
280a8003a70SNiels Sascha Reedijk BHttpFields::Field&
operator =(const BHttpFields::Field & other)281a8003a70SNiels Sascha Reedijk BHttpFields::Field::operator=(const BHttpFields::Field& other)
282a8003a70SNiels Sascha Reedijk {
283a8003a70SNiels Sascha Reedijk 	if (other.IsEmpty()) {
2848ccf8fb4SNiels Sascha Reedijk 		fRawField = BString();
285a8003a70SNiels Sascha Reedijk 		fName = std::string_view();
286a8003a70SNiels Sascha Reedijk 		fValue = std::string_view();
287a8003a70SNiels Sascha Reedijk 	} else {
2888ccf8fb4SNiels Sascha Reedijk 		fRawField = other.fRawField;
2898ccf8fb4SNiels Sascha Reedijk 		auto nameSize = other.Name().fName.size();
2908ccf8fb4SNiels Sascha Reedijk 		auto valueOffset = other.fValue.data() - other.fRawField.value().String();
2918ccf8fb4SNiels Sascha Reedijk 		fName = std::string_view((*fRawField).String(), nameSize);
2928ccf8fb4SNiels Sascha Reedijk 		fValue = std::string_view((*fRawField).String() + valueOffset, other.fValue.size());
293a8003a70SNiels Sascha Reedijk 	}
294a8003a70SNiels Sascha Reedijk 	return *this;
295a8003a70SNiels Sascha Reedijk }
296a8003a70SNiels Sascha Reedijk 
297a8003a70SNiels Sascha Reedijk 
298a8003a70SNiels Sascha Reedijk BHttpFields::Field&
operator =(BHttpFields::Field && other)299a8003a70SNiels Sascha Reedijk BHttpFields::Field::operator=(BHttpFields::Field&& other) noexcept
300a8003a70SNiels Sascha Reedijk {
3018ccf8fb4SNiels Sascha Reedijk 	fRawField = std::move(other.fRawField);
302a8003a70SNiels Sascha Reedijk 	fName = std::move(other.fName);
303a8003a70SNiels Sascha Reedijk 	other.fName.fName = std::string_view();
304a8003a70SNiels Sascha Reedijk 	fValue = std::move(other.fValue);
305a8003a70SNiels Sascha Reedijk 	fValue = std::string_view();
306a8003a70SNiels Sascha Reedijk 	return *this;
307a8003a70SNiels Sascha Reedijk }
308a8003a70SNiels Sascha Reedijk 
309a8003a70SNiels Sascha Reedijk 
310a8003a70SNiels Sascha Reedijk const BHttpFields::FieldName&
Name() const311a8003a70SNiels Sascha Reedijk BHttpFields::Field::Name() const noexcept
312a8003a70SNiels Sascha Reedijk {
313a8003a70SNiels Sascha Reedijk 	return fName;
314a8003a70SNiels Sascha Reedijk }
315a8003a70SNiels Sascha Reedijk 
316a8003a70SNiels Sascha Reedijk 
317a8003a70SNiels Sascha Reedijk std::string_view
Value() const318a8003a70SNiels Sascha Reedijk BHttpFields::Field::Value() const noexcept
319a8003a70SNiels Sascha Reedijk {
3208ccf8fb4SNiels Sascha Reedijk 	return fValue;
321a8003a70SNiels Sascha Reedijk }
3228ccf8fb4SNiels Sascha Reedijk 
3238ccf8fb4SNiels Sascha Reedijk 
3248ccf8fb4SNiels Sascha Reedijk std::string_view
RawField() const3258ccf8fb4SNiels Sascha Reedijk BHttpFields::Field::RawField() const noexcept
3268ccf8fb4SNiels Sascha Reedijk {
3278ccf8fb4SNiels Sascha Reedijk 	if (fRawField)
3288ccf8fb4SNiels Sascha Reedijk 		return std::string_view((*fRawField).String(), (*fRawField).Length());
3298ccf8fb4SNiels Sascha Reedijk 	else
3308ccf8fb4SNiels Sascha Reedijk 		return std::string_view();
331a8003a70SNiels Sascha Reedijk }
332a8003a70SNiels Sascha Reedijk 
333a8003a70SNiels Sascha Reedijk 
334a8003a70SNiels Sascha Reedijk bool
IsEmpty() const335a8003a70SNiels Sascha Reedijk BHttpFields::Field::IsEmpty() const noexcept
336a8003a70SNiels Sascha Reedijk {
337a8003a70SNiels Sascha Reedijk 	// The object is either fully empty, or it has data, so we only have to check fValue.
3388ccf8fb4SNiels Sascha Reedijk 	return !fRawField.has_value();
339a8003a70SNiels Sascha Reedijk }
340a8003a70SNiels Sascha Reedijk 
341a8003a70SNiels Sascha Reedijk 
342a8003a70SNiels Sascha Reedijk // #pragma mark -- BHttpFields
343a8003a70SNiels Sascha Reedijk 
344a8003a70SNiels Sascha Reedijk 
BHttpFields()345a8003a70SNiels Sascha Reedijk BHttpFields::BHttpFields()
346a8003a70SNiels Sascha Reedijk {
347a8003a70SNiels Sascha Reedijk }
348a8003a70SNiels Sascha Reedijk 
349a8003a70SNiels Sascha Reedijk 
BHttpFields(std::initializer_list<BHttpFields::Field> fields)350a8003a70SNiels Sascha Reedijk BHttpFields::BHttpFields(std::initializer_list<BHttpFields::Field> fields)
351a8003a70SNiels Sascha Reedijk {
3528ccf8fb4SNiels Sascha Reedijk 	AddFields(fields);
353a8003a70SNiels Sascha Reedijk }
354a8003a70SNiels Sascha Reedijk 
355a8003a70SNiels Sascha Reedijk 
356a8003a70SNiels Sascha Reedijk BHttpFields::BHttpFields(const BHttpFields& other) = default;
357a8003a70SNiels Sascha Reedijk 
358a8003a70SNiels Sascha Reedijk 
BHttpFields(BHttpFields && other)359a8003a70SNiels Sascha Reedijk BHttpFields::BHttpFields(BHttpFields&& other)
360*71e29bbeSNiels Sascha Reedijk 	:
361*71e29bbeSNiels Sascha Reedijk 	fFields(std::move(other.fFields))
362a8003a70SNiels Sascha Reedijk {
363a8003a70SNiels Sascha Reedijk 	// Explicitly clear the other list, as the C++ standard does not specify that the other list
364a8003a70SNiels Sascha Reedijk 	// will be empty.
365a8003a70SNiels Sascha Reedijk 	other.fFields.clear();
366a8003a70SNiels Sascha Reedijk }
367a8003a70SNiels Sascha Reedijk 
368a8003a70SNiels Sascha Reedijk 
~BHttpFields()369a8003a70SNiels Sascha Reedijk BHttpFields::~BHttpFields() noexcept
370a8003a70SNiels Sascha Reedijk {
371a8003a70SNiels Sascha Reedijk }
372a8003a70SNiels Sascha Reedijk 
373a8003a70SNiels Sascha Reedijk 
374*71e29bbeSNiels Sascha Reedijk BHttpFields& BHttpFields::operator=(const BHttpFields& other) = default;
375a8003a70SNiels Sascha Reedijk 
376a8003a70SNiels Sascha Reedijk 
377a8003a70SNiels Sascha Reedijk BHttpFields&
operator =(BHttpFields && other)378a8003a70SNiels Sascha Reedijk BHttpFields::operator=(BHttpFields&& other) noexcept
379a8003a70SNiels Sascha Reedijk {
380a8003a70SNiels Sascha Reedijk 	fFields = std::move(other.fFields);
381a8003a70SNiels Sascha Reedijk 
382a8003a70SNiels Sascha Reedijk 	// Explicitly clear the other list, as the C++ standard does not specify that the other list
383a8003a70SNiels Sascha Reedijk 	// will be empty.
384a8003a70SNiels Sascha Reedijk 	other.fFields.clear();
385a8003a70SNiels Sascha Reedijk 	return *this;
386a8003a70SNiels Sascha Reedijk }
387a8003a70SNiels Sascha Reedijk 
388a8003a70SNiels Sascha Reedijk 
389a8003a70SNiels Sascha Reedijk const BHttpFields::Field&
operator [](size_t index) const390a8003a70SNiels Sascha Reedijk BHttpFields::operator[](size_t index) const
391a8003a70SNiels Sascha Reedijk {
392a8003a70SNiels Sascha Reedijk 	if (index >= fFields.size())
393a8003a70SNiels Sascha Reedijk 		throw BRuntimeError(__PRETTY_FUNCTION__, "Index out of bounds");
394a8003a70SNiels Sascha Reedijk 	auto it = fFields.cbegin();
395a8003a70SNiels Sascha Reedijk 	std::advance(it, index);
396a8003a70SNiels Sascha Reedijk 	return *it;
397a8003a70SNiels Sascha Reedijk }
398a8003a70SNiels Sascha Reedijk 
399a8003a70SNiels Sascha Reedijk 
400a8003a70SNiels Sascha Reedijk void
AddField(const std::string_view & name,const std::string_view & value)401a8003a70SNiels Sascha Reedijk BHttpFields::AddField(const std::string_view& name, const std::string_view& value)
402a8003a70SNiels Sascha Reedijk {
4038ccf8fb4SNiels Sascha Reedijk 	fFields.emplace_back(name, value);
4048ccf8fb4SNiels Sascha Reedijk }
4058ccf8fb4SNiels Sascha Reedijk 
4068ccf8fb4SNiels Sascha Reedijk 
4078ccf8fb4SNiels Sascha Reedijk void
AddField(BString & field)4088ccf8fb4SNiels Sascha Reedijk BHttpFields::AddField(BString& field)
4098ccf8fb4SNiels Sascha Reedijk {
4108ccf8fb4SNiels Sascha Reedijk 	fFields.emplace_back(field);
411a8003a70SNiels Sascha Reedijk }
412a8003a70SNiels Sascha Reedijk 
413a8003a70SNiels Sascha Reedijk 
414a8003a70SNiels Sascha Reedijk void
AddFields(std::initializer_list<Field> fields)415d9a4c607SNiels Sascha Reedijk BHttpFields::AddFields(std::initializer_list<Field> fields)
416d9a4c607SNiels Sascha Reedijk {
417d9a4c607SNiels Sascha Reedijk 	for (auto& field: fields) {
418d9a4c607SNiels Sascha Reedijk 		if (!field.IsEmpty())
4198ccf8fb4SNiels Sascha Reedijk 			fFields.push_back(std::move(field));
420d9a4c607SNiels Sascha Reedijk 	}
421d9a4c607SNiels Sascha Reedijk }
422d9a4c607SNiels Sascha Reedijk 
423d9a4c607SNiels Sascha Reedijk 
424d9a4c607SNiels Sascha Reedijk void
RemoveField(const std::string_view & name)425a8003a70SNiels Sascha Reedijk BHttpFields::RemoveField(const std::string_view& name) noexcept
426a8003a70SNiels Sascha Reedijk {
427a8003a70SNiels Sascha Reedijk 	for (auto it = FindField(name); it != end(); it = FindField(name)) {
428a8003a70SNiels Sascha Reedijk 		fFields.erase(it);
429a8003a70SNiels Sascha Reedijk 	}
430a8003a70SNiels Sascha Reedijk }
431a8003a70SNiels Sascha Reedijk 
432a8003a70SNiels Sascha Reedijk 
433a8003a70SNiels Sascha Reedijk void
RemoveField(ConstIterator it)434a8003a70SNiels Sascha Reedijk BHttpFields::RemoveField(ConstIterator it) noexcept
435a8003a70SNiels Sascha Reedijk {
436a8003a70SNiels Sascha Reedijk 	fFields.erase(it);
437a8003a70SNiels Sascha Reedijk }
438a8003a70SNiels Sascha Reedijk 
439a8003a70SNiels Sascha Reedijk 
440a8003a70SNiels Sascha Reedijk void
MakeEmpty()441a8003a70SNiels Sascha Reedijk BHttpFields::MakeEmpty() noexcept
442a8003a70SNiels Sascha Reedijk {
443a8003a70SNiels Sascha Reedijk 	fFields.clear();
444a8003a70SNiels Sascha Reedijk }
445a8003a70SNiels Sascha Reedijk 
446a8003a70SNiels Sascha Reedijk 
447a8003a70SNiels Sascha Reedijk BHttpFields::ConstIterator
FindField(const std::string_view & name) const448a8003a70SNiels Sascha Reedijk BHttpFields::FindField(const std::string_view& name) const noexcept
449a8003a70SNiels Sascha Reedijk {
450a8003a70SNiels Sascha Reedijk 	for (auto it = fFields.cbegin(); it != fFields.cend(); it++) {
451a8003a70SNiels Sascha Reedijk 		if ((*it).Name() == name)
452a8003a70SNiels Sascha Reedijk 			return it;
453a8003a70SNiels Sascha Reedijk 	}
454a8003a70SNiels Sascha Reedijk 	return fFields.cend();
455a8003a70SNiels Sascha Reedijk }
456a8003a70SNiels Sascha Reedijk 
457a8003a70SNiels Sascha Reedijk 
458a8003a70SNiels Sascha Reedijk size_t
CountFields() const459a8003a70SNiels Sascha Reedijk BHttpFields::CountFields() const noexcept
460a8003a70SNiels Sascha Reedijk {
461a8003a70SNiels Sascha Reedijk 	return fFields.size();
462a8003a70SNiels Sascha Reedijk }
463a8003a70SNiels Sascha Reedijk 
464a8003a70SNiels Sascha Reedijk 
4657b1d966cSNiels Sascha Reedijk size_t
CountFields(const std::string_view & name) const4667b1d966cSNiels Sascha Reedijk BHttpFields::CountFields(const std::string_view& name) const noexcept
4677b1d966cSNiels Sascha Reedijk {
4687b1d966cSNiels Sascha Reedijk 	size_t count = 0;
4697b1d966cSNiels Sascha Reedijk 	for (auto it = fFields.cbegin(); it != fFields.cend(); it++) {
4707b1d966cSNiels Sascha Reedijk 		if ((*it).Name() == name)
4717b1d966cSNiels Sascha Reedijk 			count += 1;
4727b1d966cSNiels Sascha Reedijk 	}
4737b1d966cSNiels Sascha Reedijk 	return count;
4747b1d966cSNiels Sascha Reedijk }
4757b1d966cSNiels Sascha Reedijk 
4767b1d966cSNiels Sascha Reedijk 
477a8003a70SNiels Sascha Reedijk BHttpFields::ConstIterator
begin() const478a8003a70SNiels Sascha Reedijk BHttpFields::begin() const noexcept
479a8003a70SNiels Sascha Reedijk {
480a8003a70SNiels Sascha Reedijk 	return fFields.cbegin();
481a8003a70SNiels Sascha Reedijk }
482a8003a70SNiels Sascha Reedijk 
483a8003a70SNiels Sascha Reedijk 
484a8003a70SNiels Sascha Reedijk BHttpFields::ConstIterator
end() const485a8003a70SNiels Sascha Reedijk BHttpFields::end() const noexcept
486a8003a70SNiels Sascha Reedijk {
487a8003a70SNiels Sascha Reedijk 	return fFields.cend();
488a8003a70SNiels Sascha Reedijk }
489