Example to test the same request with HTTP, or HTTPS and asynchronously or synchronously.

ssl
Nichole Mattera 4 years ago
parent e77a50fb93
commit 7ff8743bc9
  1. 2
      Makefile
  2. 145
      src/Application.cpp
  3. 13
      src/Application.hpp
  4. 23
      src/View.cpp
  5. 4
      src/View.hpp
  6. 60
      src/WebRequest.cpp
  7. 16
      src/WebRequest.hpp
  8. 4
      src/views/AppVersionView.cpp
  9. 2
      src/views/AppVersionView.hpp
  10. 62
      src/views/AppView.cpp
  11. 35
      src/views/AppView.hpp
  12. 49
      src/views/PackageVersionView.cpp
  13. 36
      src/views/PackageVersionView.hpp
  14. 82
      src/views/PackageView.cpp
  15. 38
      src/views/PackageView.hpp

@ -57,7 +57,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fexceptions -std=gnu++17
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lcurl -lz -lnx
LIBS := -lcurl -lz -lmbedtls -lmbedx509 -lmbedcrypto -lnx
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing

@ -28,47 +28,23 @@ namespace KUDiag {
curl_global_init(CURL_GLOBAL_ALL);
vector<string> mainMenuItems;
mainMenuItems.push_back("Get latest app version");
mainMenuItems.push_back("Get latest app");
mainMenuItems.push_back("Get latest package version");
mainMenuItems.push_back("Get latest package");
mainMenuItems.push_back("Get latest app version (http - sync)");
mainMenuItems.push_back("Get latest app version (http - async)");
mainMenuItems.push_back("Get latest app version (https - sync)");
mainMenuItems.push_back("Get latest app version (https - async)");
mainMenuItems.push_back("Exit");
_mainMenu = new Menu(
string("Kosmos Updater Diagnostic ") + VERSION + " - Main Menu",
string("Kosmos Updater Diagnostic SSL Debug - Main Menu"),
mainMenuItems,
bind(&Application::_menuItemSelected, this, _1),
bind(&Application::_back, this)
);
vector<string> bundleItems;
bundleItems.push_back("Kosmos");
bundleItems.push_back("Atmosphere");
bundleItems.push_back("Back");
_bundleMenu = new Menu(
string("Kosmos Updater Diagnostic ") + VERSION + " - Select a bundle",
bundleItems,
bind(&Application::_menuItemSelected, this, _1),
bind(&Application::_back, this)
);
vector<string> channelItems;
channelItems.push_back("Stable");
channelItems.push_back("Bleeding-Edge");
channelItems.push_back("Back");
_channelMenu = new Menu(
string("Kosmos Updater Diagnostic ") + VERSION + " - Select a channel",
channelItems,
bind(&Application::_menuItemSelected, this, _1),
bind(&Application::_back, this)
);
_appVersionView = new AppVersionView(bind(&Application::_back, this));
_appView = new AppView(bind(&Application::_back, this));
_packageVersionView = new PackageVersionView(bind(&Application::_back, this));
_packageView = new PackageView(bind(&Application::_back, this));
_httpSyncAppVersionView = new AppVersionView(false, false, bind(&Application::_back, this));
_httpAsyncAppVersionView = new AppVersionView(false, true, bind(&Application::_back, this));
_httpsSyncAppVersionView = new AppVersionView(true, false, bind(&Application::_back, this));
_httpsAsyncAppVersionView = new AppVersionView(true, true, bind(&Application::_back, this));
_state = 0;
}
@ -78,28 +54,20 @@ namespace KUDiag {
delete _mainMenu;
}
if (_bundleMenu != NULL) {
delete _bundleMenu;
if (_httpSyncAppVersionView != NULL) {
delete _httpSyncAppVersionView;
}
if (_channelMenu != NULL) {
delete _channelMenu;
if (_httpAsyncAppVersionView != NULL) {
delete _httpAsyncAppVersionView;
}
if (_appVersionView != NULL) {
delete _appVersionView;
if (_httpsSyncAppVersionView != NULL) {
delete _httpsSyncAppVersionView;
}
if (_appView != NULL) {
delete _appView;
}
if (_packageVersionView != NULL) {
delete _packageVersionView;
}
if (_packageView != NULL) {
delete _packageView;
if (_httpsAsyncAppVersionView != NULL) {
delete _httpsAsyncAppVersionView;
}
curl_global_cleanup();
@ -120,28 +88,19 @@ namespace KUDiag {
break;
case 1:
_appVersionView->draw(kDown);
_httpSyncAppVersionView->draw(kDown);
break;
case 2:
_appView->draw(kDown);
_httpAsyncAppVersionView->draw(kDown);
break;
case 3:
case 5:
_channelMenu->draw(kDown);
_httpsSyncAppVersionView->draw(kDown);
break;
case 4:
_packageVersionView->draw(kDown);
break;
case 6:
_bundleMenu->draw(kDown);
break;
case 7:
_packageView->draw(kDown);
_httpsAsyncAppVersionView->draw(kDown);
break;
default:
@ -156,64 +115,19 @@ namespace KUDiag {
switch (_state) {
case 0:
if (itemSelected == 0) {
_appVersionView->reset();
_httpSyncAppVersionView->reset();
_state = 1;
} else if (itemSelected == 1) {
_appView->reset();
_httpAsyncAppVersionView->reset();
_state = 2;
} else if (itemSelected == 2) {
_channelMenu->reset();
_httpsSyncAppVersionView->reset();
_state = 3;
} else if (itemSelected == 3) {
_channelMenu->reset();
_state = 5;
} else {
_state = -1;
}
break;
case 3:
if (itemSelected == 0) {
_packageVersionView->channel = 1;
_packageVersionView->reset();
_state = 4;
} else if (itemSelected == 1) {
_packageVersionView->channel = 2;
_packageVersionView->reset();
_httpsAsyncAppVersionView->reset();
_state = 4;
} else {
_mainMenu->reset();
_state = 0;
}
break;
case 5:
if (itemSelected == 0) {
_packageView->channel = 1;
_bundleMenu->reset();
_state = 6;
} else if (itemSelected == 1) {
_packageView->channel = 2;
_bundleMenu->reset();
_state = 6;
} else {
_mainMenu->reset();
_state = 0;
}
break;
case 6:
if (itemSelected == 0) {
_packageView->bundle = 1;
_packageView->reset();
_state = 7;
} else if (itemSelected == 1) {
_packageView->bundle = 2;
_packageView->reset();
_state = 7;
} else {
_channelMenu->reset();
_state = 5;
_state = -1;
}
break;
}
@ -229,16 +143,9 @@ namespace KUDiag {
case 2:
case 3:
case 4:
case 5:
case 7:
_mainMenu->reset();
_state = 0;
break;
case 6:
_channelMenu->reset();
_state = 5;
break;
}
}
}

@ -17,9 +17,6 @@
#include "Menu.hpp"
#include "views/AppVersionView.hpp"
#include "views/AppView.hpp"
#include "views/PackageVersionView.hpp"
#include "views/PackageView.hpp"
#pragma once
@ -33,12 +30,10 @@ namespace KUDiag {
private:
int _state;
Menu * _mainMenu;
Menu * _bundleMenu;
Menu * _channelMenu;
AppVersionView * _appVersionView;
AppView * _appView;
PackageVersionView * _packageVersionView;
PackageView * _packageView;
AppVersionView * _httpSyncAppVersionView;
AppVersionView * _httpAsyncAppVersionView;
AppVersionView * _httpsSyncAppVersionView;
AppVersionView * _httpsAsyncAppVersionView;
void _menuItemSelected(int itemSelected);
void _back();

@ -25,7 +25,9 @@ using namespace std;
using namespace zipper;
namespace KUDiag {
View::View(function<void()> backCallback) {
View::View(bool secure, bool async, function<void()> backCallback) {
_secure = secure;
_async = async;
_backCallback = backCallback;
_hasDrawn = false;
_hasFinished = false;
@ -48,7 +50,7 @@ namespace KUDiag {
return;
}
if (_request) {
if (_async && _request) {
Mutex mutext = _request->getMutex();
mutexLock(&mutext);
@ -83,10 +85,12 @@ namespace KUDiag {
consoleClear();
cout << "\x1b[0;0H" << _getTitle() << "\n\n";
cout << "Downloading: 0%";
if (_async) {
cout << "Downloading: 0%";
}
_request = new WebRequest("GET", _getURL());
if (!_request->start()) {
_request = new WebRequest("GET", ((_secure) ? "https://" : "http://") + _getURL());
if (_async && !_request->startAsync()) {
cout << "\x1b[1;12H100";
cout << "\x1b[3;0HError: Problem starting thread.\n\n";
cout << "Press A to continue.";
@ -94,6 +98,15 @@ namespace KUDiag {
_hasFinished = true;
}
if (!_async) {
_request->startSync();
_requestCompletedSuccessfully();
cout << "\nPress A to continue.";
_hasFinished = true;
}
_hasDrawn = true;
}
}

@ -24,7 +24,7 @@
namespace KUDiag {
class View {
public:
View(std::function<void()> backCallback);
View(bool secure, bool async, std::function<void()> backCallback);
virtual ~View();
void draw(u64 kDown);
@ -38,6 +38,8 @@ namespace KUDiag {
virtual std::string _getURL();
virtual void _requestCompletedSuccessfully();
bool _secure;
bool _async;
std::function<void()> _backCallback;
bool _hasDrawn;
bool _hasFinished;

@ -28,9 +28,7 @@ namespace KUDiag {
_method = method;
_url = url;
_size = 0;
_data = (char *) malloc(1);
_threadCreated = false;
mutexInit(&_mutex);
_progress = 0;
_completed = false;
@ -39,12 +37,10 @@ namespace KUDiag {
}
WebRequest::~WebRequest() {
if (_data != NULL) {
free(_data);
if (_threadCreated) {
threadWaitForExit(&_thread);
threadClose(&_thread);
}
threadWaitForExit(&_thread);
threadClose(&_thread);
}
string WebRequest::getMethod() {
@ -68,14 +64,10 @@ namespace KUDiag {
}
size_t WebRequest::getSize() {
return _size;
return _data.size();
}
string WebRequest::getData() {
return string(_data);
}
char * WebRequest::getRawData() {
return _data;
}
@ -88,7 +80,7 @@ namespace KUDiag {
ofstream file;
file.open(filename, ios::out | ios::binary);
file.write(_data, _size);
file.write(_data.c_str(), _data.size());
file.flush();
file.close();
@ -103,35 +95,29 @@ namespace KUDiag {
return _errorMessage;
}
bool WebRequest::start() {
string WebRequest::startSync() {
WebRequest::_createRequest(this);
return _data;
}
bool WebRequest::startAsync() {
Result res;
if (R_FAILED( res = threadCreate(&_thread, _createRequest, (void *) this, 0x2000, 0x2B, -2)))
return false;
_threadCreated = true;
if (R_FAILED( res = threadStart(&_thread)))
return false;
return true;
}
size_t WebRequest::appendData(void *contents, size_t size, size_t nmemb) {
size_t realsize = size * nmemb;
mutexLock(&_mutex);
_data = (char *) realloc(_data, _size + realsize + 1);
if (_data == NULL) {
setErrored("Not enough memory.");
return 0;
}
memcpy(&(_data[_size]), contents, realsize);
_size += realsize;
_data[_size] = 0;
mutexUnlock(&_mutex);
return realsize;
size_t WebRequest::appendData(const char* in, std::size_t size, std::size_t num) {
const size_t totalBytes(size * num);
_data.append(in, totalBytes);
return totalBytes;
}
int WebRequest::setProgress(curl_off_t dltotal, curl_off_t dlnow) {
@ -177,6 +163,8 @@ namespace KUDiag {
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, request->getURL().c_str());
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, request->getMethod().c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _writeFunction);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) request);
@ -210,13 +198,11 @@ namespace KUDiag {
request->setCompleted();
}
size_t WebRequest::_writeFunction(void *contents, size_t size, size_t nmemb, void * ptr) {
WebRequest * request = (WebRequest *) ptr;
return request->appendData(contents, size, nmemb);
size_t WebRequest::_writeFunction(const char* in, std::size_t size, std::size_t num, WebRequest * request) {
return request->appendData(in, size, num);
}
size_t WebRequest::_progressFunction(void *ptr, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
WebRequest * request = (WebRequest *) ptr;
size_t WebRequest::_progressFunction(WebRequest * request, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
return request->setProgress(dltotal, dlnow);
}
}

@ -35,33 +35,31 @@ namespace KUDiag {
bool isComplete();
size_t getSize();
std::string getData();
char * getRawData();
bool saveData(std::string filename);
bool hasError();
std::string getErrorMessage();
bool start();
std::string startSync();
bool startAsync();
size_t appendData(void *contents, size_t size, size_t nmemb);
size_t appendData(const char* in, std::size_t size, std::size_t num);
int setProgress(curl_off_t dltotal, curl_off_t dlnow);
void setCompleted();
void setErrored(std::string message);
private:
static void _createRequest(void * ptr);
static size_t _writeFunction(void *contents, size_t size, size_t nmemb, void * ptr);
static size_t _progressFunction(void *ptr, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
static size_t _writeFunction(const char* in, std::size_t size, std::size_t num, WebRequest * request);
static size_t _progressFunction(WebRequest * request, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
std::string _method;
std::string _url;
size_t _size;
char * _data;
std::string _data;
Thread _thread;
bool _threadCreated;
Mutex _mutex;
u8 _progress;
bool _completed;

@ -22,14 +22,14 @@
using namespace std;
namespace KUDiag {
AppVersionView::AppVersionView(std::function<void()> backCallback) : View(backCallback) {}
AppVersionView::AppVersionView(bool secure, bool async, std::function<void()> backCallback) : View(secure, async, backCallback) {}
string AppVersionView::_getTitle() {
return string("Kosmos Updater Diagnostic ") + VERSION + " - Get latest app version";
}
string AppVersionView::_getURL() {
return string("http://kosmos-updater.teamatlasnx.com/") + API_VERSION + "/app/version-number";
return string("kosmos-updater.teamatlasnx.com/") + API_VERSION + "/app/version-number";
}
void AppVersionView::_requestCompletedSuccessfully() {

@ -24,7 +24,7 @@
namespace KUDiag {
class AppVersionView : public View {
public:
AppVersionView(std::function<void()> backCallback);
AppVersionView(bool secure, bool async, std::function<void()> backCallback);
private:
std::string _getTitle();

@ -1,62 +0,0 @@
// Kosmos Updater Diagnostic
// Copyright (C) 2019 Steven Mattera
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "AppView.hpp"
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
namespace KUDiag {
AppView::AppView(std::function<void()> backCallback) : View(backCallback) {}
string AppView::_getTitle() {
return string("Kosmos Updater Diagnostic ") + VERSION + " - Get latest app";
}
string AppView::_getURL() {
return string("http://kosmos-updater.teamatlasnx.com/") + API_VERSION + "/app";
}
void AppView::_requestCompletedSuccessfully() {
NacpStruct nacp = _getNACP(_request->getRawData());
NacpLanguageEntry * langEntry = NULL;
Result rc = nacpGetLanguageEntry(&nacp, &langEntry);
if (R_SUCCEEDED(rc) && langEntry != NULL) {
cout << "\x1b[5;0HApp Name: " << langEntry->name << "\n";
cout << "App Author: " << langEntry->author << "\n";
cout << "App Version: " << nacp.version << "\n\n";
} else {
cout << "\x1b[5;0HApp Version: " << nacp.version << "\n\n";
}
}
NacpStruct AppView::_getNACP(char * data) {
NroHeader header;
memcpy(&header, &(data[sizeof(NroStart)]), sizeof(NroHeader));
NroAssetHeader assetHeader;
memcpy(&assetHeader, &(data[header.size]), sizeof(NroAssetHeader));
NacpStruct nacp;
memcpy(&nacp, &(data[header.size + assetHeader.nacp.offset]), sizeof(NacpStruct));
return nacp;
}
}

@ -1,35 +0,0 @@
// Kosmos Updater Diagnostic
// Copyright (C) 2019 Steven Mattera
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <switch.h>
#include <functional>
#include "../View.hpp"
#pragma once
namespace KUDiag {
class AppView : public View {
public:
AppView(std::function<void()> backCallback);
private:
NacpStruct _getNACP(char * data);
std::string _getTitle();
std::string _getURL();
void _requestCompletedSuccessfully();
};
}

@ -1,49 +0,0 @@
// Kosmos Updater Diagnostic
// Copyright (C) 2019 Steven Mattera
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "PackageVersionView.hpp"
#include <iostream>
#include <string>
using namespace std;
namespace KUDiag {
PackageVersionView::PackageVersionView(std::function<void()> backCallback) : View(backCallback) {}
string PackageVersionView::_getTitle() {
return string("Kosmos Updater Diagnostic ") + VERSION + " - Get latest package version";
}
string PackageVersionView::_getURL() {
return string("http://kosmos-updater.teamatlasnx.com/") + API_VERSION + "/package/version-number?channel=" + _getChannel();
}
void PackageVersionView::_requestCompletedSuccessfully() {
cout << "\x1b[5;0HChannel: " << _getChannel() << "\n";
cout << "Latest Package Version: " << _request->getData() << "\n\n";
}
string PackageVersionView::_getChannel() {
switch (channel) {
case 2:
return "bleeding-edge";
default:
return "stable";
}
}
}

@ -1,36 +0,0 @@
// Kosmos Updater Diagnostic
// Copyright (C) 2019 Steven Mattera
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <switch.h>
#include <functional>
#include "../View.hpp"
#pragma once
namespace KUDiag {
class PackageVersionView : public View {
public:
PackageVersionView(std::function<void()> backCallback);
int channel;
private:
std::string _getChannel();
std::string _getTitle();
std::string _getURL();
void _requestCompletedSuccessfully();
};
}

@ -1,82 +0,0 @@
// Kosmos Updater Diagnostic
// Copyright (C) 2019 Steven Mattera
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "PackageView.hpp"
#include <iostream>
#include <string>
#include <vector>
#include "../zipper/unzipper.h"
using namespace std;
using namespace zipper;
namespace KUDiag {
PackageView::PackageView(std::function<void()> backCallback) : View(backCallback) {}
string PackageView::_getTitle() {
return string("Kosmos Updater Diagnostic ") + VERSION + " - Get latest package";
}
string PackageView::_getURL() {
return string("http://kosmos-updater.teamatlasnx.com/") + API_VERSION + "/package?channel=" + _getChannel() + "&bundle=" + _getBundle();
}
void PackageView::_requestCompletedSuccessfully() {
unsigned char * data = (unsigned char *) _request->getRawData();
vector<unsigned char> vectorData(data, data + _request->getSize());
Unzipper unzipper(vectorData);
vector<ZipEntry> entries = unzipper.entries();
cout << "\x1b[5;0HChannel: " << _getChannel() << "\n";
cout << "Bundle: " << _getBundle() << "\n";
cout << "Number of entries: " << entries.size() << "\n\n";
cout << "First Ten Entries\n";
int i = 0;
for (auto const& entry : entries) {
if (!entry.name.empty()) {
cout << "Entry #" << i << ": " << entry.name << "\n";
i++;
}
if (i == 10) {
break;
}
}
}
string PackageView::_getChannel() {
switch (channel) {
case 2:
return "bleeding-edge";
default:
return "stable";
}
}
string PackageView::_getBundle() {
switch (bundle) {
case 2:
return "atmosphere";
default:
return "kosmos";
}
}
}

@ -1,38 +0,0 @@
// Kosmos Updater Diagnostic
// Copyright (C) 2019 Steven Mattera
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <switch.h>
#include <functional>
#include "../View.hpp"
#pragma once
namespace KUDiag {
class PackageView : public View {
public:
PackageView(std::function<void()> backCallback);
int channel;
int bundle;
private:
std::string _getChannel();
std::string _getBundle();
std::string _getTitle();
std::string _getURL();
void _requestCompletedSuccessfully();
};
}
Loading…
Cancel
Save