HTTPClient is a module for sending HTTP requests to and receiving responses from a URL. It uses the curl library under the hood, but this is an implementation detail not exposed to the users of the module.
It supports the following features:
- Sending requests with
- various methods (
GET, POST, PUT, HEAD, etc.)
- custom headers
- custom content with content types
- multipart form data
- Asynchronous request execution
- Sending and receiving
IO::Files
- Redirect handling
- Timeouts
- Built-in logging
Testing and debugging considerations
The module by default validates certificates used by HTTPS utilizing the OS’s certificate store.
This might be problematic if an HTTP mock server is used by tests.
This check can be bypassed by setting the following environment variable: GS_HTTP_CLIENT_SSL_CERTIFICATE_VERIFICATION_DISABLED
Examples
A simple HEAD request
HTTPClient::PendingResult pendingResult = HTTPClient::Head (IO::URI::URI ("https://graphisoft.com"));
pendingResult.Wait ();
auto result = pendingResult.AcquireResult ();
if (result.IsValue () && result->GetStatusCode () == HTTPClient::StandardStatusCode::OK)
std::cout << "GS website seems reachable and functioning" << std::endl;
A simple GET request
HTTPClient::PendingResult pendingResult = HTTPClient::Get (IO::URI::URI ("https://httpbin.org/uuid"));
pendingResult.Wait ();
auto result = pendingResult.AcquireResult ();
if (result.IsValue () && result->GetStatusCode () == HTTPClient::StandardStatusCode::OK) {
rapidjson::Document document;
document.Parse (result->GetContent ()->ReadAsUniString ().ToCStr ());
std::cout << "Received UUID: " << document["uuid"].GetString () << std::endl;
} else {
std::cout << "Unexpected result!" << std::endl;
}
Adding request headers
HTTPClient::Headers headers = { { HTTPClient::StandardHeaderFields::Authorization, "Bearer 123" } };
HTTPClient::PendingResult pendingResult = HTTPClient::Get (IO::URI::URI ("https://httpbin.org/bearer"), headers);
pendingResult.Wait ();
auto result = pendingResult.AcquireResult ();
if (result.IsValue () && result->GetStatusCode () == HTTPClient::StandardStatusCode::OK)
std::cout << "Successful authentication!" << std::endl;
else
std::cout << "Authentication failed! " << std::endl;
Asynchronous operation
HTTPClient::PendingResult pendingResult = HTTPClient::Get (IO::URI::URI ("https://httpbin.org/delay/5"));
std::cout << "Waiting for request to complete";
do {
pendingResult.Wait (100, false);
std::cout << "." << std::flush;
} while (!pendingResult.IsCompleted ());
std::cout << std::endl;
auto result = pendingResult.AcquireResult ();
if (result.IsValue () && result->GetStatusCode () == HTTPClient::StandardStatusCode::OK)
std::cout << "Request completed successfully!" << std::endl;
else
std::cout << "Unexpected result!" << std::endl;
Response content downloaded to a file
IO::Location fileLocation;
IO::fileSystem.GetSpecialLocation (IO::FileSystem::TemporaryFolder, &fileLocation);
fileLocation.AppendToLocal (IO::Name ("result" + GS::Guid (GS::Guid::GenerateGuidSelector::GenerateGuid).ToUniString () + ".jpg"));
HTTPClient::OutputFile filePtr = new IO::File (fileLocation, IO::File::OnNotFound::Create);
filePtr->Open (IO::File::OpenMode::ReadWriteMode);
HTTPClient::PendingResult pendingResult = HTTPClient::Get (IO::URI::URI ("https://httpbin.org/image/jpeg"),
HTTPClient::Options {}.SetOutputFile (filePtr));
pendingResult.Wait ();
auto result = pendingResult.AcquireResult ();
if (result.IsValue () && result->GetStatusCode () == HTTPClient::StandardStatusCode::OK) {
UInt64 size;
filePtr->GetDataLength (&size);
std::cout << "Received result with type " << result->GetContent ()->GetContentType () << " and size " << size << std::endl;
} else {
std::cout << "Unexpected result!" << std::endl;
}
Uploading a file
IO::Location fileLocation;
IO::fileSystem.GetSpecialLocation (IO::FileSystem::TemporaryFolder, &fileLocation);
fileLocation.AppendToLocal (IO::Name ("testfile_" + GS::Guid (GS::Guid::GenerateGuidSelector::GenerateGuid).ToUniString () + ".txt"));
HTTPClient::OutputFile filePtr = new IO::File (fileLocation, IO::File::OnNotFound::Create);
filePtr->Open (IO::File::OpenMode::ReadWriteMode);
filePtr->WriteBin ("Test", 4);
filePtr->Seek (IO::File::Start, 0);
GS::Owner<GS::IBinaryChannel> fileAdapter = GS::NewOwned<GS::IBinaryChannelAdapterForIChannel> (filePtr);
HTTPClient::PendingResult pendingResult = HTTPClient::Post (IO::URI::URI ("https://httpbin.org/post"),
GS::NewOwned<HTTPClient::Content> (fileAdapter.Pass ()));
pendingResult.Wait ();
auto result = pendingResult.AcquireResult ();
if (result.IsValue () && result->GetStatusCode () == HTTPClient::StandardStatusCode::OK)
std::cout << "File uploaded successfully" << std::endl;
Redirect
HTTPClient::PendingResult pendingResult = HTTPClient::Get (IO::URI::URI ("https://httpbin.org/redirect-to?url=https://graphisoft.com"));
pendingResult.Wait ();
auto result = pendingResult.AcquireResult ();
if (result.IsValue ())
std::cout << "Got successfully redirected to " << result->GetURI ().ToString ().ToCStr () << std::endl;
Connection error
HTTPClient::PendingResult pendingResult = HTTPClient::Get (IO::URI::URI ("https://doesnotexist.com/"));
pendingResult.Wait ();
auto result = pendingResult.AcquireResult ();
if (result.IsError () && result.GetError () == HTTPClient::ErrorCode::CouldntConnect)
std::cout << "Connection failed as expected!" << std::endl;
else
std::cout << "Unexpected result!" << std::endl;
Timeout
HTTPClient::PendingResult pendingResult = HTTPClient::Get (IO::URI::URI ("https://httpbin.org/delay/5"),
HTTPClient::Options {}.SetTimeoutMilliSeconds (1'000));
pendingResult.Wait ();
auto result = pendingResult.AcquireResult ();
if (result.IsError () && result.GetError () == HTTPClient::ErrorCode::Timeout)
std::cout << "Timeout occurred as expected!" << std::endl;
else
std::cout << "Unexpected result!" << std::endl;