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:45:39 +01:00
parent 93069fc4bc
commit 71e29bbeea
31 changed files with 1502 additions and 1560 deletions

View File

@ -18,74 +18,74 @@ namespace BPrivate {
namespace Network {
class BError {
class BError
{
public:
BError(const char* origin);
BError(BString origin);
virtual ~BError() noexcept;
BError(const char* origin);
BError(BString origin);
virtual ~BError() noexcept;
BError(const BError& error);
BError(BError&& error) noexcept;
BError(const BError& error);
BError(BError&& error) noexcept;
BError& operator=(const BError& error);
BError& operator=(BError&& error) noexcept;
BError& operator=(const BError& error);
BError& operator=(BError&& error) noexcept;
virtual const char* Message() const noexcept = 0;
virtual const char* Origin() const noexcept;
virtual BString DebugMessage() const;
void WriteToStream(std::ostream& stream) const;
size_t WriteToOutput(BDataIO* output) const;
virtual const char* Message() const noexcept = 0;
virtual const char* Origin() const noexcept;
virtual BString DebugMessage() const;
void WriteToStream(std::ostream& stream) const;
size_t WriteToOutput(BDataIO* output) const;
private:
virtual void _ReservedError1();
virtual void _ReservedError2();
virtual void _ReservedError3();
virtual void _ReservedError4();
virtual void _ReservedError1();
virtual void _ReservedError2();
virtual void _ReservedError3();
virtual void _ReservedError4();
private:
BString fOrigin;
BString fOrigin;
};
class BRuntimeError : public BError {
class BRuntimeError : public BError
{
public:
BRuntimeError(const char* origin, const char* message);
BRuntimeError(const char* origin, BString message);
BRuntimeError(BString origin, BString message);
BRuntimeError(const char* origin, const char* message);
BRuntimeError(const char* origin, BString message);
BRuntimeError(BString origin, BString message);
BRuntimeError(const BRuntimeError& other);
BRuntimeError(BRuntimeError&& other) noexcept;
BRuntimeError(const BRuntimeError& other);
BRuntimeError(BRuntimeError&& other) noexcept;
BRuntimeError& operator=(const BRuntimeError& other);
BRuntimeError& operator=(BRuntimeError&& other) noexcept;
BRuntimeError& operator=(const BRuntimeError& other);
BRuntimeError& operator=(BRuntimeError&& other) noexcept;
virtual const char* Message() const noexcept override;
virtual const char* Message() const noexcept override;
private:
BString fMessage;
BString fMessage;
};
class BSystemError : public BError
{
public:
BSystemError(const char* origin, status_t error);
BSystemError(BString origin, status_t error);
BSystemError(const char* origin, status_t error);
BSystemError(BString origin, status_t error);
BSystemError(const BSystemError& other);
BSystemError& operator=(const BSystemError& other);
BSystemError(const BSystemError& other);
BSystemError& operator=(const BSystemError& other);
#if __cplusplus >= 201103L
BSystemError(BSystemError&& other) noexcept;
BSystemError& operator=(BSystemError&& other) noexcept;
#endif
BSystemError(BSystemError&& other) noexcept;
BSystemError& operator=(BSystemError&& other) noexcept;
virtual const char* Message() const noexcept override;
virtual BString DebugMessage() const override;
status_t Error() noexcept;
virtual const char* Message() const noexcept override;
virtual BString DebugMessage() const override;
status_t Error() noexcept;
private:
status_t fErrorCode;
status_t fErrorCode;
};
} // namespace Network

View File

@ -16,42 +16,37 @@ namespace BPrivate {
namespace Network {
class BBorrowError : public BError {
class BBorrowError : public BError
{
public:
BBorrowError(const char* origin)
: BError(origin)
:
BError(origin)
{
}
virtual const char*
Message() const noexcept override
{
return "BBorrowError";
}
virtual const char* Message() const noexcept override { return "BBorrowError"; }
};
class BorrowAdmin {
class BorrowAdmin
{
private:
static constexpr uint8 kOwned = 0x1;
static constexpr uint8 kBorrowed = 0x2;
std::atomic<uint8> fState = kOwned;
std::atomic<uint8> fState = kOwned;
protected:
virtual ~BorrowAdmin() = default;
virtual ~BorrowAdmin() = default;
virtual void Cleanup() noexcept {};
virtual void ReleasePointer() noexcept {};
virtual void Cleanup() noexcept {};
virtual void ReleasePointer() noexcept {};
public:
BorrowAdmin() noexcept
{
}
BorrowAdmin() noexcept {}
void
Borrow()
void Borrow()
{
auto alreadyBorrowed = (fState.fetch_or(kBorrowed) & kBorrowed) == kBorrowed;
if (alreadyBorrowed) {
@ -60,8 +55,7 @@ public:
}
void
Return() noexcept
void Return() noexcept
{
auto cleanup = (fState.fetch_and(~kBorrowed) & kOwned) != kOwned;
if (cleanup)
@ -69,8 +63,7 @@ public:
}
void
Forfeit() noexcept
void Forfeit() noexcept
{
auto cleanup = (fState.fetch_and(~kOwned) & kBorrowed) != kBorrowed;
if (cleanup)
@ -78,15 +71,10 @@ public:
}
bool
IsBorrowed() noexcept
{
return (fState.load() & kBorrowed) == kBorrowed;
}
bool IsBorrowed() noexcept { return (fState.load() & kBorrowed) == kBorrowed; }
void
Release()
void Release()
{
if ((fState.load() & kBorrowed) == kBorrowed)
throw BBorrowError(__PRETTY_FUNCTION__);
@ -96,57 +84,39 @@ public:
};
template <typename T>
class BorrowPointer : public BorrowAdmin
template<typename T> class BorrowPointer : public BorrowAdmin
{
public:
BorrowPointer(T* object) noexcept
: fPtr(object)
:
fPtr(object)
{
}
virtual ~BorrowPointer() {
delete fPtr;
}
virtual ~BorrowPointer() { delete fPtr; }
protected:
virtual void
Cleanup() noexcept override
{
delete this;
}
virtual void Cleanup() noexcept override { delete this; }
virtual void
ReleasePointer() noexcept override
{
fPtr = nullptr;
}
virtual void ReleasePointer() noexcept override { fPtr = nullptr; }
private:
T* fPtr;
T* fPtr;
};
template <typename T>
class BExclusiveBorrow {
template<typename P>
friend class BBorrow;
template<typename T> class BExclusiveBorrow
{
template<typename P> friend class BBorrow;
T* fPtr = nullptr;
BorrowAdmin* fAdminBlock = nullptr;
T* fPtr = nullptr;
BorrowAdmin* fAdminBlock = nullptr;
public:
BExclusiveBorrow() noexcept
{
}
BExclusiveBorrow() noexcept {}
BExclusiveBorrow(nullptr_t) noexcept
{
}
BExclusiveBorrow(nullptr_t) noexcept {}
BExclusiveBorrow(T* object)
@ -178,8 +148,7 @@ public:
}
BExclusiveBorrow&
operator=(BExclusiveBorrow&& other) noexcept
BExclusiveBorrow& operator=(BExclusiveBorrow&& other) noexcept
{
if (fAdminBlock)
fAdminBlock->Forfeit();
@ -191,15 +160,10 @@ public:
}
bool
HasValue() const noexcept
{
return bool(fPtr);
}
bool HasValue() const noexcept { return bool(fPtr); }
T&
operator*() const
T& operator*() const
{
if (fAdminBlock && !fAdminBlock->IsBorrowed())
return *fPtr;
@ -207,8 +171,7 @@ public:
}
T*
operator->() const
T* operator->() const
{
if (fAdminBlock && !fAdminBlock->IsBorrowed())
return fPtr;
@ -216,8 +179,7 @@ public:
}
std::unique_ptr<T>
Release()
std::unique_ptr<T> Release()
{
if (!fAdminBlock)
throw BBorrowError(__PRETTY_FUNCTION__);
@ -230,27 +192,23 @@ public:
};
template <typename T>
class BBorrow {
T* fPtr = nullptr;
BorrowAdmin* fAdminBlock = nullptr;
template<typename T> class BBorrow
{
T* fPtr = nullptr;
BorrowAdmin* fAdminBlock = nullptr;
public:
BBorrow() noexcept
{
}
BBorrow() noexcept {}
BBorrow(nullptr_t) noexcept
{
}
BBorrow(nullptr_t) noexcept {}
template<typename P>
explicit BBorrow(BExclusiveBorrow<P>& owner)
: fPtr(owner.fPtr), fAdminBlock(owner.fAdminBlock)
:
fPtr(owner.fPtr),
fAdminBlock(owner.fAdminBlock)
{
fAdminBlock->Borrow();
}
@ -263,15 +221,16 @@ public:
BBorrow(BBorrow&& other) noexcept
: fPtr(other.fPtr), fAdminBlock(other.fAdminBlock)
:
fPtr(other.fPtr),
fAdminBlock(other.fAdminBlock)
{
other.fPtr = nullptr;
other.fAdminBlock = nullptr;
}
BBorrow&
operator=(BBorrow&& other) noexcept
BBorrow& operator=(BBorrow&& other) noexcept
{
if (fAdminBlock)
fAdminBlock->Return();
@ -291,15 +250,10 @@ public:
}
bool
HasValue() const noexcept
{
return bool(fPtr);
}
bool HasValue() const noexcept { return bool(fPtr); }
T&
operator*() const
T& operator*() const
{
if (fPtr)
return *fPtr;
@ -307,8 +261,7 @@ public:
}
T*
operator->() const
T* operator->() const
{
if (fPtr)
return fPtr;
@ -316,8 +269,7 @@ public:
}
void
Return() noexcept
void Return() noexcept
{
if (fAdminBlock)
fAdminBlock->Return();
@ -327,9 +279,9 @@ public:
};
template<class T, class ..._Args>
template<class T, class... _Args>
BExclusiveBorrow<T>
make_exclusive_borrow(_Args&& ...__args)
make_exclusive_borrow(_Args&&... __args)
{
auto guardedObject = std::make_unique<T>(std::forward<_Args>(__args)...);
auto retval = BExclusiveBorrow<T>(guardedObject.get());

141
headers/private/netservices2/HttpFields.h Executable file → Normal file
View File

@ -21,70 +21,15 @@ namespace BPrivate {
namespace Network {
class BHttpFields {
class BHttpFields
{
public:
// Exceptions
class InvalidInput : public BError {
public:
InvalidInput(const char* origin, BString input);
virtual const char* Message() const noexcept override;
virtual BString DebugMessage() const override;
BString input;
};
// Exceptions
class InvalidInput;
// Wrapper Types
class 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 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;
};
class FieldName;
class Field;
// Type Aliases
using ConstIterator = std::list<Field>::const_iterator;
@ -101,14 +46,14 @@ public:
BHttpFields& operator=(BHttpFields&&) noexcept;
// Access list
const Field& operator[](size_t index) const;
const Field& operator[](size_t index) const;
// Modifiers
void AddField(const std::string_view& name,
const std::string_view& value);
void AddField(BString& field);
void AddFields(std::initializer_list<Field> fields);
void RemoveField(const std::string_view& name) noexcept;
void RemoveField(const std::string_view& name) noexcept;
void RemoveField(ConstIterator it) noexcept;
void MakeEmpty() noexcept;
@ -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 BPrivate
#endif // _B_HTTP_FIELDS_H_

View File

@ -29,30 +29,14 @@ class HttpBuffer;
class HttpSerializer;
class BHttpMethod {
class BHttpMethod
{
public:
// Constants for default methods in RFC 7230 section 4.2
enum Verb {
Get,
Head,
Post,
Put,
Delete,
Connect,
Options,
Trace
};
enum Verb { Get, Head, Post, Put, Delete, Connect, Options, Trace };
// Error type when constructing with a custom method
class InvalidMethod : public BError {
public:
InvalidMethod(const char* origin, BString input);
virtual const char* Message() const noexcept override;
virtual BString DebugMessage() const override;
BString input;
};
class InvalidMethod;
// Constructors & Destructor
BHttpMethod(Verb verb) noexcept;
@ -70,78 +54,94 @@ public:
bool operator!=(const Verb& other) const noexcept;
// Get the method as a string
const std::string_view Method() const noexcept;
const std::string_view Method() const noexcept;
private:
std::variant<Verb, BString> fMethod;
std::variant<Verb, BString> fMethod;
};
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 {
BString username;
BString password;
BString username;
BString password;
};
class BHttpRequest {
class BHttpRequest
{
public:
// Aggregate parameter types
struct Body {
std::unique_ptr<BDataIO> input;
BString mimeType;
std::optional<off_t> size;
std::optional<off_t> startPosition;
};
struct Body;
// Constructors and Destructor
BHttpRequest();
BHttpRequest(const BUrl& url);
BHttpRequest(const BHttpRequest& other) = delete;
BHttpRequest(BHttpRequest&& other) noexcept;
~BHttpRequest();
BHttpRequest();
BHttpRequest(const BUrl& url);
BHttpRequest(const BHttpRequest& other) = delete;
BHttpRequest(BHttpRequest&& other) noexcept;
~BHttpRequest();
// Assignment operators
BHttpRequest& operator=(const BHttpRequest& other) = delete;
BHttpRequest& operator=(BHttpRequest&&) noexcept;
BHttpRequest& operator=(const BHttpRequest& other) = delete;
BHttpRequest& operator=(BHttpRequest&&) noexcept;
// Access
bool IsEmpty() const noexcept;
const BHttpAuthentication* Authentication() const noexcept;
const BHttpFields& Fields() const noexcept;
uint8 MaxRedirections() const noexcept;
const BHttpMethod& Method() const noexcept;
const Body* RequestBody() const noexcept;
bool StopOnError() const noexcept;
bigtime_t Timeout() const noexcept;
const BUrl& Url() const noexcept;
bool IsEmpty() const noexcept;
const BHttpAuthentication* Authentication() const noexcept;
const BHttpFields& Fields() const noexcept;
uint8 MaxRedirections() const noexcept;
const BHttpMethod& Method() const noexcept;
const Body* RequestBody() const noexcept;
bool StopOnError() const noexcept;
bigtime_t Timeout() const noexcept;
const BUrl& Url() const noexcept;
// Named Setters
void SetAuthentication(const BHttpAuthentication& authentication);
void SetFields(const BHttpFields& fields);
void SetMaxRedirections(uint8 maxRedirections);
void SetMethod(const BHttpMethod& method);
void SetRequestBody(std::unique_ptr<BDataIO> input,
BString mimeType, std::optional<off_t> size);
void SetStopOnError(bool stopOnError);
void SetTimeout(bigtime_t timeout);
void SetUrl(const BUrl& url);
void SetAuthentication(const BHttpAuthentication& authentication);
void SetFields(const BHttpFields& fields);
void SetMaxRedirections(uint8 maxRedirections);
void SetMethod(const BHttpMethod& method);
void SetRequestBody(std::unique_ptr<BDataIO> input, BString mimeType,
std::optional<off_t> size);
void SetStopOnError(bool stopOnError);
void SetTimeout(bigtime_t timeout);
void SetUrl(const BUrl& url);
// Clearing Options
void ClearAuthentication() noexcept;
void ClearAuthentication() noexcept;
std::unique_ptr<BDataIO> ClearRequestBody() noexcept;
// Serialization
BString HeaderToString() const;
BString HeaderToString() const;
private:
friend class BHttpSession;
friend class HttpSerializer;
struct Data;
bool RewindBody() noexcept;
void SerializeHeaderTo(HttpBuffer& buffer) const;
bool RewindBody() noexcept;
void SerializeHeaderTo(HttpBuffer& buffer) const;
std::unique_ptr<Data> fData;
std::unique_ptr<Data> fData;
};
struct BHttpRequest::Body {
std::unique_ptr<BDataIO> input;
BString mimeType;
std::optional<off_t> size;
std::optional<off_t> startPosition;
};

View File

@ -22,19 +22,18 @@ class BHttpFields;
struct HttpResultPrivate;
struct BHttpBody
{
std::optional<BString> text;
struct BHttpBody {
std::optional<BString> text;
};
enum class BHttpStatusClass : int16 {
Invalid = 000,
Informational = 100,
Success = 200,
Redirection = 300,
ClientError = 400,
ServerError = 500
Invalid = 000,
Informational = 100,
Success = 200,
Redirection = 300,
ClientError = 400,
ServerError = 500
};
@ -93,14 +92,13 @@ enum class BHttpStatusCode : int16 {
};
struct BHttpStatus
{
int16 code = 0;
BString text;
struct BHttpStatus {
int16 code = 0;
BString text;
// Helpers
BHttpStatusClass StatusClass() const noexcept;
BHttpStatusCode StatusCode() const noexcept;
BHttpStatusClass StatusClass() const noexcept;
BHttpStatusCode StatusCode() const noexcept;
};
@ -108,32 +106,32 @@ class BHttpResult
{
public:
// Constructors and destructor
BHttpResult(const BHttpResult& other) = delete;
BHttpResult(BHttpResult&& other) noexcept;
~BHttpResult();
BHttpResult(const BHttpResult& other) = delete;
BHttpResult(BHttpResult&& other) noexcept;
~BHttpResult();
// Assignment operators
BHttpResult& operator=(const BHttpResult& other) = delete;
BHttpResult& operator=(BHttpResult&& other) noexcept;
BHttpResult& operator=(const BHttpResult& other) = delete;
BHttpResult& operator=(BHttpResult&& other) noexcept;
// Blocking Access Functions
const BHttpStatus& Status() const;
const BHttpFields& Fields() const;
BHttpBody& Body() const;
const BHttpStatus& Status() const;
const BHttpFields& Fields() const;
BHttpBody& Body() const;
// Check if data is available yet
bool HasStatus() const;
bool HasFields() const;
bool HasBody() const;
bool IsCompleted() const;
bool HasStatus() const;
bool HasFields() const;
bool HasBody() const;
bool IsCompleted() const;
// Identity
int32 Identity() const;
int32 Identity() const;
private:
friend class BHttpSession;
BHttpResult(std::shared_ptr<HttpResultPrivate> data);
std::shared_ptr<HttpResultPrivate> fData;
BHttpResult(std::shared_ptr<HttpResultPrivate> data);
std::shared_ptr<HttpResultPrivate> fData;
};

View File

@ -22,53 +22,48 @@ class BHttpRequest;
class BHttpResult;
class BHttpSession {
class BHttpSession
{
public:
// Constructors & Destructor
BHttpSession();
BHttpSession(const BHttpSession&) noexcept;
BHttpSession(BHttpSession&&) noexcept = delete;
~BHttpSession() noexcept;
BHttpSession();
BHttpSession(const BHttpSession&) noexcept;
BHttpSession(BHttpSession&&) noexcept = delete;
~BHttpSession() noexcept;
// Assignment operators
BHttpSession& operator=(const BHttpSession&) noexcept;
BHttpSession& operator=(BHttpSession&&) noexcept = delete;
BHttpSession& operator=(const BHttpSession&) noexcept;
BHttpSession& operator=(BHttpSession&&) noexcept = delete;
// Requests
BHttpResult Execute(BHttpRequest&& request,
BBorrow<BDataIO> target = nullptr,
BMessenger observer = BMessenger());
void Cancel(int32 identifier);
void Cancel(const BHttpResult& request);
BHttpResult Execute(BHttpRequest&& request, BBorrow<BDataIO> target = nullptr,
BMessenger observer = BMessenger());
void Cancel(int32 identifier);
void Cancel(const BHttpResult& request);
// Concurrency limits
void SetMaxConnectionsPerHost(size_t maxConnections);
void SetMaxHosts(size_t maxConnections);
void SetMaxConnectionsPerHost(size_t maxConnections);
void SetMaxHosts(size_t maxConnections);
private:
struct Redirect;
class Request;
class Impl;
std::shared_ptr<Impl> fImpl;
std::shared_ptr<Impl> fImpl;
};
namespace UrlEvent {
enum {
HttpStatus = '_HST',
HttpFields = '_HHF',
CertificateError = '_CER',
HttpRedirect = '_HRE'
};
enum { HttpStatus = '_HST', HttpFields = '_HHF', CertificateError = '_CER', HttpRedirect = '_HRE' };
}
namespace UrlEventData {
extern const char* HttpStatusCode;
extern const char* SSLCertificate;
extern const char* SSLMessage;
extern const char* HttpRedirectUrl;
}
extern const char* HttpStatusCode;
extern const char* SSLCertificate;
extern const char* SSLMessage;
extern const char* HttpRedirectUrl;
} // namespace UrlEventData
} // namespace Network

View File

@ -15,52 +15,54 @@ namespace BPrivate {
namespace Network {
enum class BHttpTimeFormat : int8 {
RFC1123 = 0,
RFC850,
AscTime
};
enum class BHttpTimeFormat : int8 { RFC1123 = 0, RFC850, AscTime };
class BHttpTime {
class BHttpTime
{
public:
// Error type
class 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 InvalidInput;
// Constructors
BHttpTime() noexcept;
BHttpTime(BDateTime date);
BHttpTime(const BString& dateString);
BHttpTime() noexcept;
BHttpTime(BDateTime date);
BHttpTime(const BString& dateString);
// Date modification
void SetTo(const BString& string);
void SetTo(BDateTime date);
void SetTo(const BString& string);
void SetTo(BDateTime date);
// Date Access
BDateTime DateTime() const noexcept;
BHttpTimeFormat DateTimeFormat() const noexcept;
BString ToString(BHttpTimeFormat outputFormat = BHttpTimeFormat::RFC1123) const;
BDateTime DateTime() const noexcept;
BHttpTimeFormat DateTimeFormat() const noexcept;
BString ToString(BHttpTimeFormat outputFormat
= BHttpTimeFormat::RFC1123) const;
private:
void _Parse(const BString& dateString);
void _Parse(const BString& dateString);
BDateTime fDate;
BHttpTimeFormat fDateFormat;
BDateTime fDate;
BHttpTimeFormat fDateFormat;
};
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
BDateTime parse_http_time(const BString& string);
BString format_http_time(BDateTime timestamp,
BDateTime parse_http_time(const BString& string);
BString format_http_time(BDateTime timestamp,
BHttpTimeFormat outputFormat = BHttpTimeFormat::RFC1123);

View File

@ -18,65 +18,62 @@ namespace Network {
// Standard exceptions
class BUnsupportedProtocol : public BError {
class BUnsupportedProtocol : public BError
{
public:
BUnsupportedProtocol(const char* origin, BUrl url,
BStringList supportedProtocols);
BUnsupportedProtocol(BString origin, BUrl url,
BStringList supportedProtocols);
BUnsupportedProtocol(const char* origin, BUrl url,
BStringList supportedProtocols);
BUnsupportedProtocol(BString origin, BUrl url,
BStringList supportedProtocols);
virtual const char* Message() const noexcept override;
virtual const char* Message() const noexcept override;
const BUrl& Url() const;
const BStringList& SupportedProtocols() const;
const BUrl& Url() const;
const BStringList& SupportedProtocols() const;
private:
BUrl fUrl;
BStringList fSupportedProtocols;
BUrl fUrl;
BStringList fSupportedProtocols;
};
class BInvalidUrl : public BError {
class BInvalidUrl : public BError
{
public:
BInvalidUrl(const char* origin, BUrl url);
BInvalidUrl(BString origin, BUrl url);
BInvalidUrl(const char* origin, BUrl url);
BInvalidUrl(BString origin, BUrl url);
virtual const char* Message() const noexcept override;
virtual const char* Message() const noexcept override;
const BUrl& Url() const;
const BUrl& Url() const;
private:
BUrl fUrl;
BUrl fUrl;
};
class BNetworkRequestError : public BError {
class BNetworkRequestError : public BError
{
public:
enum ErrorType {
HostnameError,
NetworkError,
ProtocolError,
SystemError,
Canceled
};
enum ErrorType { HostnameError, NetworkError, ProtocolError, SystemError, Canceled };
BNetworkRequestError(const char* origin, ErrorType type,
status_t errorCode, const BString& customMessage = BString());
BNetworkRequestError(const char* origin, ErrorType type,
const BString& customMessage = BString());
BNetworkRequestError(const char* origin, ErrorType type,
status_t errorCode, const BString& customMessage = BString());
BNetworkRequestError(const char* origin, ErrorType type,
const BString& customMessage = BString());
virtual const char* Message() const noexcept override;
virtual BString DebugMessage() const override;
virtual const char* Message() const noexcept override;
virtual BString DebugMessage() const override;
ErrorType Type() const noexcept;
status_t ErrorCode() const noexcept;
ErrorType Type() const noexcept;
status_t ErrorCode() const noexcept;
const char* CustomMessage() const noexcept;
const char* CustomMessage() const noexcept;
private:
ErrorType fErrorType;
status_t fErrorCode = B_OK;
BString fCustomMessage;
ErrorType fErrorType;
status_t fErrorCode = B_OK;
BString fCustomMessage;
};
@ -84,37 +81,33 @@ BString encode_to_base64(const BString& string);
namespace UrlEvent {
enum {
HostNameResolved = '_NHR',
ConnectionOpened = '_NCO',
UploadProgress = '_NUP',
ResponseStarted = '_NRS',
DownloadProgress = '_NDP',
BytesWritten = '_NBW',
RequestCompleted = '_NRC',
DebugMessage = '_NDB'
};
enum {
HostNameResolved = '_NHR',
ConnectionOpened = '_NCO',
UploadProgress = '_NUP',
ResponseStarted = '_NRS',
DownloadProgress = '_NDP',
BytesWritten = '_NBW',
RequestCompleted = '_NRC',
DebugMessage = '_NDB'
};
}
namespace UrlEventData {
extern const char* Id;
extern const char* HostName;
extern const char* NumBytes;
extern const char* TotalBytes;
extern const char* Success;
extern const char* DebugType;
extern const char* DebugMessage;
extern const char* Id;
extern const char* HostName;
extern const char* NumBytes;
extern const char* TotalBytes;
extern const char* Success;
extern const char* DebugType;
extern const char* DebugMessage;
enum {
DebugInfo = '_DBI',
DebugWarning = '_DBW',
DebugError = '_DBE'
};
}
enum { DebugInfo = '_DBI', DebugWarning = '_DBW', DebugError = '_DBE' };
} // namespace UrlEventData
}
} // namespace Network
}
} // namespace BPrivate
#endif

View File

@ -18,16 +18,16 @@ using namespace BPrivate::Network;
BError::BError(const char* origin)
: fOrigin(BString(origin))
:
fOrigin(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::operator=(const BError& error) = default;
BError& BError::operator=(const BError& error) = default;
BError&
BError::operator=(BError&& error) noexcept = default;
BError& BError::operator=(BError&& error) noexcept = default;
const char*
@ -76,18 +74,35 @@ BError::WriteToOutput(BDataIO* output) const
{
std::stringstream stream;
WriteToStream(stream);
ssize_t result
= output->Write(stream.str().c_str(), stream.str().length() + 1);
ssize_t result = output->Write(stream.str().c_str(), stream.str().length() + 1);
if (result < 0)
throw BSystemError("BDataIO::Write()", result);
return static_cast<size_t>(result);
}
void BError::_ReservedError1() {}
void BError::_ReservedError2() {}
void BError::_ReservedError3() {}
void BError::_ReservedError4() {}
void
BError::_ReservedError1()
{
}
void
BError::_ReservedError2()
{
}
void
BError::_ReservedError3()
{
}
void
BError::_ReservedError4()
{
}
/* BRuntimeError */
@ -96,7 +111,6 @@ BRuntimeError::BRuntimeError(const char* origin, const char* message)
BError(origin),
fMessage(BString(message))
{
}
@ -105,7 +119,6 @@ BRuntimeError::BRuntimeError(const char* origin, BString message)
BError(origin),
fMessage(std::move(message))
{
}
@ -114,7 +127,6 @@ BRuntimeError::BRuntimeError(BString origin, BString message)
BError(std::move(origin)),
fMessage(std::move(message))
{
}
@ -124,12 +136,10 @@ BRuntimeError::BRuntimeError(const BRuntimeError& other) = default;
BRuntimeError::BRuntimeError(BRuntimeError&& other) noexcept = default;
BRuntimeError&
BRuntimeError::operator=(const BRuntimeError& other) = default;
BRuntimeError& BRuntimeError::operator=(const BRuntimeError& other) = default;
BRuntimeError&
BRuntimeError::operator=(BRuntimeError&& other) noexcept = default;
BRuntimeError& BRuntimeError::operator=(BRuntimeError&& other) noexcept = default;
const char*
@ -145,7 +155,6 @@ BSystemError::BSystemError(const char* origin, status_t error)
BError(origin),
fErrorCode(error)
{
}
@ -154,7 +163,6 @@ BSystemError::BSystemError(BString origin, status_t error)
BError(std::move(origin)),
fErrorCode(error)
{
}
@ -164,12 +172,10 @@ BSystemError::BSystemError(const BSystemError& other) = default;
BSystemError::BSystemError(BSystemError&& other) noexcept = default;
BSystemError&
BSystemError::operator=(const BSystemError& other) = default;
BSystemError& BSystemError::operator=(const BSystemError& other) = default;
BSystemError&
BSystemError::operator=(BSystemError&& other) noexcept = default;
BSystemError& BSystemError::operator=(BSystemError&& other) noexcept = default;
const char*
@ -183,8 +189,7 @@ BString
BSystemError::DebugMessage() const
{
BString debugMessage;
debugMessage << "[" << Origin() << "] " << Message() << " (" << fErrorCode
<< ")";
debugMessage << "[" << Origin() << "] " << Message() << " (" << fErrorCode << ")";
return debugMessage;
}

View File

@ -63,8 +63,8 @@ HttpBuffer::ReadFrom(BDataIO* source, std::optional<size_t> maxSize)
fBuffer.resize(currentSize);
return bytesRead;
} else if (bytesRead < 0) {
throw BNetworkRequestError("BDataIO::Read()", BNetworkRequestError::NetworkError,
bytesRead);
throw BNetworkRequestError(
"BDataIO::Read()", BNetworkRequestError::NetworkError, bytesRead);
}
// 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.
*/
size_t
HttpBuffer::WriteTo(HttpTransferFunction func , std::optional<size_t> maxSize)
HttpBuffer::WriteTo(HttpTransferFunction func, std::optional<size_t> maxSize)
{
if (RemainingBytes() == 0)
return 0;
@ -121,7 +121,8 @@ HttpBuffer::GetNextLine()
if (result == fBuffer.cend())
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;
return line;
}
@ -173,8 +174,8 @@ std::string_view
HttpBuffer::Data() const noexcept
{
if (RemainingBytes() > 0) {
return std::string_view(reinterpret_cast<const char*>(fBuffer.data()) + fCurrentOffset,
RemainingBytes());
return std::string_view(
reinterpret_cast<const char*>(fBuffer.data()) + fCurrentOffset, RemainingBytes());
} else
return std::string_view();
}

View File

@ -19,33 +19,35 @@ namespace BPrivate {
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:
HttpBuffer(size_t capacity = 8*1024);
HttpBuffer(size_t capacity = 8 * 1024);
ssize_t ReadFrom(BDataIO* source, std::optional<size_t> maxSize = std::nullopt);
size_t WriteTo(HttpTransferFunction func,
std::optional<size_t> maxSize = std::nullopt);
void WriteExactlyTo(HttpTransferFunction func,
std::optional<size_t> maxSize = std::nullopt);
std::optional<BString> GetNextLine();
ssize_t ReadFrom(BDataIO* source,
std::optional<size_t> maxSize = std::nullopt);
size_t WriteTo(HttpTransferFunction func,
std::optional<size_t> maxSize = std::nullopt);
void WriteExactlyTo(HttpTransferFunction func,
std::optional<size_t> maxSize = std::nullopt);
std::optional<BString> GetNextLine();
size_t RemainingBytes() const noexcept;
size_t RemainingBytes() const noexcept;
void Flush() noexcept;
void Clear() noexcept;
void Flush() noexcept;
void Clear() noexcept;
std::string_view Data() const noexcept;
std::string_view Data() const noexcept;
// load data into the buffer
HttpBuffer& operator<<(const std::string_view& data);
HttpBuffer& operator<<(const std::string_view& data);
private:
std::vector<std::byte> fBuffer;
size_t fCurrentOffset = 0;
std::vector<std::byte> fBuffer;
size_t fCurrentOffset = 0;
};

55
src/kits/network/libnetservices2/HttpFields.cpp Executable file → Normal file
View File

@ -52,12 +52,8 @@ validate_value_string(const std::string_view& string)
static inline bool
iequals(const std::string_view& a, const std::string_view& b)
{
return std::equal(
a.begin(), a.end(),
b.begin(), b.end(),
[](char a, char b) {
return tolower(a) == tolower(b);
});
return std::equal(a.begin(), a.end(), 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;
for (; right > left && isspace(*right); --right);
for (; right > left && isspace(*right); --right)
;
return std::string_view(left, std::distance(left, right) + 1);
}
@ -93,7 +90,6 @@ BHttpFields::InvalidInput::InvalidInput(const char* origin, BString input)
BError(origin),
input(std::move(input))
{
}
@ -117,16 +113,16 @@ BHttpFields::InvalidInput::DebugMessage() const
BHttpFields::FieldName::FieldName() noexcept
: fName(std::string_view())
:
fName(std::string_view())
{
}
BHttpFields::FieldName::FieldName(const std::string_view& name) noexcept
: fName(name)
:
fName(name)
{
}
@ -144,17 +140,18 @@ BHttpFields::FieldName::FieldName(const FieldName& other) noexcept = default;
longer be used as an entry in a BHttpFields object.
*/
BHttpFields::FieldName::FieldName(FieldName&& other) noexcept
: fName(std::move(other.fName))
:
fName(std::move(other.fName))
{
other.fName = std::string_view();
}
/*!
\brief Copy assignment;
\brief Copy assignment;
*/
BHttpFields::FieldName&
BHttpFields::FieldName::operator=(const BHttpFields::FieldName& other) noexcept = default;
BHttpFields::FieldName& BHttpFields::FieldName::operator=(
const BHttpFields::FieldName& other) noexcept = default;
/*!
@ -204,9 +201,10 @@ BHttpFields::FieldName::operator std::string_view() const
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)
: fName(std::string_view()), fValue(std::string_view())
:
fName(std::string_view()),
fValue(std::string_view())
{
if (other.IsEmpty()) {
fRawField = BString();
@ -267,7 +267,10 @@ BHttpFields::Field::Field(const BHttpFields::Field& other)
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.fValue = std::string_view();
@ -341,7 +344,6 @@ BHttpFields::Field::IsEmpty() const noexcept
BHttpFields::BHttpFields()
{
}
@ -355,7 +357,8 @@ BHttpFields::BHttpFields(const BHttpFields& other) = default;
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
// will be empty.
@ -365,19 +368,17 @@ BHttpFields::BHttpFields(BHttpFields&& other)
BHttpFields::~BHttpFields() noexcept
{
}
BHttpFields&
BHttpFields::operator=(const BHttpFields& other) = default;
BHttpFields& BHttpFields::operator=(const BHttpFields& other) = default;
BHttpFields&
BHttpFields::operator=(BHttpFields&& other) noexcept
{
fFields = std::move(other.fFields);
// Explicitly clear the other list, as the C++ standard does not specify that the other list
// will be empty.
other.fFields.clear();
@ -423,7 +424,7 @@ BHttpFields::AddFields(std::initializer_list<Field> fields)
void
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);
}
}

View File

@ -8,8 +8,8 @@
#include "HttpParser.h"
#include <string>
#include <stdexcept>
#include <string>
#include <HttpFields.h>
#include <NetServicesDefs.h>
@ -103,13 +103,13 @@ HttpParser::ParseFields(HttpBuffer& buffer, BHttpFields& fields)
auto fieldLine = buffer.GetNextLine();
while (fieldLine && !fieldLine.value().IsEmpty()){
while (fieldLine && !fieldLine.value().IsEmpty()) {
// Parse next header line
fields.AddField(fieldLine.value());
fieldLine = buffer.GetNextLine();
}
if (!fieldLine || (fieldLine && !fieldLine.value().IsEmpty())){
if (!fieldLine || (fieldLine && !fieldLine.value().IsEmpty())) {
// there is more to parse
return false;
}
@ -117,15 +117,14 @@ HttpParser::ParseFields(HttpBuffer& buffer, BHttpFields& fields)
// Determine the properties for 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;
if (fBodyType == HttpBodyType::NoContent
|| fStatus.StatusCode() == BHttpStatusCode::NoContent
if (fBodyType == HttpBodyType::NoContent || fStatus.StatusCode() == BHttpStatusCode::NoContent
|| fStatus.StatusCode() == BHttpStatusCode::NotModified) {
// [1] In case of HEAD (set previously), status codes 1xx (TODO!), status code 204 or 304, no content
// [2] NOT SUPPORTED: when doing a CONNECT request, no content
// [1] In case of HEAD (set previously), status codes 1xx (TODO!), status code 204 or 304,
// no content [2] NOT SUPPORTED: when doing a CONNECT request, no content
fBodyType = HttpBodyType::NoContent;
fStreamState = HttpInputStreamState::Done;
} else if (auto header = fields.FindField("Transfer-Encoding"sv);
header != fields.end() && header->Value() == "chunked"sv) {
header != fields.end() && header->Value() == "chunked"sv) {
// [3] If there is a Transfer-Encoding heading set to 'chunked'
// TODO: support the more advanced rules in the RFC around the meaning of this field
fBodyType = HttpBodyType::Chunked;
@ -157,8 +156,7 @@ HttpParser::ParseFields(HttpBuffer& buffer, BHttpFields& fields)
fStreamState = HttpInputStreamState::Body;
}
} catch (const std::logic_error& e) {
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError,
throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::ProtocolError,
"Cannot parse Content-Length field value (logic_error)");
}
} else {
@ -186,9 +184,7 @@ HttpParser::ParseFields(HttpBuffer& buffer, BHttpFields& fields)
// Check Content-Encoding for compression
auto header = fields.FindField("Content-Encoding"sv);
if (header != fields.end()
&& (header->Value() == "gzip" || header->Value() == "deflate"))
{
if (header != fields.end() && (header->Value() == "gzip" || header->Value() == "deflate")) {
fBodyParser = std::make_unique<HttpBodyDecompression>(std::move(fBodyParser));
}
@ -295,7 +291,6 @@ HttpBodyParser::TransferredBodySize() const noexcept
*/
HttpRawBodyParser::HttpRawBodyParser()
{
}
@ -306,7 +301,6 @@ HttpRawBodyParser::HttpRawBodyParser(off_t bodyBytesTotal)
:
fBodyBytesTotal(bodyBytesTotal)
{
}
@ -397,95 +391,96 @@ HttpChunkedBodyParser::ParseBody(HttpBuffer& buffer, HttpTransferFunction writeT
size_t totalBytesRead = 0;
while (buffer.RemainingBytes() > 0) {
switch (fChunkParserState) {
case ChunkSize:
{
// Read the next chunk size from the buffer; if unsuccesful wait for more data
auto chunkSizeString = buffer.GetNextLine();
if (!chunkSizeString)
return {totalBytesRead, totalBytesRead, false};
auto chunkSizeStr = std::string(chunkSizeString.value().String());
try {
size_t pos = 0;
fRemainingChunkSize = std::stoll(chunkSizeStr, &pos, 16);
if (pos < chunkSizeStr.size() && chunkSizeStr[pos] != ';'){
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError);
case ChunkSize:
{
// Read the next chunk size from the buffer; if unsuccesful wait for more data
auto chunkSizeString = buffer.GetNextLine();
if (!chunkSizeString)
return {totalBytesRead, totalBytesRead, false};
auto chunkSizeStr = std::string(chunkSizeString.value().String());
try {
size_t pos = 0;
fRemainingChunkSize = std::stoll(chunkSizeStr, &pos, 16);
if (pos < chunkSizeStr.size() && chunkSizeStr[pos] != ';') {
throw BNetworkRequestError(
__PRETTY_FUNCTION__, BNetworkRequestError::ProtocolError);
}
} catch (const std::invalid_argument&) {
throw BNetworkRequestError(
__PRETTY_FUNCTION__, BNetworkRequestError::ProtocolError);
} catch (const std::out_of_range&) {
throw BNetworkRequestError(
__PRETTY_FUNCTION__, BNetworkRequestError::ProtocolError);
}
} catch (const std::invalid_argument&) {
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError);
} catch (const std::out_of_range&) {
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError);
if (fRemainingChunkSize > 0)
fChunkParserState = Chunk;
else
fChunkParserState = Trailers;
break;
}
if (fRemainingChunkSize > 0)
fChunkParserState = Chunk;
else
fChunkParserState = Trailers;
break;
}
case Chunk:
{
size_t bytesToRead;
if (fRemainingChunkSize > static_cast<off_t>(buffer.RemainingBytes()))
bytesToRead = buffer.RemainingBytes();
else
bytesToRead = fRemainingChunkSize;
case Chunk:
{
size_t bytesToRead;
if (fRemainingChunkSize > static_cast<off_t>(buffer.RemainingBytes()))
bytesToRead = buffer.RemainingBytes();
else
bytesToRead = fRemainingChunkSize;
auto bytesRead = buffer.WriteTo(writeToBody, bytesToRead);
if (bytesRead != bytesToRead) {
// Fail if not all expected bytes are written.
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::SystemError,
"Could not write all available body bytes to the target.");
}
auto bytesRead = buffer.WriteTo(writeToBody, bytesToRead);
if (bytesRead != bytesToRead) {
// Fail if not all expected bytes are written.
throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::SystemError,
"Could not write all available body bytes to the target.");
fTransferredBodySize += bytesRead;
totalBytesRead += bytesRead;
fRemainingChunkSize -= bytesRead;
if (fRemainingChunkSize == 0)
fChunkParserState = ChunkEnd;
break;
}
fTransferredBodySize += bytesRead;
totalBytesRead += bytesRead;
fRemainingChunkSize -= bytesRead;
if (fRemainingChunkSize == 0)
fChunkParserState = ChunkEnd;
break;
}
case ChunkEnd:
{
if (buffer.RemainingBytes() < 2) {
// not enough data in the buffer to finish the chunk
return {totalBytesRead, totalBytesRead, false};
}
auto chunkEndString = buffer.GetNextLine();
if (!chunkEndString || chunkEndString.value().Length() != 0) {
// There should have been an empty chunk
throw BNetworkRequestError(
__PRETTY_FUNCTION__, BNetworkRequestError::ProtocolError);
}
case ChunkEnd:
{
if (buffer.RemainingBytes() < 2) {
// not enough data in the buffer to finish the chunk
return {totalBytesRead, totalBytesRead, false};
}
auto chunkEndString = buffer.GetNextLine();
if (!chunkEndString || chunkEndString.value().Length() != 0) {
// There should have been an empty chunk
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError);
fChunkParserState = ChunkSize;
break;
}
fChunkParserState = ChunkSize;
break;
}
case Trailers:
{
auto trailerString = buffer.GetNextLine();
if (!trailerString) {
// More data to come
return {totalBytesRead, totalBytesRead, false};
}
case Trailers:
{
auto trailerString = buffer.GetNextLine();
if (!trailerString) {
// More data to come
return {totalBytesRead, totalBytesRead, false};
if (trailerString.value().Length() > 0) {
// Ignore empty trailers for now
// TODO: review if the API should support trailing headers
} else {
fChunkParserState = Complete;
return {totalBytesRead, totalBytesRead, true};
}
break;
}
if (trailerString.value().Length() > 0) {
// Ignore empty trailers for now
// TODO: review if the API should support trailing headers
} else {
fChunkParserState = Complete;
case Complete:
return {totalBytesRead, totalBytesRead, true};
}
break;
}
case Complete:
return {totalBytesRead, totalBytesRead, true};
}
}
return {totalBytesRead, totalBytesRead, false};
@ -501,12 +496,11 @@ HttpBodyDecompression::HttpBodyDecompression(std::unique_ptr<HttpBodyParser> bod
fDecompressorStorage = std::make_unique<BMallocIO>();
BDataIO* stream = nullptr;
auto result = BZlibCompressionAlgorithm()
.CreateDecompressingOutputStream(fDecompressorStorage.get(), nullptr, stream);
auto result = BZlibCompressionAlgorithm().CreateDecompressingOutputStream(
fDecompressorStorage.get(), nullptr, stream);
if (result != B_OK) {
throw BNetworkRequestError(
"BZlibCompressionAlgorithm().CreateCompressingOutputStream",
throw BNetworkRequestError("BZlibCompressionAlgorithm().CreateCompressingOutputStream",
BNetworkRequestError::SystemError, result);
}
@ -537,30 +531,34 @@ BodyParseResult
HttpBodyDecompression::ParseBody(HttpBuffer& buffer, HttpTransferFunction writeToBody, bool readEnd)
{
// 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 status = fDecompressingStream->WriteExactly(buffer, bufferSize);
if (status != B_OK) {
throw BNetworkRequestError("BDataIO::WriteExactly()",
BNetworkRequestError::SystemError, status);
}
return bufferSize;
}, readEnd);
auto parseResults = fBodyParser->ParseBody(
buffer,
[this](const std::byte* buffer, size_t bufferSize) {
auto status = fDecompressingStream->WriteExactly(buffer, bufferSize);
if (status != B_OK) {
throw BNetworkRequestError(
"BDataIO::WriteExactly()", BNetworkRequestError::SystemError, status);
}
return bufferSize;
},
readEnd);
fTransferredBodySize += parseResults.bytesParsed;
if (readEnd || parseResults.complete) {
// No more bytes expected so flush out the final bytes
if (auto status = fDecompressingStream->Flush(); status != B_OK) {
throw BNetworkRequestError("BZlibDecompressionStream::Flush()",
BNetworkRequestError::SystemError, status);
throw BNetworkRequestError(
"BZlibDecompressionStream::Flush()", BNetworkRequestError::SystemError, status);
}
}
size_t bytesWritten = 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) {
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::SystemError, B_PARTIAL_WRITE);
throw BNetworkRequestError(
__PRETTY_FUNCTION__, BNetworkRequestError::SystemError, B_PARTIAL_WRITE);
}
fDecompressorStorage->Seek(0, SEEK_SET);
}

View File

@ -20,124 +20,112 @@ namespace BPrivate {
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 {
StatusLine,
Fields,
Body,
Done
};
enum class HttpInputStreamState { StatusLine, Fields, Body, Done };
enum class HttpBodyType {
NoContent,
Chunked,
FixedSize,
VariableSize
};
enum class HttpBodyType { NoContent, Chunked, FixedSize, VariableSize };
struct BodyParseResult {
size_t bytesParsed;
size_t bytesWritten;
bool complete;
size_t bytesParsed;
size_t bytesWritten;
bool complete;
};
class HttpBodyParser;
class HttpParser {
class HttpParser
{
public:
HttpParser() {};
HttpParser(){};
// Explicitly mark request as having no content
void SetNoContent() noexcept;
void SetNoContent() noexcept;
// Parse data from response
bool ParseStatus(HttpBuffer& buffer, BHttpStatus& status);
bool ParseFields(HttpBuffer& buffer, BHttpFields& fields);
size_t ParseBody(HttpBuffer& buffer, HttpTransferFunction writeToBody,
bool readEnd);
HttpInputStreamState State() const noexcept { return fStreamState; }
bool ParseStatus(HttpBuffer& buffer, BHttpStatus& status);
bool ParseFields(HttpBuffer& buffer, BHttpFields& fields);
size_t ParseBody(HttpBuffer& buffer, HttpTransferFunction writeToBody,
bool readEnd);
HttpInputStreamState State() const noexcept { return fStreamState; }
// Details on the body status
bool HasContent() const noexcept;
std::optional<off_t> BodyBytesTotal() const noexcept;
off_t BodyBytesTransferred() const noexcept;
bool Complete() const noexcept;
bool HasContent() const noexcept;
std::optional<off_t> BodyBytesTotal() const noexcept;
off_t BodyBytesTransferred() const noexcept;
bool Complete() const noexcept;
private:
off_t fHeaderBytes = 0;
BHttpStatus fStatus;
HttpInputStreamState fStreamState = HttpInputStreamState::StatusLine;
off_t fHeaderBytes = 0;
BHttpStatus fStatus;
HttpInputStreamState fStreamState = HttpInputStreamState::StatusLine;
// Body
HttpBodyType fBodyType = HttpBodyType::VariableSize;
std::unique_ptr<HttpBodyParser> fBodyParser = nullptr;
HttpBodyType fBodyType = HttpBodyType::VariableSize;
std::unique_ptr<HttpBodyParser> fBodyParser = nullptr;
};
class HttpBodyParser {
class HttpBodyParser
{
public:
virtual BodyParseResult ParseBody(HttpBuffer& buffer,
HttpTransferFunction writeToBody, bool readEnd) = 0;
virtual BodyParseResult ParseBody(HttpBuffer& buffer,
HttpTransferFunction writeToBody, bool readEnd) = 0;
virtual std::optional<off_t> TotalBodySize() const noexcept;
virtual std::optional<off_t> TotalBodySize() const noexcept;
off_t TransferredBodySize() const noexcept;
off_t TransferredBodySize() const noexcept;
protected:
off_t fTransferredBodySize = 0;
off_t fTransferredBodySize = 0;
};
class HttpRawBodyParser : public HttpBodyParser {
class HttpRawBodyParser : public HttpBodyParser
{
public:
HttpRawBodyParser();
HttpRawBodyParser(off_t bodyBytesTotal);
virtual BodyParseResult ParseBody(HttpBuffer& buffer,
HttpTransferFunction writeToBody, bool readEnd) override;
virtual std::optional<off_t> TotalBodySize() const noexcept override;
HttpRawBodyParser();
HttpRawBodyParser(off_t bodyBytesTotal);
virtual BodyParseResult ParseBody(HttpBuffer& buffer, HttpTransferFunction writeToBody,
bool readEnd) override;
virtual std::optional<off_t> TotalBodySize() const noexcept override;
private:
std::optional<off_t> fBodyBytesTotal;
};
class HttpChunkedBodyParser : public HttpBodyParser {
class HttpChunkedBodyParser : public HttpBodyParser
{
public:
virtual BodyParseResult ParseBody(HttpBuffer& buffer,
HttpTransferFunction writeToBody, bool readEnd) override;
virtual BodyParseResult ParseBody(
HttpBuffer& buffer, HttpTransferFunction writeToBody, bool readEnd) override;
private:
enum {
ChunkSize,
ChunkEnd,
Chunk,
Trailers,
Complete
} fChunkParserState = ChunkSize;
off_t fRemainingChunkSize = 0;
bool fLastChunk = false;
enum { ChunkSize, ChunkEnd, Chunk, Trailers, Complete } fChunkParserState = ChunkSize;
off_t fRemainingChunkSize = 0;
bool fLastChunk = false;
};
class HttpBodyDecompression : public HttpBodyParser {
class HttpBodyDecompression : public HttpBodyParser
{
public:
HttpBodyDecompression(
std::unique_ptr<HttpBodyParser> bodyParser);
virtual BodyParseResult ParseBody(HttpBuffer& buffer,
HttpTransferFunction writeToBody, bool readEnd) override;
HttpBodyDecompression(std::unique_ptr<HttpBodyParser> bodyParser);
virtual BodyParseResult ParseBody(HttpBuffer& buffer, HttpTransferFunction writeToBody,
bool readEnd) override;
virtual std::optional<off_t> TotalBodySize() const noexcept;
virtual std::optional<off_t> TotalBodySize() const noexcept;
private:
std::unique_ptr<HttpBodyParser> fBodyParser;
std::unique_ptr<BMallocIO> fDecompressorStorage;
std::unique_ptr<BDataIO> fDecompressingStream;
std::unique_ptr<HttpBodyParser> fBodyParser;
std::unique_ptr<BMallocIO> fDecompressorStorage;
std::unique_ptr<BDataIO> fDecompressingStream;
};

View File

@ -29,9 +29,9 @@ validate_http_token_string(const std::string_view& string)
{
for (auto it = string.cbegin(); it < string.cend(); 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 true;

View File

@ -34,7 +34,6 @@ BHttpMethod::InvalidMethod::InvalidMethod(const char* origin, BString input)
BError(origin),
input(std::move(input))
{
}
@ -62,17 +61,19 @@ BHttpMethod::InvalidMethod::DebugMessage() const
BHttpMethod::BHttpMethod(Verb verb) noexcept
: fMethod(verb)
:
fMethod(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))
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
: fMethod(std::move(other.fMethod))
:
fMethod(std::move(other.fMethod))
{
other.fMethod = Get;
}
@ -89,8 +91,7 @@ BHttpMethod::BHttpMethod(BHttpMethod&& other) noexcept
BHttpMethod::~BHttpMethod() = default;
BHttpMethod&
BHttpMethod::operator=(const BHttpMethod& other) = default;
BHttpMethod& BHttpMethod::operator=(const BHttpMethod& other) = default;
BHttpMethod&
@ -127,25 +128,25 @@ BHttpMethod::Method() const noexcept
{
if (std::holds_alternative<Verb>(fMethod)) {
switch (std::get<Verb>(fMethod)) {
case Get:
return "GET"sv;
case Head:
return "HEAD"sv;
case Post:
return "POST"sv;
case Put:
return "PUT"sv;
case Delete:
return "DELETE"sv;
case Connect:
return "CONNECT"sv;
case Options:
return "OPTIONS"sv;
case Trace:
return "TRACE"sv;
default:
// should never be reached
std::abort();
case Get:
return "GET"sv;
case Head:
return "HEAD"sv;
case Post:
return "POST"sv;
case Put:
return "PUT"sv;
case Delete:
return "DELETE"sv;
case Connect:
return "CONNECT"sv;
case Options:
return "OPTIONS"sv;
case Trace:
return "TRACE"sv;
default:
// should never be reached
std::abort();
}
} else {
const auto& methodString = std::get<BString>(fMethod);
@ -161,14 +162,14 @@ static const BHttpMethod kDefaultMethod = BHttpMethod::Get;
static const BHttpFields kDefaultOptionalFields = BHttpFields();
struct BHttpRequest::Data {
BUrl url = kDefaultUrl;
BHttpMethod method = kDefaultMethod;
uint8 maxRedirections = 8;
BHttpFields optionalFields;
std::optional<BHttpAuthentication> authentication;
bool stopOnError = false;
bigtime_t timeout = B_INFINITE_TIMEOUT;
std::optional<Body> requestBody;
BUrl url = kDefaultUrl;
BHttpMethod method = kDefaultMethod;
uint8 maxRedirections = 8;
BHttpFields optionalFields;
std::optional<BHttpAuthentication> authentication;
bool stopOnError = false;
bigtime_t timeout = B_INFINITE_TIMEOUT;
std::optional<Body> requestBody;
};
@ -192,14 +193,15 @@ build_basic_http_header(const BString& username, const BString& password)
BHttpRequest::BHttpRequest()
: fData(std::make_unique<Data>())
:
fData(std::make_unique<Data>())
{
}
BHttpRequest::BHttpRequest(const BUrl& url)
: fData(std::make_unique<Data>())
:
fData(std::make_unique<Data>())
{
SetUrl(url);
}
@ -211,8 +213,7 @@ BHttpRequest::BHttpRequest(BHttpRequest&& other) noexcept = default;
BHttpRequest::~BHttpRequest() = default;
BHttpRequest&
BHttpRequest::operator=(BHttpRequest&&) noexcept = default;
BHttpRequest& BHttpRequest::operator=(BHttpRequest&&) noexcept = default;
bool
@ -304,13 +305,8 @@ BHttpRequest::SetAuthentication(const BHttpAuthentication& authentication)
}
static constexpr std::array<std::string_view, 6> fReservedOptionalFieldNames = {
"Host"sv,
"Accept-Encoding"sv,
"Connection"sv,
"Content-Type"sv,
"Content-Length"sv
};
static constexpr std::array<std::string_view, 6> fReservedOptionalFieldNames
= {"Host"sv, "Accept-Encoding"sv, "Connection"sv, "Content-Type"sv, "Content-Length"sv};
void
@ -321,10 +317,11 @@ BHttpRequest::SetFields(const BHttpFields& fields)
for (auto& field: fields) {
if (std::find(fReservedOptionalFieldNames.begin(), fReservedOptionalFieldNames.end(),
field.Name()) != fReservedOptionalFieldNames.end())
{
field.Name())
!= fReservedOptionalFieldNames.end()) {
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;
@ -350,8 +347,8 @@ BHttpRequest::SetMethod(const BHttpMethod& method)
void
BHttpRequest::SetRequestBody(std::unique_ptr<BDataIO> input, BString mimeType,
std::optional<off_t> size)
BHttpRequest::SetRequestBody(
std::unique_ptr<BDataIO> input, BString mimeType, std::optional<off_t> size)
{
if (input == nullptr)
throw std::invalid_argument("input cannot be null");
@ -490,32 +487,33 @@ BHttpRequest::SerializeHeaderTo(HttpBuffer& buffer) const
host << ':' << fData->url.Port();
outputFields.AddFields({
{"Host"sv, std::string_view(host.String())},
{"Accept-Encoding"sv, "gzip"sv},
// Allows the server to compress data using the "gzip" format.
// "deflate" is not supported, because there are two interpretations
// of what it means (the RFC and Microsoft products), and we don't
// want to handle this. Very few websites support only deflate,
// and most of them will send gzip, or at worst, uncompressed data.
{"Host"sv, std::string_view(host.String())}, {"Accept-Encoding"sv, "gzip"sv},
// Allows the server to compress data using the "gzip" format.
// "deflate" is not supported, because there are two interpretations
// of what it means (the RFC and Microsoft products), and we don't
// want to handle this. Very few websites support only deflate,
// and most of them will send gzip, or at worst, uncompressed data.
{"Connection"sv, "close"sv}
// Let the remote server close the connection after response since
// we don't handle multiple request on a single connection
// Let the remote server close the connection after response since
// we don't handle multiple request on a single connection
});
}
if (fData->authentication) {
// This request will add a Basic authorization header
BString authorization = build_basic_http_header(fData->authentication->username,
fData->authentication->password);
BString authorization = build_basic_http_header(
fData->authentication->username, fData->authentication->password);
outputFields.AddField("Authorization"sv, std::string_view(authorization.String()));
}
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)
outputFields.AddField("Content-Length"sv, std::to_string(*fData->requestBody->size));
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)

View File

@ -23,13 +23,18 @@ BHttpStatusClass
BHttpStatus::StatusClass() const noexcept
{
switch (code / 100) {
case 1: return BHttpStatusClass::Informational;
case 2: return BHttpStatusClass::Success;
case 3: return BHttpStatusClass::Redirection;
case 4: return BHttpStatusClass::ClientError;
case 5: return BHttpStatusClass::ServerError;
default:
break;
case 1:
return BHttpStatusClass::Informational;
case 2:
return BHttpStatusClass::Success;
case 3:
return BHttpStatusClass::Redirection;
case 4:
return BHttpStatusClass::ClientError;
case 5:
return BHttpStatusClass::ServerError;
default:
break;
}
return BHttpStatusClass::Invalid;
}
@ -39,59 +44,98 @@ BHttpStatusCode
BHttpStatus::StatusCode() const noexcept
{
switch (static_cast<BHttpStatusCode>(code)) {
// 1xx
case BHttpStatusCode::Continue: [[fallthrough]];
case BHttpStatusCode::SwitchingProtocols: [[fallthrough]];
// 1xx
case BHttpStatusCode::Continue:
[[fallthrough]];
case BHttpStatusCode::SwitchingProtocols:
[[fallthrough]];
// 2xx
case BHttpStatusCode::Ok: [[fallthrough]];
case BHttpStatusCode::Created: [[fallthrough]];
case BHttpStatusCode::Accepted: [[fallthrough]];
case BHttpStatusCode::NonAuthoritativeInformation: [[fallthrough]];
case BHttpStatusCode::NoContent: [[fallthrough]];
case BHttpStatusCode::ResetContent: [[fallthrough]];
case BHttpStatusCode::PartialContent: [[fallthrough]];
// 2xx
case BHttpStatusCode::Ok:
[[fallthrough]];
case BHttpStatusCode::Created:
[[fallthrough]];
case BHttpStatusCode::Accepted:
[[fallthrough]];
case BHttpStatusCode::NonAuthoritativeInformation:
[[fallthrough]];
case BHttpStatusCode::NoContent:
[[fallthrough]];
case BHttpStatusCode::ResetContent:
[[fallthrough]];
case BHttpStatusCode::PartialContent:
[[fallthrough]];
// 3xx
case BHttpStatusCode::MultipleChoice: [[fallthrough]];
case BHttpStatusCode::MovedPermanently: [[fallthrough]];
case BHttpStatusCode::Found: [[fallthrough]];
case BHttpStatusCode::SeeOther: [[fallthrough]];
case BHttpStatusCode::NotModified: [[fallthrough]];
case BHttpStatusCode::UseProxy: [[fallthrough]];
case BHttpStatusCode::TemporaryRedirect: [[fallthrough]];
case BHttpStatusCode::PermanentRedirect: [[fallthrough]];
// 3xx
case BHttpStatusCode::MultipleChoice:
[[fallthrough]];
case BHttpStatusCode::MovedPermanently:
[[fallthrough]];
case BHttpStatusCode::Found:
[[fallthrough]];
case BHttpStatusCode::SeeOther:
[[fallthrough]];
case BHttpStatusCode::NotModified:
[[fallthrough]];
case BHttpStatusCode::UseProxy:
[[fallthrough]];
case BHttpStatusCode::TemporaryRedirect:
[[fallthrough]];
case BHttpStatusCode::PermanentRedirect:
[[fallthrough]];
// 4xx
case BHttpStatusCode::BadRequest: [[fallthrough]];
case BHttpStatusCode::Unauthorized: [[fallthrough]];
case BHttpStatusCode::PaymentRequired: [[fallthrough]];
case BHttpStatusCode::Forbidden: [[fallthrough]];
case BHttpStatusCode::NotFound: [[fallthrough]];
case BHttpStatusCode::MethodNotAllowed: [[fallthrough]];
case BHttpStatusCode::NotAcceptable: [[fallthrough]];
case BHttpStatusCode::ProxyAuthenticationRequired: [[fallthrough]];
case BHttpStatusCode::RequestTimeout: [[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]];
// 4xx
case BHttpStatusCode::BadRequest:
[[fallthrough]];
case BHttpStatusCode::Unauthorized:
[[fallthrough]];
case BHttpStatusCode::PaymentRequired:
[[fallthrough]];
case BHttpStatusCode::Forbidden:
[[fallthrough]];
case BHttpStatusCode::NotFound:
[[fallthrough]];
case BHttpStatusCode::MethodNotAllowed:
[[fallthrough]];
case BHttpStatusCode::NotAcceptable:
[[fallthrough]];
case BHttpStatusCode::ProxyAuthenticationRequired:
[[fallthrough]];
case BHttpStatusCode::RequestTimeout:
[[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
case BHttpStatusCode::InternalServerError: [[fallthrough]];
case BHttpStatusCode::NotImplemented: [[fallthrough]];
case BHttpStatusCode::BadGateway: [[fallthrough]];
case BHttpStatusCode::ServiceUnavailable: [[fallthrough]];
case BHttpStatusCode::GatewayTimeout:
return static_cast<BHttpStatusCode>(code);
// 5xx
case BHttpStatusCode::InternalServerError:
[[fallthrough]];
case BHttpStatusCode::NotImplemented:
[[fallthrough]];
case BHttpStatusCode::BadGateway:
[[fallthrough]];
case BHttpStatusCode::ServiceUnavailable:
[[fallthrough]];
case BHttpStatusCode::GatewayTimeout:
return static_cast<BHttpStatusCode>(code);
default:
break;
default:
break;
}
return BHttpStatusCode::Unknown;
@ -103,9 +147,9 @@ BHttpStatus::StatusCode() const noexcept
/*private*/
BHttpResult::BHttpResult(std::shared_ptr<HttpResultPrivate> data)
: fData(data)
:
fData(data)
{
}
@ -119,8 +163,7 @@ BHttpResult::~BHttpResult()
}
BHttpResult&
BHttpResult::operator=(BHttpResult&& other) noexcept = default;
BHttpResult& BHttpResult::operator=(BHttpResult&& other) noexcept = default;
const BHttpStatus&

View File

@ -16,7 +16,7 @@
#include <DataIO.h>
#include <ExclusiveBorrow.h>
#include <OS.h>
#include <OS.h>
#include <String.h>
@ -26,46 +26,40 @@ namespace Network {
struct HttpResultPrivate {
// Read-only properties (multi-thread safe)
const int32 id;
const int32 id;
// Locking and atomic variables
sem_id data_wait;
enum {
kNoData = 0,
kStatusReady,
kHeadersReady,
kBodyReady,
kError
};
int32 requestStatus = kNoData;
int32 canCancel = 0;
enum { kNoData = 0, kStatusReady, kHeadersReady, kBodyReady, kError };
int32 requestStatus = kNoData;
int32 canCancel = 0;
sem_id data_wait;
// Data
std::optional<BHttpStatus> status;
std::optional<BHttpFields> fields;
std::optional<BHttpBody> body;
std::optional<std::exception_ptr> error;
std::optional<BHttpStatus> status;
std::optional<BHttpFields> fields;
std::optional<BHttpBody> body;
std::optional<std::exception_ptr> error;
// Interim body storage (used while the request is running)
BString bodyString;
BBorrow<BDataIO> bodyTarget;
BString bodyString;
BBorrow<BDataIO> bodyTarget;
// Utility functions
HttpResultPrivate(int32 identifier);
int32 GetStatusAtomic();
bool CanCancel();
void SetCancel();
void SetError(std::exception_ptr e);
void SetStatus(BHttpStatus&& s);
void SetFields(BHttpFields&& f);
void SetBody();
size_t WriteToBody(const void* buffer, size_t size);
HttpResultPrivate(int32 identifier);
int32 GetStatusAtomic();
bool CanCancel();
void SetCancel();
void SetError(std::exception_ptr e);
void SetStatus(BHttpStatus&& s);
void SetFields(BHttpFields&& f);
void SetBody();
size_t WriteToBody(const void* buffer, size_t size);
};
inline
HttpResultPrivate::HttpResultPrivate(int32 identifier)
: id(identifier)
inline HttpResultPrivate::HttpResultPrivate(int32 identifier)
:
id(identifier)
{
std::string name = "httpresult:" + std::to_string(identifier);
data_wait = create_sem(1, name.c_str());

View File

@ -63,8 +63,9 @@ HttpSerializer::Serialize(HttpBuffer& buffer, BDataIO* target)
fState = HttpSerializerState::Done;
return 0;
} else if (_IsChunked())
//fState = HttpSerializerState::ChunkHeader;
throw BRuntimeError(__PRETTY_FUNCTION__, "Chunked serialization not implemented");
// fState = HttpSerializerState::ChunkHeader;
throw BRuntimeError(
__PRETTY_FUNCTION__, "Chunked serialization not implemented");
else
fState = HttpSerializerState::Body;
break;
@ -75,7 +76,8 @@ HttpSerializer::Serialize(HttpBuffer& buffer, BDataIO* target)
bodyBytesWritten += bytesWritten;
fTransferredBodySize += bytesWritten;
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;
break;
}
@ -115,15 +117,15 @@ size_t
HttpSerializer::_WriteToTarget(HttpBuffer& buffer, BDataIO* target) const
{
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;
while (result == B_INTERRUPTED) {
result = target->Write(buffer, size);
}
if (result <= 0 && result != B_WOULD_BLOCK) {
throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::NetworkError,
result);
throw BNetworkRequestError(
__PRETTY_FUNCTION__, BNetworkRequestError::NetworkError, result);
} else if (result > 0) {
bytesWritten += result;
return size_t(result);

View File

@ -19,43 +19,66 @@ namespace Network {
class BHttpRequest;
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 {
Uninitialized,
Header,
ChunkHeader,
Body,
Done
};
enum class HttpSerializerState { Uninitialized, Header, ChunkHeader, Body, Done };
class HttpSerializer {
class HttpSerializer
{
public:
HttpSerializer() {};
HttpSerializer(){};
void SetTo(HttpBuffer& buffer, const BHttpRequest& request);
bool IsInitialized() const noexcept { return fState != HttpSerializerState::Uninitialized; }
void SetTo(HttpBuffer& buffer, const BHttpRequest& request);
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; };
off_t BodyBytesTransferred() const noexcept { return fTransferredBodySize; };
bool Complete() const noexcept { return fState == HttpSerializerState::Done; };
std::optional<off_t> BodyBytesTotal() const noexcept;
off_t BodyBytesTransferred() const noexcept;
bool Complete() const noexcept;
private:
bool _IsChunked() const noexcept;
size_t _WriteToTarget(HttpBuffer& buffer, BDataIO* target) const;
bool _IsChunked() const noexcept;
size_t _WriteToTarget(HttpBuffer& buffer, BDataIO* target) const;
private:
HttpSerializerState fState = HttpSerializerState::Uninitialized;
BDataIO* fBody = nullptr;
off_t fTransferredBodySize = 0;
std::optional<off_t> fBodySize;
HttpSerializerState fState = HttpSerializerState::Uninitialized;
BDataIO* fBody = nullptr;
off_t fTransferredBodySize = 0;
std::optional<off_t> fBodySize;
};
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 BPrivate

View File

@ -33,8 +33,8 @@
#include "HttpBuffer.h"
#include "HttpParser.h"
#include "HttpSerializer.h"
#include "HttpResultPrivate.h"
#include "HttpSerializer.h"
#include "NetServicesPrivate.h"
using namespace std::literals;
@ -51,140 +51,128 @@ static constexpr ssize_t kMaxHeaderLineSize = 64 * 1024;
struct CounterDeleter {
void operator()(int32* counter) const noexcept
{
atomic_add(counter, -1);
}
void operator()(int32* counter) const noexcept { atomic_add(counter, -1); }
};
class BHttpSession::Request {
class BHttpSession::Request
{
public:
Request(BHttpRequest&& request,
BBorrow<BDataIO> target,
BMessenger observer);
Request(BHttpRequest&& request, BBorrow<BDataIO> target, BMessenger observer);
Request(Request& original, const Redirect& redirect);
Request(Request& original, const Redirect& redirect);
// States
enum RequestState {
InitialState,
Connected,
RequestSent,
ContentReceived
};
RequestState State() const noexcept { return fRequestStatus; }
enum RequestState { InitialState, Connected, RequestSent, ContentReceived };
RequestState State() const noexcept { return fRequestStatus; }
// Result Helpers
std::shared_ptr<HttpResultPrivate>
Result() { return fResult; }
void SetError(std::exception_ptr e);
std::shared_ptr<HttpResultPrivate> Result() { return fResult; }
void SetError(std::exception_ptr e);
// Helpers for maintaining the connection count
std::pair<BString, int> GetHost() const;
void SetCounter(int32* counter) noexcept;
std::pair<BString, int> GetHost() const;
void SetCounter(int32* counter) noexcept;
// Operational methods
void ResolveHostName();
void OpenConnection();
void TransferRequest();
bool ReceiveResult();
void Disconnect() noexcept;
void ResolveHostName();
void OpenConnection();
void TransferRequest();
bool ReceiveResult();
void Disconnect() noexcept;
// Object information
int Socket() const noexcept { return fSocket->Socket(); }
int32 Id() const noexcept { return fResult->id; }
bool CanCancel() const noexcept { return fResult->CanCancel(); }
int Socket() const noexcept { return fSocket->Socket(); }
int32 Id() const noexcept { return fResult->id; }
bool CanCancel() const noexcept { return fResult->CanCancel(); }
// Message helper
void SendMessage(uint32 what,
std::function<void (BMessage&)> dataFunc = nullptr) const;
void SendMessage(uint32 what, std::function<void(BMessage&)> dataFunc = nullptr) const;
private:
BHttpRequest fRequest;
BHttpRequest fRequest;
// Request state/events
RequestState fRequestStatus = InitialState;
RequestState fRequestStatus = InitialState;
// Communication
BMessenger fObserver;
BMessenger fObserver;
std::shared_ptr<HttpResultPrivate> fResult;
// Connection
BNetworkAddress fRemoteAddress;
std::unique_ptr<BSocket> fSocket;
BNetworkAddress fRemoteAddress;
std::unique_ptr<BSocket> fSocket;
// Sending and receiving
HttpBuffer fBuffer;
HttpSerializer fSerializer;
HttpParser fParser;
HttpBuffer fBuffer;
HttpSerializer fSerializer;
HttpParser fParser;
// Receive state
BHttpStatus fStatus;
BHttpFields fFields;
BHttpStatus fStatus;
BHttpFields fFields;
// Redirection
bool fMightRedirect = false;
int8 fRemainingRedirects;
bool fMightRedirect = false;
int8 fRemainingRedirects;
// Connection counter
std::unique_ptr<int32, CounterDeleter>
fConnectionCounter;
std::unique_ptr<int32, CounterDeleter> fConnectionCounter;
};
class BHttpSession::Impl {
class BHttpSession::Impl
{
public:
Impl();
~Impl() noexcept;
Impl();
~Impl() noexcept;
BHttpResult Execute(BHttpRequest&& request,
BBorrow<BDataIO> target,
BMessenger observer);
void Cancel(int32 identifier);
void SetMaxConnectionsPerHost(size_t maxConnections);
void SetMaxHosts(size_t maxConnections);
BHttpResult Execute(BHttpRequest&& request, BBorrow<BDataIO> target, BMessenger observer);
void Cancel(int32 identifier);
void SetMaxConnectionsPerHost(size_t maxConnections);
void SetMaxHosts(size_t maxConnections);
private:
// Thread functions
static status_t ControlThreadFunc(void* arg);
static status_t DataThreadFunc(void* arg);
// Thread functions
static status_t ControlThreadFunc(void* arg);
static status_t DataThreadFunc(void* arg);
// Helper functions
std::vector<BHttpSession::Request> GetRequestsForControlThread();
std::vector<BHttpSession::Request> GetRequestsForControlThread();
private:
// constants (can be accessed unlocked)
const sem_id fControlQueueSem;
const sem_id fDataQueueSem;
const thread_id fControlThread;
const thread_id fDataThread;
// constants (can be accessed unlocked)
const sem_id fControlQueueSem;
const sem_id fDataQueueSem;
const thread_id fControlThread;
const thread_id fDataThread;
// locking mechanism
BLocker fLock;
std::atomic<bool> fQuitting = false;
BLocker fLock;
std::atomic<bool> fQuitting = false;
// queues & shared data
std::list<BHttpSession::Request> fControlQueue;
std::deque<BHttpSession::Request> fDataQueue;
std::vector<int32> fCancelList;
std::list<BHttpSession::Request> fControlQueue;
std::deque<BHttpSession::Request> fDataQueue;
std::vector<int32> fCancelList;
// data owned by the controlThread
using Host = std::pair<BString, int>;
std::map<Host, int32> fConnectionCount;
std::map<Host, int32> fConnectionCount;
// data that can only be accessed atomically
std::atomic<size_t> fMaxConnectionsPerHost = 2;
std::atomic<size_t> fMaxHosts = 10;
std::atomic<size_t> fMaxConnectionsPerHost = 2;
std::atomic<size_t> fMaxHosts = 10;
// data owned by the dataThread
std::map<int,BHttpSession::Request> connectionMap;
std::vector<object_wait_info> objectList;
std::map<int, BHttpSession::Request> connectionMap;
std::vector<object_wait_info> objectList;
};
struct BHttpSession::Redirect {
BUrl url;
bool redirectToGet;
BUrl url;
bool redirectToGet;
};
@ -229,8 +217,7 @@ BHttpSession::Impl::~Impl() noexcept
BHttpResult
BHttpSession::Impl::Execute(BHttpRequest&& request, BBorrow<BDataIO> target,
BMessenger observer)
BHttpSession::Impl::Execute(BHttpRequest&& request, BBorrow<BDataIO> target, BMessenger 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);
// 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) {
try {
throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::Canceled);
@ -269,8 +256,8 @@ void
BHttpSession::Impl::SetMaxConnectionsPerHost(size_t maxConnections)
{
if (maxConnections <= 0 || maxConnections >= INT32_MAX) {
throw BRuntimeError(__PRETTY_FUNCTION__,
"MaxConnectionsPerHost must be between 1 and INT32_MAX");
throw BRuntimeError(
__PRETTY_FUNCTION__, "MaxConnectionsPerHost must be between 1 and INT32_MAX");
}
fMaxConnectionsPerHost.store(maxConnections, std::memory_order_relaxed);
}
@ -347,8 +334,8 @@ BHttpSession::Impl::ControlThreadFunc(void* arg)
}
}
} else {
throw BRuntimeError(__PRETTY_FUNCTION__,
"Unknown reason that the controlQueueSem is deleted");
throw BRuntimeError(
__PRETTY_FUNCTION__, "Unknown reason that the controlQueueSem is deleted");
}
// Cleanup: wait for data thread
@ -365,12 +352,12 @@ BHttpSession::Impl::DataThreadFunc(void* arg)
BHttpSession::Impl* data = static_cast<BHttpSession::Impl*>(arg);
// initial initialization of wait list
data->objectList.push_back(object_wait_info{data->fDataQueueSem,
B_OBJECT_TYPE_SEMAPHORE, B_EVENT_ACQUIRE_SEMAPHORE});
data->objectList.push_back(
object_wait_info{data->fDataQueueSem, B_OBJECT_TYPE_SEMAPHORE, B_EVENT_ACQUIRE_SEMAPHORE});
while (true) {
if (auto status = wait_for_objects(data->objectList.data(), data->objectList.size());
status == B_INTERRUPTED)
status == B_INTERRUPTED)
continue;
else if (status < 0) {
// Something went inexplicably wrong
@ -399,9 +386,8 @@ BHttpSession::Impl::DataThreadFunc(void* arg)
data->connectionMap.insert(std::make_pair(socket, std::move(request)));
// Add to objectList
data->objectList.push_back(object_wait_info{socket,
B_OBJECT_TYPE_FD, B_EVENT_WRITE
});
data->objectList.push_back(
object_wait_info{socket, B_OBJECT_TYPE_FD, B_EVENT_WRITE});
}
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
// so the fun starts at offset 1.
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++;
if (it->second.Id() == id) {
data->objectList[offset].events = EVENT_CANCELLED;
@ -427,7 +414,7 @@ BHttpSession::Impl::DataThreadFunc(void* arg)
// Process all objects that are ready
bool resizeObjectList = false;
for(auto& item: data->objectList) {
for (auto& item: data->objectList) {
if (item.type != B_OBJECT_TYPE_FD)
continue;
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) {
auto& request = data->connectionMap.find(item.object)->second;
try {
throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::NetworkError);
throw BNetworkRequestError(
__PRETTY_FUNCTION__, BNetworkRequestError::NetworkError);
} catch (...) {
request.SetError(std::current_exception());
}
@ -504,12 +492,12 @@ BHttpSession::Impl::DataThreadFunc(void* arg)
} else {
// Likely to be B_EVENT_INVALID. This should not happen
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.AddString(UrlEventData::DebugMessage, "Unexpected event; socket deleted?");
});
throw BRuntimeError(__PRETTY_FUNCTION__,
"Socket was deleted at an unexpected time");
throw BRuntimeError(
__PRETTY_FUNCTION__, "Socket was deleted at an unexpected time");
}
}
@ -539,10 +527,9 @@ BHttpSession::Impl::DataThreadFunc(void* arg)
} catch (...) {
it->second.SetError(std::current_exception());
}
}
}
} else {
throw BRuntimeError(__PRETTY_FUNCTION__,
"Unknown reason that the dataQueueSem is deleted");
throw BRuntimeError(__PRETTY_FUNCTION__, "Unknown reason that the dataQueueSem is deleted");
}
return B_OK;
@ -562,7 +549,7 @@ BHttpSession::Impl::GetRequestsForControlThread()
// Clean up connection list if it is at the max number of hosts
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) {
it = fConnectionCount.erase(it);
} else {
@ -573,13 +560,13 @@ BHttpSession::Impl::GetRequestsForControlThread()
// Process the list of pending requests and review if they can be started.
auto lock = AutoLocker<BLocker>(fLock);
fControlQueue.remove_if([this, &requests](auto& request){
fControlQueue.remove_if([this, &requests](auto& request) {
auto host = request.GetHost();
auto it = fConnectionCount.find(host);
if (it != fConnectionCount.end()) {
if (static_cast<size_t>(atomic_get(std::addressof(it->second)))
>= fMaxConnectionsPerHost.load(std::memory_order_relaxed)) {
request.SendMessage(UrlEvent::DebugMessage, [](BMessage& msg){
>= fMaxConnectionsPerHost.load(std::memory_order_relaxed)) {
request.SendMessage(UrlEvent::DebugMessage, [](BMessage& msg) {
msg.AddUInt32(UrlEventData::DebugType, UrlEventData::DebugWarning);
msg.AddString(UrlEventData::DebugMessage,
"Request is queued: too many active connections for host");
@ -591,17 +578,16 @@ BHttpSession::Impl::GetRequestsForControlThread()
}
} else {
if (fConnectionCount.size() == fMaxHosts.load()) {
request.SendMessage(UrlEvent::DebugMessage, [](BMessage& msg){
request.SendMessage(UrlEvent::DebugMessage, [](BMessage& msg) {
msg.AddUInt32(UrlEventData::DebugType, UrlEventData::DebugWarning);
msg.AddString(UrlEventData::DebugMessage,
"Request is queued: maximum number of concurrent hosts");
});
return false;
}
auto[newIt, success] = fConnectionCount.insert({host, 1});
auto [newIt, success] = fConnectionCount.insert({host, 1});
if (!success) {
throw BRuntimeError(__PRETTY_FUNCTION__,
"Cannot insert into fConnectionCount");
throw BRuntimeError(__PRETTY_FUNCTION__, "Cannot insert into fConnectionCount");
}
request.SetCounter(std::addressof(newIt->second));
}
@ -627,8 +613,7 @@ BHttpSession::~BHttpSession() = default;
BHttpSession::BHttpSession(const BHttpSession&) noexcept = default;
BHttpSession&
BHttpSession::operator=(const BHttpSession&) noexcept = default;
BHttpSession& BHttpSession::operator=(const BHttpSession&) noexcept = default;
BHttpResult
@ -667,9 +652,10 @@ BHttpSession::SetMaxHosts(size_t maxConnections)
// #pragma mark -- BHttpSession::Request (helpers)
BHttpSession::Request::Request(BHttpRequest&& request, BBorrow<BDataIO> target,
BMessenger observer)
: fRequest(std::move(request)), fObserver(observer)
BHttpSession::Request::Request(BHttpRequest&& request, BBorrow<BDataIO> target, BMessenger observer)
:
fRequest(std::move(request)),
fObserver(observer)
{
auto identifier = get_netservices_request_identifier();
@ -690,8 +676,10 @@ BHttpSession::Request::Request(BHttpRequest&& request, BBorrow<BDataIO> target,
BHttpSession::Request::Request(Request& original, const BHttpSession::Redirect& redirect)
: fRequest(std::move(original.fRequest)), fObserver(original.fObserver),
fResult(original.fResult)
:
fRequest(std::move(original.fRequest)),
fObserver(original.fObserver),
fResult(original.fResult)
{
// update the original request with the new location
fRequest.SetUrl(redirect.url);
@ -717,7 +705,7 @@ void
BHttpSession::Request::SetError(std::exception_ptr e)
{
fResult->SetError(e);
SendMessage(UrlEvent::DebugMessage, [&e](BMessage& msg){
SendMessage(UrlEvent::DebugMessage, [&e](BMessage& msg) {
msg.AddUInt32(UrlEventData::DebugType, UrlEventData::DebugError);
try {
std::rethrow_exception(e);
@ -729,9 +717,8 @@ BHttpSession::Request::SetError(std::exception_ptr e)
msg.AddString(UrlEventData::DebugMessage, "Unknown exception");
}
});
SendMessage(UrlEvent::RequestCompleted, [](BMessage& msg){
msg.AddBool(UrlEventData::Success, false);
});
SendMessage(UrlEvent::RequestCompleted,
[](BMessage& msg) { msg.AddBool(UrlEventData::Success, false); });
}
@ -765,13 +752,12 @@ BHttpSession::Request::ResolveHostName()
// TODO: proxy
if (auto status = fRemoteAddress.SetTo(fRequest.Url().Host(), port); status != B_OK) {
throw BNetworkRequestError("BNetworkAddress::SetTo()",
BNetworkRequestError::HostnameError, status);
throw BNetworkRequestError(
"BNetworkAddress::SetTo()", BNetworkRequestError::HostnameError, status);
}
SendMessage(UrlEvent::HostNameResolved, [this](BMessage& msg) {
msg.AddString(UrlEventData::HostName, fRequest.Url().Host());
});
SendMessage(UrlEvent::HostNameResolved,
[this](BMessage& msg) { msg.AddString(UrlEventData::HostName, fRequest.Url().Host()); });
}
@ -795,8 +781,8 @@ BHttpSession::Request::OpenConnection()
// Open connection
if (auto status = fSocket->Connect(fRemoteAddress); status != B_OK) {
// TODO: inform listeners that the connection failed
throw BNetworkRequestError("BSocket::Connect()",
BNetworkRequestError::NetworkError, status);
throw BNetworkRequestError(
"BSocket::Connect()", BNetworkRequestError::NetworkError, status);
}
// Make the rest of the interaction non-blocking
@ -822,8 +808,8 @@ BHttpSession::Request::TransferRequest()
{
// Assert that we are in the right state
if (fRequestStatus != Connected)
throw BRuntimeError(__PRETTY_FUNCTION__,
"Write request for object that is not in the Connected state");
throw BRuntimeError(
__PRETTY_FUNCTION__, "Write request for object that is not in the Connected state");
if (!fSerializer.IsInitialized())
fSerializer.SetTo(fBuffer, fRequest);
@ -861,185 +847,185 @@ BHttpSession::Request::ReceiveResult()
// Parse the content in the buffer
switch (fParser.State()) {
case HttpInputStreamState::StatusLine:
{
if (fBuffer.RemainingBytes() == static_cast<size_t>(bytesRead)) {
// In the initial run, the bytes in the buffer will match the bytes read to indicate
// the response has started.
SendMessage(UrlEvent::ResponseStarted);
case HttpInputStreamState::StatusLine:
{
if (fBuffer.RemainingBytes() == static_cast<size_t>(bytesRead)) {
// In the initial run, the bytes in the buffer will match the bytes read to indicate
// the response has started.
SendMessage(UrlEvent::ResponseStarted);
}
if (fParser.ParseStatus(fBuffer, fStatus)) {
// the status headers are now received, decide what to do next
// Determine if we can handle redirects; else notify of receiving status
if (fRemainingRedirects > 0) {
switch (fStatus.StatusCode()) {
case BHttpStatusCode::MovedPermanently:
case BHttpStatusCode::TemporaryRedirect:
case BHttpStatusCode::PermanentRedirect:
// These redirects require the request body to be sent again. It this is
// possible, BHttpRequest::RewindBody() will return true in which case
// we can handle the redirect.
if (!fRequest.RewindBody())
break;
[[fallthrough]];
case BHttpStatusCode::Found:
case BHttpStatusCode::SeeOther:
// These redirects redirect to GET, so we don't care if we can rewind
// the body; in this case redirect
fMightRedirect = true;
break;
default:
break;
}
}
if ((fStatus.StatusClass() == BHttpStatusClass::ClientError
|| fStatus.StatusClass() == BHttpStatusClass::ServerError)
&& fRequest.StopOnError()) {
fRequestStatus = ContentReceived;
fResult->SetStatus(std::move(fStatus));
fResult->SetFields(BHttpFields());
fResult->SetBody();
return true;
}
if (!fMightRedirect) {
// we are not redirecting and there is no error, so inform listeners
SendMessage(UrlEvent::HttpStatus, [this](BMessage& msg) {
msg.AddInt16(UrlEventData::HttpStatusCode, fStatus.code);
});
fResult->SetStatus(BHttpStatus{fStatus.code, std::move(fStatus.text)});
}
} else {
// We do not have enough data for the status line yet
if (readEnd) {
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError,
"Response did not include a complete status line");
}
return false;
}
[[fallthrough]];
}
case HttpInputStreamState::Fields:
{
if (!fParser.ParseFields(fBuffer, fFields)) {
// there may be more headers to receive, throw an error if there will be no more
if (readEnd) {
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError,
"Response did not include a complete header section");
}
break;
}
if (fParser.ParseStatus(fBuffer, fStatus)) {
// the status headers are now received, decide what to do next
// The headers have been received, now set up the rest of the response handling
// Determine if we can handle redirects; else notify of receiving status
if (fRemainingRedirects > 0) {
// Handle redirects
if (fMightRedirect) {
auto redirectToGet = false;
switch (fStatus.StatusCode()) {
case BHttpStatusCode::Found:
case BHttpStatusCode::SeeOther:
// 302 and 303 redirections convert all requests to GET request, except for
// HEAD
redirectToGet = true;
[[fallthrough]];
case BHttpStatusCode::MovedPermanently:
case BHttpStatusCode::TemporaryRedirect:
case BHttpStatusCode::PermanentRedirect:
// These redirects require the request body to be sent again. It this is
// possible, BHttpRequest::RewindBody() will return true in which case we can
// handle the redirect.
if (!fRequest.RewindBody())
break;
[[fallthrough]];
case BHttpStatusCode::Found:
case BHttpStatusCode::SeeOther:
// These redirects redirect to GET, so we don't care if we can rewind the
// body; in this case redirect
fMightRedirect = true;
break;
{
auto locationField = fFields.FindField("Location");
if (locationField == fFields.end()) {
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError,
"Redirect; the Location field must be present and cannot be found");
}
auto locationString = BString(
(*locationField).Value().data(), (*locationField).Value().size());
auto redirect = BHttpSession::Redirect{
BUrl(fRequest.Url(), locationString), redirectToGet};
if (!redirect.url.IsValid()) {
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError,
"Redirect; invalid URL in the Location field");
}
// Notify of redirect
SendMessage(UrlEvent::HttpRedirect, [&locationString](BMessage& msg) {
msg.AddString(UrlEventData::HttpRedirectUrl, locationString);
});
throw redirect;
}
default:
// ignore other status codes and continue regular processing
SendMessage(UrlEvent::HttpStatus, [this](BMessage& msg) {
msg.AddInt16(UrlEventData::HttpStatusCode, fStatus.code);
});
fResult->SetStatus(BHttpStatus{fStatus.code, std::move(fStatus.text)});
break;
}
}
if ((fStatus.StatusClass() == BHttpStatusClass::ClientError
|| fStatus.StatusClass() == BHttpStatusClass::ServerError)
&& fRequest.StopOnError())
{
fRequestStatus = ContentReceived;
fResult->SetStatus(std::move(fStatus));
fResult->SetFields(BHttpFields());
// TODO: Parse received cookies
// Move headers to the result and inform listener
fResult->SetFields(std::move(fFields));
SendMessage(UrlEvent::HttpFields);
if (!fParser.HasContent()) {
// Any requests with not content are finished
fResult->SetBody();
SendMessage(UrlEvent::RequestCompleted,
[](BMessage& msg) { msg.AddBool(UrlEventData::Success, true); });
fRequestStatus = ContentReceived;
return true;
}
if (!fMightRedirect) {
// we are not redirecting and there is no error, so inform listeners
SendMessage(UrlEvent::HttpStatus, [this](BMessage& msg) {
msg.AddInt16(UrlEventData::HttpStatusCode, fStatus.code);
});
fResult->SetStatus(BHttpStatus{fStatus.code, std::move(fStatus.text)});
}
} else {
// We do not have enough data for the status line yet
if (readEnd) {
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError,
"Response did not include a complete status line");
}
return false;
[[fallthrough]];
}
[[fallthrough]];
}
case HttpInputStreamState::Fields:
{
if (!fParser.ParseFields(fBuffer, fFields)) {
// there may be more headers to receive, throw an error if there will be no more
if (readEnd) {
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError,
"Response did not include a complete header section");
case HttpInputStreamState::Body:
{
size_t bytesWrittenToBody;
// The bytesWrittenToBody may differ from the bytes parsed from the buffer when
// there is compression on the incoming stream.
bytesRead = fParser.ParseBody(
fBuffer,
[this, &bytesWrittenToBody](const std::byte* buffer, size_t size) {
bytesWrittenToBody = fResult->WriteToBody(buffer, size);
return bytesWrittenToBody;
},
readEnd);
SendMessage(UrlEvent::DownloadProgress, [this, bytesRead](BMessage& msg) {
msg.AddInt64(UrlEventData::NumBytes, bytesRead);
if (fParser.BodyBytesTotal())
msg.AddInt64(UrlEventData::TotalBytes, fParser.BodyBytesTotal().value());
});
if (bytesWrittenToBody > 0) {
SendMessage(UrlEvent::BytesWritten, [bytesWrittenToBody](BMessage& msg) {
msg.AddInt64(UrlEventData::NumBytes, bytesWrittenToBody);
});
}
if (fParser.Complete()) {
fResult->SetBody();
SendMessage(UrlEvent::RequestCompleted,
[](BMessage& msg) { msg.AddBool(UrlEventData::Success, true); });
fRequestStatus = ContentReceived;
return true;
} else if (readEnd) {
// the parsing of the body is not complete but we are at the end of the data
throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::ProtocolError,
"Unexpected end of data: more data was expected");
}
break;
}
// The headers have been received, now set up the rest of the response handling
// Handle redirects
if (fMightRedirect) {
auto redirectToGet = false;
switch (fStatus.StatusCode()) {
case BHttpStatusCode::Found:
case BHttpStatusCode::SeeOther:
// 302 and 303 redirections convert all requests to GET request, except for HEAD
redirectToGet = true;
[[fallthrough]];
case BHttpStatusCode::MovedPermanently:
case BHttpStatusCode::TemporaryRedirect:
case BHttpStatusCode::PermanentRedirect:
{
auto locationField = fFields.FindField("Location");
if (locationField == fFields.end()) {
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError,
"Redirect; the Location field must be present and cannot be found");
}
auto locationString = BString((*locationField).Value().data(),
(*locationField).Value().size());
auto redirect =
BHttpSession::Redirect{BUrl(fRequest.Url(), locationString), redirectToGet};
if (!redirect.url.IsValid()) {
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError,
"Redirect; invalid URL in the Location field");
}
// Notify of redirect
SendMessage(UrlEvent::HttpRedirect, [&locationString](BMessage& msg) {
msg.AddString(UrlEventData::HttpRedirectUrl, locationString);
});
throw redirect;
}
default:
// ignore other status codes and continue regular processing
SendMessage(UrlEvent::HttpStatus, [this](BMessage& msg) {
msg.AddInt16(UrlEventData::HttpStatusCode, fStatus.code);
});
fResult->SetStatus(BHttpStatus{fStatus.code, std::move(fStatus.text)});
break;
}
}
// TODO: Parse received cookies
// Move headers to the result and inform listener
fResult->SetFields(std::move(fFields));
SendMessage(UrlEvent::HttpFields);
if (!fParser.HasContent()) {
// Any requests with not content are finished
fResult->SetBody();
SendMessage(UrlEvent::RequestCompleted, [](BMessage& msg) {
msg.AddBool(UrlEventData::Success, true);
});
fRequestStatus = ContentReceived;
return true;
}
[[fallthrough]];
}
case HttpInputStreamState::Body:
{
size_t bytesWrittenToBody;
// The bytesWrittenToBody may differ from the bytes parsed from the buffer when
// there is compression on the incoming stream.
bytesRead = fParser.ParseBody(fBuffer, [this, &bytesWrittenToBody](const std::byte* buffer, size_t size) {
bytesWrittenToBody = fResult->WriteToBody(buffer, size);
return bytesWrittenToBody;
}, readEnd);
SendMessage(UrlEvent::DownloadProgress, [this, bytesRead](BMessage& msg) {
msg.AddInt64(UrlEventData::NumBytes, bytesRead);
if (fParser.BodyBytesTotal())
msg.AddInt64(UrlEventData::TotalBytes, fParser.BodyBytesTotal().value());
});
if (bytesWrittenToBody > 0) {
SendMessage(UrlEvent::BytesWritten, [bytesWrittenToBody](BMessage& msg) {
msg.AddInt64(UrlEventData::NumBytes, bytesWrittenToBody);
});
}
if (fParser.Complete()) {
fResult->SetBody();
SendMessage(UrlEvent::RequestCompleted, [](BMessage& msg) {
msg.AddBool(UrlEventData::Success, true);
});
fRequestStatus = ContentReceived;
return true;
} else if (readEnd) {
// the parsing of the body is not complete but we are at the end of the data
throw BNetworkRequestError(__PRETTY_FUNCTION__,
BNetworkRequestError::ProtocolError,
"Unexpected end of data: more data was expected");
}
break;
}
default:
throw BRuntimeError(__PRETTY_FUNCTION__, "Not reachable");
default:
throw BRuntimeError(__PRETTY_FUNCTION__, "Not reachable");
}
// There is more to receive
@ -1047,7 +1033,6 @@ BHttpSession::Request::ReceiveResult()
}
/*!
\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.
*/
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()) {
BMessage msg(what);
@ -1081,8 +1066,8 @@ BHttpSession::Request::SendMessage(uint32 what, std::function<void (BMessage&)>
namespace BPrivate::Network::UrlEventData {
const char* HttpStatusCode = "url:httpstatuscode";
const char* SSLCertificate = "url:sslcertificate";
const char* SSLMessage = "url:sslmessage";
const char* HttpRedirectUrl = "url:httpredirecturl";
}
const char* HttpStatusCode = "url:httpstatuscode";
const char* SSLCertificate = "url:sslcertificate";
const char* SSLMessage = "url:sslmessage";
const char* HttpRedirectUrl = "url:httpredirecturl";
} // namespace BPrivate::Network::UrlEventData

View File

@ -35,15 +35,15 @@ using namespace BPrivate::Network;
// - Invalid weekday
static const std::list<std::pair<BHttpTimeFormat, const char*>> kDateFormats = {
// RFC822
{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 GMT"}, // canonical
{BHttpTimeFormat::RFC1123, "%a, %d %b %Y %H:%M:%S"}, // without timezone
// Standard RFC850
{BHttpTimeFormat::RFC850, "%A, %d-%b-%y %H:%M:%S GMT"}, // canonical
{BHttpTimeFormat::RFC850, "%A, %d-%b-%y %H:%M:%S"}, // without timezone
{BHttpTimeFormat::RFC850, "%A, %d-%b-%y %H:%M:%S GMT"}, // canonical
{BHttpTimeFormat::RFC850, "%A, %d-%b-%y %H:%M:%S"}, // without timezone
// RFC 850 with 4 digit year
{BHttpTimeFormat::RFC850, "%a, %d-%b-%Y %H:%M:%S"}, // without timezone
{BHttpTimeFormat::RFC850, "%a, %d-%b-%Y %H:%M:%S GMT"}, // with 4-digit year
{BHttpTimeFormat::RFC850, "%a, %d-%b-%Y %H:%M:%S UTC"}, // "UTC" timezone
{BHttpTimeFormat::RFC850, "%a, %d-%b-%Y %H:%M:%S"}, // without timezone
{BHttpTimeFormat::RFC850, "%a, %d-%b-%Y %H:%M:%S GMT"}, // with 4-digit year
{BHttpTimeFormat::RFC850, "%a, %d-%b-%Y %H:%M:%S UTC"}, // "UTC" timezone
// asctime
{BHttpTimeFormat::AscTime, "%a %b %e %H:%M:%S %Y"},
};
@ -57,7 +57,6 @@ BHttpTime::InvalidInput::InvalidInput(const char* origin, BString input)
BError(origin),
input(std::move(input))
{
}
@ -174,8 +173,8 @@ BHttpTime::ToString(BHttpTimeFormat outputFormat) const
char expirationString[kTimetToStringMaxLength + 1];
size_t strLength;
strLength = strftime(expirationString, kTimetToStringMaxLength, formatString,
&expirationTm);
strLength
= strftime(expirationString, kTimetToStringMaxLength, formatString, &expirationTm);
expirationFinal.SetTo(expirationString, strLength);
break;
@ -210,8 +209,7 @@ BHttpTime::_Parse(const BString& dateString)
// Now convert the struct tm from strptime into a BDateTime.
BTime time(expireTime.tm_hour, expireTime.tm_min, expireTime.tm_sec);
BDate date(expireTime.tm_year + 1900, expireTime.tm_mon + 1,
expireTime.tm_mday);
BDate date(expireTime.tm_year + 1900, expireTime.tm_mon + 1, expireTime.tm_mday);
fDate = BDateTime(date, time);
}

View File

@ -17,25 +17,22 @@ namespace Network {
// #pragma mark -- BUnsupportedProtocol
BUnsupportedProtocol::BUnsupportedProtocol(const char* origin,
BUrl url, BStringList supportedProtocols)
BUnsupportedProtocol::BUnsupportedProtocol(
const char* origin, BUrl url, BStringList supportedProtocols)
:
BError(origin),
fUrl(std::move(url)),
fSupportedProtocols(std::move(supportedProtocols))
{
}
BUnsupportedProtocol::BUnsupportedProtocol(BString origin,
BUrl url, BStringList supportedProtocols)
BUnsupportedProtocol::BUnsupportedProtocol(BString origin, BUrl url, BStringList supportedProtocols)
:
BError(std::move(origin)),
fUrl(std::move(url)),
fSupportedProtocols(std::move(supportedProtocols))
{
}
@ -68,7 +65,6 @@ BInvalidUrl::BInvalidUrl(const char* origin, BUrl url)
BError(origin),
fUrl(std::move(url))
{
}
@ -77,7 +73,6 @@ BInvalidUrl::BInvalidUrl(BString origin, BUrl url)
BError(std::move(origin)),
fUrl(std::move(origin))
{
}
@ -98,19 +93,24 @@ BInvalidUrl::Url() const
// #pragma mark -- BNetworkRequestError
BNetworkRequestError::BNetworkRequestError(const char* origin, ErrorType type, status_t errorCode,
const BString& customMessage)
: BError(origin), fErrorType(type), fErrorCode(errorCode), fCustomMessage(customMessage)
BNetworkRequestError::BNetworkRequestError(
const char* origin, ErrorType type, status_t errorCode, const BString& customMessage)
:
BError(origin),
fErrorType(type),
fErrorCode(errorCode),
fCustomMessage(customMessage)
{
}
BNetworkRequestError::BNetworkRequestError(const char* origin, ErrorType type,
const BString& customMessage)
: BError(origin), fErrorType(type), fCustomMessage(customMessage)
BNetworkRequestError::BNetworkRequestError(
const char* origin, ErrorType type, const BString& customMessage)
:
BError(origin),
fErrorType(type),
fCustomMessage(customMessage)
{
}
@ -118,16 +118,16 @@ const char*
BNetworkRequestError::Message() const noexcept
{
switch (fErrorType) {
case HostnameError:
return "Cannot resolving hostname";
case NetworkError:
return "Network error during operation";
case ProtocolError:
return "Protocol error";
case SystemError:
return "System error";
case Canceled:
return "Network request was canceled";
case HostnameError:
return "Cannot resolving hostname";
case NetworkError:
return "Network error during operation";
case ProtocolError:
return "Protocol error";
case SystemError:
return "System error";
case Canceled:
return "Network request was canceled";
}
// Unreachable
return "Network request error";
@ -141,7 +141,7 @@ BNetworkRequestError::DebugMessage() const
debugMessage << "[" << Origin() << "] " << Message();
if (fErrorCode != B_OK) {
debugMessage << "\n\tUnderlying System Error: " << fErrorCode << " ("
<< strerror(fErrorCode) << ")";
<< strerror(fErrorCode) << ")";
}
if (fCustomMessage.Length() > 0) {
debugMessage << "\n\tAdditional Info: " << fCustomMessage;
@ -185,8 +185,8 @@ encode_to_base64(const BString& string)
BString tmpString = string;
while (tmpString.Length()) {
char in[3] = { 0, 0, 0 };
char out[4] = { 0, 0, 0, 0 };
char in[3] = {0, 0, 0};
char out[4] = {0, 0, 0, 0};
int8 remaining = tmpString.Length();
tmpString.MoveInto(in, 0, 3);
@ -197,7 +197,7 @@ encode_to_base64(const BString& string)
out[3] = in[2] & 0x3F;
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
// of 3
@ -219,14 +219,14 @@ encode_to_base64(const BString& string)
// #pragma mark -- message constants
namespace UrlEventData {
const char* Id = "url:identifier";
const char* HostName = "url:hostname";
const char* NumBytes = "url:numbytes";
const char* TotalBytes = "url:totalbytes";
const char* Success = "url:success";
const char* DebugType = "url:debugtype";
const char* DebugMessage = "url:debugmessage";
}
const char* Id = "url:identifier";
const char* HostName = "url:hostname";
const char* NumBytes = "url:numbytes";
const char* TotalBytes = "url:totalbytes";
const char* Success = "url:success";
const char* DebugType = "url:debugtype";
const char* DebugMessage = "url:debugmessage";
} // namespace UrlEventData
// #pragma mark -- Private functions and data

View File

@ -26,64 +26,46 @@ class DeleteTestHelper
{
public:
DeleteTestHelper(std::atomic<bool>& deleted)
: fDeleted(deleted)
:
fDeleted(deleted)
{
}
~DeleteTestHelper()
{
fDeleted.store(true);
}
~DeleteTestHelper() { fDeleted.store(true); }
private:
std::atomic<bool>& fDeleted;
std::atomic<bool>& fDeleted;
};
class Base {
class Base
{
public:
Base()
{
}
Base() {}
virtual ~Base()
{
}
virtual ~Base() {}
virtual bool IsDerived()
{
return false;
}
virtual bool IsDerived() { return false; }
};
class Derived : public Base {
class Derived : public Base
{
public:
Derived() {
}
Derived() {}
virtual ~Derived() {
}
virtual ~Derived() {}
virtual bool IsDerived() override
{
return true;
}
virtual bool IsDerived() override { return true; }
};
ExclusiveBorrowTest::ExclusiveBorrowTest()
{
}

View File

@ -9,18 +9,18 @@
#include <TestSuite.h>
class ExclusiveBorrowTest: public BTestCase {
class ExclusiveBorrowTest : public BTestCase
{
public:
ExclusiveBorrowTest();
ExclusiveBorrowTest();
void ObjectDeleteTest();
void OwnershipTest();
void PolymorphismTest();
void ReleaseTest();
void ObjectDeleteTest();
void OwnershipTest();
void PolymorphismTest();
void ReleaseTest();
static void AddTests(BTestSuite& suite);
static void AddTests(BTestSuite& suite);
};
#endif // EXCLUSIVE_BORROW_TEST_H

View File

@ -18,9 +18,9 @@ using namespace BPrivate::Network;
HttpDebugLogger::HttpDebugLogger()
: BLooper("HttpDebugLogger")
:
BLooper("HttpDebugLogger")
{
}
@ -39,6 +39,7 @@ HttpDebugLogger::SetFileLogging(const char* path)
throw BSystemError("BFile::SetTo()", status);
}
void
HttpDebugLogger::MessageReceived(BMessage* message)
{
@ -50,102 +51,102 @@ HttpDebugLogger::MessageReceived(BMessage* message)
output << "[" << id << "] ";
switch (message->what) {
case UrlEvent::HostNameResolved:
{
BString hostname;
message->FindString(UrlEventData::HostName, &hostname);
output << "<HostNameResolved> " << hostname;
break;
}
case UrlEvent::ConnectionOpened:
output << "<ConnectionOpened>";
break;
case UrlEvent::UploadProgress:
{
off_t numBytes = message->GetInt64(UrlEventData::NumBytes, -1);
off_t totalBytes = message->GetInt64(UrlEventData::TotalBytes, -1);
output << "<UploadProgress> bytes uploaded " << numBytes;
if (totalBytes == -1)
output << " (total unknown)";
else
output << " (" << totalBytes << " total)";
break;
}
case UrlEvent::ResponseStarted:
{
output << "<ResponseStarted>";
break;
}
case UrlEvent::HttpRedirect:
{
BString redirectUrl;
message->FindString(UrlEventData::HttpRedirectUrl, &redirectUrl);
output << "<HttpRedirect> to: " << redirectUrl;
break;
}
case UrlEvent::HttpStatus:
{
int16 status = message->FindInt16(UrlEventData::HttpStatusCode);
output << "<HttpStatus> code: " << status;
break;
}
case UrlEvent::HttpFields:
{
output << "<HttpFields> All fields parsed";
break;
}
case UrlEvent::DownloadProgress:
{
off_t numBytes = message->GetInt64(UrlEventData::NumBytes, -1);
off_t totalBytes = message->GetInt64(UrlEventData::TotalBytes, -1);
output << "<DownloadProgress> bytes downloaded " << numBytes;
if (totalBytes == -1)
output << " (total unknown)";
else
output << " (" << totalBytes << " total)";
break;
}
case UrlEvent::BytesWritten:
{
off_t numBytes = message->GetInt64(UrlEventData::NumBytes, -1);
output << "<BytesWritten> bytes written to output: " << numBytes;
break;
}
case UrlEvent::RequestCompleted:
{
bool success = message->GetBool(UrlEventData::Success, false);
output << "<RequestCompleted> success: ";
if (success)
output << "true";
else
output << "false";
break;
}
case UrlEvent::DebugMessage:
{
uint32 debugType = message->GetUInt32(UrlEventData::DebugType, 0);
BString debugMessage;
message->FindString(UrlEventData::DebugMessage, &debugMessage);
output << "<DebugMessage> ";
switch (debugType) {
case UrlEventData::DebugInfo:
output << "INFO: ";
break;
case UrlEventData::DebugWarning:
output << "WARNING: ";
break;
case UrlEventData::DebugError:
output << "ERROR: ";
break;
default:
output << "UNKNOWN: ";
break;
case UrlEvent::HostNameResolved:
{
BString hostname;
message->FindString(UrlEventData::HostName, &hostname);
output << "<HostNameResolved> " << hostname;
break;
}
output << debugMessage;
break;
}
default:
return BLooper::MessageReceived(message);
case UrlEvent::ConnectionOpened:
output << "<ConnectionOpened>";
break;
case UrlEvent::UploadProgress:
{
off_t numBytes = message->GetInt64(UrlEventData::NumBytes, -1);
off_t totalBytes = message->GetInt64(UrlEventData::TotalBytes, -1);
output << "<UploadProgress> bytes uploaded " << numBytes;
if (totalBytes == -1)
output << " (total unknown)";
else
output << " (" << totalBytes << " total)";
break;
}
case UrlEvent::ResponseStarted:
{
output << "<ResponseStarted>";
break;
}
case UrlEvent::HttpRedirect:
{
BString redirectUrl;
message->FindString(UrlEventData::HttpRedirectUrl, &redirectUrl);
output << "<HttpRedirect> to: " << redirectUrl;
break;
}
case UrlEvent::HttpStatus:
{
int16 status = message->FindInt16(UrlEventData::HttpStatusCode);
output << "<HttpStatus> code: " << status;
break;
}
case UrlEvent::HttpFields:
{
output << "<HttpFields> All fields parsed";
break;
}
case UrlEvent::DownloadProgress:
{
off_t numBytes = message->GetInt64(UrlEventData::NumBytes, -1);
off_t totalBytes = message->GetInt64(UrlEventData::TotalBytes, -1);
output << "<DownloadProgress> bytes downloaded " << numBytes;
if (totalBytes == -1)
output << " (total unknown)";
else
output << " (" << totalBytes << " total)";
break;
}
case UrlEvent::BytesWritten:
{
off_t numBytes = message->GetInt64(UrlEventData::NumBytes, -1);
output << "<BytesWritten> bytes written to output: " << numBytes;
break;
}
case UrlEvent::RequestCompleted:
{
bool success = message->GetBool(UrlEventData::Success, false);
output << "<RequestCompleted> success: ";
if (success)
output << "true";
else
output << "false";
break;
}
case UrlEvent::DebugMessage:
{
uint32 debugType = message->GetUInt32(UrlEventData::DebugType, 0);
BString debugMessage;
message->FindString(UrlEventData::DebugMessage, &debugMessage);
output << "<DebugMessage> ";
switch (debugType) {
case UrlEventData::DebugInfo:
output << "INFO: ";
break;
case UrlEventData::DebugWarning:
output << "WARNING: ";
break;
case UrlEventData::DebugError:
output << "ERROR: ";
break;
default:
output << "UNKNOWN: ";
break;
}
output << debugMessage;
break;
}
default:
return BLooper::MessageReceived(message);
}
if (fConsoleLogging)

View File

@ -12,16 +12,16 @@
class HttpDebugLogger : public BLooper
{
public:
HttpDebugLogger();
void SetConsoleLogging(bool enabled = true);
void SetFileLogging(const char* path);
HttpDebugLogger();
void SetConsoleLogging(bool enabled = true);
void SetFileLogging(const char* path);
protected:
virtual void MessageReceived(BMessage* message) override;
virtual void MessageReceived(BMessage* message) override;
private:
bool fConsoleLogging = false;
BFile fLogFile;
bool fConsoleLogging = false;
BFile fLogFile;
};
#endif // HTTP_DEBUG_LOGGER_H

View File

@ -47,7 +47,6 @@ constexpr bool LOG_TO_CONSOLE = false;
HttpProtocolTest::HttpProtocolTest()
{
}
@ -147,14 +146,11 @@ HttpProtocolTest::HttpFieldsTest()
}
// Set up a generic set of headers for further use
const BHttpFields defaultFields = {
{"Host"sv, "haiku-os.org"sv},
{"Accept"sv, "*/*"sv},
const BHttpFields defaultFields = {{"Host"sv, "haiku-os.org"sv}, {"Accept"sv, "*/*"sv},
{"Set-Cookie"sv, "qwerty=494793ddkl; Domain=haiku-os.co.uk"sv},
{"Set-Cookie"sv, "afbzyi=0kdnke0lyv; Domain=haiku-os.co.uk"sv},
{}, // Empty; should be ignored by the constructor
{"Accept-Encoding"sv, "gzip"sv}
};
{}, // Empty; should be ignored by the constructor
{"Accept-Encoding"sv, "gzip"sv}};
// Validate std::initializer_list constructor
CPPUNIT_ASSERT_EQUAL(5, defaultFields.CountFields());
@ -216,12 +212,8 @@ HttpProtocolTest::HttpFieldsTest()
// Iterate through the fields using a constant iterator
{
const BHttpFields fields = {
{"key1"sv, "value1"sv},
{"key2"sv, "value2"sv},
{"key3"sv, "value3"sv},
{"key4"sv, "value4"sv}
};
const BHttpFields fields = {{"key1"sv, "value1"sv}, {"key2"sv, "value2"sv},
{"key3"sv, "value3"sv}, {"key4"sv, "value4"sv}};
auto count = 0L;
for (const auto& field: fields) {
@ -283,12 +275,11 @@ HttpProtocolTest::HttpMethodTest()
}
constexpr std::string_view kExpectedRequestText =
"GET / HTTP/1.1\r\n"
"Host: www.haiku-os.org\r\n"
"Accept-Encoding: gzip\r\n"
"Connection: close\r\n"
"Api-Key: 01234567890abcdef\r\n\r\n";
constexpr std::string_view kExpectedRequestText = "GET / HTTP/1.1\r\n"
"Host: www.haiku-os.org\r\n"
"Accept-Encoding: gzip\r\n"
"Connection: close\r\n"
"Api-Key: 01234567890abcdef\r\n\r\n";
void
@ -323,16 +314,11 @@ HttpProtocolTest::HttpRequestTest()
void
HttpProtocolTest::HttpTimeTest()
{
const std::vector<BString> kValidTimeStrings = {
"Sun, 07 Dec 2003 16:01:00 GMT",
"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-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 std::vector<BString> kValidTimeStrings
= {"Sun, 07 Dec 2003 16:01:00 GMT", "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-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}};
for (const auto& timeString: kValidTimeStrings) {
@ -340,9 +326,9 @@ HttpProtocolTest::HttpTimeTest()
}
const std::vector<BString> kInvalidTimeStrings = {
"Sun, 07 Dec 2003", // Date only
"Sun, 07 Dec 2003 16:01:00 BST", // Invalid timezone
"On Sun, 07 Dec 2003 16:01:00 GMT", // Extra data in front of the string
"Sun, 07 Dec 2003", // Date only
"Sun, 07 Dec 2003 16:01:00 BST", // Invalid timezone
"On Sun, 07 Dec 2003 16:01:00 GMT", // Extra data in front of the string
};
for (const auto& timeString: kInvalidTimeStrings) {
@ -357,8 +343,8 @@ HttpProtocolTest::HttpTimeTest()
}
// Validate format_http_time()
CPPUNIT_ASSERT_EQUAL(BString("Sun, 07 Dec 2003 16:01:00 GMT"),
format_http_time(kExpectedDateTime));
CPPUNIT_ASSERT_EQUAL(
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"),
format_http_time(kExpectedDateTime, BHttpTimeFormat::RFC850));
CPPUNIT_ASSERT_EQUAL(BString("Sun Dec 7 16:01:00 2003"),
@ -387,16 +373,18 @@ HttpProtocolTest::AddTests(BTestSuite& parent)
// Observer test
#include <iostream>
class ObserverHelper : public BLooper {
class ObserverHelper : public BLooper
{
public:
ObserverHelper()
: BLooper("ObserverHelper") {}
void MessageReceived(BMessage* msg) override {
messages.emplace_back(*msg);
:
BLooper("ObserverHelper")
{
}
std::vector<BMessage> messages;
void MessageReceived(BMessage* msg) override { messages.emplace_back(*msg); }
std::vector<BMessage> messages;
};
@ -404,7 +392,8 @@ public:
HttpIntegrationTest::HttpIntegrationTest(TestServerMode mode)
: fTestServer(mode)
:
fTestServer(mode)
{
// increase number of concurrent connections to 4 (from 2)
fSession.SetMaxConnectionsPerHost(4);
@ -425,10 +414,7 @@ HttpIntegrationTest::HttpIntegrationTest(TestServerMode mode)
void
HttpIntegrationTest::setUp()
{
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"Starting up test server",
B_OK,
fTestServer.Start());
CPPUNIT_ASSERT_EQUAL_MESSAGE("Starting up test server", B_OK, fTestServer.Start());
}
@ -454,8 +440,8 @@ HttpIntegrationTest::AddTests(BTestSuite& parent)
= new BThreadedTestCaller<HttpIntegrationTest>("HttpTest::", httpIntegrationTest);
// HTTP
testCaller->addThread("HostAndNetworkFailTest",
&HttpIntegrationTest::HostAndNetworkFailTest);
testCaller->addThread(
"HostAndNetworkFailTest", &HttpIntegrationTest::HostAndNetworkFailTest);
testCaller->addThread("GetTest", &HttpIntegrationTest::GetTest);
testCaller->addThread("GetWithBufferTest", &HttpIntegrationTest::GetWithBufferTest);
testCaller->addThread("HeadTest", &HttpIntegrationTest::HeadTest);
@ -479,16 +465,16 @@ HttpIntegrationTest::AddTests(BTestSuite& parent)
= new BThreadedTestCaller<HttpIntegrationTest>("HttpsTest::", httpsIntegrationTest);
// HTTPS
testCaller->addThread("HostAndNetworkFailTest",
&HttpIntegrationTest::HostAndNetworkFailTest);
testCaller->addThread(
"HostAndNetworkFailTest", &HttpIntegrationTest::HostAndNetworkFailTest);
testCaller->addThread("GetTest", &HttpIntegrationTest::GetTest);
testCaller->addThread("GetWithBufferTest", &HttpIntegrationTest::GetWithBufferTest);
testCaller->addThread("HeadTest", &HttpIntegrationTest::HeadTest);
testCaller->addThread("NoContentTest", &HttpIntegrationTest::NoContentTest);
testCaller->addThread("AutoRedirectTest", &HttpIntegrationTest::AutoRedirectTest);
// testCaller->addThread("BasicAuthTest", &HttpIntegrationTest::BasicAuthTest);
// Skip BasicAuthTest for HTTPS: it seems like it does not close the socket properly,
// raising a SSL EOF error.
// Skip BasicAuthTest for HTTPS: it seems like it does not close the socket properly,
// raising a SSL EOF error.
testCaller->addThread("StopOnErrorTest", &HttpIntegrationTest::StopOnErrorTest);
testCaller->addThread("RequestCancelTest", &HttpIntegrationTest::RequestCancelTest);
testCaller->addThread("PostTest", &HttpIntegrationTest::PostTest);
@ -538,15 +524,13 @@ static const BHttpFields kExpectedGetFields = {
};
constexpr std::string_view kExpectedGetBody = {
"Path: /\r\n"
"\r\n"
"Headers:\r\n"
"--------\r\n"
"Host: 127.0.0.1:PORT\r\n"
"Accept-Encoding: gzip\r\n"
"Connection: close\r\n"
};
constexpr std::string_view kExpectedGetBody = {"Path: /\r\n"
"\r\n"
"Headers:\r\n"
"--------\r\n"
"Host: 127.0.0.1:PORT\r\n"
"Accept-Encoding: gzip\r\n"
"Connection: close\r\n"};
void
@ -583,8 +567,8 @@ HttpIntegrationTest::GetWithBufferTest()
auto result = fSession.Execute(std::move(request), BBorrow<BDataIO>(body), fLoggerMessenger);
try {
result.Body();
auto bodyString = std::string(reinterpret_cast<const char*>(body->Buffer()),
body->BufferLength());
auto bodyString
= std::string(reinterpret_cast<const char*>(body->Buffer()), body->BufferLength());
CPPUNIT_ASSERT_EQUAL(kExpectedGetBody, bodyString);
} catch (const BPrivate::Network::BError& e) {
CPPUNIT_FAIL(e.DebugMessage().String());
@ -597,7 +581,7 @@ HttpIntegrationTest::HeadTest()
{
auto request = BHttpRequest(BUrl(fTestServer.BaseUrl(), "/"));
request.SetMethod(BHttpMethod::Head);
auto result = fSession.Execute(std::move(request), nullptr, fLoggerMessenger);
auto result = fSession.Execute(std::move(request), nullptr, fLoggerMessenger);
try {
auto receivedFields = result.Fields();
CPPUNIT_ASSERT_EQUAL_MESSAGE("Mismatch in number of headers",
@ -627,7 +611,7 @@ void
HttpIntegrationTest::NoContentTest()
{
auto request = BHttpRequest(BUrl(fTestServer.BaseUrl(), "/204"));
auto result = fSession.Execute(std::move(request), nullptr, fLoggerMessenger);
auto result = fSession.Execute(std::move(request), nullptr, fLoggerMessenger);
try {
auto receivedStatus = result.Status();
CPPUNIT_ASSERT_EQUAL(204, receivedStatus.code);
@ -654,7 +638,7 @@ void
HttpIntegrationTest::AutoRedirectTest()
{
auto request = BHttpRequest(BUrl(fTestServer.BaseUrl(), "/302"));
auto result = fSession.Execute(std::move(request), nullptr, fLoggerMessenger);
auto result = fSession.Execute(std::move(request), nullptr, fLoggerMessenger);
try {
auto receivedFields = result.Fields();
@ -682,15 +666,15 @@ HttpIntegrationTest::BasicAuthTest()
// Basic Authentication
auto request = BHttpRequest(BUrl(fTestServer.BaseUrl(), "/auth/basic/walter/secret"));
request.SetAuthentication({"walter", "secret"});
auto result = fSession.Execute(std::move(request), nullptr, fLoggerMessenger);
auto result = fSession.Execute(std::move(request), nullptr, fLoggerMessenger);
CPPUNIT_ASSERT(result.Status().code == 200);
// Basic Authentication with incorrect credentials
try {
request = BHttpRequest(BUrl(fTestServer.BaseUrl(), "/auth/basic/walter/secret"));
request.SetAuthentication({"invaliduser", "invalidpassword"});
result = fSession.Execute(std::move(request), nullptr, fLoggerMessenger);
CPPUNIT_ASSERT(result.Status().code == 401);
request = BHttpRequest(BUrl(fTestServer.BaseUrl(), "/auth/basic/walter/secret"));
request.SetAuthentication({"invaliduser", "invalidpassword"});
result = fSession.Execute(std::move(request), nullptr, fLoggerMessenger);
CPPUNIT_ASSERT(result.Status().code == 401);
} catch (const BPrivate::Network::BError& e) {
CPPUNIT_FAIL(e.DebugMessage().String());
}
@ -703,7 +687,7 @@ HttpIntegrationTest::StopOnErrorTest()
// Test the Stop on Error functionality
auto request = BHttpRequest(BUrl(fTestServer.BaseUrl(), "/400"));
request.SetStopOnError(true);
auto result = fSession.Execute(std::move(request), nullptr, fLoggerMessenger);
auto result = fSession.Execute(std::move(request), nullptr, fLoggerMessenger);
CPPUNIT_ASSERT(result.Status().code == 400);
CPPUNIT_ASSERT(result.Fields().CountFields() == 0);
CPPUNIT_ASSERT(result.Body().text->Length() == 0);
@ -718,7 +702,7 @@ HttpIntegrationTest::RequestCancelTest()
// processed. In practise, the cancellation always comes first. When the server
// supports a wait parameter, then this test can be made more robust.
auto request = BHttpRequest(BUrl(fTestServer.BaseUrl(), "/"));
auto result = fSession.Execute(std::move(request), nullptr, fLoggerMessenger);
auto result = fSession.Execute(std::move(request), nullptr, fLoggerMessenger);
fSession.Cancel(result);
try {
result.Body();
@ -729,46 +713,45 @@ HttpIntegrationTest::RequestCancelTest()
}
static const BString kPostText =
"The MIT License\n"
"\n"
"Copyright (c) <year> <copyright holders>\n"
"\n"
"Permission is hereby granted, free of charge, to any person obtaining a copy\n"
"of this software and associated documentation files (the \"Software\"), to deal\n"
"in the Software without restriction, including without limitation the rights\n"
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
"copies of the Software, and to permit persons to whom the Software is\n"
"furnished to do so, subject to the following conditions:\n"
"\n"
"The above copyright notice and this permission notice shall be included in\n"
"all copies or substantial portions of the Software.\n"
"\n"
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n"
"THE SOFTWARE.\n"
"\n";
static const BString kPostText
= "The MIT License\n"
"\n"
"Copyright (c) <year> <copyright holders>\n"
"\n"
"Permission is hereby granted, free of charge, to any person obtaining a copy\n"
"of this software and associated documentation files (the \"Software\"), to deal\n"
"in the Software without restriction, including without limitation the rights\n"
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
"copies of the Software, and to permit persons to whom the Software is\n"
"furnished to do so, subject to the following conditions:\n"
"\n"
"The above copyright notice and this permission notice shall be included in\n"
"all copies or substantial portions of the Software.\n"
"\n"
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n"
"THE SOFTWARE.\n"
"\n";
static BString kExpectedPostBody
= BString().SetToFormat(
"Path: /post\r\n"
"\r\n"
"Headers:\r\n"
"--------\r\n"
"Host: 127.0.0.1:PORT\r\n"
"Accept-Encoding: gzip\r\n"
"Connection: close\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: 1083\r\n"
"\r\n"
"Request body:\r\n"
"-------------\r\n"
"%s\r\n", kPostText.String());
static BString kExpectedPostBody = BString().SetToFormat("Path: /post\r\n"
"\r\n"
"Headers:\r\n"
"--------\r\n"
"Host: 127.0.0.1:PORT\r\n"
"Accept-Encoding: gzip\r\n"
"Connection: close\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: 1083\r\n"
"\r\n"
"Request body:\r\n"
"-------------\r\n"
"%s\r\n",
kPostText.String());
void
@ -796,16 +779,15 @@ HttpIntegrationTest::PostTest()
usleep(2000); // give some time to catch up on receiving all messages
observer->Lock();
while (observer->IsMessageWaiting())
{
while (observer->IsMessageWaiting()) {
observer->Unlock();
usleep(1000); // give some time to catch up on receiving all messages
observer->Lock();
}
// Assert that the messages have the right contents.
CPPUNIT_ASSERT_MESSAGE("Expected at least 8 observer messages for this request.",
observer->messages.size() >= 8);
CPPUNIT_ASSERT_MESSAGE(
"Expected at least 8 observer messages for this request.", observer->messages.size() >= 8);
uint32 previousMessage = 0;
for (const auto& message: observer->messages) {
@ -817,20 +799,20 @@ HttpIntegrationTest::PostTest()
continue;
}
switch(previousMessage) {
switch (previousMessage) {
case 0:
CPPUNIT_ASSERT_MESSAGE("message should be HostNameResolved",
HostNameResolved == message.what);
CPPUNIT_ASSERT_MESSAGE(
"message should be HostNameResolved", HostNameResolved == message.what);
break;
case HostNameResolved:
CPPUNIT_ASSERT_MESSAGE("message should be ConnectionOpened",
ConnectionOpened == message.what);
CPPUNIT_ASSERT_MESSAGE(
"message should be ConnectionOpened", ConnectionOpened == message.what);
break;
case ConnectionOpened:
CPPUNIT_ASSERT_MESSAGE("message should be UploadProgress",
UploadProgress == message.what);
CPPUNIT_ASSERT_MESSAGE(
"message should be UploadProgress", UploadProgress == message.what);
[[fallthrough]];
case UploadProgress:
@ -851,20 +833,18 @@ HttpIntegrationTest::PostTest()
break;
case ResponseStarted:
CPPUNIT_ASSERT_MESSAGE("message should be HttpStatus",
HttpStatus == message.what);
CPPUNIT_ASSERT_MESSAGE("message should be HttpStatus", HttpStatus == message.what);
CPPUNIT_ASSERT_MESSAGE("message must have UrlEventData::HttpStatusCode data",
message.HasInt16(HttpStatusCode));
break;
case HttpStatus:
CPPUNIT_ASSERT_MESSAGE("message should be HttpFields",
HttpFields == message.what);
CPPUNIT_ASSERT_MESSAGE("message should be HttpFields", HttpFields == message.what);
break;
case HttpFields:
CPPUNIT_ASSERT_MESSAGE("message should be DownloadProgress",
DownloadProgress == message.what);
CPPUNIT_ASSERT_MESSAGE(
"message should be DownloadProgress", DownloadProgress == message.what);
[[fallthrough]];
case DownloadProgress:
@ -883,12 +863,12 @@ HttpIntegrationTest::PostTest()
case RequestCompleted:
CPPUNIT_ASSERT_MESSAGE("message must have UrlEventData::Success data",
message.HasBool(Success));
CPPUNIT_ASSERT_MESSAGE("UrlEventData::Success must be true",
message.GetBool(Success));
CPPUNIT_ASSERT_MESSAGE(
"UrlEventData::Success must be true", message.GetBool(Success));
break;
default:
CPPUNIT_FAIL("Expected DownloadProgress, BytesWritten or HttpStatus "
"message");
"message");
}
break;

View File

@ -17,16 +17,17 @@
using BPrivate::Network::BHttpSession;
class HttpProtocolTest: public BTestCase {
class HttpProtocolTest : public BTestCase
{
public:
HttpProtocolTest();
HttpProtocolTest();
void HttpFieldsTest();
void HttpMethodTest();
void HttpRequestTest();
void HttpTimeTest();
void HttpFieldsTest();
void HttpMethodTest();
void HttpRequestTest();
void HttpTimeTest();
static void AddTests(BTestSuite& suite);
static void AddTests(BTestSuite& suite);
};
@ -36,7 +37,7 @@ public:
HttpIntegrationTest(TestServerMode mode);
virtual void setUp() override;
virtual void tearDown() override;
virtual void tearDown() override;
void HostAndNetworkFailTest();
void GetTest();

View File

@ -23,8 +23,10 @@
namespace {
template <typename T>
std::string to_string(T value)
template<typename T>
std::string
to_string(T value)
{
std::ostringstream s;
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];
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.
std::string TestFilePath(const std::string& relativePath)
std::string
TestFilePath(const std::string& relativePath)
{
char *testFileSource = strdup(__FILE__);
char* testFileSource = strdup(__FILE__);
MemoryDeleter _(testFileSource);
std::string testSrcDir(::dirname(testFileSource));
@ -57,7 +61,7 @@ std::string TestFilePath(const std::string& relativePath)
return testSrcDir + "/" + relativePath;
}
}
} // namespace
RandomTCPServerPort::RandomTCPServerPort()
@ -70,10 +74,7 @@ RandomTCPServerPort::RandomTCPServerPort()
// kernel.
int socket_fd = ::socket(AF_INET, SOCK_STREAM, 0);
if (socket_fd == -1) {
fprintf(
stderr,
"ERROR: Unable to create socket: %s\n",
strerror(errno));
fprintf(stderr, "ERROR: Unable to create socket: %s\n", strerror(errno));
fInitStatus = B_ERROR;
return;
}
@ -84,18 +85,10 @@ RandomTCPServerPort::RandomTCPServerPort()
// for reuse.
{
int reuse = 1;
int result = ::setsockopt(
socket_fd,
SOL_SOCKET,
SO_REUSEPORT,
&reuse,
sizeof(reuse));
int result = ::setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
if (result == -1) {
fInitStatus = errno;
fprintf(
stderr,
"ERROR: Unable to set socket options on fd %d: %s\n",
socket_fd,
fprintf(stderr, "ERROR: Unable to set socket options on fd %d: %s\n", socket_fd,
strerror(fInitStatus));
return;
}
@ -106,15 +99,10 @@ RandomTCPServerPort::RandomTCPServerPort()
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
int bind_result = ::bind(
socket_fd,
reinterpret_cast<struct sockaddr*>(&server_address),
sizeof(server_address));
socket_fd, reinterpret_cast<struct sockaddr*>(&server_address), sizeof(server_address));
if (bind_result == -1) {
fInitStatus = errno;
fprintf(
stderr,
"ERROR: Unable to bind to loopback interface: %s\n",
strerror(fInitStatus));
fprintf(stderr, "ERROR: Unable to bind to loopback interface: %s\n", strerror(fInitStatus));
return;
}
@ -129,9 +117,7 @@ RandomTCPServerPort::RandomTCPServerPort()
// Now get the port from the socket.
socklen_t server_address_length = sizeof(server_address);
::getsockname(
socket_fd,
reinterpret_cast<struct sockaddr*>(&server_address),
&server_address_length);
socket_fd, reinterpret_cast<struct sockaddr*>(&server_address), &server_address_length);
fServerPort = ntohs(server_address.sin_port);
fInitStatus = B_OK;
@ -148,19 +134,22 @@ RandomTCPServerPort::~RandomTCPServerPort()
}
status_t RandomTCPServerPort::InitCheck() const
status_t
RandomTCPServerPort::InitCheck() const
{
return fInitStatus;
}
int RandomTCPServerPort::FileDescriptor() const
int
RandomTCPServerPort::FileDescriptor() const
{
return fSocketFd;
}
uint16_t RandomTCPServerPort::Port() const
uint16_t
RandomTCPServerPort::Port() const
{
return fServerPort;
}
@ -188,7 +177,8 @@ ChildProcess::~ChildProcess()
// The job of this method is to spawn a child process that will later be killed
// 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) {
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.
std::ostringstream ostr;
for (std::vector<std::string>::const_iterator iter = args.begin();
iter != args.end();
++iter) {
for (std::vector<std::string>::const_iterator iter = args.begin(); iter != args.end(); ++iter) {
ostr << " " << *iter;
}
fprintf(
stderr,
"Unable to spawn `%s': %s\n",
ostr.str().c_str(),
strerror(errno));
fprintf(stderr, "Unable to spawn `%s': %s\n", ostr.str().c_str(), strerror(errno));
exit(1);
}
@ -233,7 +217,8 @@ TestServer::TestServer(TestServerMode mode)
// Start a child testserver.py process with the random TCP port chosen by
// fPort.
status_t TestServer::Start()
status_t
TestServer::Start()
{
if (fPort.InitCheck() != B_OK) {
return fPort.InitCheck();
@ -241,10 +226,7 @@ status_t TestServer::Start()
auto testFilePath = TestFilePath("testserver.py");
if (::access(testFilePath.data(), R_OK) != 0) {
fprintf(
stderr,
"ERROR: No access to the test server script at: %s\n",
testFilePath.data());
fprintf(stderr, "ERROR: No access to the test server script at: %s\n", testFilePath.data());
return B_IO_ERROR;
}
@ -272,17 +254,18 @@ status_t TestServer::Start()
}
BUrl TestServer::BaseUrl() const
BUrl
TestServer::BaseUrl() const
{
std::string scheme;
switch(fMode) {
case TestServerMode::Http:
scheme = "http://";
break;
switch (fMode) {
case TestServerMode::Http:
scheme = "http://";
break;
case TestServerMode::Https:
scheme = "https://";
break;
case TestServerMode::Https:
scheme = "https://";
break;
}
std::string port_string = to_string(fPort.Port());
@ -293,7 +276,8 @@ BUrl TestServer::BaseUrl() const
// 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) {
return fPort.InitCheck();
@ -301,10 +285,7 @@ status_t TestProxyServer::Start()
auto testFilePath = TestFilePath("proxy.py");
if (::access(testFilePath.data(), R_OK) != 0) {
fprintf(
stderr,
"ERROR: No access to the test server script at: %s\n",
testFilePath.data());
fprintf(stderr, "ERROR: No access to the test server script at: %s\n", testFilePath.data());
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();
}

View File

@ -16,30 +16,33 @@
// Binds to a random unused TCP port.
class RandomTCPServerPort {
class RandomTCPServerPort
{
public:
RandomTCPServerPort();
~RandomTCPServerPort();
RandomTCPServerPort();
~RandomTCPServerPort();
status_t InitCheck() const;
int FileDescriptor() const;
uint16_t Port() const;
status_t InitCheck() const;
int FileDescriptor() const;
uint16_t Port() const;
private:
status_t fInitStatus;
int fSocketFd;
uint16_t fServerPort;
status_t fInitStatus;
int fSocketFd;
uint16_t fServerPort;
};
class ChildProcess {
class ChildProcess
{
public:
ChildProcess();
~ChildProcess();
ChildProcess();
~ChildProcess();
status_t Start(const std::vector<std::string>& args);
status_t Start(const std::vector<std::string>& args);
private:
pid_t fChildPid;
pid_t fChildPid;
};
@ -49,28 +52,30 @@ enum class TestServerMode {
};
class TestServer {
class TestServer
{
public:
TestServer(TestServerMode mode);
TestServer(TestServerMode mode);
status_t Start();
BUrl BaseUrl() const;
status_t Start();
BUrl BaseUrl() const;
private:
TestServerMode fMode;
ChildProcess fChildProcess;
TestServerMode fMode;
ChildProcess fChildProcess;
RandomTCPServerPort fPort;
};
class TestProxyServer {
class TestProxyServer
{
public:
status_t Start();
uint16_t Port() const;
status_t Start();
uint16_t Port() const;
private:
ChildProcess fChildProcess;
RandomTCPServerPort fPort;
ChildProcess fChildProcess;
RandomTCPServerPort fPort;
};