Added back Swurl not as a submodule.

This commit is contained in:
Nichole Mattera 2020-04-09 21:23:19 -04:00
parent de1ee06c02
commit 738752374e
10 changed files with 559 additions and 1 deletions

5
.gitignore vendored
View File

@ -1,5 +1,8 @@
build
.vscode
build
Swurl/debug
Swurl/lib
Swurl/release
*.elf
*.nacp
*.nro

13
Swurl/LICENSE Normal file
View File

@ -0,0 +1,13 @@
Copyright (c) 2019 Nichole Mattera
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

157
Swurl/Makefile Normal file
View File

@ -0,0 +1,157 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
#---------------------------------------------------------------------------------
TARGET := Swurl
VERSION := 1.0.0
SOURCES := source source/Swurl
INCLUDES := include include/Swurl
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec
CFLAGS := -g -Wall -Werror \
-ffunction-sections \
-fdata-sections \
$(ARCH) \
$(BUILD_CFLAGS)
CFLAGS += $(INCLUDE)
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
ifneq (,$(shell which ccache))
CXX := $(shell which ccache) $(CXX)
CC := $(shell which ccache) $(CC)
endif
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I. \
-iquote $(CURDIR)/include/switch/
.PHONY: clean all
#---------------------------------------------------------------------------------
all: lib/lib$(TARGET).a lib/lib$(TARGET)d.a
lib:
@[ -d $@ ] || mkdir -p $@
release:
@[ -d $@ ] || mkdir -p $@
debug:
@[ -d $@ ] || mkdir -p $@
lib/lib$(TARGET).a : lib release $(SOURCES) $(INCLUDES)
@$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \
BUILD_CFLAGS="-DNDEBUG=1 -O2" \
DEPSDIR=$(CURDIR)/release \
--no-print-directory -C release \
-f $(CURDIR)/Makefile
lib/lib$(TARGET)d.a : lib debug $(SOURCES) $(INCLUDES)
@$(MAKE) BUILD=debug OUTPUT=$(CURDIR)/$@ \
BUILD_CFLAGS="-DDEBUG=1 -Og" \
DEPSDIR=$(CURDIR)/debug \
--no-print-directory -C debug \
-f $(CURDIR)/Makefile
dist-bin: all
@tar --exclude=*~ -cjf lib$(TARGET)-$(VERSION).tar.bz2 include lib LICENSE
dist-src:
@tar --exclude=*~ -cjf lib$(TARGET)-src-$(VERSION).tar.bz2 example include source LICENSE Makefile README.md
dist: dist-src dist-bin
install: dist-bin
mkdir -p $(DESTDIR)$(PORTLIBS)
bzip2 -cd lib$(TARGET)-$(VERSION).tar.bz2 | tar -xf - -C $(DESTDIR)$(PORTLIBS)
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr release debug lib *.bz2
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT) : $(OFILES)
$(OFILES_SRC) : $(HFILES)
#---------------------------------------------------------------------------------
%_bin.h %.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

11
Swurl/README.md Normal file
View File

