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