Browse Source

Added back Swurl not as a submodule.

tags/v3.1.0
Nichole Mattera 6 months ago
parent
commit
f3e77cffde
10 changed files with 559 additions and 1 deletions
  1. +4
    -1
      .gitignore
  2. +13
    -0
      Swurl/LICENSE
  3. +157
    -0
      Swurl/Makefile
  4. +11
    -0
      Swurl/README.md
  5. +20
    -0
      Swurl/include/Swurl.hpp
  6. +52
    -0
      Swurl/include/Swurl/SessionManager.hpp
  7. +45
    -0
      Swurl/include/Swurl/WebRequest.hpp
  8. +31
    -0
      Swurl/include/Swurl/WebResponse.hpp
  9. +190
    -0
      Swurl/source/Swurl/SessionManager.cpp
  10. +36
    -0
      Swurl/source/Swurl/WebRequest.cpp

+ 4
- 1
.gitignore View File

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

+ 13
- 0
Swurl/LICENSE 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
- 0
Swurl/Makefile 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
- 0
Swurl/README.md 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
- 0
Swurl/include/Swurl.hpp 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"

+ 52
- 0
Swurl/include/Swurl/SessionManager.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);
};
}

+ 45
- 0
Swurl/include/Swurl/WebRequest.hpp 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);
};
}

+ 31
- 0
Swurl/include/Swurl/WebResponse.hpp 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;
};
}

+ 190
- 0
Swurl/source/Swurl/SessionManager.cpp 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))
)
);
}
}
}
}
}

+ 36
- 0
Swurl/source/Swurl/WebRequest.cpp 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;
}
}

Loading…
Cancel
Save