@ -0,0 +1,11 @@
# Swurl
A wrapper library for CURL on the Nintendo Switch. The objective is to have a simple library where you can pass it a request and recieve back calls on its progress updates, completion and if it incountered an error. Right now the response object is very basic with the parsed headers, raw headers, raw body, and status code. However I plan on in the future allowing for you to pass the response type through the request and have the body be parsed out. (Ex JSON)
## Installation
I recommend adding this as a git submodule to your and then modifying your makefile to look in this directory for libs. For examples on how to do this please look at [Kosmos Updater](https://github.com/AtlasNX/Kosmos-Updater).
## Usage
I've included an example under the example folder. Remember to add `-lSwurl -lcurl -lz -lmbedtls -lmbedx509 -lmbedcrypto` to `LIBS` in your makefile. Other than that remember to run `SessionManager::initialize();` at the beginning of your app as this initializes sockets and curl, and call `SessionManager::dealloc();` when cleaning up your app as this closes sockets and curl.

20
Swurl/include/Swurl.hpp Normal file
View File

@ -0,0 +1,20 @@
/*
* Swurl
* Copyright (c) 2019 Nichole Mattera
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "Swurl/SessionManager.hpp"
#include "Swurl/WebRequest.hpp"
#include "Swurl/WebResponse.hpp"

View File

@ -0,0 +1,52 @@
/*
* Swurl
* Copyright (c) 2019 Nichole Mattera
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <string>
#include <map>
#include <functional>
#include <curl/curl.h>
#include "WebRequest.hpp"
namespace swurl {
class SessionManager {
public:
static std::string proxyUrl;
static std::string proxyUsername;
static std::string proxyPassword;
static std::string userAgent;
static std::map<std::string, std::string> requestHeaders;
static std::function<void(WebRequest *, double)> onProgressChanged;
static std::function<void(WebRequest *)> onCompleted;
static std::function<void(WebRequest *, std::string)> onError;
static void initialize();
static void dealloc();
static void makeRequest(WebRequest * request);
private:
static curl_slist * _generateHeaders(WebRequest * request);
static std::string _getMethod(WebRequest * request);
static size_t _writeHeader(const char * in, std::size_t size, std::size_t num, WebRequest * request);
static size_t _write(const char* in, std::size_t size, std::size_t num, WebRequest * request);
static size_t _progress(WebRequest * request, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
static void _parseResponseHeader(WebRequest * request);
};
}

View File

@ -0,0 +1,45 @@
/*
* Swurl
* Copyright (c) 2019 Nichole Mattera
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <string>
#include <map>
#include "WebResponse.hpp"
namespace swurl {
typedef enum {
GET,
POST,
PUT,
DELETE
} WebRequestMethod;
class WebRequest {
public:
WebRequestMethod method;
std::string url;
std::map<std::string, std::string> headers;
bool sslVerifyHost;
WebResponse response;
WebRequest(std::string url);
WebRequest(WebRequestMethod method, std::string url);
WebRequest(WebRequestMethod method, std::string url, std::map<std::string, std::string> headers);
};
}

View File

@ -0,0 +1,31 @@
/*
* Swurl
* Copyright (c) 2019 Nichole Mattera
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <string>
#include <map>
namespace swurl {
class WebResponse {
public:
std::map<std::string, std::string> headers;
std::string rawResponseHeader;
std::string rawResponseBody;
long statusCode;
};
}

View File

@ -0,0 +1,190 @@
/*
* Swurl
* Copyright (c) 2019 Nichole Mattera
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sstream>
#include <switch.h>
#include "SessionManager.hpp"
#include "WebResponse.hpp"
using namespace std;
namespace swurl {
std::string SessionManager::proxyUrl = "";
std::string SessionManager::proxyUsername = "";
std::string SessionManager::proxyPassword = "";
std::string SessionManager::userAgent = "";
std::map<std::string, std::string> SessionManager::requestHeaders;
std::function<void(WebRequest *, double)> SessionManager::onProgressChanged;
std::function<void(WebRequest *)> SessionManager::onCompleted;
std::function<void(WebRequest *, std::string)> SessionManager::onError;
void SessionManager::initialize() {
socketInitializeDefault();
curl_global_init(CURL_GLOBAL_ALL);
}
void SessionManager::dealloc() {
curl_global_cleanup();
socketExit();
}
void SessionManager::makeRequest(WebRequest * request) {
CURL * curl = curl_easy_init();
if (!curl) {
onError(request, "Unable to initialize CURL.");
return;
}
struct curl_slist * headers = _generateHeaders(request);
if (headers != NULL) {
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
}
if (userAgent.size() > 0) {
curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.c_str());
}
if (proxyUrl.size() > 0) {
curl_easy_setopt(curl, CURLOPT_PROXY, proxyUrl.c_str());
if (proxyUsername.size() > 0) {
curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, proxyUsername.c_str());
if (proxyPassword.size() > 0)
curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, proxyPassword.c_str());
}
}
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, _getMethod(request).c_str());
curl_easy_setopt(curl, CURLOPT_URL, request->url.c_str());
// TODO: For this to work you have to pass CURL a CA bundle.
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
if (!request->sslVerifyHost) {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
}
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, _writeHeader);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *) request);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _write);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) request);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, _progress);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, (void *) request);
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK) {
onError(request, string(curl_easy_strerror(res)));
curl_easy_cleanup(curl);
curl_slist_free_all(headers);
return;
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &request->response.statusCode);
curl_easy_cleanup(curl);
curl_slist_free_all(headers);
_parseResponseHeader(request);
onCompleted(request);
}
curl_slist * SessionManager::_generateHeaders(WebRequest * request) {
struct curl_slist * result = NULL;
// Add the global headers.
map<string, string>::iterator glIt;
for (glIt = requestHeaders.begin(); glIt != requestHeaders.end(); glIt++) {
// Skip global headers that have been overwritten in the request.
auto findReqIt = request->headers.find(glIt->first);
if (findReqIt == request->headers.end()) {
result = curl_slist_append(result, (glIt->first + ": " + glIt->second).c_str());
}
}
// Add the request headers.
map<string, string>::iterator reqIt;
for (reqIt = request->headers.begin(); reqIt != request->headers.end(); reqIt++) {
result = curl_slist_append(result, (reqIt->first + ": " + reqIt->second).c_str());
}
return result;
}
string SessionManager::_getMethod(WebRequest * request) {
switch (request->method) {
case GET:
return "GET";
case POST:
return "POST";
case PUT:
return "PUT";
case DELETE:
return "DELETE";
}
return NULL;
}
size_t SessionManager::_writeHeader(const char * in, size_t size, size_t num, WebRequest * request) {
const size_t totalBytes(size * num);
request->response.rawResponseHeader.append(in, totalBytes);
return totalBytes;
}
size_t SessionManager::_write(const char * in, size_t size, size_t num, WebRequest * request) {
const size_t totalBytes(size * num);
request->response.rawResponseBody.append(in, totalBytes);
return totalBytes;
}
size_t SessionManager::_progress(WebRequest * request, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
auto progress = (double) dlnow / (double) dltotal;
if (onProgressChanged) {
onProgressChanged(request, progress);
}
return 0;
}
void SessionManager::_parseResponseHeader(WebRequest * request) {
if (request->response.rawResponseHeader.size() != 0) {
istringstream ss(request->response.rawResponseHeader);
string header;
while(getline(ss, header)) {
if (header.size() == 0)
continue;
auto colonPos = header.find(":");
if (colonPos != string::npos) {
request->response.headers.insert(
pair<string, string>(
header.substr(0, colonPos),
header.substr(colonPos + 2, header.size() - (colonPos + 2))
)
);
}
}
}
}
}

View File

@ -0,0 +1,36 @@
/*
* Swurl
* Copyright (c) 2019 Nichole Mattera
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <switch.h>
#include "SessionManager.hpp"
using namespace std;
namespace swurl {
WebRequest::WebRequest(std::string url) : WebRequest(GET, url) {}
WebRequest::WebRequest(WebRequestMethod method, std::string url) {
this->method = method;
this->url = url;
this->sslVerifyHost = true;
}
WebRequest::WebRequest(WebRequestMethod method, std::string url, std::map<std::string, std::string> headers) : WebRequest(method, url) {
this->headers = headers;
}
}