NetServices: format code using haiku-format

This commit formats all the netservices2 code with the `haiku-format` tool from
https://github.com/owenca/haiku-format (commit aa7408e), with the following
customizations:
 * SpaceBeforeRangeBasedForLoopColon is set to false
 * Braces before a catch block are not wrapped
 * Most headers, except for ExclusiveBorrow.h, have been manually reformatted
   to adhere to Haiku's header format (issue #19 in the repository)

Change-Id: I693c4515cf26402e48f35d1213ab6d5fcf14bd1e
This commit is contained in:
Niels Sascha Reedijk
2022-10-29 22:53:57 +01:00
parent 93069fc4bc
commit 71e29bbeea
31 changed files with 1502 additions and 1560 deletions
+4 -4
View File
@@ -18,7 +18,8 @@ namespace BPrivate {
namespace Network { namespace Network {
class BError { class BError
{
public: public:
BError(const char* origin); BError(const char* origin);
BError(BString origin); BError(BString origin);
@@ -47,7 +48,8 @@ private:
}; };
class BRuntimeError : public BError { class BRuntimeError : public BError
{
public: public:
BRuntimeError(const char* origin, const char* message); BRuntimeError(const char* origin, const char* message);
BRuntimeError(const char* origin, BString message); BRuntimeError(const char* origin, BString message);
@@ -75,10 +77,8 @@ public:
BSystemError(const BSystemError& other); BSystemError(const BSystemError& other);
BSystemError& operator=(const BSystemError& other); BSystemError& operator=(const BSystemError& other);
#if __cplusplus >= 201103L
BSystemError(BSystemError&& other) noexcept; BSystemError(BSystemError&& other) noexcept;
BSystemError& operator=(BSystemError&& other) noexcept; BSystemError& operator=(BSystemError&& other) noexcept;
#endif
virtual const char* Message() const noexcept override; virtual const char* Message() const noexcept override;
virtual BString DebugMessage() const override; virtual BString DebugMessage() const override;
+47 -95
View File
@@ -16,23 +16,21 @@ namespace BPrivate {
namespace Network { namespace Network {
class BBorrowError : public BError { class BBorrowError : public BError
{
public: public:
BBorrowError(const char* origin) BBorrowError(const char* origin)
: BError(origin) :
BError(origin)
{ {
} }
virtual const char* virtual const char* Message() const noexcept override { return "BBorrowError"; }
Message() const noexcept override
{
return "BBorrowError";
}
}; };
class BorrowAdmin { class BorrowAdmin
{
private: private:
static constexpr uint8 kOwned = 0x1; static constexpr uint8 kOwned = 0x1;
static constexpr uint8 kBorrowed = 0x2; static constexpr uint8 kBorrowed = 0x2;
@@ -43,15 +41,12 @@ protected:
virtual void Cleanup() noexcept {}; virtual void Cleanup() noexcept {};
virtual void ReleasePointer() noexcept {}; virtual void ReleasePointer() noexcept {};
public: public:
BorrowAdmin() noexcept BorrowAdmin() noexcept {}
{
}
void void Borrow()
Borrow()
{ {
auto alreadyBorrowed = (fState.fetch_or(kBorrowed) & kBorrowed) == kBorrowed; auto alreadyBorrowed = (fState.fetch_or(kBorrowed) & kBorrowed) == kBorrowed;
if (alreadyBorrowed) { if (alreadyBorrowed) {
@@ -60,8 +55,7 @@ public:
} }
void void Return() noexcept
Return() noexcept
{ {
auto cleanup = (fState.fetch_and(~kBorrowed) & kOwned) != kOwned; auto cleanup = (fState.fetch_and(~kBorrowed) & kOwned) != kOwned;
if (cleanup) if (cleanup)
@@ -69,8 +63,7 @@ public:
} }
void void Forfeit() noexcept
Forfeit() noexcept
{ {
auto cleanup = (fState.fetch_and(~kOwned) & kBorrowed) != kBorrowed; auto cleanup = (fState.fetch_and(~kOwned) & kBorrowed) != kBorrowed;
if (cleanup) if (cleanup)
@@ -78,15 +71,10 @@ public:
} }
bool bool IsBorrowed() noexcept { return (fState.load() & kBorrowed) == kBorrowed; }
IsBorrowed() noexcept
{
return (fState.load() & kBorrowed) == kBorrowed;
}
void void Release()
Release()
{ {
if ((fState.load() & kBorrowed) == kBorrowed) if ((fState.load() & kBorrowed) == kBorrowed)
throw BBorrowError(__PRETTY_FUNCTION__); throw BBorrowError(__PRETTY_FUNCTION__);
@@ -96,57 +84,39 @@ public:
}; };
template <typename T> template<typename T> class BorrowPointer : public BorrowAdmin
class BorrowPointer : public BorrowAdmin
{ {
public: public:
BorrowPointer(T* object) noexcept BorrowPointer(T* object) noexcept
: fPtr(object) :
fPtr(object)
{ {
} }
virtual ~BorrowPointer() { virtual ~BorrowPointer() { delete fPtr; }
delete fPtr;
}
protected: protected:
virtual void virtual void Cleanup() noexcept override { delete this; }
Cleanup() noexcept override
{
delete this;
}
virtual void virtual void ReleasePointer() noexcept override { fPtr = nullptr; }
ReleasePointer() noexcept override
{
fPtr = nullptr;
}
private: private:
T* fPtr; T* fPtr;
}; };
template <typename T> template<typename T> class BExclusiveBorrow
class BExclusiveBorrow { {
template<typename P> template<typename P> friend class BBorrow;
friend class BBorrow;
T* fPtr = nullptr; T* fPtr = nullptr;
BorrowAdmin* fAdminBlock = nullptr; BorrowAdmin* fAdminBlock = nullptr;
public: public:
BExclusiveBorrow() noexcept BExclusiveBorrow() noexcept {}
{
}
BExclusiveBorrow(nullptr_t) noexcept BExclusiveBorrow(nullptr_t) noexcept {}
{
}
BExclusiveBorrow(T* object) BExclusiveBorrow(T* object)
@@ -178,8 +148,7 @@ public:
} }
BExclusiveBorrow& BExclusiveBorrow& operator=(BExclusiveBorrow&& other) noexcept
operator=(BExclusiveBorrow&& other) noexcept
{ {
if (fAdminBlock) if (fAdminBlock)
fAdminBlock->Forfeit(); fAdminBlock->Forfeit();
@@ -191,15 +160,10 @@ public:
} }
bool bool HasValue() const noexcept { return bool(fPtr); }
HasValue() const noexcept
{
return bool(fPtr);
}
T& T& operator*() const
operator*() const
{ {
if (fAdminBlock && !fAdminBlock->IsBorrowed()) if (fAdminBlock && !fAdminBlock->IsBorrowed())
return *fPtr; return *fPtr;
@@ -207,8 +171,7 @@ public:
} }
T* T* operator->() const
operator->() const
{ {
if (fAdminBlock && !fAdminBlock->IsBorrowed()) if (fAdminBlock && !fAdminBlock->IsBorrowed())
return fPtr; return fPtr;
@@ -216,8 +179,7 @@ public:
} }
std::unique_ptr<T> std::unique_ptr<T> Release()
Release()
{ {
if (!fAdminBlock) if (!fAdminBlock)
throw BBorrowError(__PRETTY_FUNCTION__); throw BBorrowError(__PRETTY_FUNCTION__);
@@ -230,27 +192,23 @@ public:
}; };
template <typename T> template<typename T> class BBorrow
class BBorrow { {
T* fPtr = nullptr; T* fPtr = nullptr;
BorrowAdmin* fAdminBlock = nullptr; BorrowAdmin* fAdminBlock = nullptr;
public: public:
BBorrow() noexcept BBorrow() noexcept {}
{
}
BBorrow(nullptr_t) noexcept BBorrow(nullptr_t) noexcept {}
{
}
template<typename P> template<typename P>
explicit BBorrow(BExclusiveBorrow<P>& owner) explicit BBorrow(BExclusiveBorrow<P>& owner)
: fPtr(owner.fPtr), fAdminBlock(owner.fAdminBlock) :
fPtr(owner.fPtr),
fAdminBlock(owner.fAdminBlock)
{ {
fAdminBlock->Borrow(); fAdminBlock->Borrow();
} }
@@ -263,15 +221,16 @@ public:
BBorrow(BBorrow&& other) noexcept BBorrow(BBorrow&& other) noexcept
: fPtr(other.fPtr), fAdminBlock(other.fAdminBlock) :
fPtr(other.fPtr),
fAdminBlock(other.fAdminBlock)
{ {
other.fPtr = nullptr; other.fPtr = nullptr;
other.fAdminBlock = nullptr; other.fAdminBlock = nullptr;
} }
BBorrow& BBorrow& operator=(BBorrow&& other) noexcept
operator=(BBorrow&& other) noexcept
{ {
if (fAdminBlock) if (fAdminBlock)
fAdminBlock->Return(); fAdminBlock->Return();
@@ -291,15 +250,10 @@ public:
} }
bool bool HasValue() const noexcept { return bool(fPtr); }
HasValue() const noexcept
{
return bool(fPtr);
}
T& T& operator*() const
operator*() const
{ {
if (fPtr) if (fPtr)
return *fPtr; return *fPtr;
@@ -307,8 +261,7 @@ public:
} }
T* T* operator->() const
operator->() const
{ {
if (fPtr) if (fPtr)
return fPtr; return fPtr;
@@ -316,8 +269,7 @@ public:
} }
void void Return() noexcept
Return() noexcept
{ {
if (fAdminBlock) if (fAdminBlock)
fAdminBlock->Return(); fAdminBlock->Return();
@@ -327,9 +279,9 @@ public:
}; };
template<class T, class ..._Args> template<class T, class... _Args>
BExclusiveBorrow<T> BExclusiveBorrow<T>
make_exclusive_borrow(_Args&& ...__args) make_exclusive_borrow(_Args&&... __args)
{ {
auto guardedObject = std::make_unique<T>(std::forward<_Args>(__args)...); auto guardedObject = std::make_unique<T>(std::forward<_Args>(__args)...);
auto retval = BExclusiveBorrow<T>(guardedObject.get()); auto retval = BExclusiveBorrow<T>(guardedObject.get());
+74 -61
View File
@@ -21,70 +21,15 @@ namespace BPrivate {
namespace Network { namespace Network {
class BHttpFields { class BHttpFields
{
public: public:
// Exceptions // Exceptions
class InvalidInput : public BError { class InvalidInput;
public:
InvalidInput(const char* origin, BString input);
virtual const char* Message() const noexcept override;
virtual BString DebugMessage() const override;
BString input;
};
// Wrapper Types // Wrapper Types
class FieldName { class FieldName;
public: class Field;
// Comparison
bool operator==(const BString& other) const noexcept;
bool operator==(const std::string_view& other) const noexcept;
bool operator==(const FieldName& other) const noexcept;
// Conversion
operator std::string_view() const;
private:
friend class BHttpFields;
FieldName() noexcept;
FieldName(const std::string_view& name) noexcept;
FieldName(const FieldName& other) noexcept;
FieldName(FieldName&&) noexcept;
FieldName& operator=(const FieldName& other) noexcept;
FieldName& operator=(FieldName&&) noexcept;
std::string_view fName;
};
class Field {
public:
// Constructors
Field() noexcept;
Field(const std::string_view& name, const std::string_view& value);
Field(BString& field);
Field(const Field& other);
Field(Field&&) noexcept;
// Assignment
Field& operator=(const Field& other);
Field& operator=(Field&& other) noexcept;
// Access Operators
const FieldName& Name() const noexcept;
std::string_view Value() const noexcept;
std::string_view RawField() const noexcept;
bool IsEmpty() const noexcept;
private:
friend class BHttpFields;
Field(BString&& rawField);
std::optional<BString> fRawField;
FieldName fName;
std::string_view fValue;
};
// Type Aliases // Type Aliases
using ConstIterator = std::list<Field>::const_iterator; using ConstIterator = std::list<Field>::const_iterator;
@@ -126,9 +71,77 @@ private:
}; };
class BHttpFields::InvalidInput : public BError
{
public:
InvalidInput(const char* origin, BString input);
virtual const char* Message() const noexcept override;
virtual BString DebugMessage() const override;
BString input;
};
class BHttpFields::FieldName
{
public:
// Comparison
bool operator==(const BString& other) const noexcept;
bool operator==(const std::string_view& other) const noexcept;
bool operator==(const FieldName& other) const noexcept;
// Conversion
operator std::string_view() const;
private:
friend class BHttpFields;
FieldName() noexcept;
FieldName(const std::string_view& name) noexcept;
FieldName(const FieldName& other) noexcept;
FieldName(FieldName&&) noexcept;
FieldName& operator=(const FieldName& other) noexcept;
FieldName& operator=(FieldName&&) noexcept;
std::string_view fName;
};
class BHttpFields::Field
{
public:
// Constructors
Field() noexcept;
Field(const std::string_view& name, const std::string_view& value);
Field(BString& field);
Field(const Field& other);
Field(Field&&) noexcept;
// Assignment
Field& operator=(const Field& other);
Field& operator=(Field&& other) noexcept;
// Access Operators
const FieldName& Name() const noexcept;
std::string_view Value() const noexcept;
std::string_view RawField() const noexcept;
bool IsEmpty() const noexcept;
private:
friend class BHttpFields;
Field(BString&& rawField);
std::optional<BString> fRawField;
FieldName fName;
std::string_view fValue;
};
} // namespace Network } // namespace Network
} // namespace BPrivate } // namespace BPrivate
#endif // _B_HTTP_FIELDS_H_ #endif // _B_HTTP_FIELDS_H_
+29 -29
View File
@@ -29,30 +29,14 @@ class HttpBuffer;
class HttpSerializer; class HttpSerializer;
class BHttpMethod { class BHttpMethod
{
public: public:
// Constants for default methods in RFC 7230 section 4.2 // Constants for default methods in RFC 7230 section 4.2
enum Verb { enum Verb { Get, Head, Post, Put, Delete, Connect, Options, Trace };
Get,
Head,
Post,
Put,
Delete,
Connect,
Options,
Trace
};
// Error type when constructing with a custom method // Error type when constructing with a custom method
class InvalidMethod : public BError { class InvalidMethod;
public:
InvalidMethod(const char* origin, BString input);
virtual const char* Message() const noexcept override;
virtual BString DebugMessage() const override;
BString input;
};
// Constructors & Destructor // Constructors & Destructor
BHttpMethod(Verb verb) noexcept; BHttpMethod(Verb verb) noexcept;
@@ -77,21 +61,29 @@ private:
}; };
class BHttpMethod::InvalidMethod : public BError
{
public:
InvalidMethod(const char* origin, BString input);
virtual const char* Message() const noexcept override;
virtual BString DebugMessage() const override;
BString input;
};
struct BHttpAuthentication { struct BHttpAuthentication {
BString username; BString username;
BString password; BString password;
}; };
class BHttpRequest { class BHttpRequest
{
public: public:
// Aggregate parameter types // Aggregate parameter types
struct Body { struct Body;
std::unique_ptr<BDataIO> input;
BString mimeType;
std::optional<off_t> size;
std::optional<off_t> startPosition;
};
// Constructors and Destructor // Constructors and Destructor
BHttpRequest(); BHttpRequest();
@@ -120,8 +112,8 @@ public:
void SetFields(const BHttpFields& fields); void SetFields(const BHttpFields& fields);
void SetMaxRedirections(uint8 maxRedirections); void SetMaxRedirections(uint8 maxRedirections);
void SetMethod(const BHttpMethod& method); void SetMethod(const BHttpMethod& method);
void SetRequestBody(std::unique_ptr<BDataIO> input, void SetRequestBody(std::unique_ptr<BDataIO> input, BString mimeType,
BString mimeType, std::optional<off_t> size); std::optional<off_t> size);
void SetStopOnError(bool stopOnError); void SetStopOnError(bool stopOnError);
void SetTimeout(bigtime_t timeout); void SetTimeout(bigtime_t timeout);
void SetUrl(const BUrl& url); void SetUrl(const BUrl& url);
@@ -145,6 +137,14 @@ private:
}; };
struct BHttpRequest::Body {
std::unique_ptr<BDataIO> input;
BString mimeType;
std::optional<off_t> size;
std::optional<off_t> startPosition;
};
} // namespace Network } // namespace Network
} // namespace BPrivate } // namespace BPrivate
+2 -4
View File
@@ -22,8 +22,7 @@ class BHttpFields;
struct HttpResultPrivate; struct HttpResultPrivate;
struct BHttpBody struct BHttpBody {
{
std::optional<BString> text; std::optional<BString> text;
}; };
@@ -93,8 +92,7 @@ enum class BHttpStatusCode : int16 {
}; };
struct BHttpStatus struct BHttpStatus {
{
int16 code = 0; int16 code = 0;
BString text; BString text;
+9 -14
View File
@@ -22,7 +22,8 @@ class BHttpRequest;
class BHttpResult; class BHttpResult;
class BHttpSession { class BHttpSession
{
public: public:
// Constructors & Destructor // Constructors & Destructor
BHttpSession(); BHttpSession();
@@ -35,8 +36,7 @@ public:
BHttpSession& operator=(BHttpSession&&) noexcept = delete; BHttpSession& operator=(BHttpSession&&) noexcept = delete;
// Requests // Requests
BHttpResult Execute(BHttpRequest&& request, BHttpResult Execute(BHttpRequest&& request, BBorrow<BDataIO> target = nullptr,
BBorrow<BDataIO> target = nullptr,
BMessenger observer = BMessenger()); BMessenger observer = BMessenger());
void Cancel(int32 identifier); void Cancel(int32 identifier);
void Cancel(const BHttpResult& request); void Cancel(const BHttpResult& request);
@@ -54,21 +54,16 @@ private:
namespace UrlEvent { namespace UrlEvent {
enum { enum { HttpStatus = '_HST', HttpFields = '_HHF', CertificateError = '_CER', HttpRedirect = '_HRE' };
HttpStatus = '_HST',
HttpFields = '_HHF',
CertificateError = '_CER',
HttpRedirect = '_HRE'
};
} }
namespace UrlEventData { namespace UrlEventData {
extern const char* HttpStatusCode; extern const char* HttpStatusCode;
extern const char* SSLCertificate; extern const char* SSLCertificate;
extern const char* SSLMessage; extern const char* SSLMessage;
extern const char* HttpRedirectUrl; extern const char* HttpRedirectUrl;
} } // namespace UrlEventData
} // namespace Network } // namespace Network
+18 -16
View File
@@ -15,25 +15,14 @@ namespace BPrivate {
namespace Network { namespace Network {
enum class BHttpTimeFormat : int8 { enum class BHttpTimeFormat : int8 { RFC1123 = 0, RFC850, AscTime };
RFC1123 = 0,
RFC850,
AscTime
};
class BHttpTime { class BHttpTime
{
public: public:
// Error type // Error type
class InvalidInput : public BError { class InvalidInput;
public:
InvalidInput(const char* origin, BString input);
virtual const char* Message() const noexcept override;
virtual BString DebugMessage() const override;
BString input;
};
// Constructors // Constructors
BHttpTime() noexcept; BHttpTime() noexcept;
@@ -48,7 +37,8 @@ public:
// Date Access // Date Access
BDateTime DateTime() const noexcept; BDateTime DateTime() const noexcept;
BHttpTimeFormat DateTimeFormat() const noexcept; BHttpTimeFormat DateTimeFormat() const noexcept;
BString ToString(BHttpTimeFormat outputFormat = BHttpTimeFormat::RFC1123) const; BString ToString(BHttpTimeFormat outputFormat
= BHttpTimeFormat::RFC1123) const;
private: private:
void _Parse(const BString& dateString); void _Parse(const BString& dateString);
@@ -58,6 +48,18 @@ private:
}; };
class BHttpTime::InvalidInput : public BError
{
public:
InvalidInput(const char* origin, BString input);
virtual const char* Message() const noexcept override;
virtual BString DebugMessage() const override;
BString input;
};
// Convenience functions // Convenience functions
BDateTime parse_http_time(const BString& string); BDateTime parse_http_time(const BString& string);
BString format_http_time(BDateTime timestamp, BString format_http_time(BDateTime timestamp,
+20 -27
View File
@@ -18,7 +18,8 @@ namespace Network {
// Standard exceptions // Standard exceptions
class BUnsupportedProtocol : public BError { class BUnsupportedProtocol : public BError
{
public: public:
BUnsupportedProtocol(const char* origin, BUrl url, BUnsupportedProtocol(const char* origin, BUrl url,
BStringList supportedProtocols); BStringList supportedProtocols);
@@ -36,7 +37,8 @@ private:
}; };
class BInvalidUrl : public BError { class BInvalidUrl : public BError
{
public: public:
BInvalidUrl(const char* origin, BUrl url); BInvalidUrl(const char* origin, BUrl url);
BInvalidUrl(BString origin, BUrl url); BInvalidUrl(BString origin, BUrl url);
@@ -50,15 +52,10 @@ private:
}; };
class BNetworkRequestError : public BError { class BNetworkRequestError : public BError
{
public: public:
enum ErrorType { enum ErrorType { HostnameError, NetworkError, ProtocolError, SystemError, Canceled };
HostnameError,
NetworkError,
ProtocolError,
SystemError,
Canceled
};
BNetworkRequestError(const char* origin, ErrorType type, BNetworkRequestError(const char* origin, ErrorType type,
status_t errorCode, const BString& customMessage = BString()); status_t errorCode, const BString& customMessage = BString());
@@ -84,7 +81,7 @@ BString encode_to_base64(const BString& string);
namespace UrlEvent { namespace UrlEvent {
enum { enum {
HostNameResolved = '_NHR', HostNameResolved = '_NHR',
ConnectionOpened = '_NCO', ConnectionOpened = '_NCO',
UploadProgress = '_NUP', UploadProgress = '_NUP',
@@ -93,28 +90,24 @@ namespace UrlEvent {
BytesWritten = '_NBW', BytesWritten = '_NBW',
RequestCompleted = '_NRC', RequestCompleted = '_NRC',
DebugMessage = '_NDB' DebugMessage = '_NDB'
}; };
} }
namespace UrlEventData { namespace UrlEventData {
extern const char* Id; extern const char* Id;
extern const char* HostName; extern const char* HostName;
extern const char* NumBytes; extern const char* NumBytes;
extern const char* TotalBytes; extern const char* TotalBytes;
extern const char* Success; extern const char* Success;
extern const char* DebugType; extern const char* DebugType;
extern const char* DebugMessage; extern const char* DebugMessage;
enum { enum { DebugInfo = '_DBI', DebugWarning = '_DBW', DebugError = '_DBE' };
DebugInfo = '_DBI', } // namespace UrlEventData
DebugWarning = '_DBW',
DebugError = '_DBE'
};
}
} } // namespace Network
} } // namespace BPrivate
#endif #endif
+34 -29
View File
@@ -18,16 +18,16 @@ using namespace BPrivate::Network;
BError::BError(const char* origin) BError::BError(const char* origin)
: fOrigin(BString(origin)) :
fOrigin(BString(origin))
{ {
} }
BError::BError(BString origin) BError::BError(BString origin)
: fOrigin(std::move(origin)) :
fOrigin(std::move(origin))
{ {
} }
@@ -40,12 +40,10 @@ BError::BError(const BError& error) = default;
BError::BError(BError&& error) noexcept = default; BError::BError(BError&& error) noexcept = default;
BError& BError& BError::operator=(const BError& error) = default;
BError::operator=(const BError& error) = default;
BError& BError& BError::operator=(BError&& error) noexcept = default;
BError::operator=(BError&& error) noexcept = default;
const char* const char*
@@ -76,18 +74,35 @@ BError::WriteToOutput(BDataIO* output) const
{ {
std::stringstream stream; std::stringstream stream;
WriteToStream(stream); WriteToStream(stream);
ssize_t result ssize_t result = output->Write(stream.str().c_str(), stream.str().length() + 1);
= output->Write(stream.str().c_str(), stream.str().length() + 1);
if (result < 0) if (result < 0)
throw BSystemError("BDataIO::Write()", result); throw BSystemError("BDataIO::Write()", result);
return static_cast<size_t>(result); return static_cast<size_t>(result);
} }
void BError::_ReservedError1() {} void
void BError::_ReservedError2() {} BError::_ReservedError1()
void BError::_ReservedError3() {} {
void BError::_ReservedError4() {} }
void
BError::_ReservedError2()
{
}
void
BError::_ReservedError3()
{
}
void
BError::_ReservedError4()
{
}
/* BRuntimeError */ /* BRuntimeError */
@@ -96,7 +111,6 @@ BRuntimeError::BRuntimeError(const char* origin, const char* message)
BError(origin), BError(origin),
fMessage(BString(message)) fMessage(BString(message))
{ {
} }
@@ -105,7 +119,6 @@ BRuntimeError::BRuntimeError(const char* origin, BString message)
BError(origin), BError(origin),
fMessage(std::move(message)) fMessage(std::move(message))
{ {
} }
@@ -114,7 +127,6 @@ BRuntimeError::BRuntimeError(BString origin, BString message)
BError(std::move(origin)), BError(std::move(origin)),
fMessage(std::move(message)) fMessage(std::move(message))
{ {
} }
@@ -124,12 +136,10 @@ BRuntimeError::BRuntimeError(const BRuntimeError& other) = default;
BRuntimeError::BRuntimeError(BRuntimeError&& other) noexcept = default; BRuntimeError::BRuntimeError(BRuntimeError&& other) noexcept = default;
BRuntimeError& BRuntimeError& BRuntimeError::operator=(const BRuntimeError& other) = default;
BRuntimeError::operator=(const BRuntimeError& other) = default;
BRuntimeError& BRuntimeError& BRuntimeError::operator=(BRuntimeError&& other) noexcept = default;
BRuntimeError::operator=(BRuntimeError&& other) noexcept = default;
const char* const char*
@@ -145,7 +155,6 @@ BSystemError::BSystemError(const char* origin, status_t error)
BError(origin), BError(origin),
fErrorCode(error) fErrorCode(error)
{ {
} }
@@ -154,7 +163,6 @@ BSystemError::BSystemError(BString origin, status_t error)
BError(std::move(origin)), BError(std::move(origin)),
fErrorCode(error) fErrorCode(error)
{ {
} }
@@ -164,12 +172,10 @@ BSystemError::BSystemError(const BSystemError& other) = default;
BSystemError::BSystemError(BSystemError&& other) noexcept = default; BSystemError::BSystemError(BSystemError&& other) noexcept = default;
BSystemError& BSystemError& BSystemError::operator=(const BSystemError& other) = default;
BSystemError::operator=(const BSystemError& other) = default;
BSystemError& BSystemError& BSystemError::operator=(BSystemError&& other) noexcept = default;
BSystemError::operator=(BSystemError&& other) noexcept = default;
const char* const char*
@@ -183,8 +189,7 @@ BString
BSystemError::DebugMessage() const BSystemError::DebugMessage() const
{ {
BString debugMessage; BString debugMessage;
debugMessage << "[" << Origin() << "] " << Message() << " (" << fErrorCode debugMessage << "[" << Origin() << "] " << Message() << " (" << fErrorCode << ")";
<< ")";
return debugMessage; return debugMessage;
} }
@@ -63,8 +63,8 @@ HttpBuffer::ReadFrom(BDataIO* source, std::optional<size_t> maxSize)
fBuffer.resize(currentSize); fBuffer.resize(currentSize);
return bytesRead; return bytesRead;
} else if (bytesRead < 0) { } else if (bytesRead < 0) {
throw BNetworkRequestError("BDataIO::Read()", BNetworkRequestError::NetworkError, throw BNetworkRequestError(
bytesRead); "BDataIO::Read()", BNetworkRequestError::NetworkError, bytesRead);
} }
// Adjust the buffer to the current size // Adjust the buffer to the current size
@@ -84,7 +84,7 @@ HttpBuffer::ReadFrom(BDataIO* source, std::optional<size_t> maxSize)
\returns the actual number of bytes written to the \a func. \returns the actual number of bytes written to the \a func.
*/ */
size_t size_t
HttpBuffer::WriteTo(HttpTransferFunction func , std::optional<size_t> maxSize) HttpBuffer::WriteTo(HttpTransferFunction func, std::optional<size_t> maxSize)
{ {
if (RemainingBytes() == 0) if (RemainingBytes() == 0)
return 0; return 0;
@@ -121,7 +121,8 @@ HttpBuffer::GetNextLine()
if (result == fBuffer.cend()) if (result == fBuffer.cend())
return std::nullopt; return std::nullopt;
BString line(reinterpret_cast<const char*>(std::addressof(*offset)), std::distance(offset, result)); BString line(
reinterpret_cast<const char*>(std::addressof(*offset)), std::distance(offset, result));
fCurrentOffset = std::distance(fBuffer.cbegin(), result) + 2; fCurrentOffset = std::distance(fBuffer.cbegin(), result) + 2;
return line; return line;
} }
@@ -173,8 +174,8 @@ std::string_view
HttpBuffer::Data() const noexcept HttpBuffer::Data() const noexcept
{ {
if (RemainingBytes() > 0) { if (RemainingBytes() > 0) {
return std::string_view(reinterpret_cast<const char*>(fBuffer.data()) + fCurrentOffset, return std::string_view(
RemainingBytes()); reinterpret_cast<const char*>(fBuffer.data()) + fCurrentOffset, RemainingBytes());
} else } else
return std::string_view(); return std::string_view();
} }
@@ -19,14 +19,16 @@ namespace BPrivate {
namespace Network { namespace Network {
using HttpTransferFunction = std::function<size_t (const std::byte*, size_t)>; using HttpTransferFunction = std::function<size_t(const std::byte*, size_t)>;
class HttpBuffer { class HttpBuffer
{
public: public:
HttpBuffer(size_t capacity = 8*1024); HttpBuffer(size_t capacity = 8 * 1024);
ssize_t ReadFrom(BDataIO* source, std::optional<size_t> maxSize = std::nullopt); ssize_t ReadFrom(BDataIO* source,
std::optional<size_t> maxSize = std::nullopt);
size_t WriteTo(HttpTransferFunction func, size_t WriteTo(HttpTransferFunction func,
std::optional<size_t> maxSize = std::nullopt); std::optional<size_t> maxSize = std::nullopt);
void WriteExactlyTo(HttpTransferFunction func, void WriteExactlyTo(HttpTransferFunction func,
+26 -25
View File
@@ -52,12 +52,8 @@ validate_value_string(const std::string_view& string)
static inline bool static inline bool
iequals(const std::string_view& a, const std::string_view& b) iequals(const std::string_view& a, const std::string_view& b)
{ {
return std::equal( return std::equal(a.begin(), a.end(), b.begin(), b.end(),
a.begin(), a.end(), [](char a, char b) { return tolower(a) == tolower(b); });
b.begin(), b.end(),
[](char a, char b) {
return tolower(a) == tolower(b);
});
} }
@@ -79,7 +75,8 @@ trim(std::string_view in)
} }
auto right = in.end() - 1; auto right = in.end() - 1;
for (; right > left && isspace(*right); --right); for (; right > left && isspace(*right); --right)
;
return std::string_view(left, std::distance(left, right) + 1); return std::string_view(left, std::distance(left, right) + 1);
} }
@@ -93,7 +90,6 @@ BHttpFields::InvalidInput::InvalidInput(const char* origin, BString input)
BError(origin), BError(origin),
input(std::move(input)) input(std::move(input))
{ {
} }
@@ -117,16 +113,16 @@ BHttpFields::InvalidInput::DebugMessage() const
BHttpFields::FieldName::FieldName() noexcept BHttpFields::FieldName::FieldName() noexcept
: fName(std::string_view()) :
fName(std::string_view())
{ {
} }
BHttpFields::FieldName::FieldName(const std::string_view& name) noexcept BHttpFields::FieldName::FieldName(const std::string_view& name) noexcept
: fName(name) :
fName(name)
{ {
} }
@@ -144,7 +140,8 @@ BHttpFields::FieldName::FieldName(const FieldName& other) noexcept = default;
longer be used as an entry in a BHttpFields object. longer be used as an entry in a BHttpFields object.
*/ */
BHttpFields::FieldName::FieldName(FieldName&& other) noexcept BHttpFields::FieldName::FieldName(FieldName&& other) noexcept
: fName(std::move(other.fName)) :
fName(std::move(other.fName))
{ {
other.fName = std::string_view(); other.fName = std::string_view();
} }
@@ -153,8 +150,8 @@ BHttpFields::FieldName::FieldName(FieldName&& other) noexcept
/*! /*!
\brief Copy assignment; \brief Copy assignment;
*/ */
BHttpFields::FieldName& BHttpFields::FieldName& BHttpFields::FieldName::operator=(
BHttpFields::FieldName::operator=(const BHttpFields::FieldName& other) noexcept = default; const BHttpFields::FieldName& other) noexcept = default;
/*! /*!
@@ -204,9 +201,10 @@ BHttpFields::FieldName::operator std::string_view() const
BHttpFields::Field::Field() noexcept BHttpFields::Field::Field() noexcept
: fName(std::string_view()), fValue(std::string_view()) :
fName(std::string_view()),
fValue(std::string_view())
{ {
} }
@@ -250,7 +248,9 @@ BHttpFields::Field::Field(BString& field)
BHttpFields::Field::Field(const BHttpFields::Field& other) BHttpFields::Field::Field(const BHttpFields::Field& other)
: fName(std::string_view()), fValue(std::string_view()) :
fName(std::string_view()),
fValue(std::string_view())
{ {
if (other.IsEmpty()) { if (other.IsEmpty()) {
fRawField = BString(); fRawField = BString();
@@ -267,7 +267,10 @@ BHttpFields::Field::Field(const BHttpFields::Field& other)
BHttpFields::Field::Field(BHttpFields::Field&& other) noexcept BHttpFields::Field::Field(BHttpFields::Field&& other) noexcept
: fRawField(std::move(other.fRawField)), fName(std::move(other.fName)), fValue(std::move(other.fValue)) :
fRawField(std::move(other.fRawField)),
fName(std::move(other.fName)),
fValue(std::move(other.fValue))
{ {
other.fName.fName = std::string_view(); other.fName.fName = std::string_view();
other.fValue = std::string_view(); other.fValue = std::string_view();
@@ -341,7 +344,6 @@ BHttpFields::Field::IsEmpty() const noexcept
BHttpFields::BHttpFields() BHttpFields::BHttpFields()
{ {
} }
@@ -355,7 +357,8 @@ BHttpFields::BHttpFields(const BHttpFields& other) = default;
BHttpFields::BHttpFields(BHttpFields&& other) BHttpFields::BHttpFields(BHttpFields&& other)
: fFields(std::move(other.fFields)) :
fFields(std::move(other.fFields))
{ {
// Explicitly clear the other list, as the C++ standard does not specify that the other list // Explicitly clear the other list, as the C++ standard does not specify that the other list
// will be empty. // will be empty.
@@ -365,12 +368,10 @@ BHttpFields::BHttpFields(BHttpFields&& other)
BHttpFields::~BHttpFields() noexcept BHttpFields::~BHttpFields() noexcept
{ {
} }
BHttpFields& BHttpFields& BHttpFields::operator=(const BHttpFields& other) = default;
BHttpFields::operator=(const BHttpFields& other) = default;
BHttpFields& BHttpFields&
@@ -423,7 +424,7 @@ BHttpFields::AddFields(std::initializer_list<Field> fields)
void void
BHttpFields::RemoveField(const std::string_view& name) noexcept BHttpFields::RemoveField(const std::string_view& name) noexcept
{ {
for(auto it = FindField(name); it != end(); it = FindField(name)) { for (auto it = FindField(name); it != end(); it = FindField(name)) {
fFields.erase(it); fFields.erase(it);
} }
} }
+35 -37
View File
@@ -8,8 +8,8 @@
#include "HttpParser.h" #include "HttpParser.h"
#include <string>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <HttpFields.h> #include <HttpFields.h>
#include <NetServicesDefs.h> #include <NetServicesDefs.h>
@@ -103,13 +103,13 @@ HttpParser::ParseFields(HttpBuffer& buffer, BHttpFields& fields)
auto fieldLine = buffer.GetNextLine(); auto fieldLine = buffer.GetNextLine();
while (fieldLine && !fieldLine.value().IsEmpty()){ while (fieldLine && !fieldLine.value().IsEmpty()) {
// Parse next header line // Parse next header line
fields.AddField(fieldLine.value()); fields.AddField(fieldLine.value());
fieldLine = buffer.GetNextLine(); fieldLine = buffer.GetNextLine();
} }
if (!fieldLine || (fieldLine && !fieldLine.value().IsEmpty())){ if (!fieldLine || (fieldLine && !fieldLine.value().IsEmpty())) {
// there is more to parse // there is more to parse
return false; return false;
} }
@@ -117,11 +117,10 @@ HttpParser::ParseFields(HttpBuffer& buffer, BHttpFields& fields)
// Determine the properties for the body // Determine the properties for the body
// RFC 7230 section 3.3.3 has a prioritized list of 7 rules around determining the body: // RFC 7230 section 3.3.3 has a prioritized list of 7 rules around determining the body:
std::optional<off_t> bodyBytesTotal = std::nullopt; std::optional<off_t> bodyBytesTotal = std::nullopt;
if (fBodyType == HttpBodyType::NoContent if (fBodyType == HttpBodyType::NoContent || fStatus.StatusCode() == BHttpStatusCode::NoContent
|| fStatus.StatusCode() == BHttpStatusCode::NoContent
|| fStatus.StatusCode() == BHttpStatusCode::NotModified) { || fStatus.StatusCode() == BHttpStatusCode::NotModified) {
// [1] In case of HEAD (set previously), status codes 1xx (TODO!), status code 204 or 304, no content // [1] In case of HEAD (set previously), status codes 1xx (TODO!), status code 204 or 304,
// [2] NOT SUPPORTED: when doing a CONNECT request, no content // no content [2] NOT SUPPORTED: when doing a CONNECT request, no content
fBodyType = HttpBodyType::NoContent; fBodyType = HttpBodyType::NoContent;
fStreamState = HttpInputStreamState::Done; fStreamState = HttpInputStreamState::Done;
} else if (auto header = fields.FindField("Transfer-Encoding"sv); } else if (auto header = fields.FindField("Transfer-Encoding"sv);
@@ -157,8 +156,7 @@ HttpParser::ParseFields(HttpBuffer& buffer, BHttpFields& fields)
fStreamState = HttpInputStreamState::Body; fStreamState = HttpInputStreamState::Body;
} }
} catch (const std::logic_error& e) { } catch (const std::logic_error& e) {
throw BNetworkRequestError(__PRETTY_FUNCTION__, throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::ProtocolError,
BNetworkRequestError::ProtocolError,
"Cannot parse Content-Length field value (logic_error)"); "Cannot parse Content-Length field value (logic_error)");
} }
} else { } else {
@@ -186,9 +184,7 @@ HttpParser::ParseFields(HttpBuffer& buffer, BHttpFields& fields)
// Check Content-Encoding for compression // Check Content-Encoding for compression
auto header = fields.FindField("Content-Encoding"sv); auto header = fields.FindField("Content-Encoding"sv);
if (header != fields.end() if (header != fields.end() && (header->Value() == "gzip" || header->Value() == "deflate")) {
&& (header->Value() == "gzip" || header->Value() == "deflate"))
{
fBodyParser = std::make_unique<HttpBodyDecompression>(std::move(fBodyParser)); fBodyParser = std::make_unique<HttpBodyDecompression>(std::move(fBodyParser));
} }
@@ -295,7 +291,6 @@ HttpBodyParser::TransferredBodySize() const noexcept
*/ */
HttpRawBodyParser::HttpRawBodyParser() HttpRawBodyParser::HttpRawBodyParser()
{ {
} }
@@ -306,7 +301,6 @@ HttpRawBodyParser::HttpRawBodyParser(off_t bodyBytesTotal)
: :
fBodyBytesTotal(bodyBytesTotal) fBodyBytesTotal(bodyBytesTotal)
{ {
} }
@@ -407,16 +401,16 @@ HttpChunkedBodyParser::ParseBody(HttpBuffer& buffer, HttpTransferFunction writeT
try { try {
size_t pos = 0; size_t pos = 0;
fRemainingChunkSize = std::stoll(chunkSizeStr, &pos, 16); fRemainingChunkSize = std::stoll(chunkSizeStr, &pos, 16);
if (pos < chunkSizeStr.size() && chunkSizeStr[pos] != ';'){ if (pos < chunkSizeStr.size() && chunkSizeStr[pos] != ';') {
throw BNetworkRequestError(__PRETTY_FUNCTION__, throw BNetworkRequestError(
BNetworkRequestError::ProtocolError); __PRETTY_FUNCTION__, BNetworkRequestError::ProtocolError);
} }
} catch (const std::invalid_argument&) { } catch (const std::invalid_argument&) {
throw BNetworkRequestError(__PRETTY_FUNCTION__, throw BNetworkRequestError(
BNetworkRequestError::ProtocolError); __PRETTY_FUNCTION__, BNetworkRequestError::ProtocolError);
} catch (const std::out_of_range&) { } catch (const std::out_of_range&) {
throw BNetworkRequestError(__PRETTY_FUNCTION__, throw BNetworkRequestError(
BNetworkRequestError::ProtocolError); __PRETTY_FUNCTION__, BNetworkRequestError::ProtocolError);
} }
if (fRemainingChunkSize > 0) if (fRemainingChunkSize > 0)
@@ -437,7 +431,8 @@ HttpChunkedBodyParser::ParseBody(HttpBuffer& buffer, HttpTransferFunction writeT
auto bytesRead = buffer.WriteTo(writeToBody, bytesToRead); auto bytesRead = buffer.WriteTo(writeToBody, bytesToRead);
if (bytesRead != bytesToRead) { if (bytesRead != bytesToRead) {
// Fail if not all expected bytes are written. // Fail if not all expected bytes are written.
throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::SystemError, throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::SystemError,
"Could not write all available body bytes to the target."); "Could not write all available body bytes to the target.");
} }
@@ -458,8 +453,8 @@ HttpChunkedBodyParser::ParseBody(HttpBuffer& buffer, HttpTransferFunction writeT
auto chunkEndString = buffer.GetNextLine(); auto chunkEndString = buffer.GetNextLine();
if (!chunkEndString || chunkEndString.value().Length() != 0) { if (!chunkEndString || chunkEndString.value().Length() != 0) {
// There should have been an empty chunk // There should have been an empty chunk
throw BNetworkRequestError(__PRETTY_FUNCTION__, throw BNetworkRequestError(
BNetworkRequestError::ProtocolError); __PRETTY_FUNCTION__, BNetworkRequestError::ProtocolError);
} }
fChunkParserState = ChunkSize; fChunkParserState = ChunkSize;
@@ -501,12 +496,11 @@ HttpBodyDecompression::HttpBodyDecompression(std::unique_ptr<HttpBodyParser> bod
fDecompressorStorage = std::make_unique<BMallocIO>(); fDecompressorStorage = std::make_unique<BMallocIO>();
BDataIO* stream = nullptr; BDataIO* stream = nullptr;
auto result = BZlibCompressionAlgorithm() auto result = BZlibCompressionAlgorithm().CreateDecompressingOutputStream(
.CreateDecompressingOutputStream(fDecompressorStorage.get(), nullptr, stream); fDecompressorStorage.get(), nullptr, stream);
if (result != B_OK) { if (result != B_OK) {
throw BNetworkRequestError( throw BNetworkRequestError("BZlibCompressionAlgorithm().CreateCompressingOutputStream",
"BZlibCompressionAlgorithm().CreateCompressingOutputStream",
BNetworkRequestError::SystemError, result); BNetworkRequestError::SystemError, result);
} }
@@ -537,30 +531,34 @@ BodyParseResult
HttpBodyDecompression::ParseBody(HttpBuffer& buffer, HttpTransferFunction writeToBody, bool readEnd) HttpBodyDecompression::ParseBody(HttpBuffer& buffer, HttpTransferFunction writeToBody, bool readEnd)
{ {
// Get the underlying raw or chunked parser to write data to our decompressionstream // Get the underlying raw or chunked parser to write data to our decompressionstream
auto parseResults = fBodyParser->ParseBody(buffer, [this](const std::byte* buffer, size_t bufferSize){ auto parseResults = fBodyParser->ParseBody(
buffer,
[this](const std::byte* buffer, size_t bufferSize) {
auto status = fDecompressingStream->WriteExactly(buffer, bufferSize); auto status = fDecompressingStream->WriteExactly(buffer, bufferSize);
if (status != B_OK) { if (status != B_OK) {
throw BNetworkRequestError("BDataIO::WriteExactly()", throw BNetworkRequestError(
BNetworkRequestError::SystemError, status); "BDataIO::WriteExactly()", BNetworkRequestError::SystemError, status);
} }
return bufferSize; return bufferSize;
}, readEnd); },
readEnd);
fTransferredBodySize += parseResults.bytesParsed; fTransferredBodySize += parseResults.bytesParsed;
if (readEnd || parseResults.complete) { if (readEnd || parseResults.complete) {
// No more bytes expected so flush out the final bytes // No more bytes expected so flush out the final bytes
if (auto status = fDecompressingStream->Flush(); status != B_OK) { if (auto status = fDecompressingStream->Flush(); status != B_OK) {
throw BNetworkRequestError("BZlibDecompressionStream::Flush()", throw BNetworkRequestError(
BNetworkRequestError::SystemError, status); "BZlibDecompressionStream::Flush()", BNetworkRequestError::SystemError, status);
} }
} }
size_t bytesWritten = 0; size_t bytesWritten = 0;
if (auto bodySize = fDecompressorStorage->Position(); bodySize > 0) { if (auto bodySize = fDecompressorStorage->Position(); bodySize > 0) {
bytesWritten = writeToBody(static_cast<const std::byte*>(fDecompressorStorage->Buffer()), bodySize); bytesWritten
= writeToBody(static_cast<const std::byte*>(fDecompressorStorage->Buffer()), bodySize);
if (static_cast<off_t>(bytesWritten) != bodySize) { if (static_cast<off_t>(bytesWritten) != bodySize) {
throw BNetworkRequestError(__PRETTY_FUNCTION__, throw BNetworkRequestError(
BNetworkRequestError::SystemError, B_PARTIAL_WRITE); __PRETTY_FUNCTION__, BNetworkRequestError::SystemError, B_PARTIAL_WRITE);
} }
fDecompressorStorage->Seek(0, SEEK_SET); fDecompressorStorage->Seek(0, SEEK_SET);
} }
+22 -34
View File
@@ -20,23 +20,13 @@ namespace BPrivate {
namespace Network { namespace Network {
using HttpTransferFunction = std::function<size_t (const std::byte*, size_t)>; using HttpTransferFunction = std::function<size_t(const std::byte*, size_t)>;
enum class HttpInputStreamState { enum class HttpInputStreamState { StatusLine, Fields, Body, Done };
StatusLine,
Fields,
Body,
Done
};
enum class HttpBodyType { enum class HttpBodyType { NoContent, Chunked, FixedSize, VariableSize };
NoContent,
Chunked,
FixedSize,
VariableSize
};
struct BodyParseResult { struct BodyParseResult {
@@ -49,9 +39,10 @@ struct BodyParseResult {
class HttpBodyParser; class HttpBodyParser;
class HttpParser { class HttpParser
{
public: public:
HttpParser() {}; HttpParser(){};
// Explicitly mark request as having no content // Explicitly mark request as having no content
void SetNoContent() noexcept; void SetNoContent() noexcept;
@@ -80,7 +71,8 @@ private:
}; };
class HttpBodyParser { class HttpBodyParser
{
public: public:
virtual BodyParseResult ParseBody(HttpBuffer& buffer, virtual BodyParseResult ParseBody(HttpBuffer& buffer,
HttpTransferFunction writeToBody, bool readEnd) = 0; HttpTransferFunction writeToBody, bool readEnd) = 0;
@@ -94,12 +86,13 @@ protected:
}; };
class HttpRawBodyParser : public HttpBodyParser { class HttpRawBodyParser : public HttpBodyParser
{
public: public:
HttpRawBodyParser(); HttpRawBodyParser();
HttpRawBodyParser(off_t bodyBytesTotal); HttpRawBodyParser(off_t bodyBytesTotal);
virtual BodyParseResult ParseBody(HttpBuffer& buffer, virtual BodyParseResult ParseBody(HttpBuffer& buffer, HttpTransferFunction writeToBody,
HttpTransferFunction writeToBody, bool readEnd) override; bool readEnd) override;
virtual std::optional<off_t> TotalBodySize() const noexcept override; virtual std::optional<off_t> TotalBodySize() const noexcept override;
private: private:
@@ -107,30 +100,25 @@ private:
}; };
class HttpChunkedBodyParser : public HttpBodyParser { class HttpChunkedBodyParser : public HttpBodyParser
{
public: public:
virtual BodyParseResult ParseBody(HttpBuffer& buffer, virtual BodyParseResult ParseBody(
HttpTransferFunction writeToBody, bool readEnd) override; HttpBuffer& buffer, HttpTransferFunction writeToBody, bool readEnd) override;
private: private:
enum { enum { ChunkSize, ChunkEnd, Chunk, Trailers, Complete } fChunkParserState = ChunkSize;
ChunkSize,
ChunkEnd,
Chunk,
Trailers,
Complete
} fChunkParserState = ChunkSize;
off_t fRemainingChunkSize = 0; off_t fRemainingChunkSize = 0;
bool fLastChunk = false; bool fLastChunk = false;
}; };
class HttpBodyDecompression : public HttpBodyParser { class HttpBodyDecompression : public HttpBodyParser
{
public: public:
HttpBodyDecompression( HttpBodyDecompression(std::unique_ptr<HttpBodyParser> bodyParser);
std::unique_ptr<HttpBodyParser> bodyParser); virtual BodyParseResult ParseBody(HttpBuffer& buffer, HttpTransferFunction writeToBody,
virtual BodyParseResult ParseBody(HttpBuffer& buffer, bool readEnd) override;
HttpTransferFunction writeToBody, bool readEnd) override;
virtual std::optional<off_t> TotalBodySize() const noexcept; virtual std::optional<off_t> TotalBodySize() const noexcept;
@@ -29,9 +29,9 @@ validate_http_token_string(const std::string_view& string)
{ {
for (auto it = string.cbegin(); it < string.cend(); it++) { for (auto it = string.cbegin(); it < string.cend(); it++) {
if (*it <= 31 || *it == 127 || *it == '(' || *it == ')' || *it == '<' || *it == '>' if (*it <= 31 || *it == 127 || *it == '(' || *it == ')' || *it == '<' || *it == '>'
|| *it == '@' || *it == ',' || *it == ';' || *it == '\\' || *it == '"' || *it == '@' || *it == ',' || *it == ';' || *it == '\\' || *it == '"' || *it == '/'
|| *it == '/' || *it == '[' || *it == ']' || *it == '?' || *it == '=' || *it == '[' || *it == ']' || *it == '?' || *it == '=' || *it == '{' || *it == '}'
|| *it == '{' || *it == '}' || *it == ' ') || *it == ' ')
return false; return false;
} }
return true; return true;
@@ -34,7 +34,6 @@ BHttpMethod::InvalidMethod::InvalidMethod(const char* origin, BString input)
BError(origin), BError(origin),
input(std::move(input)) input(std::move(input))
{ {
} }
@@ -62,17 +61,19 @@ BHttpMethod::InvalidMethod::DebugMessage() const
BHttpMethod::BHttpMethod(Verb verb) noexcept BHttpMethod::BHttpMethod(Verb verb) noexcept
: fMethod(verb) :
fMethod(verb)
{ {
} }
BHttpMethod::BHttpMethod(const std::string_view& verb) BHttpMethod::BHttpMethod(const std::string_view& verb)
: fMethod(BString(verb.data(), verb.length())) :
fMethod(BString(verb.data(), verb.length()))
{ {
if (verb.size() == 0 || !validate_http_token_string(verb)) if (verb.size() == 0 || !validate_http_token_string(verb))
throw BHttpMethod::InvalidMethod(__PRETTY_FUNCTION__, std::move(std::get<BString>(fMethod))); throw BHttpMethod::InvalidMethod(
__PRETTY_FUNCTION__, std::move(std::get<BString>(fMethod)));
} }
@@ -80,7 +81,8 @@ BHttpMethod::BHttpMethod(const BHttpMethod& other) = default;
BHttpMethod::BHttpMethod(BHttpMethod&& other) noexcept BHttpMethod::BHttpMethod(BHttpMethod&& other) noexcept
: fMethod(std::move(other.fMethod)) :
fMethod(std::move(other.fMethod))
{ {
other.fMethod = Get; other.fMethod = Get;
} }
@@ -89,8 +91,7 @@ BHttpMethod::BHttpMethod(BHttpMethod&& other) noexcept
BHttpMethod::~BHttpMethod() = default; BHttpMethod::~BHttpMethod() = default;
BHttpMethod& BHttpMethod& BHttpMethod::operator=(const BHttpMethod& other) = default;
BHttpMethod::operator=(const BHttpMethod& other) = default;
BHttpMethod& BHttpMethod&
@@ -192,14 +193,15 @@ build_basic_http_header(const BString& username, const BString& password)
BHttpRequest::BHttpRequest() BHttpRequest::BHttpRequest()
: fData(std::make_unique<Data>()) :
fData(std::make_unique<Data>())
{ {
} }
BHttpRequest::BHttpRequest(const BUrl& url) BHttpRequest::BHttpRequest(const BUrl& url)
: fData(std::make_unique<Data>()) :
fData(std::make_unique<Data>())
{ {
SetUrl(url); SetUrl(url);
} }
@@ -211,8 +213,7 @@ BHttpRequest::BHttpRequest(BHttpRequest&& other) noexcept = default;
BHttpRequest::~BHttpRequest() = default; BHttpRequest::~BHttpRequest() = default;
BHttpRequest& BHttpRequest& BHttpRequest::operator=(BHttpRequest&&) noexcept = default;
BHttpRequest::operator=(BHttpRequest&&) noexcept = default;
bool bool
@@ -304,13 +305,8 @@ BHttpRequest::SetAuthentication(const BHttpAuthentication& authentication)
} }
static constexpr std::array<std::string_view, 6> fReservedOptionalFieldNames = { static constexpr std::array<std::string_view, 6> fReservedOptionalFieldNames
"Host"sv, = {"Host"sv, "Accept-Encoding"sv, "Connection"sv, "Content-Type"sv, "Content-Length"sv};
"Accept-Encoding"sv,
"Connection"sv,
"Content-Type"sv,
"Content-Length"sv
};
void void
@@ -321,10 +317,11 @@ BHttpRequest::SetFields(const BHttpFields& fields)
for (auto& field: fields) { for (auto& field: fields) {
if (std::find(fReservedOptionalFieldNames.begin(), fReservedOptionalFieldNames.end(), if (std::find(fReservedOptionalFieldNames.begin(), fReservedOptionalFieldNames.end(),
field.Name()) != fReservedOptionalFieldNames.end()) field.Name())
{ != fReservedOptionalFieldNames.end()) {
std::string_view fieldName = field.Name(); std::string_view fieldName = field.Name();
throw BHttpFields::InvalidInput(__PRETTY_FUNCTION__, BString(fieldName.data(), fieldName.size())); throw BHttpFields::InvalidInput(
__PRETTY_FUNCTION__, BString(fieldName.data(), fieldName.size()));
} }
} }
fData->optionalFields = fields; fData->optionalFields = fields;
@@ -350,8 +347,8 @@ BHttpRequest::SetMethod(const BHttpMethod& method)
void void
BHttpRequest::SetRequestBody(std::unique_ptr<BDataIO> input, BString mimeType, BHttpRequest::SetRequestBody(
std::optional<off_t> size) std::unique_ptr<BDataIO> input, BString mimeType, std::optional<off_t> size)
{ {
if (input == nullptr) if (input == nullptr)
throw std::invalid_argument("input cannot be null"); throw std::invalid_argument("input cannot be null");
@@ -490,8 +487,7 @@ BHttpRequest::SerializeHeaderTo(HttpBuffer& buffer) const
host << ':' << fData->url.Port(); host << ':' << fData->url.Port();
outputFields.AddFields({ outputFields.AddFields({
{"Host"sv, std::string_view(host.String())}, {"Host"sv, std::string_view(host.String())}, {"Accept-Encoding"sv, "gzip"sv},
{"Accept-Encoding"sv, "gzip"sv},
// Allows the server to compress data using the "gzip" format. // Allows the server to compress data using the "gzip" format.
// "deflate" is not supported, because there are two interpretations // "deflate" is not supported, because there are two interpretations
// of what it means (the RFC and Microsoft products), and we don't // of what it means (the RFC and Microsoft products), and we don't
@@ -505,17 +501,19 @@ BHttpRequest::SerializeHeaderTo(HttpBuffer& buffer) const
if (fData->authentication) { if (fData->authentication) {
// This request will add a Basic authorization header // This request will add a Basic authorization header
BString authorization = build_basic_http_header(fData->authentication->username, BString authorization = build_basic_http_header(
fData->authentication->password); fData->authentication->username, fData->authentication->password);
outputFields.AddField("Authorization"sv, std::string_view(authorization.String())); outputFields.AddField("Authorization"sv, std::string_view(authorization.String()));
} }
if (fData->requestBody) { if (fData->requestBody) {
outputFields.AddField("Content-Type"sv, std::string_view(fData->requestBody->mimeType.String())); outputFields.AddField(
"Content-Type"sv, std::string_view(fData->requestBody->mimeType.String()));
if (fData->requestBody->size) if (fData->requestBody->size)
outputFields.AddField("Content-Length"sv, std::to_string(*fData->requestBody->size)); outputFields.AddField("Content-Length"sv, std::to_string(*fData->requestBody->size));
else else
throw BRuntimeError(__PRETTY_FUNCTION__, "Transfer body with unknown content length; chunked transfer not supported"); throw BRuntimeError(__PRETTY_FUNCTION__,
"Transfer body with unknown content length; chunked transfer not supported");
} }
for (const auto& field: outputFields) for (const auto& field: outputFields)
+91 -48
View File
@@ -23,11 +23,16 @@ BHttpStatusClass
BHttpStatus::StatusClass() const noexcept BHttpStatus::StatusClass() const noexcept
{ {
switch (code / 100) { switch (code / 100) {
case 1: return BHttpStatusClass::Informational; case 1:
case 2: return BHttpStatusClass::Success; return BHttpStatusClass::Informational;
case 3: return BHttpStatusClass::Redirection; case 2:
case 4: return BHttpStatusClass::ClientError; return BHttpStatusClass::Success;
case 5: return BHttpStatusClass::ServerError; case 3:
return BHttpStatusClass::Redirection;
case 4:
return BHttpStatusClass::ClientError;
case 5:
return BHttpStatusClass::ServerError;
default: default:
break; break;
} }
@@ -40,53 +45,92 @@ BHttpStatus::StatusCode() const noexcept
{ {
switch (static_cast<BHttpStatusCode>(code)) { switch (static_cast<BHttpStatusCode>(code)) {
// 1xx // 1xx
case BHttpStatusCode::Continue: [[fallthrough]]; case BHttpStatusCode::Continue:
case BHttpStatusCode::SwitchingProtocols: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::SwitchingProtocols:
[[fallthrough]];
// 2xx // 2xx
case BHttpStatusCode::Ok: [[fallthrough]]; case BHttpStatusCode::Ok:
case BHttpStatusCode::Created: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::Accepted: [[fallthrough]]; case BHttpStatusCode::Created:
case BHttpStatusCode::NonAuthoritativeInformation: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::NoContent: [[fallthrough]]; case BHttpStatusCode::Accepted:
case BHttpStatusCode::ResetContent: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::PartialContent: [[fallthrough]]; case BHttpStatusCode::NonAuthoritativeInformation:
[[fallthrough]];
case BHttpStatusCode::NoContent:
[[fallthrough]];
case BHttpStatusCode::ResetContent:
[[fallthrough]];
case BHttpStatusCode::PartialContent:
[[fallthrough]];
// 3xx // 3xx
case BHttpStatusCode::MultipleChoice: [[fallthrough]]; case BHttpStatusCode::MultipleChoice:
case BHttpStatusCode::MovedPermanently: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::Found: [[fallthrough]]; case BHttpStatusCode::MovedPermanently:
case BHttpStatusCode::SeeOther: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::NotModified: [[fallthrough]]; case BHttpStatusCode::Found:
case BHttpStatusCode::UseProxy: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::TemporaryRedirect: [[fallthrough]]; case BHttpStatusCode::SeeOther:
case BHttpStatusCode::PermanentRedirect: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::NotModified:
[[fallthrough]];
case BHttpStatusCode::UseProxy:
[[fallthrough]];
case BHttpStatusCode::TemporaryRedirect:
[[fallthrough]];
case BHttpStatusCode::PermanentRedirect:
[[fallthrough]];
// 4xx // 4xx
case BHttpStatusCode::BadRequest: [[fallthrough]]; case BHttpStatusCode::BadRequest:
case BHttpStatusCode::Unauthorized: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::PaymentRequired: [[fallthrough]]; case BHttpStatusCode::Unauthorized:
case BHttpStatusCode::Forbidden: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::NotFound: [[fallthrough]]; case BHttpStatusCode::PaymentRequired:
case BHttpStatusCode::MethodNotAllowed: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::NotAcceptable: [[fallthrough]]; case BHttpStatusCode::Forbidden:
case BHttpStatusCode::ProxyAuthenticationRequired: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::RequestTimeout: [[fallthrough]]; case BHttpStatusCode::NotFound:
case BHttpStatusCode::Conflict: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::Gone: [[fallthrough]]; case BHttpStatusCode::MethodNotAllowed:
case BHttpStatusCode::LengthRequired: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::PreconditionFailed: [[fallthrough]]; case BHttpStatusCode::NotAcceptable:
case BHttpStatusCode::RequestEntityTooLarge: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::RequestUriTooLarge: [[fallthrough]]; case BHttpStatusCode::ProxyAuthenticationRequired:
case BHttpStatusCode::UnsupportedMediaType: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::RequestedRangeNotSatisfiable: [[fallthrough]]; case BHttpStatusCode::RequestTimeout:
case BHttpStatusCode::ExpectationFailed: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::Conflict:
[[fallthrough]];
case BHttpStatusCode::Gone:
[[fallthrough]];
case BHttpStatusCode::LengthRequired:
[[fallthrough]];
case BHttpStatusCode::PreconditionFailed:
[[fallthrough]];
case BHttpStatusCode::RequestEntityTooLarge:
[[fallthrough]];
case BHttpStatusCode::RequestUriTooLarge:
[[fallthrough]];
case BHttpStatusCode::UnsupportedMediaType:
[[fallthrough]];
case BHttpStatusCode::RequestedRangeNotSatisfiable:
[[fallthrough]];
case BHttpStatusCode::ExpectationFailed:
[[fallthrough]];
// 5xx // 5xx
case BHttpStatusCode::InternalServerError: [[fallthrough]]; case BHttpStatusCode::InternalServerError:
case BHttpStatusCode::NotImplemented: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::BadGateway: [[fallthrough]]; case BHttpStatusCode::NotImplemented:
case BHttpStatusCode::ServiceUnavailable: [[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::BadGateway:
[[fallthrough]];
case BHttpStatusCode::ServiceUnavailable:
[[fallthrough]];
case BHttpStatusCode::GatewayTimeout: case BHttpStatusCode::GatewayTimeout:
return static_cast<BHttpStatusCode>(code); return static_cast<BHttpStatusCode>(code);
@@ -103,9 +147,9 @@ BHttpStatus::StatusCode() const noexcept
/*private*/ /*private*/
BHttpResult::BHttpResult(std::shared_ptr<HttpResultPrivate> data) BHttpResult::BHttpResult(std::shared_ptr<HttpResultPrivate> data)
: fData(data) :
fData(data)
{ {
} }
@@ -119,8 +163,7 @@ BHttpResult::~BHttpResult()
} }
BHttpResult& BHttpResult& BHttpResult::operator=(BHttpResult&& other) noexcept = default;
BHttpResult::operator=(BHttpResult&& other) noexcept = default;
const BHttpStatus& const BHttpStatus&
@@ -29,16 +29,10 @@ struct HttpResultPrivate {
const int32 id; const int32 id;
// Locking and atomic variables // Locking and atomic variables
sem_id data_wait; enum { kNoData = 0, kStatusReady, kHeadersReady, kBodyReady, kError };
enum {
kNoData = 0,
kStatusReady,
kHeadersReady,
kBodyReady,
kError
};
int32 requestStatus = kNoData; int32 requestStatus = kNoData;
int32 canCancel = 0; int32 canCancel = 0;
sem_id data_wait;
// Data // Data
std::optional<BHttpStatus> status; std::optional<BHttpStatus> status;
@@ -63,9 +57,9 @@ struct HttpResultPrivate {
}; };
inline inline HttpResultPrivate::HttpResultPrivate(int32 identifier)
HttpResultPrivate::HttpResultPrivate(int32 identifier) :
: id(identifier) id(identifier)
{ {
std::string name = "httpresult:" + std::to_string(identifier); std::string name = "httpresult:" + std::to_string(identifier);
data_wait = create_sem(1, name.c_str()); data_wait = create_sem(1, name.c_str());
@@ -63,8 +63,9 @@ HttpSerializer::Serialize(HttpBuffer& buffer, BDataIO* target)
fState = HttpSerializerState::Done; fState = HttpSerializerState::Done;
return 0; return 0;
} else if (_IsChunked()) } else if (_IsChunked())
//fState = HttpSerializerState::ChunkHeader; // fState = HttpSerializerState::ChunkHeader;
throw BRuntimeError(__PRETTY_FUNCTION__, "Chunked serialization not implemented"); throw BRuntimeError(
__PRETTY_FUNCTION__, "Chunked serialization not implemented");
else else
fState = HttpSerializerState::Body; fState = HttpSerializerState::Body;
break; break;
@@ -75,7 +76,8 @@ HttpSerializer::Serialize(HttpBuffer& buffer, BDataIO* target)
bodyBytesWritten += bytesWritten; bodyBytesWritten += bytesWritten;
fTransferredBodySize += bytesWritten; fTransferredBodySize += bytesWritten;
if (buffer.RemainingBytes() > 0) { if (buffer.RemainingBytes() > 0) {
// did not manage to write all the bytes in the buffer; continue in the next round // did not manage to write all the bytes in the buffer; continue in the next
// round
finishing = true; finishing = true;
break; break;
} }
@@ -115,15 +117,15 @@ size_t
HttpSerializer::_WriteToTarget(HttpBuffer& buffer, BDataIO* target) const HttpSerializer::_WriteToTarget(HttpBuffer& buffer, BDataIO* target) const
{ {
size_t bytesWritten = 0; size_t bytesWritten = 0;
buffer.WriteTo([target, &bytesWritten](const std::byte* buffer, size_t size){ buffer.WriteTo([target, &bytesWritten](const std::byte* buffer, size_t size) {
ssize_t result = B_INTERRUPTED; ssize_t result = B_INTERRUPTED;
while (result == B_INTERRUPTED) { while (result == B_INTERRUPTED) {
result = target->Write(buffer, size); result = target->Write(buffer, size);
} }
if (result <= 0 && result != B_WOULD_BLOCK) { if (result <= 0 && result != B_WOULD_BLOCK) {
throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::NetworkError, throw BNetworkRequestError(
result); __PRETTY_FUNCTION__, BNetworkRequestError::NetworkError, result);
} else if (result > 0) { } else if (result > 0) {
bytesWritten += result; bytesWritten += result;
return size_t(result); return size_t(result);
@@ -19,30 +19,25 @@ namespace Network {
class BHttpRequest; class BHttpRequest;
class HttpBuffer; class HttpBuffer;
using HttpTransferFunction = std::function<size_t (const std::byte*, size_t)>; using HttpTransferFunction = std::function<size_t(const std::byte*, size_t)>;
enum class HttpSerializerState { enum class HttpSerializerState { Uninitialized, Header, ChunkHeader, Body, Done };
Uninitialized,
Header,
ChunkHeader,
Body,
Done
};
class HttpSerializer { class HttpSerializer
{
public: public:
HttpSerializer() {}; HttpSerializer(){};
void SetTo(HttpBuffer& buffer, const BHttpRequest& request); void SetTo(HttpBuffer& buffer, const BHttpRequest& request);
bool IsInitialized() const noexcept { return fState != HttpSerializerState::Uninitialized; } bool IsInitialized() const noexcept;
size_t Serialize(HttpBuffer& buffer, BDataIO* target); size_t Serialize(HttpBuffer& buffer, BDataIO* target);
std::optional<off_t> BodyBytesTotal() const noexcept { return fBodySize; }; std::optional<off_t> BodyBytesTotal() const noexcept;
off_t BodyBytesTransferred() const noexcept { return fTransferredBodySize; }; off_t BodyBytesTransferred() const noexcept;
bool Complete() const noexcept { return fState == HttpSerializerState::Done; }; bool Complete() const noexcept;
private: private:
bool _IsChunked() const noexcept; bool _IsChunked() const noexcept;
@@ -56,6 +51,34 @@ private:
}; };
inline bool
HttpSerializer::IsInitialized() const noexcept
{
return fState != HttpSerializerState::Uninitialized;
}
inline std::optional<off_t>
HttpSerializer::BodyBytesTotal() const noexcept
{
return fBodySize;
}
inline off_t
HttpSerializer::BodyBytesTransferred() const noexcept
{
return fTransferredBodySize;
}
inline bool
HttpSerializer::Complete() const noexcept
{
return fState == HttpSerializerState::Done;
}
} // namespace Network } // namespace Network
} // namespace BPrivate } // namespace BPrivate
+85 -100
View File
@@ -33,8 +33,8 @@
#include "HttpBuffer.h" #include "HttpBuffer.h"
#include "HttpParser.h" #include "HttpParser.h"
#include "HttpSerializer.h"
#include "HttpResultPrivate.h" #include "HttpResultPrivate.h"
#include "HttpSerializer.h"
#include "NetServicesPrivate.h" #include "NetServicesPrivate.h"
using namespace std::literals; using namespace std::literals;
@@ -51,33 +51,23 @@ static constexpr ssize_t kMaxHeaderLineSize = 64 * 1024;
struct CounterDeleter { struct CounterDeleter {
void operator()(int32* counter) const noexcept void operator()(int32* counter) const noexcept { atomic_add(counter, -1); }
{
atomic_add(counter, -1);
}
}; };
class BHttpSession::Request { class BHttpSession::Request
{
public: public:
Request(BHttpRequest&& request, Request(BHttpRequest&& request, BBorrow<BDataIO> target, BMessenger observer);
BBorrow<BDataIO> target,
BMessenger observer);
Request(Request& original, const Redirect& redirect); Request(Request& original, const Redirect& redirect);
// States // States
enum RequestState { enum RequestState { InitialState, Connected, RequestSent, ContentReceived };
InitialState,
Connected,
RequestSent,
ContentReceived
};
RequestState State() const noexcept { return fRequestStatus; } RequestState State() const noexcept { return fRequestStatus; }
// Result Helpers // Result Helpers
std::shared_ptr<HttpResultPrivate> std::shared_ptr<HttpResultPrivate> Result() { return fResult; }
Result() { return fResult; }
void SetError(std::exception_ptr e); void SetError(std::exception_ptr e);
// Helpers for maintaining the connection count // Helpers for maintaining the connection count
@@ -97,8 +87,7 @@ public:
bool CanCancel() const noexcept { return fResult->CanCancel(); } bool CanCancel() const noexcept { return fResult->CanCancel(); }
// Message helper // Message helper
void SendMessage(uint32 what, void SendMessage(uint32 what, std::function<void(BMessage&)> dataFunc = nullptr) const;
std::function<void (BMessage&)> dataFunc = nullptr) const;
private: private:
BHttpRequest fRequest; BHttpRequest fRequest;
@@ -128,19 +117,17 @@ private:
int8 fRemainingRedirects; int8 fRemainingRedirects;
// Connection counter // Connection counter
std::unique_ptr<int32, CounterDeleter> std::unique_ptr<int32, CounterDeleter> fConnectionCounter;
fConnectionCounter;
}; };
class BHttpSession::Impl { class BHttpSession::Impl
{
public: public:
Impl(); Impl();
~Impl() noexcept; ~Impl() noexcept;
BHttpResult Execute(BHttpRequest&& request, BHttpResult Execute(BHttpRequest&& request, BBorrow<BDataIO> target, BMessenger observer);
BBorrow<BDataIO> target,
BMessenger observer);
void Cancel(int32 identifier); void Cancel(int32 identifier);
void SetMaxConnectionsPerHost(size_t maxConnections); void SetMaxConnectionsPerHost(size_t maxConnections);
void SetMaxHosts(size_t maxConnections); void SetMaxHosts(size_t maxConnections);
@@ -152,6 +139,7 @@ private:
// Helper functions // Helper functions
std::vector<BHttpSession::Request> GetRequestsForControlThread(); std::vector<BHttpSession::Request> GetRequestsForControlThread();
private: private:
// constants (can be accessed unlocked) // constants (can be accessed unlocked)
const sem_id fControlQueueSem; const sem_id fControlQueueSem;
@@ -177,7 +165,7 @@ private:
std::atomic<size_t> fMaxHosts = 10; std::atomic<size_t> fMaxHosts = 10;
// data owned by the dataThread // data owned by the dataThread
std::map<int,BHttpSession::Request> connectionMap; std::map<int, BHttpSession::Request> connectionMap;
std::vector<object_wait_info> objectList; std::vector<object_wait_info> objectList;
}; };
@@ -229,8 +217,7 @@ BHttpSession::Impl::~Impl() noexcept
BHttpResult BHttpResult
BHttpSession::Impl::Execute(BHttpRequest&& request, BBorrow<BDataIO> target, BHttpSession::Impl::Execute(BHttpRequest&& request, BBorrow<BDataIO> target, BMessenger observer)
BMessenger observer)
{ {
auto wRequest = Request(std::move(request), std::move(target), observer); auto wRequest = Request(std::move(request), std::move(target), observer);
@@ -247,7 +234,7 @@ BHttpSession::Impl::Cancel(int32 identifier)
{ {
auto lock = AutoLocker<BLocker>(fLock); auto lock = AutoLocker<BLocker>(fLock);
// Check if the item is on the control queue // Check if the item is on the control queue
fControlQueue.remove_if([&identifier](auto& request){ fControlQueue.remove_if([&identifier](auto& request) {
if (request.Id() == identifier) { if (request.Id() == identifier) {
try { try {
throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::Canceled); throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::Canceled);
@@ -269,8 +256,8 @@ void
BHttpSession::Impl::SetMaxConnectionsPerHost(size_t maxConnections) BHttpSession::Impl::SetMaxConnectionsPerHost(size_t maxConnections)
{ {
if (maxConnections <= 0 || maxConnections >= INT32_MAX) { if (maxConnections <= 0 || maxConnections >= INT32_MAX) {
throw BRuntimeError(__PRETTY_FUNCTION__, throw BRuntimeError(
"MaxConnectionsPerHost must be between 1 and INT32_MAX"); __PRETTY_FUNCTION__, "MaxConnectionsPerHost must be between 1 and INT32_MAX");
} }
fMaxConnectionsPerHost.store(maxConnections, std::memory_order_relaxed); fMaxConnectionsPerHost.store(maxConnections, std::memory_order_relaxed);
} }
@@ -347,8 +334,8 @@ BHttpSession::Impl::ControlThreadFunc(void* arg)
} }
} }
} else { } else {
throw BRuntimeError(__PRETTY_FUNCTION__, throw BRuntimeError(
"Unknown reason that the controlQueueSem is deleted"); __PRETTY_FUNCTION__, "Unknown reason that the controlQueueSem is deleted");
} }
// Cleanup: wait for data thread // Cleanup: wait for data thread
@@ -365,8 +352,8 @@ BHttpSession::Impl::DataThreadFunc(void* arg)
BHttpSession::Impl* data = static_cast<BHttpSession::Impl*>(arg); BHttpSession::Impl* data = static_cast<BHttpSession::Impl*>(arg);
// initial initialization of wait list // initial initialization of wait list
data->objectList.push_back(object_wait_info{data->fDataQueueSem, data->objectList.push_back(
B_OBJECT_TYPE_SEMAPHORE, B_EVENT_ACQUIRE_SEMAPHORE}); object_wait_info{data->fDataQueueSem, B_OBJECT_TYPE_SEMAPHORE, B_EVENT_ACQUIRE_SEMAPHORE});
while (true) { while (true) {
if (auto status = wait_for_objects(data->objectList.data(), data->objectList.size()); if (auto status = wait_for_objects(data->objectList.data(), data->objectList.size());
@@ -399,9 +386,8 @@ BHttpSession::Impl::DataThreadFunc(void* arg)
data->connectionMap.insert(std::make_pair(socket, std::move(request))); data->connectionMap.insert(std::make_pair(socket, std::move(request)));
// Add to objectList // Add to objectList
data->objectList.push_back(object_wait_info{socket, data->objectList.push_back(
B_OBJECT_TYPE_FD, B_EVENT_WRITE object_wait_info{socket, B_OBJECT_TYPE_FD, B_EVENT_WRITE});
});
} }
for (auto id: data->fCancelList) { for (auto id: data->fCancelList) {
@@ -410,7 +396,8 @@ BHttpSession::Impl::DataThreadFunc(void* arg)
// Also: the first item in the waitlist is always the semaphore // Also: the first item in the waitlist is always the semaphore
// so the fun starts at offset 1. // so the fun starts at offset 1.
size_t offset = 0; size_t offset = 0;
for (auto it = data->connectionMap.cbegin(); it != data->connectionMap.cend(); it++) { for (auto it = data->connectionMap.cbegin(); it != data->connectionMap.cend();
it++) {
offset++; offset++;
if (it->second.Id() == id) { if (it->second.Id() == id) {
data->objectList[offset].events = EVENT_CANCELLED; data->objectList[offset].events = EVENT_CANCELLED;
@@ -427,7 +414,7 @@ BHttpSession::Impl::DataThreadFunc(void* arg)
// Process all objects that are ready // Process all objects that are ready
bool resizeObjectList = false; bool resizeObjectList = false;
for(auto& item: data->objectList) { for (auto& item: data->objectList) {
if (item.type != B_OBJECT_TYPE_FD) if (item.type != B_OBJECT_TYPE_FD)
continue; continue;
if ((item.events & B_EVENT_WRITE) == B_EVENT_WRITE) { if ((item.events & B_EVENT_WRITE) == B_EVENT_WRITE) {
@@ -480,7 +467,8 @@ BHttpSession::Impl::DataThreadFunc(void* arg)
} else if ((item.events & B_EVENT_DISCONNECTED) == B_EVENT_DISCONNECTED) { } else if ((item.events & B_EVENT_DISCONNECTED) == B_EVENT_DISCONNECTED) {
auto& request = data->connectionMap.find(item.object)->second; auto& request = data->connectionMap.find(item.object)->second;
try { try {
throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::NetworkError); throw BNetworkRequestError(
__PRETTY_FUNCTION__, BNetworkRequestError::NetworkError);
} catch (...) { } catch (...) {
request.SetError(std::current_exception()); request.SetError(std::current_exception());
} }
@@ -504,12 +492,12 @@ BHttpSession::Impl::DataThreadFunc(void* arg)
} else { } else {
// Likely to be B_EVENT_INVALID. This should not happen // Likely to be B_EVENT_INVALID. This should not happen
auto& request = data->connectionMap.find(item.object)->second; auto& request = data->connectionMap.find(item.object)->second;
request.SendMessage(UrlEvent::DebugMessage, [](BMessage& msg){ request.SendMessage(UrlEvent::DebugMessage, [](BMessage& msg) {
msg.AddUInt32(UrlEventData::DebugType, UrlEventData::DebugError); msg.AddUInt32(UrlEventData::DebugType, UrlEventData::DebugError);
msg.AddString(UrlEventData::DebugMessage, "Unexpected event; socket deleted?"); msg.AddString(UrlEventData::DebugMessage, "Unexpected event; socket deleted?");
}); });
throw BRuntimeError(__PRETTY_FUNCTION__, throw BRuntimeError(
"Socket was deleted at an unexpected time"); __PRETTY_FUNCTION__, "Socket was deleted at an unexpected time");
} }
} }
@@ -541,8 +529,7 @@ BHttpSession::Impl::DataThreadFunc(void* arg)
} }
} }
} else { } else {
throw BRuntimeError(__PRETTY_FUNCTION__, throw BRuntimeError(__PRETTY_FUNCTION__, "Unknown reason that the dataQueueSem is deleted");
"Unknown reason that the dataQueueSem is deleted");
} }
return B_OK; return B_OK;
@@ -562,7 +549,7 @@ BHttpSession::Impl::GetRequestsForControlThread()
// Clean up connection list if it is at the max number of hosts // Clean up connection list if it is at the max number of hosts
if (fConnectionCount.size() >= fMaxHosts.load()) { if (fConnectionCount.size() >= fMaxHosts.load()) {
for (auto it = fConnectionCount.begin(); it != fConnectionCount.end(); ) { for (auto it = fConnectionCount.begin(); it != fConnectionCount.end();) {
if (atomic_get(std::addressof(it->second)) == 0) { if (atomic_get(std::addressof(it->second)) == 0) {
it = fConnectionCount.erase(it); it = fConnectionCount.erase(it);
} else { } else {
@@ -573,13 +560,13 @@ BHttpSession::Impl::GetRequestsForControlThread()
// Process the list of pending requests and review if they can be started. // Process the list of pending requests and review if they can be started.
auto lock = AutoLocker<BLocker>(fLock); auto lock = AutoLocker<BLocker>(fLock);
fControlQueue.remove_if([this, &requests](auto& request){ fControlQueue.remove_if([this, &requests](auto& request) {
auto host = request.GetHost(); auto host = request.GetHost();
auto it = fConnectionCount.find(host); auto it = fConnectionCount.find(host);
if (it != fConnectionCount.end()) { if (it != fConnectionCount.end()) {
if (static_cast<size_t>(atomic_get(std::addressof(it->second))) if (static_cast<size_t>(atomic_get(std::addressof(it->second)))
>= fMaxConnectionsPerHost.load(std::memory_order_relaxed)) { >= fMaxConnectionsPerHost.load(std::memory_order_relaxed)) {
request.SendMessage(UrlEvent::DebugMessage, [](BMessage& msg){ request.SendMessage(UrlEvent::DebugMessage, [](BMessage& msg) {
msg.AddUInt32(UrlEventData::DebugType, UrlEventData::DebugWarning); msg.AddUInt32(UrlEventData::DebugType, UrlEventData::DebugWarning);
msg.AddString(UrlEventData::DebugMessage, msg.AddString(UrlEventData::DebugMessage,
"Request is queued: too many active connections for host"); "Request is queued: too many active connections for host");
@@ -591,17 +578,16 @@ BHttpSession::Impl::GetRequestsForControlThread()
} }
} else { } else {
if (fConnectionCount.size() == fMaxHosts.load()) { if (fConnectionCount.size() == fMaxHosts.load()) {
request.SendMessage(UrlEvent::DebugMessage, [](BMessage& msg){ request.SendMessage(UrlEvent::DebugMessage, [](BMessage& msg) {
msg.AddUInt32(UrlEventData::DebugType, UrlEventData::DebugWarning); msg.AddUInt32(UrlEventData::DebugType, UrlEventData::DebugWarning);
msg.AddString(UrlEventData::DebugMessage, msg.AddString(UrlEventData::DebugMessage,
"Request is queued: maximum number of concurrent hosts"); "Request is queued: maximum number of concurrent hosts");
}); });
return false; return false;
} }
auto[newIt, success] = fConnectionCount.insert({host, 1}); auto [newIt, success] = fConnectionCount.insert({host, 1});
if (!success) { if (!success) {
throw BRuntimeError(__PRETTY_FUNCTION__, throw BRuntimeError(__PRETTY_FUNCTION__, "Cannot insert into fConnectionCount");
"Cannot insert into fConnectionCount");
} }
request.SetCounter(std::addressof(newIt->second)); request.SetCounter(std::addressof(newIt->second));
} }
@@ -627,8 +613,7 @@ BHttpSession::~BHttpSession() = default;
BHttpSession::BHttpSession(const BHttpSession&) noexcept = default; BHttpSession::BHttpSession(const BHttpSession&) noexcept = default;
BHttpSession& BHttpSession& BHttpSession::operator=(const BHttpSession&) noexcept = default;
BHttpSession::operator=(const BHttpSession&) noexcept = default;
BHttpResult BHttpResult
@@ -667,9 +652,10 @@ BHttpSession::SetMaxHosts(size_t maxConnections)
// #pragma mark -- BHttpSession::Request (helpers) // #pragma mark -- BHttpSession::Request (helpers)
BHttpSession::Request::Request(BHttpRequest&& request, BBorrow<BDataIO> target, BHttpSession::Request::Request(BHttpRequest&& request, BBorrow<BDataIO> target, BMessenger observer)
BMessenger observer) :
: fRequest(std::move(request)), fObserver(observer) fRequest(std::move(request)),
fObserver(observer)
{ {
auto identifier = get_netservices_request_identifier(); auto identifier = get_netservices_request_identifier();
@@ -690,7 +676,9 @@ BHttpSession::Request::Request(BHttpRequest&& request, BBorrow<BDataIO> target,
BHttpSession::Request::Request(Request& original, const BHttpSession::Redirect& redirect) BHttpSession::Request::Request(Request& original, const BHttpSession::Redirect& redirect)
: fRequest(std::move(original.fRequest)), fObserver(original.fObserver), :
fRequest(std::move(original.fRequest)),
fObserver(original.fObserver),
fResult(original.fResult) fResult(original.fResult)
{ {
// update the original request with the new location // update the original request with the new location
@@ -717,7 +705,7 @@ void
BHttpSession::Request::SetError(std::exception_ptr e) BHttpSession::Request::SetError(std::exception_ptr e)
{ {
fResult->SetError(e); fResult->SetError(e);
SendMessage(UrlEvent::DebugMessage, [&e](BMessage& msg){ SendMessage(UrlEvent::DebugMessage, [&e](BMessage& msg) {
msg.AddUInt32(UrlEventData::DebugType, UrlEventData::DebugError); msg.AddUInt32(UrlEventData::DebugType, UrlEventData::DebugError);
try { try {
std::rethrow_exception(e); std::rethrow_exception(e);
@@ -729,9 +717,8 @@ BHttpSession::Request::SetError(std::exception_ptr e)
msg.AddString(UrlEventData::DebugMessage, "Unknown exception"); msg.AddString(UrlEventData::DebugMessage, "Unknown exception");
} }
}); });
SendMessage(UrlEvent::RequestCompleted, [](BMessage& msg){ SendMessage(UrlEvent::RequestCompleted,
msg.AddBool(UrlEventData::Success, false); [](BMessage& msg) { msg.AddBool(UrlEventData::Success, false); });
});
} }
@@ -765,13 +752,12 @@ BHttpSession::Request::ResolveHostName()
// TODO: proxy // TODO: proxy
if (auto status = fRemoteAddress.SetTo(fRequest.Url().Host(), port); status != B_OK) { if (auto status = fRemoteAddress.SetTo(fRequest.Url().Host(), port); status != B_OK) {
throw BNetworkRequestError("BNetworkAddress::SetTo()", throw BNetworkRequestError(
BNetworkRequestError::HostnameError, status); "BNetworkAddress::SetTo()", BNetworkRequestError::HostnameError, status);
} }
SendMessage(UrlEvent::HostNameResolved, [this](BMessage& msg) { SendMessage(UrlEvent::HostNameResolved,
msg.AddString(UrlEventData::HostName, fRequest.Url().Host()); [this](BMessage& msg) { msg.AddString(UrlEventData::HostName, fRequest.Url().Host()); });
});
} }
@@ -795,8 +781,8 @@ BHttpSession::Request::OpenConnection()
// Open connection // Open connection
if (auto status = fSocket->Connect(fRemoteAddress); status != B_OK) { if (auto status = fSocket->Connect(fRemoteAddress); status != B_OK) {
// TODO: inform listeners that the connection failed // TODO: inform listeners that the connection failed
throw BNetworkRequestError("BSocket::Connect()", throw BNetworkRequestError(
BNetworkRequestError::NetworkError, status); "BSocket::Connect()", BNetworkRequestError::NetworkError, status);
} }
// Make the rest of the interaction non-blocking // Make the rest of the interaction non-blocking
@@ -822,8 +808,8 @@ BHttpSession::Request::TransferRequest()
{ {
// Assert that we are in the right state // Assert that we are in the right state
if (fRequestStatus != Connected) if (fRequestStatus != Connected)
throw BRuntimeError(__PRETTY_FUNCTION__, throw BRuntimeError(
"Write request for object that is not in the Connected state"); __PRETTY_FUNCTION__, "Write request for object that is not in the Connected state");
if (!fSerializer.IsInitialized()) if (!fSerializer.IsInitialized())
fSerializer.SetTo(fBuffer, fRequest); fSerializer.SetTo(fBuffer, fRequest);
@@ -879,15 +865,15 @@ BHttpSession::Request::ReceiveResult()
case BHttpStatusCode::TemporaryRedirect: case BHttpStatusCode::TemporaryRedirect:
case BHttpStatusCode::PermanentRedirect: case BHttpStatusCode::PermanentRedirect:
// These redirects require the request body to be sent again. It this is // These redirects require the request body to be sent again. It this is
// possible, BHttpRequest::RewindBody() will return true in which case we can // possible, BHttpRequest::RewindBody() will return true in which case
// handle the redirect. // we can handle the redirect.
if (!fRequest.RewindBody()) if (!fRequest.RewindBody())
break; break;
[[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::Found: case BHttpStatusCode::Found:
case BHttpStatusCode::SeeOther: case BHttpStatusCode::SeeOther:
// These redirects redirect to GET, so we don't care if we can rewind the // These redirects redirect to GET, so we don't care if we can rewind
// body; in this case redirect // the body; in this case redirect
fMightRedirect = true; fMightRedirect = true;
break; break;
default: default:
@@ -897,8 +883,7 @@ BHttpSession::Request::ReceiveResult()
if ((fStatus.StatusClass() == BHttpStatusClass::ClientError if ((fStatus.StatusClass() == BHttpStatusClass::ClientError
|| fStatus.StatusClass() == BHttpStatusClass::ServerError) || fStatus.StatusClass() == BHttpStatusClass::ServerError)
&& fRequest.StopOnError()) && fRequest.StopOnError()) {
{
fRequestStatus = ContentReceived; fRequestStatus = ContentReceived;
fResult->SetStatus(std::move(fStatus)); fResult->SetStatus(std::move(fStatus));
fResult->SetFields(BHttpFields()); fResult->SetFields(BHttpFields());
@@ -944,7 +929,8 @@ BHttpSession::Request::ReceiveResult()
switch (fStatus.StatusCode()) { switch (fStatus.StatusCode()) {
case BHttpStatusCode::Found: case BHttpStatusCode::Found:
case BHttpStatusCode::SeeOther: case BHttpStatusCode::SeeOther:
// 302 and 303 redirections convert all requests to GET request, except for HEAD // 302 and 303 redirections convert all requests to GET request, except for
// HEAD
redirectToGet = true; redirectToGet = true;
[[fallthrough]]; [[fallthrough]];
case BHttpStatusCode::MovedPermanently: case BHttpStatusCode::MovedPermanently:
@@ -957,10 +943,10 @@ BHttpSession::Request::ReceiveResult()
BNetworkRequestError::ProtocolError, BNetworkRequestError::ProtocolError,
"Redirect; the Location field must be present and cannot be found"); "Redirect; the Location field must be present and cannot be found");
} }
auto locationString = BString((*locationField).Value().data(), auto locationString = BString(
(*locationField).Value().size()); (*locationField).Value().data(), (*locationField).Value().size());
auto redirect = auto redirect = BHttpSession::Redirect{
BHttpSession::Redirect{BUrl(fRequest.Url(), locationString), redirectToGet}; BUrl(fRequest.Url(), locationString), redirectToGet};
if (!redirect.url.IsValid()) { if (!redirect.url.IsValid()) {
throw BNetworkRequestError(__PRETTY_FUNCTION__, throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError, BNetworkRequestError::ProtocolError,
@@ -992,9 +978,8 @@ BHttpSession::Request::ReceiveResult()
if (!fParser.HasContent()) { if (!fParser.HasContent()) {
// Any requests with not content are finished // Any requests with not content are finished
fResult->SetBody(); fResult->SetBody();
SendMessage(UrlEvent::RequestCompleted, [](BMessage& msg) { SendMessage(UrlEvent::RequestCompleted,
msg.AddBool(UrlEventData::Success, true); [](BMessage& msg) { msg.AddBool(UrlEventData::Success, true); });
});
fRequestStatus = ContentReceived; fRequestStatus = ContentReceived;
return true; return true;
} }
@@ -1005,10 +990,13 @@ BHttpSession::Request::ReceiveResult()
size_t bytesWrittenToBody; size_t bytesWrittenToBody;
// The bytesWrittenToBody may differ from the bytes parsed from the buffer when // The bytesWrittenToBody may differ from the bytes parsed from the buffer when
// there is compression on the incoming stream. // there is compression on the incoming stream.
bytesRead = fParser.ParseBody(fBuffer, [this, &bytesWrittenToBody](const std::byte* buffer, size_t size) { bytesRead = fParser.ParseBody(
fBuffer,
[this, &bytesWrittenToBody](const std::byte* buffer, size_t size) {
bytesWrittenToBody = fResult->WriteToBody(buffer, size); bytesWrittenToBody = fResult->WriteToBody(buffer, size);
return bytesWrittenToBody; return bytesWrittenToBody;
}, readEnd); },
readEnd);
SendMessage(UrlEvent::DownloadProgress, [this, bytesRead](BMessage& msg) { SendMessage(UrlEvent::DownloadProgress, [this, bytesRead](BMessage& msg) {
msg.AddInt64(UrlEventData::NumBytes, bytesRead); msg.AddInt64(UrlEventData::NumBytes, bytesRead);
@@ -1024,15 +1012,13 @@ BHttpSession::Request::ReceiveResult()
if (fParser.Complete()) { if (fParser.Complete()) {
fResult->SetBody(); fResult->SetBody();
SendMessage(UrlEvent::RequestCompleted, [](BMessage& msg) { SendMessage(UrlEvent::RequestCompleted,
msg.AddBool(UrlEventData::Success, true); [](BMessage& msg) { msg.AddBool(UrlEventData::Success, true); });
});
fRequestStatus = ContentReceived; fRequestStatus = ContentReceived;
return true; return true;
} else if (readEnd) { } else if (readEnd) {
// the parsing of the body is not complete but we are at the end of the data // the parsing of the body is not complete but we are at the end of the data
throw BNetworkRequestError(__PRETTY_FUNCTION__, throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::ProtocolError,
BNetworkRequestError::ProtocolError,
"Unexpected end of data: more data was expected"); "Unexpected end of data: more data was expected");
} }
@@ -1047,7 +1033,6 @@ BHttpSession::Request::ReceiveResult()
} }
/*! /*!
\brief Disconnect the socket. Does not validate if it actually succeeded. \brief Disconnect the socket. Does not validate if it actually succeeded.
*/ */
@@ -1065,7 +1050,7 @@ BHttpSession::Request::Disconnect() noexcept
\param dataFunc Optional function that adds additional data to the message. \param dataFunc Optional function that adds additional data to the message.
*/ */
void void
BHttpSession::Request::SendMessage(uint32 what, std::function<void (BMessage&)> dataFunc) const BHttpSession::Request::SendMessage(uint32 what, std::function<void(BMessage&)> dataFunc) const
{ {
if (fObserver.IsValid()) { if (fObserver.IsValid()) {
BMessage msg(what); BMessage msg(what);
@@ -1081,8 +1066,8 @@ BHttpSession::Request::SendMessage(uint32 what, std::function<void (BMessage&)>
namespace BPrivate::Network::UrlEventData { namespace BPrivate::Network::UrlEventData {
const char* HttpStatusCode = "url:httpstatuscode"; const char* HttpStatusCode = "url:httpstatuscode";
const char* SSLCertificate = "url:sslcertificate"; const char* SSLCertificate = "url:sslcertificate";
const char* SSLMessage = "url:sslmessage"; const char* SSLMessage = "url:sslmessage";
const char* HttpRedirectUrl = "url:httpredirecturl"; const char* HttpRedirectUrl = "url:httpredirecturl";
} } // namespace BPrivate::Network::UrlEventData
@@ -35,7 +35,7 @@ using namespace BPrivate::Network;
// - Invalid weekday // - Invalid weekday
static const std::list<std::pair<BHttpTimeFormat, const char*>> kDateFormats = { static const std::list<std::pair<BHttpTimeFormat, const char*>> kDateFormats = {
// RFC822 // RFC822
{BHttpTimeFormat::RFC1123, "%a, %d %b %Y %H:%M:%S GMT"},// canonical {BHttpTimeFormat::RFC1123, "%a, %d %b %Y %H:%M:%S GMT"}, // canonical
{BHttpTimeFormat::RFC1123, "%a, %d %b %Y %H:%M:%S"}, // without timezone {BHttpTimeFormat::RFC1123, "%a, %d %b %Y %H:%M:%S"}, // without timezone
// Standard RFC850 // Standard RFC850
{BHttpTimeFormat::RFC850, "%A, %d-%b-%y %H:%M:%S GMT"}, // canonical {BHttpTimeFormat::RFC850, "%A, %d-%b-%y %H:%M:%S GMT"}, // canonical
@@ -57,7 +57,6 @@ BHttpTime::InvalidInput::InvalidInput(const char* origin, BString input)
BError(origin), BError(origin),
input(std::move(input)) input(std::move(input))
{ {
} }
@@ -174,8 +173,8 @@ BHttpTime::ToString(BHttpTimeFormat outputFormat) const
char expirationString[kTimetToStringMaxLength + 1]; char expirationString[kTimetToStringMaxLength + 1];
size_t strLength; size_t strLength;
strLength = strftime(expirationString, kTimetToStringMaxLength, formatString, strLength
&expirationTm); = strftime(expirationString, kTimetToStringMaxLength, formatString, &expirationTm);
expirationFinal.SetTo(expirationString, strLength); expirationFinal.SetTo(expirationString, strLength);
break; break;
@@ -210,8 +209,7 @@ BHttpTime::_Parse(const BString& dateString)
// Now convert the struct tm from strptime into a BDateTime. // Now convert the struct tm from strptime into a BDateTime.
BTime time(expireTime.tm_hour, expireTime.tm_min, expireTime.tm_sec); BTime time(expireTime.tm_hour, expireTime.tm_min, expireTime.tm_sec);
BDate date(expireTime.tm_year + 1900, expireTime.tm_mon + 1, BDate date(expireTime.tm_year + 1900, expireTime.tm_mon + 1, expireTime.tm_mday);
expireTime.tm_mday);
fDate = BDateTime(date, time); fDate = BDateTime(date, time);
} }
@@ -17,25 +17,22 @@ namespace Network {
// #pragma mark -- BUnsupportedProtocol // #pragma mark -- BUnsupportedProtocol
BUnsupportedProtocol::BUnsupportedProtocol(const char* origin, BUnsupportedProtocol::BUnsupportedProtocol(
BUrl url, BStringList supportedProtocols) const char* origin, BUrl url, BStringList supportedProtocols)
: :
BError(origin), BError(origin),
fUrl(std::move(url)), fUrl(std::move(url)),
fSupportedProtocols(std::move(supportedProtocols)) fSupportedProtocols(std::move(supportedProtocols))
{ {
} }
BUnsupportedProtocol::BUnsupportedProtocol(BString origin, BUnsupportedProtocol::BUnsupportedProtocol(BString origin, BUrl url, BStringList supportedProtocols)
BUrl url, BStringList supportedProtocols)
: :
BError(std::move(origin)), BError(std::move(origin)),
fUrl(std::move(url)), fUrl(std::move(url)),
fSupportedProtocols(std::move(supportedProtocols)) fSupportedProtocols(std::move(supportedProtocols))
{ {
} }
@@ -68,7 +65,6 @@ BInvalidUrl::BInvalidUrl(const char* origin, BUrl url)
BError(origin), BError(origin),
fUrl(std::move(url)) fUrl(std::move(url))
{ {
} }
@@ -77,7 +73,6 @@ BInvalidUrl::BInvalidUrl(BString origin, BUrl url)
BError(std::move(origin)), BError(std::move(origin)),
fUrl(std::move(origin)) fUrl(std::move(origin))
{ {
} }
@@ -98,19 +93,24 @@ BInvalidUrl::Url() const
// #pragma mark -- BNetworkRequestError // #pragma mark -- BNetworkRequestError
BNetworkRequestError::BNetworkRequestError(const char* origin, ErrorType type, status_t errorCode, BNetworkRequestError::BNetworkRequestError(
const BString& customMessage) const char* origin, ErrorType type, status_t errorCode, const BString& customMessage)
: BError(origin), fErrorType(type), fErrorCode(errorCode), fCustomMessage(customMessage) :
BError(origin),
fErrorType(type),
fErrorCode(errorCode),
fCustomMessage(customMessage)
{ {
} }
BNetworkRequestError::BNetworkRequestError(const char* origin, ErrorType type, BNetworkRequestError::BNetworkRequestError(
const BString& customMessage) const char* origin, ErrorType type, const BString& customMessage)
: BError(origin), fErrorType(type), fCustomMessage(customMessage) :
BError(origin),
fErrorType(type),
fCustomMessage(customMessage)
{ {
} }
@@ -185,8 +185,8 @@ encode_to_base64(const BString& string)
BString tmpString = string; BString tmpString = string;
while (tmpString.Length()) { while (tmpString.Length()) {
char in[3] = { 0, 0, 0 }; char in[3] = {0, 0, 0};
char out[4] = { 0, 0, 0, 0 }; char out[4] = {0, 0, 0, 0};
int8 remaining = tmpString.Length(); int8 remaining = tmpString.Length();
tmpString.MoveInto(in, 0, 3); tmpString.MoveInto(in, 0, 3);
@@ -197,7 +197,7 @@ encode_to_base64(const BString& string)
out[3] = in[2] & 0x3F; out[3] = in[2] & 0x3F;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
out[i] = kBase64Symbols[(int)out[i]]; out[i] = kBase64Symbols[(int) out[i]];
// Add padding if the input length is not a multiple // Add padding if the input length is not a multiple
// of 3 // of 3
@@ -219,14 +219,14 @@ encode_to_base64(const BString& string)
// #pragma mark -- message constants // #pragma mark -- message constants
namespace UrlEventData { namespace UrlEventData {
const char* Id = "url:identifier"; const char* Id = "url:identifier";
const char* HostName = "url:hostname"; const char* HostName = "url:hostname";
const char* NumBytes = "url:numbytes"; const char* NumBytes = "url:numbytes";
const char* TotalBytes = "url:totalbytes"; const char* TotalBytes = "url:totalbytes";
const char* Success = "url:success"; const char* Success = "url:success";
const char* DebugType = "url:debugtype"; const char* DebugType = "url:debugtype";
const char* DebugMessage = "url:debugmessage"; const char* DebugMessage = "url:debugmessage";
} } // namespace UrlEventData
// #pragma mark -- Private functions and data // #pragma mark -- Private functions and data
@@ -26,64 +26,46 @@ class DeleteTestHelper
{ {
public: public:
DeleteTestHelper(std::atomic<bool>& deleted) DeleteTestHelper(std::atomic<bool>& deleted)
: fDeleted(deleted) :
fDeleted(deleted)
{ {
} }
~DeleteTestHelper() ~DeleteTestHelper() { fDeleted.store(true); }
{
fDeleted.store(true);
}
private: private:
std::atomic<bool>& fDeleted; std::atomic<bool>& fDeleted;
}; };
class Base { class Base
{
public: public:
Base() Base() {}
{
}
virtual ~Base() virtual ~Base() {}
{
}
virtual bool IsDerived() virtual bool IsDerived() { return false; }
{
return false;
}
}; };
class Derived : public Base { class Derived : public Base
{
public: public:
Derived() { Derived() {}
}
virtual ~Derived() { virtual ~Derived() {}
}
virtual bool IsDerived() override virtual bool IsDerived() override { return true; }
{
return true;
}
}; };
ExclusiveBorrowTest::ExclusiveBorrowTest() ExclusiveBorrowTest::ExclusiveBorrowTest()
{ {
} }
@@ -9,7 +9,8 @@
#include <TestSuite.h> #include <TestSuite.h>
class ExclusiveBorrowTest: public BTestCase { class ExclusiveBorrowTest : public BTestCase
{
public: public:
ExclusiveBorrowTest(); ExclusiveBorrowTest();
@@ -22,5 +23,4 @@ public:
}; };
#endif // EXCLUSIVE_BORROW_TEST_H #endif // EXCLUSIVE_BORROW_TEST_H
@@ -18,9 +18,9 @@ using namespace BPrivate::Network;
HttpDebugLogger::HttpDebugLogger() HttpDebugLogger::HttpDebugLogger()
: BLooper("HttpDebugLogger") :
BLooper("HttpDebugLogger")
{ {
} }
@@ -39,6 +39,7 @@ HttpDebugLogger::SetFileLogging(const char* path)
throw BSystemError("BFile::SetTo()", status); throw BSystemError("BFile::SetTo()", status);
} }
void void
HttpDebugLogger::MessageReceived(BMessage* message) HttpDebugLogger::MessageReceived(BMessage* message)
{ {
@@ -47,7 +47,6 @@ constexpr bool LOG_TO_CONSOLE = false;
HttpProtocolTest::HttpProtocolTest() HttpProtocolTest::HttpProtocolTest()
{ {
} }
@@ -147,14 +146,11 @@ HttpProtocolTest::HttpFieldsTest()
} }
// Set up a generic set of headers for further use // Set up a generic set of headers for further use
const BHttpFields defaultFields = { const BHttpFields defaultFields = {{"Host"sv, "haiku-os.org"sv}, {"Accept"sv, "*/*"sv},
{"Host"sv, "haiku-os.org"sv},
{"Accept"sv, "*/*"sv},
{"Set-Cookie"sv, "qwerty=494793ddkl; Domain=haiku-os.co.uk"sv}, {"Set-Cookie"sv, "qwerty=494793ddkl; Domain=haiku-os.co.uk"sv},
{"Set-Cookie"sv, "afbzyi=0kdnke0lyv; Domain=haiku-os.co.uk"sv}, {"Set-Cookie"sv, "afbzyi=0kdnke0lyv; Domain=haiku-os.co.uk"sv},
{}, // Empty; should be ignored by the constructor {}, // Empty; should be ignored by the constructor
{"Accept-Encoding"sv, "gzip"sv} {"Accept-Encoding"sv, "gzip"sv}};
};
// Validate std::initializer_list constructor // Validate std::initializer_list constructor
CPPUNIT_ASSERT_EQUAL(5, defaultFields.CountFields()); CPPUNIT_ASSERT_EQUAL(5, defaultFields.CountFields());
@@ -216,12 +212,8 @@ HttpProtocolTest::HttpFieldsTest()
// Iterate through the fields using a constant iterator // Iterate through the fields using a constant iterator
{ {
const BHttpFields fields = { const BHttpFields fields = {{"key1"sv, "value1"sv}, {"key2"sv, "value2"sv},
{"key1"sv, "value1"sv}, {"key3"sv, "value3"sv}, {"key4"sv, "value4"sv}};
{"key2"sv, "value2"sv},
{"key3"sv, "value3"sv},
{"key4"sv, "value4"sv}
};
auto count = 0L; auto count = 0L;
for (const auto& field: fields) { for (const auto& field: fields) {
@@ -283,8 +275,7 @@ HttpProtocolTest::HttpMethodTest()
} }
constexpr std::string_view kExpectedRequestText = constexpr std::string_view kExpectedRequestText = "GET / HTTP/1.1\r\n"
"GET / HTTP/1.1\r\n"
"Host: www.haiku-os.org\r\n" "Host: www.haiku-os.org\r\n"
"Accept-Encoding: gzip\r\n" "Accept-Encoding: gzip\r\n"
"Connection: close\r\n" "Connection: close\r\n"
@@ -323,16 +314,11 @@ HttpProtocolTest::HttpRequestTest()
void void
HttpProtocolTest::HttpTimeTest() HttpProtocolTest::HttpTimeTest()
{ {
const std::vector<BString> kValidTimeStrings = { const std::vector<BString> kValidTimeStrings
"Sun, 07 Dec 2003 16:01:00 GMT", = {"Sun, 07 Dec 2003 16:01:00 GMT", "Sun, 07 Dec 2003 16:01:00",
"Sun, 07 Dec 2003 16:01:00", "Sunday, 07-Dec-03 16:01:00 GMT", "Sunday, 07-Dec-03 16:01:00 GMT",
"Sunday, 07-Dec-03 16:01:00 GMT", "Sunday, 07-Dec-2003 16:01:00", "Sunday, 07-Dec-2003 16:01:00 GMT",
"Sunday, 07-Dec-03 16:01:00 GMT", "Sunday, 07-Dec-2003 16:01:00 UTC", "Sun Dec 7 16:01:00 2003"};
"Sunday, 07-Dec-2003 16:01:00",
"Sunday, 07-Dec-2003 16:01:00 GMT",
"Sunday, 07-Dec-2003 16:01:00 UTC",
"Sun Dec 7 16:01:00 2003"
};
const BDateTime kExpectedDateTime = {BDate{2003, 12, 7}, BTime{16, 01, 0}}; const BDateTime kExpectedDateTime = {BDate{2003, 12, 7}, BTime{16, 01, 0}};
for (const auto& timeString: kValidTimeStrings) { for (const auto& timeString: kValidTimeStrings) {
@@ -357,8 +343,8 @@ HttpProtocolTest::HttpTimeTest()
} }
// Validate format_http_time() // Validate format_http_time()
CPPUNIT_ASSERT_EQUAL(BString("Sun, 07 Dec 2003 16:01:00 GMT"), CPPUNIT_ASSERT_EQUAL(
format_http_time(kExpectedDateTime)); BString("Sun, 07 Dec 2003 16:01:00 GMT"), format_http_time(kExpectedDateTime));
CPPUNIT_ASSERT_EQUAL(BString("Sunday, 07-Dec-03 16:01:00 GMT"), CPPUNIT_ASSERT_EQUAL(BString("Sunday, 07-Dec-03 16:01:00 GMT"),
format_http_time(kExpectedDateTime, BHttpTimeFormat::RFC850)); format_http_time(kExpectedDateTime, BHttpTimeFormat::RFC850));
CPPUNIT_ASSERT_EQUAL(BString("Sun Dec 7 16:01:00 2003"), CPPUNIT_ASSERT_EQUAL(BString("Sun Dec 7 16:01:00 2003"),
@@ -387,15 +373,17 @@ HttpProtocolTest::AddTests(BTestSuite& parent)
// Observer test // Observer test
#include <iostream> #include <iostream>
class ObserverHelper : public BLooper { class ObserverHelper : public BLooper
{
public: public:
ObserverHelper() ObserverHelper()
: BLooper("ObserverHelper") {} :
BLooper("ObserverHelper")
void MessageReceived(BMessage* msg) override { {
messages.emplace_back(*msg);
} }
void MessageReceived(BMessage* msg) override { messages.emplace_back(*msg); }
std::vector<BMessage> messages; std::vector<BMessage> messages;
}; };
@@ -404,7 +392,8 @@ public:
HttpIntegrationTest::HttpIntegrationTest(TestServerMode mode) HttpIntegrationTest::HttpIntegrationTest(TestServerMode mode)
: fTestServer(mode) :
fTestServer(mode)
{ {
// increase number of concurrent connections to 4 (from 2) // increase number of concurrent connections to 4 (from 2)
fSession.SetMaxConnectionsPerHost(4); fSession.SetMaxConnectionsPerHost(4);
@@ -425,10 +414,7 @@ HttpIntegrationTest::HttpIntegrationTest(TestServerMode mode)
void void
HttpIntegrationTest::setUp() HttpIntegrationTest::setUp()
{ {
CPPUNIT_ASSERT_EQUAL_MESSAGE( CPPUNIT_ASSERT_EQUAL_MESSAGE("Starting up test server", B_OK, fTestServer.Start());
"Starting up test server",
B_OK,
fTestServer.Start());
} }
@@ -454,8 +440,8 @@ HttpIntegrationTest::AddTests(BTestSuite& parent)
= new BThreadedTestCaller<HttpIntegrationTest>("HttpTest::", httpIntegrationTest); = new BThreadedTestCaller<HttpIntegrationTest>("HttpTest::", httpIntegrationTest);
// HTTP // HTTP
testCaller->addThread("HostAndNetworkFailTest", testCaller->addThread(
&HttpIntegrationTest::HostAndNetworkFailTest); "HostAndNetworkFailTest", &HttpIntegrationTest::HostAndNetworkFailTest);
testCaller->addThread("GetTest", &HttpIntegrationTest::GetTest); testCaller->addThread("GetTest", &HttpIntegrationTest::GetTest);
testCaller->addThread("GetWithBufferTest", &HttpIntegrationTest::GetWithBufferTest); testCaller->addThread("GetWithBufferTest", &HttpIntegrationTest::GetWithBufferTest);
testCaller->addThread("HeadTest", &HttpIntegrationTest::HeadTest); testCaller->addThread("HeadTest", &HttpIntegrationTest::HeadTest);
@@ -479,8 +465,8 @@ HttpIntegrationTest::AddTests(BTestSuite& parent)
= new BThreadedTestCaller<HttpIntegrationTest>("HttpsTest::", httpsIntegrationTest); = new BThreadedTestCaller<HttpIntegrationTest>("HttpsTest::", httpsIntegrationTest);
// HTTPS // HTTPS
testCaller->addThread("HostAndNetworkFailTest", testCaller->addThread(
&HttpIntegrationTest::HostAndNetworkFailTest); "HostAndNetworkFailTest", &HttpIntegrationTest::HostAndNetworkFailTest);
testCaller->addThread("GetTest", &HttpIntegrationTest::GetTest); testCaller->addThread("GetTest", &HttpIntegrationTest::GetTest);
testCaller->addThread("GetWithBufferTest", &HttpIntegrationTest::GetWithBufferTest); testCaller->addThread("GetWithBufferTest", &HttpIntegrationTest::GetWithBufferTest);
testCaller->addThread("HeadTest", &HttpIntegrationTest::HeadTest); testCaller->addThread("HeadTest", &HttpIntegrationTest::HeadTest);
@@ -538,15 +524,13 @@ static const BHttpFields kExpectedGetFields = {
}; };
constexpr std::string_view kExpectedGetBody = { constexpr std::string_view kExpectedGetBody = {"Path: /\r\n"
"Path: /\r\n"
"\r\n" "\r\n"
"Headers:\r\n" "Headers:\r\n"
"--------\r\n" "--------\r\n"
"Host: 127.0.0.1:PORT\r\n" "Host: 127.0.0.1:PORT\r\n"
"Accept-Encoding: gzip\r\n" "Accept-Encoding: gzip\r\n"
"Connection: close\r\n" "Connection: close\r\n"};
};
void void
@@ -583,8 +567,8 @@ HttpIntegrationTest::GetWithBufferTest()
auto result = fSession.Execute(std::move(request), BBorrow<BDataIO>(body), fLoggerMessenger); auto result = fSession.Execute(std::move(request), BBorrow<BDataIO>(body), fLoggerMessenger);
try { try {
result.Body(); result.Body();
auto bodyString = std::string(reinterpret_cast<const char*>(body->Buffer()), auto bodyString
body->BufferLength()); = std::string(reinterpret_cast<const char*>(body->Buffer()), body->BufferLength());
CPPUNIT_ASSERT_EQUAL(kExpectedGetBody, bodyString); CPPUNIT_ASSERT_EQUAL(kExpectedGetBody, bodyString);
} catch (const BPrivate::Network::BError& e) { } catch (const BPrivate::Network::BError& e) {
CPPUNIT_FAIL(e.DebugMessage().String()); CPPUNIT_FAIL(e.DebugMessage().String());
@@ -729,8 +713,8 @@ HttpIntegrationTest::RequestCancelTest()
} }
static const BString kPostText = static const BString kPostText
"The MIT License\n" = "The MIT License\n"
"\n" "\n"
"Copyright (c) <year> <copyright holders>\n" "Copyright (c) <year> <copyright holders>\n"
"\n" "\n"
@@ -754,9 +738,7 @@ static const BString kPostText =
"\n"; "\n";
static BString kExpectedPostBody static BString kExpectedPostBody = BString().SetToFormat("Path: /post\r\n"
= BString().SetToFormat(
"Path: /post\r\n"
"\r\n" "\r\n"
"Headers:\r\n" "Headers:\r\n"
"--------\r\n" "--------\r\n"
@@ -768,7 +750,8 @@ static BString kExpectedPostBody
"\r\n" "\r\n"
"Request body:\r\n" "Request body:\r\n"
"-------------\r\n" "-------------\r\n"
"%s\r\n", kPostText.String()); "%s\r\n",
kPostText.String());
void void
@@ -796,16 +779,15 @@ HttpIntegrationTest::PostTest()
usleep(2000); // give some time to catch up on receiving all messages usleep(2000); // give some time to catch up on receiving all messages
observer->Lock(); observer->Lock();
while (observer->IsMessageWaiting()) while (observer->IsMessageWaiting()) {
{
observer->Unlock(); observer->Unlock();
usleep(1000); // give some time to catch up on receiving all messages usleep(1000); // give some time to catch up on receiving all messages
observer->Lock(); observer->Lock();
} }
// Assert that the messages have the right contents. // Assert that the messages have the right contents.
CPPUNIT_ASSERT_MESSAGE("Expected at least 8 observer messages for this request.", CPPUNIT_ASSERT_MESSAGE(
observer->messages.size() >= 8); "Expected at least 8 observer messages for this request.", observer->messages.size() >= 8);
uint32 previousMessage = 0; uint32 previousMessage = 0;
for (const auto& message: observer->messages) { for (const auto& message: observer->messages) {
@@ -817,20 +799,20 @@ HttpIntegrationTest::PostTest()
continue; continue;
} }
switch(previousMessage) { switch (previousMessage) {
case 0: case 0:
CPPUNIT_ASSERT_MESSAGE("message should be HostNameResolved", CPPUNIT_ASSERT_MESSAGE(
HostNameResolved == message.what); "message should be HostNameResolved", HostNameResolved == message.what);
break; break;
case HostNameResolved: case HostNameResolved:
CPPUNIT_ASSERT_MESSAGE("message should be ConnectionOpened", CPPUNIT_ASSERT_MESSAGE(
ConnectionOpened == message.what); "message should be ConnectionOpened", ConnectionOpened == message.what);
break; break;
case ConnectionOpened: case ConnectionOpened:
CPPUNIT_ASSERT_MESSAGE("message should be UploadProgress", CPPUNIT_ASSERT_MESSAGE(
UploadProgress == message.what); "message should be UploadProgress", UploadProgress == message.what);
[[fallthrough]]; [[fallthrough]];
case UploadProgress: case UploadProgress:
@@ -851,20 +833,18 @@ HttpIntegrationTest::PostTest()
break; break;
case ResponseStarted: case ResponseStarted:
CPPUNIT_ASSERT_MESSAGE("message should be HttpStatus", CPPUNIT_ASSERT_MESSAGE("message should be HttpStatus", HttpStatus == message.what);
HttpStatus == message.what);
CPPUNIT_ASSERT_MESSAGE("message must have UrlEventData::HttpStatusCode data", CPPUNIT_ASSERT_MESSAGE("message must have UrlEventData::HttpStatusCode data",
message.HasInt16(HttpStatusCode)); message.HasInt16(HttpStatusCode));
break; break;
case HttpStatus: case HttpStatus:
CPPUNIT_ASSERT_MESSAGE("message should be HttpFields", CPPUNIT_ASSERT_MESSAGE("message should be HttpFields", HttpFields == message.what);
HttpFields == message.what);
break; break;
case HttpFields: case HttpFields:
CPPUNIT_ASSERT_MESSAGE("message should be DownloadProgress", CPPUNIT_ASSERT_MESSAGE(
DownloadProgress == message.what); "message should be DownloadProgress", DownloadProgress == message.what);
[[fallthrough]]; [[fallthrough]];
case DownloadProgress: case DownloadProgress:
@@ -883,8 +863,8 @@ HttpIntegrationTest::PostTest()
case RequestCompleted: case RequestCompleted:
CPPUNIT_ASSERT_MESSAGE("message must have UrlEventData::Success data", CPPUNIT_ASSERT_MESSAGE("message must have UrlEventData::Success data",
message.HasBool(Success)); message.HasBool(Success));
CPPUNIT_ASSERT_MESSAGE("UrlEventData::Success must be true", CPPUNIT_ASSERT_MESSAGE(
message.GetBool(Success)); "UrlEventData::Success must be true", message.GetBool(Success));
break; break;
default: default:
CPPUNIT_FAIL("Expected DownloadProgress, BytesWritten or HttpStatus " CPPUNIT_FAIL("Expected DownloadProgress, BytesWritten or HttpStatus "
@@ -17,7 +17,8 @@
using BPrivate::Network::BHttpSession; using BPrivate::Network::BHttpSession;
class HttpProtocolTest: public BTestCase { class HttpProtocolTest : public BTestCase
{
public: public:
HttpProtocolTest(); HttpProtocolTest();
+37 -55
View File
@@ -23,8 +23,10 @@
namespace { namespace {
template <typename T>
std::string to_string(T value) template<typename T>
std::string
to_string(T value)
{ {
std::ostringstream s; std::ostringstream s;
s << value; s << value;
@@ -32,7 +34,8 @@ std::string to_string(T value)
} }
void exec(const std::vector<std::string>& args) void
exec(const std::vector<std::string>& args)
{ {
const char** argv = new const char*[args.size() + 1]; const char** argv = new const char*[args.size() + 1];
ArrayDeleter<const char*> _(argv); ArrayDeleter<const char*> _(argv);
@@ -47,9 +50,10 @@ void exec(const std::vector<std::string>& args)
// Return the path of a file path relative to this source file. // Return the path of a file path relative to this source file.
std::string TestFilePath(const std::string& relativePath) std::string
TestFilePath(const std::string& relativePath)
{ {
char *testFileSource = strdup(__FILE__); char* testFileSource = strdup(__FILE__);
MemoryDeleter _(testFileSource); MemoryDeleter _(testFileSource);
std::string testSrcDir(::dirname(testFileSource)); std::string testSrcDir(::dirname(testFileSource));
@@ -57,7 +61,7 @@ std::string TestFilePath(const std::string& relativePath)
return testSrcDir + "/" + relativePath; return testSrcDir + "/" + relativePath;
} }
} } // namespace
RandomTCPServerPort::RandomTCPServerPort() RandomTCPServerPort::RandomTCPServerPort()
@@ -70,10 +74,7 @@ RandomTCPServerPort::RandomTCPServerPort()
// kernel. // kernel.
int socket_fd = ::socket(AF_INET, SOCK_STREAM, 0); int socket_fd = ::socket(AF_INET, SOCK_STREAM, 0);
if (socket_fd == -1) { if (socket_fd == -1) {
fprintf( fprintf(stderr, "ERROR: Unable to create socket: %s\n", strerror(errno));
stderr,
"ERROR: Unable to create socket: %s\n",
strerror(errno));
fInitStatus = B_ERROR; fInitStatus = B_ERROR;
return; return;
} }
@@ -84,18 +85,10 @@ RandomTCPServerPort::RandomTCPServerPort()
// for reuse. // for reuse.
{ {
int reuse = 1; int reuse = 1;
int result = ::setsockopt( int result = ::setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
socket_fd,
SOL_SOCKET,
SO_REUSEPORT,
&reuse,
sizeof(reuse));
if (result == -1) { if (result == -1) {
fInitStatus = errno; fInitStatus = errno;
fprintf( fprintf(stderr, "ERROR: Unable to set socket options on fd %d: %s\n", socket_fd,
stderr,
"ERROR: Unable to set socket options on fd %d: %s\n",
socket_fd,
strerror(fInitStatus)); strerror(fInitStatus));
return; return;
} }
@@ -106,15 +99,10 @@ RandomTCPServerPort::RandomTCPServerPort()
server_address.sin_family = AF_INET; server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); server_address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
int bind_result = ::bind( int bind_result = ::bind(
socket_fd, socket_fd, reinterpret_cast<struct sockaddr*>(&server_address), sizeof(server_address));
reinterpret_cast<struct sockaddr*>(&server_address),
sizeof(server_address));
if (bind_result == -1) { if (bind_result == -1) {
fInitStatus = errno; fInitStatus = errno;
fprintf( fprintf(stderr, "ERROR: Unable to bind to loopback interface: %s\n", strerror(fInitStatus));
stderr,
"ERROR: Unable to bind to loopback interface: %s\n",
strerror(fInitStatus));
return; return;
} }
@@ -129,9 +117,7 @@ RandomTCPServerPort::RandomTCPServerPort()
// Now get the port from the socket. // Now get the port from the socket.
socklen_t server_address_length = sizeof(server_address); socklen_t server_address_length = sizeof(server_address);
::getsockname( ::getsockname(
socket_fd, socket_fd, reinterpret_cast<struct sockaddr*>(&server_address), &server_address_length);
reinterpret_cast<struct sockaddr*>(&server_address),
&server_address_length);
fServerPort = ntohs(server_address.sin_port); fServerPort = ntohs(server_address.sin_port);
fInitStatus = B_OK; fInitStatus = B_OK;
@@ -148,19 +134,22 @@ RandomTCPServerPort::~RandomTCPServerPort()
} }
status_t RandomTCPServerPort::InitCheck() const status_t
RandomTCPServerPort::InitCheck() const
{ {
return fInitStatus; return fInitStatus;
} }
int RandomTCPServerPort::FileDescriptor() const int
RandomTCPServerPort::FileDescriptor() const
{ {
return fSocketFd; return fSocketFd;
} }
uint16_t RandomTCPServerPort::Port() const uint16_t
RandomTCPServerPort::Port() const
{ {
return fServerPort; return fServerPort;
} }
@@ -188,7 +177,8 @@ ChildProcess::~ChildProcess()
// The job of this method is to spawn a child process that will later be killed // The job of this method is to spawn a child process that will later be killed
// by the destructor. // by the destructor.
status_t ChildProcess::Start(const std::vector<std::string>& args) status_t
ChildProcess::Start(const std::vector<std::string>& args)
{ {
if (fChildPid != -1) { if (fChildPid != -1) {
return B_ALREADY_RUNNING; return B_ALREADY_RUNNING;
@@ -209,17 +199,11 @@ status_t ChildProcess::Start(const std::vector<std::string>& args)
// If we reach this point we failed to load the Python image. // If we reach this point we failed to load the Python image.
std::ostringstream ostr; std::ostringstream ostr;
for (std::vector<std::string>::const_iterator iter = args.begin(); for (std::vector<std::string>::const_iterator iter = args.begin(); iter != args.end(); ++iter) {
iter != args.end();
++iter) {
ostr << " " << *iter; ostr << " " << *iter;
} }
fprintf( fprintf(stderr, "Unable to spawn `%s': %s\n", ostr.str().c_str(), strerror(errno));
stderr,
"Unable to spawn `%s': %s\n",
ostr.str().c_str(),
strerror(errno));
exit(1); exit(1);
} }
@@ -233,7 +217,8 @@ TestServer::TestServer(TestServerMode mode)
// Start a child testserver.py process with the random TCP port chosen by // Start a child testserver.py process with the random TCP port chosen by
// fPort. // fPort.
status_t TestServer::Start() status_t
TestServer::Start()
{ {
if (fPort.InitCheck() != B_OK) { if (fPort.InitCheck() != B_OK) {
return fPort.InitCheck(); return fPort.InitCheck();
@@ -241,10 +226,7 @@ status_t TestServer::Start()
auto testFilePath = TestFilePath("testserver.py"); auto testFilePath = TestFilePath("testserver.py");
if (::access(testFilePath.data(), R_OK) != 0) { if (::access(testFilePath.data(), R_OK) != 0) {
fprintf( fprintf(stderr, "ERROR: No access to the test server script at: %s\n", testFilePath.data());
stderr,
"ERROR: No access to the test server script at: %s\n",
testFilePath.data());
return B_IO_ERROR; return B_IO_ERROR;
} }
@@ -272,10 +254,11 @@ status_t TestServer::Start()
} }
BUrl TestServer::BaseUrl() const BUrl
TestServer::BaseUrl() const
{ {
std::string scheme; std::string scheme;
switch(fMode) { switch (fMode) {
case TestServerMode::Http: case TestServerMode::Http:
scheme = "http://"; scheme = "http://";
break; break;
@@ -293,7 +276,8 @@ BUrl TestServer::BaseUrl() const
// Start a child proxy.py process using the random TCP port chosen by fPort. // Start a child proxy.py process using the random TCP port chosen by fPort.
status_t TestProxyServer::Start() status_t
TestProxyServer::Start()
{ {
if (fPort.InitCheck() != B_OK) { if (fPort.InitCheck() != B_OK) {
return fPort.InitCheck(); return fPort.InitCheck();
@@ -301,10 +285,7 @@ status_t TestProxyServer::Start()
auto testFilePath = TestFilePath("proxy.py"); auto testFilePath = TestFilePath("proxy.py");
if (::access(testFilePath.data(), R_OK) != 0) { if (::access(testFilePath.data(), R_OK) != 0) {
fprintf( fprintf(stderr, "ERROR: No access to the test server script at: %s\n", testFilePath.data());
stderr,
"ERROR: No access to the test server script at: %s\n",
testFilePath.data());
return B_IO_ERROR; return B_IO_ERROR;
} }
@@ -327,7 +308,8 @@ status_t TestProxyServer::Start()
} }
uint16_t TestProxyServer::Port() const uint16_t
TestProxyServer::Port() const
{ {
return fPort.Port(); return fPort.Port();
} }
+9 -4
View File
@@ -16,7 +16,8 @@
// Binds to a random unused TCP port. // Binds to a random unused TCP port.
class RandomTCPServerPort { class RandomTCPServerPort
{
public: public:
RandomTCPServerPort(); RandomTCPServerPort();
~RandomTCPServerPort(); ~RandomTCPServerPort();
@@ -32,12 +33,14 @@ private:
}; };
class ChildProcess { class ChildProcess
{
public: public:
ChildProcess(); ChildProcess();
~ChildProcess(); ~ChildProcess();
status_t Start(const std::vector<std::string>& args); status_t Start(const std::vector<std::string>& args);
private: private:
pid_t fChildPid; pid_t fChildPid;
}; };
@@ -49,7 +52,8 @@ enum class TestServerMode {
}; };
class TestServer { class TestServer
{
public: public:
TestServer(TestServerMode mode); TestServer(TestServerMode mode);
@@ -63,7 +67,8 @@ private:
}; };
class TestProxyServer { class TestProxyServer
{
public: public:
status_t Start(); status_t Start();
uint16_t Port() const; uint16_t Port() const;