NetServices: implement hostname resolution and connection for BHttpRequest
BHttpSession::Execute() moves the request into the session, and returns a future BHttpResponse object. Currently implemented are resolving the hostname, and opening the connection. There is some scaffolding for the actual data transfer. Change-Id: I5a8a7a7f8680036b91cdba4beee140bbed6bfd5a
This commit is contained in:
@@ -13,6 +13,16 @@
|
|||||||
|
|
||||||
#if __cplusplus >= 201703L
|
#if __cplusplus >= 201703L
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\file HttpSession.h
|
||||||
|
\ingroup netservices
|
||||||
|
\brief Provides classes and tools to schedule and execute HTTP requests.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace BPrivate {
|
namespace BPrivate {
|
||||||
|
|
||||||
namespace Network {
|
namespace Network {
|
||||||
@@ -153,6 +163,26 @@ namespace Network {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn BHttpResult BHttpSession::Execute(BHttpRequest &&request,
|
||||||
|
std::unique_ptr< BDataIO > target=nullptr, BMessenger observer=BMessenger())
|
||||||
|
\brief Schedule and execute a \a request.
|
||||||
|
|
||||||
|
\param request The (valid) request to move from.
|
||||||
|
\param target An optional data buffer to write the incoming body of the request to. This can be
|
||||||
|
\c nullptr if you want to use the default internal storage. If you provide a buffer, it
|
||||||
|
must be wrapped in a \c std::unique_ptr. This means that you transfer ownership to the
|
||||||
|
session. After the request is finished, you can regain ownership.
|
||||||
|
\param observer An optional observer that will receive the progress and status messages for
|
||||||
|
this request.
|
||||||
|
|
||||||
|
\return The \ref BHttpResult object that corresponds to this request, and that can be used to
|
||||||
|
monitor the progress.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
} // namespace Network
|
} // namespace Network
|
||||||
|
|
||||||
} // namespace BPrivate
|
} // namespace BPrivate
|
||||||
|
|||||||
@@ -88,8 +88,9 @@ public:
|
|||||||
void SetUrl(const BUrl& url);
|
void SetUrl(const BUrl& url);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Impl;
|
friend class BHttpSession;
|
||||||
std::unique_ptr<Impl> fData;
|
struct Data;
|
||||||
|
std::unique_ptr<Data> fData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2021 Haiku Inc. All rights reserved.
|
* Copyright 2022 Haiku Inc. All rights reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -8,12 +8,18 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <Messenger.h>
|
||||||
|
|
||||||
class BUrl;
|
class BUrl;
|
||||||
|
|
||||||
|
|
||||||
namespace BPrivate {
|
namespace BPrivate {
|
||||||
|
|
||||||
namespace Network {
|
namespace Network {
|
||||||
|
|
||||||
|
class BHttpRequest;
|
||||||
|
class BHttpResult;
|
||||||
|
|
||||||
|
|
||||||
class BHttpSession {
|
class BHttpSession {
|
||||||
public:
|
public:
|
||||||
@@ -27,6 +33,12 @@ public:
|
|||||||
BHttpSession& operator=(const BHttpSession&) noexcept;
|
BHttpSession& operator=(const BHttpSession&) noexcept;
|
||||||
BHttpSession& operator=(BHttpSession&&) noexcept = delete;
|
BHttpSession& operator=(BHttpSession&&) noexcept = delete;
|
||||||
|
|
||||||
|
|
||||||
|
// Requests
|
||||||
|
BHttpResult Execute(BHttpRequest&& request,
|
||||||
|
std::unique_ptr<BDataIO> target = nullptr,
|
||||||
|
BMessenger observer = BMessenger());
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Request;
|
class Request;
|
||||||
class Impl;
|
class Impl;
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <HttpRequest.h>
|
||||||
|
#include <Url.h>
|
||||||
|
|
||||||
|
|
||||||
namespace BPrivate {
|
namespace BPrivate {
|
||||||
|
|
||||||
|
|||||||
@@ -130,15 +130,14 @@ BHttpMethod::Method() const noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -- BHttpRequest::Impl
|
// #pragma mark -- BHttpRequest::Data
|
||||||
static const BUrl kDefaultUrl = BUrl();
|
static const BUrl kDefaultUrl = BUrl();
|
||||||
static const BHttpMethod kDefaultMethod = BHttpMethod::Get;
|
static const BHttpMethod kDefaultMethod = BHttpMethod::Get;
|
||||||
|
|
||||||
|
|
||||||
struct BHttpRequest::Impl {
|
struct BHttpRequest::Data {
|
||||||
BUrl url;
|
BUrl url = kDefaultUrl;
|
||||||
BHttpMethod method = kDefaultMethod;
|
BHttpMethod method = kDefaultMethod;
|
||||||
bool ssl = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -146,14 +145,14 @@ struct BHttpRequest::Impl {
|
|||||||
|
|
||||||
|
|
||||||
BHttpRequest::BHttpRequest()
|
BHttpRequest::BHttpRequest()
|
||||||
: fData(std::make_unique<Impl>())
|
: fData(std::make_unique<Data>())
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BHttpRequest::BHttpRequest(const BUrl& url)
|
BHttpRequest::BHttpRequest(const BUrl& url)
|
||||||
: fData(std::make_unique<Impl>())
|
: fData(std::make_unique<Data>())
|
||||||
{
|
{
|
||||||
SetUrl(url);
|
SetUrl(url);
|
||||||
}
|
}
|
||||||
@@ -198,7 +197,7 @@ void
|
|||||||
BHttpRequest::SetMethod(const BHttpMethod& method)
|
BHttpRequest::SetMethod(const BHttpMethod& method)
|
||||||
{
|
{
|
||||||
if (!fData)
|
if (!fData)
|
||||||
fData = std::make_unique<Impl>();
|
fData = std::make_unique<Data>();
|
||||||
fData->method = method;
|
fData->method = method;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,15 +206,11 @@ void
|
|||||||
BHttpRequest::SetUrl(const BUrl& url)
|
BHttpRequest::SetUrl(const BUrl& url)
|
||||||
{
|
{
|
||||||
if (!fData)
|
if (!fData)
|
||||||
fData = std::make_unique<Impl>();
|
fData = std::make_unique<Data>();
|
||||||
|
|
||||||
if (!url.IsValid())
|
if (!url.IsValid())
|
||||||
throw BInvalidUrl(__PRETTY_FUNCTION__, BUrl(url));
|
throw BInvalidUrl(__PRETTY_FUNCTION__, BUrl(url));
|
||||||
if (url.Protocol() == "http")
|
if (url.Protocol() != "http" && url.Protocol() != "https") {
|
||||||
fData->ssl = false;
|
|
||||||
else if (url.Protocol() == "https")
|
|
||||||
fData->ssl = true;
|
|
||||||
else {
|
|
||||||
// TODO: optimize BStringList with modern language features
|
// TODO: optimize BStringList with modern language features
|
||||||
BStringList list;
|
BStringList list;
|
||||||
list.Add("http");
|
list.Add("http");
|
||||||
|
|||||||
@@ -10,15 +10,86 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <AutoLocker.h>
|
||||||
|
#include <DynamicBuffer.h>
|
||||||
#include <ErrorsExt.h>
|
#include <ErrorsExt.h>
|
||||||
|
#include <HttpFields.h>
|
||||||
|
#include <HttpRequest.h>
|
||||||
|
#include <HttpResult.h>
|
||||||
#include <HttpSession.h>
|
#include <HttpSession.h>
|
||||||
#include <Locker.h>
|
#include <Locker.h>
|
||||||
|
#include <Messenger.h>
|
||||||
|
#include <NetBuffer.h>
|
||||||
|
#include <NetServicesDefs.h>
|
||||||
|
#include <NetworkAddress.h>
|
||||||
#include <OS.h>
|
#include <OS.h>
|
||||||
|
#include <SecureSocket.h>
|
||||||
|
#include <Socket.h>
|
||||||
|
#include <StackOrHeapArray.h>
|
||||||
|
#include <ZlibCompressionAlgorithm.h>
|
||||||
|
|
||||||
|
#include "HttpResultPrivate.h"
|
||||||
|
#include "NetServicesPrivate.h"
|
||||||
|
|
||||||
using namespace BPrivate::Network;
|
using namespace BPrivate::Network;
|
||||||
|
|
||||||
|
|
||||||
class BHttpSession::Request {
|
class BHttpSession::Request {
|
||||||
|
public:
|
||||||
|
Request(BHttpRequest&& request,
|
||||||
|
std::unique_ptr<BDataIO> target,
|
||||||
|
BMessenger observer);
|
||||||
|
|
||||||
|
// States
|
||||||
|
enum RequestState {
|
||||||
|
InitialState,
|
||||||
|
Connected,
|
||||||
|
StatusReceived,
|
||||||
|
HeadersReceived,
|
||||||
|
ContentReceived,
|
||||||
|
TrailingHeadersReceived
|
||||||
|
};
|
||||||
|
RequestState State() const { return fRequestStatus; }
|
||||||
|
|
||||||
|
// Result Helpers
|
||||||
|
std::shared_ptr<HttpResultPrivate>
|
||||||
|
Result() { return fResult; }
|
||||||
|
void SetError(std::exception_ptr e) { fResult->SetError(e); }
|
||||||
|
|
||||||
|
// Operational methods
|
||||||
|
void ResolveHostName();
|
||||||
|
void OpenConnection();
|
||||||
|
|
||||||
|
//
|
||||||
|
private:
|
||||||
|
BHttpRequest fRequest;
|
||||||
|
|
||||||
|
// Request state/events
|
||||||
|
RequestState fRequestStatus = InitialState;
|
||||||
|
|
||||||
|
// Communication
|
||||||
|
BMessenger fObserver;
|
||||||
|
std::shared_ptr<HttpResultPrivate> fResult;
|
||||||
|
|
||||||
|
// Connection
|
||||||
|
BNetworkAddress fRemoteAddress;
|
||||||
|
std::unique_ptr<BSocket> fSocket;
|
||||||
|
|
||||||
|
// Receive state
|
||||||
|
/* bool receiveEnd = false;
|
||||||
|
bool parseEnd = false;
|
||||||
|
BNetBuffer inputBuffer;
|
||||||
|
size_t previousBufferSize = 0;
|
||||||
|
off_t bytesReceived = 0;
|
||||||
|
off_t bytesTotal = 0;
|
||||||
|
BHttpFields headers;
|
||||||
|
bool readByChunks = false;
|
||||||
|
bool decompress = false;
|
||||||
|
DynamicBuffer decompressorStorage;
|
||||||
|
std::unique_ptr<BDataIO> decompressingStream = nullptr;
|
||||||
|
std::vector<char> inputTempBuffer = std::vector<char>(4096);
|
||||||
|
BHttpStatus status; */
|
||||||
|
// TODO: reset method to reset Connection and Receive State when redirected
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -28,6 +99,10 @@ public:
|
|||||||
Impl();
|
Impl();
|
||||||
~Impl() noexcept;
|
~Impl() noexcept;
|
||||||
|
|
||||||
|
BHttpResult Execute(BHttpRequest&& request,
|
||||||
|
std::unique_ptr<BDataIO> target,
|
||||||
|
BMessenger observer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Thread functions
|
// Thread functions
|
||||||
static status_t ControlThreadFunc(void* arg);
|
static status_t ControlThreadFunc(void* arg);
|
||||||
@@ -55,6 +130,9 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -- BHttpSession::Impl
|
||||||
|
|
||||||
|
|
||||||
BHttpSession::Impl::Impl()
|
BHttpSession::Impl::Impl()
|
||||||
:
|
:
|
||||||
fControlQueueSem(create_sem(0, "http:control")),
|
fControlQueueSem(create_sem(0, "http:control")),
|
||||||
@@ -92,15 +170,112 @@ BHttpSession::Impl::~Impl() noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BHttpResult
|
||||||
|
BHttpSession::Impl::Execute(BHttpRequest&& request, std::unique_ptr<BDataIO> target,
|
||||||
|
BMessenger observer)
|
||||||
|
{
|
||||||
|
auto wRequest = Request(std::move(request), std::move(target), observer);
|
||||||
|
|
||||||
|
auto retval = BHttpResult(wRequest.Result());
|
||||||
|
auto lock = AutoLocker<BLocker>(fLock);
|
||||||
|
fControlQueue.push_back(std::move(wRequest));
|
||||||
|
release_sem(fControlQueueSem);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*static*/ status_t
|
/*static*/ status_t
|
||||||
BHttpSession::Impl::ControlThreadFunc(void* arg)
|
BHttpSession::Impl::ControlThreadFunc(void* arg)
|
||||||
{
|
{
|
||||||
BHttpSession::Impl* impl = static_cast<BHttpSession::Impl*>(arg);
|
BHttpSession::Impl* impl = static_cast<BHttpSession::Impl*>(arg);
|
||||||
|
|
||||||
|
// Outer loop to use the fControlQueueSem when new items have entered the queue
|
||||||
|
while (true) {
|
||||||
|
if (auto status = acquire_sem(impl->fControlQueueSem); status == B_INTERRUPTED)
|
||||||
|
continue;
|
||||||
|
else if (status != B_OK) {
|
||||||
|
// Most likely B_BAD_SEM_ID indicating that the sem was deleted; go to cleanup
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inner loop to process items on the queue
|
||||||
|
while (true) {
|
||||||
|
impl->fLock.Lock();
|
||||||
|
if (impl->fControlQueue.empty() || atomic_get(&impl->fQuitting) == 1) {
|
||||||
|
impl->fLock.Unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto request = std::move(impl->fControlQueue.front());
|
||||||
|
impl->fControlQueue.pop_front();
|
||||||
|
impl->fLock.Unlock();
|
||||||
|
|
||||||
|
switch (request.State()) {
|
||||||
|
case Request::InitialState:
|
||||||
|
{
|
||||||
|
bool hasError = false;
|
||||||
|
try {
|
||||||
|
request.ResolveHostName();
|
||||||
|
request.OpenConnection();
|
||||||
|
} catch (...) {
|
||||||
|
request.SetError(std::current_exception());
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasError) {
|
||||||
|
// Do not add the request back to the queue
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: temporary end of the line here, as data thread not implemented
|
||||||
|
try {
|
||||||
|
throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::Canceled);
|
||||||
|
} catch (...) {
|
||||||
|
request.SetError(std::current_exception());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
impl->fLock.Lock();
|
||||||
|
impl->fDataQueue.push_back(std::move(request));
|
||||||
|
impl->fLock.Unlock();
|
||||||
|
release_sem(impl->fDataQueueSem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// not handled at this stage
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up and make sure we are quitting
|
||||||
|
if (atomic_get(&impl->fQuitting) == 1) {
|
||||||
|
// First wait for the data thread to complete
|
||||||
|
status_t threadResult;
|
||||||
|
wait_for_thread(impl->fDataThread, &threadResult);
|
||||||
|
// Cancel all requests
|
||||||
|
for (auto& request: impl->fControlQueue) {
|
||||||
|
try {
|
||||||
|
throw BNetworkRequestError(__PRETTY_FUNCTION__, BNetworkRequestError::Canceled);
|
||||||
|
} catch (...) {
|
||||||
|
request.SetError(std::current_exception());
|
||||||
|
}
|
||||||
|
/* TODO
|
||||||
|
if (request.observer.IsValid()) {
|
||||||
|
BMessage msg(UrlEvent::RequestCompleted);
|
||||||
|
msg.AddInt32(UrlEventData::Id, request.result->id);
|
||||||
|
msg.AddBool(UrlEventData::Success, false);
|
||||||
|
request.observer.SendMessage(&msg);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw BRuntimeError(__PRETTY_FUNCTION__,
|
||||||
|
"Unknown reason that the controlQueueSem is deleted");
|
||||||
|
}
|
||||||
|
|
||||||
// Cleanup: wait for data thread
|
// Cleanup: wait for data thread
|
||||||
status_t threadResult;
|
return B_OK;
|
||||||
wait_for_thread(impl->fDataThread, &threadResult);
|
|
||||||
return threadResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -112,16 +287,16 @@ BHttpSession::Impl::DataThreadFunc(void* arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -- BHttpSession (public interface)
|
||||||
|
|
||||||
|
|
||||||
BHttpSession::BHttpSession()
|
BHttpSession::BHttpSession()
|
||||||
{
|
{
|
||||||
fImpl = std::make_shared<BHttpSession::Impl>();
|
fImpl = std::make_shared<BHttpSession::Impl>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BHttpSession::~BHttpSession()
|
BHttpSession::~BHttpSession() = default;
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BHttpSession::BHttpSession(const BHttpSession&) noexcept = default;
|
BHttpSession::BHttpSession(const BHttpSession&) noexcept = default;
|
||||||
@@ -129,3 +304,80 @@ BHttpSession::BHttpSession(const BHttpSession&) noexcept = default;
|
|||||||
|
|
||||||
BHttpSession&
|
BHttpSession&
|
||||||
BHttpSession::operator=(const BHttpSession&) noexcept = default;
|
BHttpSession::operator=(const BHttpSession&) noexcept = default;
|
||||||
|
|
||||||
|
|
||||||
|
BHttpResult
|
||||||
|
BHttpSession::Execute(BHttpRequest&& request, std::unique_ptr<BDataIO> target, BMessenger observer)
|
||||||
|
{
|
||||||
|
return fImpl->Execute(std::move(request), std::move(target), observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -- BHttpSession::Request (helpers)
|
||||||
|
|
||||||
|
BHttpSession::Request::Request(BHttpRequest&& request, std::unique_ptr<BDataIO> target,
|
||||||
|
BMessenger observer)
|
||||||
|
: fRequest(std::move(request)), fObserver(observer)
|
||||||
|
{
|
||||||
|
auto identifier = get_netservices_request_identifier();
|
||||||
|
|
||||||
|
// create shared data
|
||||||
|
fResult = std::make_shared<HttpResultPrivate>(identifier);
|
||||||
|
fResult->owned_body = std::move(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Resolve the hostname for a request
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
BHttpSession::Request::ResolveHostName()
|
||||||
|
{
|
||||||
|
int port;
|
||||||
|
if (fRequest.Url().HasPort())
|
||||||
|
port = fRequest.Url().Port();
|
||||||
|
else if (fRequest.Url().Protocol() == "https")
|
||||||
|
port = 443;
|
||||||
|
else
|
||||||
|
port = 80;
|
||||||
|
|
||||||
|
// TODO: proxy
|
||||||
|
if (auto status = fRemoteAddress.SetTo(fRequest.Url().Host(), port); status != B_OK) {
|
||||||
|
throw BNetworkRequestError("BNetworkAddress::SetTo()",
|
||||||
|
BNetworkRequestError::HostnameError, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Open the connection and make the socket non-blocking after opening it
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
BHttpSession::Request::OpenConnection()
|
||||||
|
{
|
||||||
|
// Set up the socket
|
||||||
|
if (fRequest.Url().Protocol() == "https") {
|
||||||
|
// To do: secure socket with callbacks to check certificates
|
||||||
|
fSocket = std::make_unique<BSecureSocket>();
|
||||||
|
} else {
|
||||||
|
fSocket = std::make_unique<BSocket>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open connection
|
||||||
|
if (auto status = fSocket->Connect(fRemoteAddress); status != B_OK) {
|
||||||
|
// TODO: inform listeners that the connection failed
|
||||||
|
throw BNetworkRequestError("BSocket::Connect()",
|
||||||
|
BNetworkRequestError::NetworkError, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the rest of the interaction non-blocking
|
||||||
|
auto flags = fcntl(fSocket->Socket(), F_GETFL, 0);
|
||||||
|
if (flags == -1)
|
||||||
|
throw BRuntimeError("fcntl()", "Error getting socket flags");
|
||||||
|
if (fcntl(fSocket->Socket(), F_SETFL, flags | O_NONBLOCK) != 0)
|
||||||
|
throw BRuntimeError("fcntl()", "Error setting non-blocking flag on socket");
|
||||||
|
|
||||||
|
// TODO: inform the listeners that the connection was opened.
|
||||||
|
|
||||||
|
fRequestStatus = Connected;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
SubDir HAIKU_TOP src kits network libnetservices2 ;
|
SubDir HAIKU_TOP src kits network libnetservices2 ;
|
||||||
|
|
||||||
|
UsePrivateHeaders net ;
|
||||||
UsePrivateHeaders netservices2 ;
|
UsePrivateHeaders netservices2 ;
|
||||||
|
UsePrivateHeaders support ;
|
||||||
|
UsePrivateHeaders shared ;
|
||||||
|
|
||||||
local architectureObject ;
|
local architectureObject ;
|
||||||
for architectureObject in [ MultiArchSubDirSetup ] {
|
for architectureObject in [ MultiArchSubDirSetup ] {
|
||||||
|
|||||||
@@ -8,7 +8,10 @@
|
|||||||
|
|
||||||
#include <NetServicesDefs.h>
|
#include <NetServicesDefs.h>
|
||||||
|
|
||||||
using namespace BPrivate::Network;
|
|
||||||
|
namespace BPrivate {
|
||||||
|
|
||||||
|
namespace Network {
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -- BUnsupportedProtocol
|
// #pragma mark -- BUnsupportedProtocol
|
||||||
@@ -147,3 +150,21 @@ BNetworkRequestError::ErrorCode() const noexcept
|
|||||||
{
|
{
|
||||||
return fErrorCode;
|
return fErrorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -- Private functions and data
|
||||||
|
|
||||||
|
|
||||||
|
static int32 gRequestIdentifier = 1;
|
||||||
|
|
||||||
|
|
||||||
|
int32
|
||||||
|
get_netservices_request_identifier()
|
||||||
|
{
|
||||||
|
return atomic_add(&gRequestIdentifier, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace Network
|
||||||
|
|
||||||
|
} // namespace BPrivate
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Niels Sascha Reedijk, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NET_SERVICES_PRIVATE_H_
|
||||||
|
#define _NET_SERVICES_PRIVATE_H_
|
||||||
|
|
||||||
|
|
||||||
|
namespace BPrivate {
|
||||||
|
|
||||||
|
namespace Network {
|
||||||
|
|
||||||
|
|
||||||
|
int32 get_netservices_request_identifier();
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace Network
|
||||||
|
|
||||||
|
} // namespace BPrivate
|
||||||
|
|
||||||
|
#endif // _NET_SERVICES_PRIVATE_H
|
||||||
@@ -14,12 +14,16 @@
|
|||||||
|
|
||||||
#include <HttpFields.h>
|
#include <HttpFields.h>
|
||||||
#include <HttpRequest.h>
|
#include <HttpRequest.h>
|
||||||
|
#include <HttpResult.h>
|
||||||
|
#include <NetServicesDefs.h>
|
||||||
#include <Url.h>
|
#include <Url.h>
|
||||||
|
|
||||||
using BPrivate::Network::BHttpFields;
|
using BPrivate::Network::BHttpFields;
|
||||||
using BPrivate::Network::BHttpMethod;
|
using BPrivate::Network::BHttpMethod;
|
||||||
using BPrivate::Network::BHttpRequest;
|
using BPrivate::Network::BHttpRequest;
|
||||||
using BPrivate::Network::BHttpSession;
|
using BPrivate::Network::BHttpSession;
|
||||||
|
using BPrivate::Network::BHttpResult;
|
||||||
|
using BPrivate::Network::BNetworkRequestError;
|
||||||
|
|
||||||
|
|
||||||
HttpProtocolTest::HttpProtocolTest()
|
HttpProtocolTest::HttpProtocolTest()
|
||||||
@@ -233,6 +237,54 @@ HttpProtocolTest::HttpRequestTest()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
HttpProtocolTest::HttpIntegrationTest()
|
||||||
|
{
|
||||||
|
// Test hostname resolution fail
|
||||||
|
{
|
||||||
|
auto request = BHttpRequest(BUrl("http://doesnotexist/"));
|
||||||
|
auto result = fSession.Execute(std::move(request));
|
||||||
|
try {
|
||||||
|
result.Status();
|
||||||
|
CPPUNIT_FAIL("Expecting exception when trying to connect to invalid hostname");
|
||||||
|
} catch (const BNetworkRequestError& e) {
|
||||||
|
CPPUNIT_ASSERT_EQUAL(BNetworkRequestError::HostnameError, e.Type());
|
||||||
|
} catch (...) {
|
||||||
|
CPPUNIT_FAIL("Unknown exception raised when getting invalid hostname");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test connection error fail
|
||||||
|
{
|
||||||
|
// FIXME: find a better way to get an unused local port, instead of hardcoding one
|
||||||
|
auto request = BHttpRequest(BUrl("http://localhost:59445/"));
|
||||||
|
auto result = fSession.Execute(std::move(request));
|
||||||
|
try {
|
||||||
|
result.Status();
|
||||||
|
CPPUNIT_FAIL("Expecting exception when trying to connect to invalid hostname");
|
||||||
|
} catch (const BNetworkRequestError& e) {
|
||||||
|
CPPUNIT_ASSERT_EQUAL(BNetworkRequestError::NetworkError, e.Type());
|
||||||
|
} catch (...) {
|
||||||
|
CPPUNIT_FAIL("Unknown exception raised when getting invalid hostname");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Succesful connection (fails as canceled right now)
|
||||||
|
{
|
||||||
|
auto request = BHttpRequest(BUrl("https://www.haiku-os.org/"));
|
||||||
|
auto result = fSession.Execute(std::move(request));
|
||||||
|
try {
|
||||||
|
result.Status();
|
||||||
|
CPPUNIT_FAIL("Expecting exception");
|
||||||
|
} catch (const BNetworkRequestError& e) {
|
||||||
|
CPPUNIT_ASSERT_EQUAL(BNetworkRequestError::Canceled, e.Type());
|
||||||
|
} catch (...) {
|
||||||
|
CPPUNIT_FAIL("Unknown exception raised when executing request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* static */ void
|
/* static */ void
|
||||||
HttpProtocolTest::AddTests(BTestSuite& parent)
|
HttpProtocolTest::AddTests(BTestSuite& parent)
|
||||||
{
|
{
|
||||||
@@ -244,6 +296,8 @@ HttpProtocolTest::AddTests(BTestSuite& parent)
|
|||||||
"HttpProtocolTest::HttpMethodTest", &HttpProtocolTest::HttpMethodTest));
|
"HttpProtocolTest::HttpMethodTest", &HttpProtocolTest::HttpMethodTest));
|
||||||
suite.addTest(new CppUnit::TestCaller<HttpProtocolTest>(
|
suite.addTest(new CppUnit::TestCaller<HttpProtocolTest>(
|
||||||
"HttpProtocolTest::HttpRequestTest", &HttpProtocolTest::HttpRequestTest));
|
"HttpProtocolTest::HttpRequestTest", &HttpProtocolTest::HttpRequestTest));
|
||||||
|
suite.addTest(new CppUnit::TestCaller<HttpProtocolTest>(
|
||||||
|
"HttpProtocolTest::HttpIntegrationTest", &HttpProtocolTest::HttpIntegrationTest));
|
||||||
|
|
||||||
parent.addTest("HttpProtocolTest", &suite);
|
parent.addTest("HttpProtocolTest", &suite);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ public:
|
|||||||
void HttpFieldsTest();
|
void HttpFieldsTest();
|
||||||
void HttpMethodTest();
|
void HttpMethodTest();
|
||||||
void HttpRequestTest();
|
void HttpRequestTest();
|
||||||
|
void HttpIntegrationTest();
|
||||||
|
|
||||||
static void AddTests(BTestSuite& suite);
|
static void AddTests(BTestSuite& suite);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user