Files
wxWidgets/tests/net/webrequest.cpp
Vadim Zeitlin ef08d499ce Remove unnecessary SetIgnoreServerErrorStatus() from the API
It's up to the application code to decide how it handles the HTTP status
codes it gets back from server, there is no need to have a special
method for handling them in wxWebRequest itself.

We also shouldn't skip downloading the response body just because it was
unsuccessful, we may still need it (e.g. it's very common to return the
detailed error description in a JSON object in the message body when
returning some 4xx error), so don't do it in wxMSW implementation and
add a test verifying that we still get the expected body even for an
error status.

Also improve wxWebRequest::State values documentation.
2021-01-15 03:08:18 +01:00

361 lines
10 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: tests/net/webrequest.cpp
// Purpose: wxWebRequest test
// Author: Tobias Taschner
// Created: 2018-10-24
// Copyright: (c) 2018 wxWidgets development team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif // WX_PRECOMP
#if wxUSE_WEBREQUEST
#include "wx/webrequest.h"
#include "wx/filename.h"
#include "wx/wfstream.h"
// This test uses https://httpbin.org by default, but this can be overridden by
// setting WX_TEST_WEBREQUEST_URL, e.g. when running httpbin locally in a
// docker container. This variable can also be set to a special value "0" to
// disable running the test entirely.
static const char* WX_TEST_WEBREQUEST_URL_DEFAULT = "https://httpbin.org";
class RequestFixture : public wxTimer
{
public:
RequestFixture()
{
expectedFileSize = 0;
dataSize = 0;
}
// All tests should call this function first and skip the test entirely if
// it returns false, as this indicates that web requests tests are disabled.
bool InitBaseURL()
{
if ( !wxGetEnv("WX_TEST_WEBREQUEST_URL", &baseURL) )
baseURL = WX_TEST_WEBREQUEST_URL_DEFAULT;
return baseURL != "0";
}
void Create(const wxString& subURL)
{
CreateAbs(baseURL + subURL);
}
void CreateAbs(const wxString& url)
{
request = wxWebSession::GetDefault().CreateRequest(this, url);
Bind(wxEVT_WEBREQUEST_STATE, &RequestFixture::OnRequestState, this);
Bind(wxEVT_WEBREQUEST_DATA, &RequestFixture::OnData, this);
}
void OnRequestState(wxWebRequestEvent& evt)
{
switch (evt.GetState())
{
case wxWebRequest::State_Unauthorized:
case wxWebRequest::State_Completed:
if ( request.GetStorage() == wxWebRequest::Storage_File )
{
wxFileName fn(evt.GetDataFile());
REQUIRE( fn.GetSize() == expectedFileSize );
}
wxFALLTHROUGH;
case wxWebRequest::State_Failed:
case wxWebRequest::State_Cancelled:
loop.Exit();
break;
case wxWebRequest::State_Idle:
case wxWebRequest::State_Active:
break;
}
}
void Notify() wxOVERRIDE
{
WARN("Exiting loop on timeout");
loop.Exit();
}
void OnData(wxWebRequestEvent& evt)
{
// Count all bytes recieved via data event for Storage_None
dataSize += evt.GetDataSize();
}
void RunLoopWithTimeout()
{
StartOnce(10000); // Ensure that we exit the loop after 10s.
loop.Run();
Stop();
}
void Run(wxWebRequest::State requiredState = wxWebRequest::State_Completed,
int requiredStatus = 200)
{
REQUIRE( request.GetState() == wxWebRequest::State_Idle );
request.Start();
RunLoopWithTimeout();
REQUIRE( request.GetState() == requiredState );
if (requiredStatus)
REQUIRE( request.GetResponse().GetStatus() == requiredStatus );
}
// Precondition: we must have an auth challenge.
void UseCredentials(const wxString& user, const wxString& password)
{
request.GetAuthChallenge().SetCredentials(
wxWebCredentials(user, wxSecretValue(password)));
}
wxString baseURL;
wxEventLoop loop;
wxWebRequest request;
wxInt64 expectedFileSize;
wxInt64 dataSize;
};
TEST_CASE_METHOD(RequestFixture,
"WebRequest::Get::Bytes", "[net][webrequest][get]")
{
if ( !InitBaseURL() )
return;
Create("/bytes/65536");
Run();
REQUIRE( request.GetResponse().GetContentLength() == 65536 );
REQUIRE( request.GetBytesExpectedToReceive() == 65536 );
REQUIRE( request.GetBytesReceived() == 65536 );
}
TEST_CASE_METHOD(RequestFixture,
"WebRequest::Get::String", "[net][webrequest][get]")
{
if ( !InitBaseURL() )
return;
Create("/base64/VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==");
Run();
REQUIRE( request.GetResponse().AsString() == "The quick brown fox jumps over the lazy dog" );
}
TEST_CASE_METHOD(RequestFixture,
"WebRequest::Get::File", "[net][webrequest][get]")
{
if ( !InitBaseURL() )
return;
expectedFileSize = 99 * 1024;
Create(wxString::Format("/bytes/%lld", expectedFileSize));
request.SetStorage(wxWebRequest::Storage_File);
Run();
REQUIRE( request.GetBytesReceived() == expectedFileSize );
}
TEST_CASE_METHOD(RequestFixture,
"WebRequest::Get::None", "[net][webrequest][get]")
{
if ( !InitBaseURL() )
return;
int processingSize = 99 * 1024;
Create(wxString::Format("/bytes/%d", processingSize));
request.SetStorage(wxWebRequest::Storage_None);
Run();
REQUIRE( request.GetBytesReceived() == processingSize );
REQUIRE( dataSize == processingSize );
}
TEST_CASE_METHOD(RequestFixture,
"WebRequest::Error::HTTP", "[net][webrequest][error]")
{
if ( !InitBaseURL() )
return;
Create("/status/404");
Run(wxWebRequest::State_Failed, 404);
}
TEST_CASE_METHOD(RequestFixture,
"WebRequest::Error::Body", "[net][webrequest][error]")
{
if ( !InitBaseURL() )
return;
// We can't use the same httpbin.org server that we use for the other tests
// for this one because it doesn't return anything in the body when
// returning an error status code, so use another one.
CreateAbs("https://httpstat.us/418");
Run(wxWebRequest::State_Failed, 418);
CHECK( request.GetResponse().AsString() == "418 I'm a teapot" );
}
TEST_CASE_METHOD(RequestFixture,
"WebRequest::Error::Connect", "[net][webrequest][error]")
{
if ( !InitBaseURL() )
return;
CreateAbs("http://127.0.0.1:51234");
Run(wxWebRequest::State_Failed, 0);
}
TEST_CASE_METHOD(RequestFixture,
"WebRequest::Post", "[net][webrequest]")
{
if ( !InitBaseURL() )
return;
Create("/post");
request.SetData("app=WebRequestSample&version=1", "application/x-www-form-urlencoded");
Run();
}
TEST_CASE_METHOD(RequestFixture,
"WebRequest::Put", "[net][webrequest]")
{
if ( !InitBaseURL() )
return;
Create("/put");
wxScopedPtr<wxInputStream> is(new wxFileInputStream("horse.png"));
REQUIRE( is->IsOk() );
request.SetData(is.release(), "image/png");
request.SetMethod("PUT");
Run();
}
TEST_CASE_METHOD(RequestFixture,
"WebRequest::Auth::Basic", "[net][webrequest][auth]")
{
if ( !InitBaseURL() )
return;
Create("/basic-auth/wxtest/wxwidgets");
Run(wxWebRequest::State_Unauthorized, 401);
REQUIRE( request.GetAuthChallenge().IsOk() );
SECTION("Good password")
{
UseCredentials("wxtest", "wxwidgets");
RunLoopWithTimeout();
CHECK( request.GetResponse().GetStatus() == 200 );
CHECK( request.GetState() == wxWebRequest::State_Completed );
}
SECTION("Bad password")
{
UseCredentials("wxtest", "foobar");
RunLoopWithTimeout();
CHECK( request.GetResponse().GetStatus() == 401 );
CHECK( request.GetState() == wxWebRequest::State_Unauthorized );
}
}
TEST_CASE_METHOD(RequestFixture,
"WebRequest::Auth::Digest", "[net][webrequest][auth]")
{
if ( !InitBaseURL() )
return;
Create("/digest-auth/auth/wxtest/wxwidgets");
Run(wxWebRequest::State_Unauthorized, 401);
REQUIRE( request.GetAuthChallenge().IsOk() );
SECTION("Good password")
{
UseCredentials("wxtest", "wxwidgets");
RunLoopWithTimeout();
CHECK( request.GetResponse().GetStatus() == 200 );
CHECK( request.GetState() == wxWebRequest::State_Completed );
}
SECTION("Bad password")
{
UseCredentials("foo", "bar");
RunLoopWithTimeout();
CHECK( request.GetResponse().GetStatus() == 401 );
CHECK( request.GetState() == wxWebRequest::State_Unauthorized );
}
}
TEST_CASE_METHOD(RequestFixture,
"WebRequest::Cancel", "[net][webrequest]")
{
if ( !InitBaseURL() )
return;
Create("/delay/10");
request.Start();
request.Cancel();
RunLoopWithTimeout();
REQUIRE( request.GetState() == wxWebRequest::State_Cancelled );
}
// This test is not run by default and has to be explicitly selected to run.
TEST_CASE_METHOD(RequestFixture,
"WebRequest::Manual", "[.]")
{
// Allow getting 8-bit strings from the environment correctly.
setlocale(LC_ALL, "");
wxString url;
if ( !wxGetEnv("WX_TEST_WEBREQUEST_URL", &url) )
{
FAIL("Specify WX_TEST_WEBREQUEST_URL");
}
CreateAbs(url);
request.Start();
RunLoopWithTimeout();
WARN("Request state " << request.GetState());
wxWebResponse response = request.GetResponse();
REQUIRE( response.IsOk() );
WARN("Status: " << response.GetStatus()
<< " (" << response.GetStatusText() << ")\n" <<
"Body length: " << response.GetContentLength() << "\n" <<
"Body: " << response.AsString() << "\n");
}
WX_DECLARE_STRING_HASH_MAP(wxString, wxWebRequestHeaderMap);
namespace wxPrivate
{
WXDLLIMPEXP_NET wxString
SplitParameters(const wxString& s, wxWebRequestHeaderMap& parameters);
}
TEST_CASE("WebRequestUtils", "[net][webrequest]")
{
wxString value;
wxWebRequestHeaderMap params;
wxString header = "multipart/mixed; boundary=\"MIME_boundary_01234567\"";
value = wxPrivate::SplitParameters(header, params);
REQUIRE( value == "multipart/mixed" );
REQUIRE( params.size() == 1 );
REQUIRE( params["boundary"] == "MIME_boundary_01234567" );
}
#endif // wxUSE_WEBREQUEST