first commit

This commit is contained in:
Mike Nolan 2024-12-06 04:58:55 -06:00
commit 856373b396
61 changed files with 5920 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build
.vscode

149
CMakeLists.txt Normal file
View File

@ -0,0 +1,149 @@
cmake_minimum_required(VERSION 3.20)
project(TessesFramework VERSION 1.0)
set(CMAKE_CXX_STANDARD 20)
list(APPEND TESSESFRAMEWORK_SOURCE
src/Http/FileServer.cpp
src/Http/HttpServer.cpp
src/Http/HttpUtils.cpp
src/Http/HttpClient.cpp
src/Http/HttpStream.cpp
src/Http/ContentDisposition.cpp
src/Streams/FileStream.cpp
src/Streams/MemoryStream.cpp
src/Streams/NetworkStream.cpp
src/Streams/Stream.cpp
src/Streams/BufferedStream.cpp
src/TextStreams/StreamReader.cpp
src/TextStreams/StreamWriter.cpp
src/TextStreams/TextReader.cpp
src/TextStreams/TextWriter.cpp
src/Threading/Thread.cpp
src/Threading/Mutex.cpp
src/Filesystem/VFS.cpp
src/Filesystem/LocalFS.cpp
src/Filesystem/SubdirFilesystem.cpp
src/Filesystem/NullFilesystem.cpp
src/Filesystem/MountableFilesystem.cpp
src/Crypto/ClientTLSStream.cpp
src/TF_Init.cpp
)
set(TESSESFRAMEWORK_CERT_BUNDLE_FILE "/etc/ssl/certs/ca-certificates.crt")
option(TESSESFRAMEWORK_EMBED_CERT_BUNDLE "Embed the certificate chain bundle" ON)
option(TESSESFRAMEWORK_ENABLE_MBED "Enable Tesses Framework mbedtls" ON)
option(TESSESFRAMEWORK_ENABLE_EXAMPLES "Enable Tesses Framework examples" ON)
option(TESSESFRAMEWORK_ENABLE_STATIC "Enable Tesses Framework Static Libraries" ON)
option(TESSESFRAMEWORK_ENABLE_SHARED "Enable Tesses Framework Shared Libraries" ON)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include)
if(TESSESFRAMEWORK_ENABLE_MBED)
if(TESSESFRAMEWORK_EMBED_CERT_BUNDLE)
include(cmake/bin2h.cmake)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/include/CertificateChain.h "#pragma once\n")
#target_compile_definitions(TessesFramework PUBLIC TESSESFRAMEWORK_EMBED_CERT_BUNDLE)
bin2h(SOURCE_FILE ${TESSESFRAMEWORK_CERT_BUNDLE_FILE} HEADER_FILE ${CMAKE_CURRENT_BINARY_DIR}/include/CertificateChain.h VARIABLE_NAME CertificateChain APPEND NULL_TERMINATE)
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/include/CertificateChain.h "\n")
#else()
#target_compile_definitions(TessesFramework PUBLIC TESSESFRAMEWORK_CERT_BUNDLE_FILE=${TESSESFRAMEWORK_CERT_BUNDLE_FILE})
endif()
endif()
set(MBEDTLS_DIR "")
function(link_deps TessesFramework_TARGET)
if(TESSESFRAMEWORK_ENABLE_MBED)
target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_ENABLE_MBED)
if(TESSESFRAMEWORK_EMBED_CERT_BUNDLE)
target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_EMBED_CERT_BUNDLE)
else()
target_compile_definitions(TessesFramework PUBLIC TESSESFRAMEWORK_CERT_BUNDLE_FILE=${TESSESFRAMEWORK_CERT_BUNDLE_FILE})
endif()
if(MBEDTLS_DIR STREQUAL "")
else()
target_include_directories(${TessesFramework_TARGET} PUBLIC ${MBEDTLS_DIR}/include)
target_link_directories(${TessesFramework_TARGET} PUBLIC ${MBEDTLS_DIR}/lib)
endif()
target_link_libraries(${TessesFramework_TARGET} PUBLIC mbedtls mbedx509 mbedcrypto)
target_include_directories(${TessesFramework_TARGET} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include)
endif()
target_include_directories(${TessesFramework_TARGET}
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
if("${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoGameCube")
target_link_libraries(${TessesFramework_TARGET} PUBLIC fat)
endif()
if("${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii")
target_link_libraries(${TessesFramework_TARGET} PUBLIC wiisocket)
target_compile_definitions(${TessesFramework_TARGET} PUBLIC TESSESFRAMEWORK_USE_WII_SOCKET)
endif()
endfunction()
include(GNUInstallDirs)
if(TESSESFRAMEWORK_ENABLE_STATIC)
add_library(tessesframework STATIC ${TESSESFRAMEWORK_SOURCE})
link_deps(tessesframework)
list(APPEND TessesFrameworkLibs tessesframework)
endif()
if(TESSESFRAMEWORK_ENABLE_SHARED)
add_library(tessesframework_shared SHARED ${TESSESFRAMEWORK_SOURCE})
link_deps(tessesframework_shared)
list(APPEND TessesFrameworkLibs tessesframework_shared)
endif()
install(TARGETS ${TessesFrameworkLibs}
EXPORT TessesFrameworkTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(DIRECTORY include/TessesFramework DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(EXPORT TessesFrameworkTargets
FILE TessesFrameworkTargets.cmake
NAMESPACE TessesFramework::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/TessesFramework
)
include(CMakePackageConfigHelpers)
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/TessesFrameworkConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/TessesFramework)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/TessesFrameworkConfig.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/TessesFramework)
if(TESSESFRAMEWORK_ENABLE_EXAMPLES)
add_executable(copyfile examples/copyfile.cpp)
target_link_libraries(copyfile PUBLIC tessesframework)
add_executable(fileserver examples/fileserver.cpp)
target_link_libraries(fileserver PUBLIC tessesframework)
add_executable(webserverex examples/webserverex.cpp)
target_link_libraries(webserverex PUBLIC tessesframework)
add_executable(safesubpath examples/safesubpath.cpp)
target_link_libraries(safesubpath PUBLIC tessesframework)
add_executable(mountabletest examples/mountabletest.cpp)
target_link_libraries(mountabletest PUBLIC tessesframework)
add_executable(download examples/download.cpp)
target_link_libraries(download PUBLIC tessesframework)
endif()

5
Config.cmake.in Normal file
View File

@ -0,0 +1,5 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/TessesFrameworkTargets.cmake")
check_required_components(TessesFramework)

21
LICENSE.md Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Mike Nolan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

6
OTHERS_CODE.md Normal file
View File

@ -0,0 +1,6 @@
Other peoples code
==================
- cmake/bin2h.cmake, from: https://github.com/sivachandran/cmake-bin2h (MIT)
- Cppified the multipart/form-data code from: https://github.com/dajuric/simple-http (MIT)
- mbedtls ssl code from mbedtls examples (Apache 2.0)

84
cmake/bin2h.cmake Normal file
View File

@ -0,0 +1,84 @@
#Released under MIT from https://github.com/sivachandran/cmake-bin2h
include(CMakeParseArguments)
# Function to wrap a given string into multiple lines at the given column position.
# Parameters:
# VARIABLE - The name of the CMake variable holding the string.
# AT_COLUMN - The column position at which string will be wrapped.
function(WRAP_STRING)
set(oneValueArgs VARIABLE AT_COLUMN)
cmake_parse_arguments(WRAP_STRING "${options}" "${oneValueArgs}" "" ${ARGN})
string(LENGTH ${${WRAP_STRING_VARIABLE}} stringLength)
math(EXPR offset "0")
while(stringLength GREATER 0)
if(stringLength GREATER ${WRAP_STRING_AT_COLUMN})
math(EXPR length "${WRAP_STRING_AT_COLUMN}")
else()
math(EXPR length "${stringLength}")
endif()
string(SUBSTRING ${${WRAP_STRING_VARIABLE}} ${offset} ${length} line)
set(lines "${lines}\n${line}")
math(EXPR stringLength "${stringLength} - ${length}")
math(EXPR offset "${offset} + ${length}")
endwhile()
set(${WRAP_STRING_VARIABLE} "${lines}" PARENT_SCOPE)
endfunction()
# Function to embed contents of a file as byte array in C/C++ header file(.h). The header file
# will contain a byte array and integer variable holding the size of the array.
# Parameters
# SOURCE_FILE - The path of source file whose contents will be embedded in the header file.
# VARIABLE_NAME - The name of the variable for the byte array. The string "_SIZE" will be append
# to this name and will be used a variable name for size variable.
# HEADER_FILE - The path of header file.
# APPEND - If specified appends to the header file instead of overwriting it
# NULL_TERMINATE - If specified a null byte(zero) will be append to the byte array. This will be
# useful if the source file is a text file and we want to use the file contents
# as string. But the size variable holds size of the byte array without this
# null byte.
# Usage:
# bin2h(SOURCE_FILE "Logo.png" HEADER_FILE "Logo.h" VARIABLE_NAME "LOGO_PNG")
function(BIN2H)
set(options APPEND NULL_TERMINATE)
set(oneValueArgs SOURCE_FILE VARIABLE_NAME HEADER_FILE)
cmake_parse_arguments(BIN2H "${options}" "${oneValueArgs}" "" ${ARGN})
# reads source file contents as hex string
file(READ ${BIN2H_SOURCE_FILE} hexString HEX)
string(LENGTH ${hexString} hexStringLength)
# appends null byte if asked
if(BIN2H_NULL_TERMINATE)
set(hexString "${hexString}00")
endif()
# wraps the hex string into multiple lines at column 32(i.e. 16 bytes per line)
wrap_string(VARIABLE hexString AT_COLUMN 32)
math(EXPR arraySize "${hexStringLength} / 2")
# adds '0x' prefix and comma suffix before and after every byte respectively
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " arrayValues ${hexString})
# removes trailing comma
string(REGEX REPLACE ", $" "" arrayValues ${arrayValues})
# converts the variable name into proper C identifier
string(MAKE_C_IDENTIFIER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME)
string(TOUPPER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME)
# declares byte array and the length variables
set(arrayDefinition "const unsigned char ${BIN2H_VARIABLE_NAME}[] = { ${arrayValues} };")
set(arraySizeDefinition "const size_t ${BIN2H_VARIABLE_NAME}_SIZE = ${arraySize};")
set(declarations "${arrayDefinition}\n\n${arraySizeDefinition}\n\n")
if(BIN2H_APPEND)
file(APPEND ${BIN2H_HEADER_FILE} "${declarations}")
else()
file(WRITE ${BIN2H_HEADER_FILE} "${declarations}")
endif()
endfunction()

16
examples/copyfile.cpp Normal file
View File

@ -0,0 +1,16 @@
#include "TessesFramework/TessesFramework.hpp"
using namespace Tesses::Framework::Filesystem;
int main(int argc, char** argv)
{
LocalFilesystem fs;
VFSPath src = fs.SystemToVFSPath(argv[1]);
VFSPath dest = fs.SystemToVFSPath(argv[2]);
auto srcs = fs.OpenFile(src,"rb");
auto dests = fs.OpenFile(dest,"wb");
srcs->CopyTo(*dests);
delete srcs;
delete dests;
}

22
examples/download.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "TessesFramework/TessesFramework.hpp"
#include <iostream>
using namespace Tesses::Framework;
using namespace Tesses::Framework::Streams;
using namespace Tesses::Framework::Http;
int main(int argc, char** argv)
{
if(argc < 3)
{
printf("USAGE: %s <url> <path>\n",argv[0]);
return 1;
}
FileStream strm(argv[2],"wb");
HttpRequest req;
req.url = argv[1];
HttpResponse resp(req);
resp.CopyToStream(&strm);
return 0;
}

23
examples/fileserver.cpp Normal file
View File

@ -0,0 +1,23 @@
#include "TessesFramework/TessesFramework.hpp"
#include <iostream>
using namespace Tesses::Framework;
using namespace Tesses::Framework::Http;
using namespace Tesses::Framework::Streams;
using namespace Tesses::Framework::TextStreams;
using namespace Tesses::Framework::Threading;
int main(int argc, char** argv)
{
TF_Init();
if(argc < 1)
{
printf("USAGE: %s <dir>\n",argv[0]);
return 0;
}
FileServer fs(argv[1],true,false);
HttpServer server(10000U,fs);
server.StartAccepting();
TF_RunEventLoop();
std::cout << "Closing server" << std::endl;
return 0;
}

View File

@ -0,0 +1,63 @@
#include "TessesFramework/TessesFramework.hpp"
#include <iostream>
using namespace Tesses::Framework;
using namespace Tesses::Framework::Filesystem;
using namespace Tesses::Framework::Streams;
int main(int argc, char** argv)
{
if(argc < 2)
{
printf("USAGE: %s <command> <args...>\n",argv[0]);
return 1;
}
LocalFilesystem lfs;
std::string root = "./root";
std::string mountDemi = "./demi";
std::string mountJoelSlashJim = "./joelslashjim";
SubdirFilesystem rootdir(&lfs,root,false);
SubdirFilesystem mountDemidir(&lfs,mountDemi,false);
SubdirFilesystem mountjohnslashjim(&lfs,mountJoelSlashJim,false);
MountableFilesystem fs(&rootdir,false);
fs.Mount(std::string("/demi"), &mountDemidir,false);
fs.Mount(std::string("/joel/jim"), &mountjohnslashjim,false);
std::string command = argv[1];
if(command == "ls")
{
std::string dir = "/";
if(argc > 2) dir = argv[2];
std::vector<VFSPath> paths;
fs.GetPaths(dir, paths);
for(auto item : paths)
{
std::cout << item.GetFileName() << std::endl;
}
}
else if(command == "cat")
{
FileStream strm(stdout, false,"wb",false);
for(int a = 2; a < argc; a++)
{
std::string path = argv[a];
auto f = fs.OpenFile(path,"rb");
if(f != nullptr)
{
f->CopyTo(strm);
delete f;
}
}
}
}

19
examples/safesubpath.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "TessesFramework/TessesFramework.hpp"
#include <iostream>
using namespace Tesses::Framework::Filesystem;
int main(int argc, char** argv)
{
if(argc < 3)
{
std::cout << "USAGE: " << argv[0] << " <parent> <subpath>\n";
return 1;
}
VFSPath parent(argv[1]);
VFSPath subpath(argv[2]);
VFSPath newPath(parent,subpath.CollapseRelativeParents());
std::cout << newPath.ToString() << "\n";
}

100
examples/webserverex.cpp Normal file
View File

@ -0,0 +1,100 @@
#include "TessesFramework/TessesFramework.hpp"
#include <iostream>
using namespace Tesses::Framework;
using namespace Tesses::Framework::Http;
using namespace Tesses::Framework::Streams;
using namespace Tesses::Framework::TextStreams;
using namespace Tesses::Framework::Threading;
class MyWebServer : public IHttpServer {
public:
bool Handle(ServerContext& ctx)
{
if(ctx.path == "/")
{
FileStream fs("index.html","rb");
ctx
.WithMimeType("text/html")
.SendStream(fs);
return true;
}
else if(ctx.path == "/streaming.html")
{
StreamWriter writer(ctx.OpenResponseStream(),true);
writer.WriteLine("<html>");
writer.WriteLine("<head><title>Streaming</title></head>");
writer.WriteLine("<body>");
writer.WriteLine("<h1>Streaming</h1>");
writer.WriteLine("<ul>");
for(size_t i=0;i<10000; i++)
{
writer.WriteLine("<li>" + std::to_string(i) + "</li>");
}
writer.WriteLine("</ul>");
writer.WriteLine("</body>");
writer.WriteLine("</html>");
return true;
}
else if(ctx.path == "/main.js")
{
FileStream fs("main.js","rb");
ctx
.WithMimeType("text/js")
.SendStream(fs);
return true;
}
else if(ctx.path == "/upload")
{
ctx.ParseFormData([](std::string mime, std::string filename, std::string name)->Tesses::Framework::Streams::Stream*{
return new FileStream(filename,"wb");
});
}
return false;
}
bool Handle1(ServerContext& ctx)
{
std::string name;
if(ctx.queryParams.TryGetFirst("name",name))
{
std::cout << name << std::endl;
ctx
.WithMimeType("text/plain")
.WithContentDisposition(HttpUtils::Sanitise(name) + ".txt",false)
.SendText(name + " is cool.");
//do something with q
return true;
}
return false;
}
};
int main(int argc, char** argv)
{
TF_Init();
TcpServer server(9985U,10);
MyWebServer svr;
while(true)
{
std::string ip;
uint16_t port;
auto res = server.GetStream(ip, port);
if(res == nullptr) return 0;
std::cout << "Got from " << ip << ":" << port << std::endl;
Thread thrd([ip,port,res,&svr]()->void {
HttpServer::Process(*res, svr, ip,port, false);
delete res;
});
thrd.Detach();
}
}

View File

@ -0,0 +1,19 @@
#pragma once
#include <cstring>
#include <cstdint>
#include <cstddef>
#include <string>
#include <filesystem>
#include <map>
#include <vector>
namespace Tesses::Framework
{
void TF_Init();
void TF_ConnectToSelf(uint16_t port);
void TF_RunEventLoop();
void TF_RunEventLoopItteration();
bool TF_IsRunning();
void TF_Quit();
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "../Streams/Stream.hpp"
namespace Tesses::Framework::Crypto
{
class ClientTLSStream : public Tesses::Framework::Streams::Stream {
void* privateData;
static int strm_send(void* ctx,const unsigned char* buf,size_t len);
static int strm_recv(void* ctx,unsigned char* buf,size_t len);
public:
static std::string GetCertChain();
ClientTLSStream(Tesses::Framework::Streams::Stream* innerStream, bool owns, bool verify, std::string domain);
ClientTLSStream(Tesses::Framework::Streams::Stream* innerStream, bool owns, bool verify, std::string domain, std::string cert);
size_t Read(uint8_t* buff, size_t sz);
size_t Write(const uint8_t* buff, size_t sz);
bool CanRead();
bool CanWrite();
bool EndOfStream();
~ClientTLSStream();
};
}

View File

@ -0,0 +1,28 @@
#pragma once
#include "VFS.hpp"
namespace Tesses::Framework::Filesystem
{
class LocalFilesystem : public VFS
{
public:
Tesses::Framework::Streams::Stream* OpenFile(VFSPath path, std::string mode);
void CreateDirectory(VFSPath path);
void DeleteDirectory(VFSPath path);
bool RegularFileExists(VFSPath path);
bool SymlinkExists(VFSPath path);
bool CharacterDeviceExists(VFSPath path);
bool BlockDeviceExists(VFSPath path);
bool SocketFileExists(VFSPath path);
bool FIFOFileExists(VFSPath path);
bool DirectoryExists(VFSPath path);
void DeleteFile(VFSPath path);
void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile);
void GetPaths(VFSPath path, std::vector<VFSPath>& paths);
void CreateHardlink(VFSPath existingFile, VFSPath newName);
void MoveFile(VFSPath src, VFSPath dest);
void MoveDirectory(VFSPath src, VFSPath dest);
VFSPath ReadLink(VFSPath path);
std::string VFSPathToSystem(VFSPath path);
VFSPath SystemToVFSPath(std::string path);
};
}

View File

@ -0,0 +1,53 @@
#pragma once
#include "VFS.hpp"
namespace Tesses::Framework::Filesystem
{
class MountableDirectory {
public:
std::string name;
VFS* vfs;
bool owns;
std::vector<MountableDirectory*> dirs;
void GetFS(VFSPath srcPath, VFSPath curDir, VFSPath& destRoot, VFSPath& destPath, VFS*& vfs);
~MountableDirectory();
};
class MountableFilesystem : public VFS
{
bool owns;
VFS* root;
std::vector<MountableDirectory*> directories;
void GetFS(VFSPath srcPath, VFSPath& destRoot, VFSPath& destPath, VFS*& vfs);
public:
MountableFilesystem();
MountableFilesystem(VFS* root, bool owns);
void Mount(VFSPath path, VFS* fs, bool owns);
void Unmount(VFSPath path);
Tesses::Framework::Streams::Stream* OpenFile(VFSPath path, std::string mode);
void CreateDirectory(VFSPath path);
void DeleteDirectory(VFSPath path);
bool SpecialFileExists(VFSPath path);
bool FileExists(VFSPath path);
bool RegularFileExists(VFSPath path);
bool SymlinkExists(VFSPath path);
bool CharacterDeviceExists(VFSPath path);
bool BlockDeviceExists(VFSPath path);
bool SocketFileExists(VFSPath path);
bool FIFOFileExists(VFSPath path);
bool DirectoryExists(VFSPath path);
void DeleteFile(VFSPath path);
void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile);
void GetPaths(VFSPath path, std::vector<VFSPath>& paths);
void CreateHardlink(VFSPath existingFile, VFSPath newName);
void MoveFile(VFSPath src, VFSPath dest);
void MoveDirectory(VFSPath src, VFSPath dest);
VFSPath ReadLink(VFSPath path);
std::string VFSPathToSystem(VFSPath path);
VFSPath SystemToVFSPath(std::string path);
~MountableFilesystem();
};
}

View File

@ -0,0 +1,19 @@
#pragma once
#include "VFS.hpp"
namespace Tesses::Framework::Filesystem
{
class NullFilesystem : public VFS
{
public:
Tesses::Framework::Streams::Stream* OpenFile(VFSPath path, std::string mode);
void CreateDirectory(VFSPath path);
void DeleteDirectory(VFSPath path);
bool RegularFileExists(VFSPath path);
bool DirectoryExists(VFSPath path);
void DeleteFile(VFSPath path);
void GetPaths(VFSPath path, std::vector<VFSPath>& paths);
void MoveFile(VFSPath src, VFSPath dest);
std::string VFSPathToSystem(VFSPath path);
VFSPath SystemToVFSPath(std::string path);
};
}

View File

@ -0,0 +1,38 @@
#pragma once
#include "VFS.hpp"
namespace Tesses::Framework::Filesystem
{
class SubdirFilesystem : public VFS
{
bool owns;
VFS* parent;
VFSPath path;
VFSPath ToParent(VFSPath path);
VFSPath FromParent(VFSPath path);
public:
SubdirFilesystem(VFS* parent, VFSPath path, bool owns);
Tesses::Framework::Streams::Stream* OpenFile(VFSPath path, std::string mode);
void CreateDirectory(VFSPath path);
void DeleteDirectory(VFSPath path);
bool SpecialFileExists(VFSPath path);
bool FileExists(VFSPath path);
bool RegularFileExists(VFSPath path);
bool SymlinkExists(VFSPath path);
bool CharacterDeviceExists(VFSPath path);
bool BlockDeviceExists(VFSPath path);
bool SocketFileExists(VFSPath path);
bool FIFOFileExists(VFSPath path);
bool DirectoryExists(VFSPath path);
void DeleteFile(VFSPath path);
void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile);
void GetPaths(VFSPath path, std::vector<VFSPath>& paths);
void CreateHardlink(VFSPath existingFile, VFSPath newName);
void MoveFile(VFSPath src, VFSPath dest);
void MoveDirectory(VFSPath src, VFSPath dest);
void DeleteDirectoryRecurse(VFSPath path);
VFSPath ReadLink(VFSPath path);
std::string VFSPathToSystem(VFSPath path);
VFSPath SystemToVFSPath(std::string path);
~SubdirFilesystem();
};
}

View File

@ -0,0 +1,50 @@
#pragma once
#include "../Common.hpp"
#include "../Streams/Stream.hpp"
namespace Tesses::Framework::Filesystem
{
class VFSPath {
public:
static VFSPath RelativeCurrentDirectory();
bool relative;
static std::vector<std::string> SplitPath(std::string path,bool native=false);
std::vector<std::string> path;
VFSPath();
VFSPath(std::vector<std::string> path);
VFSPath(std::string path);
VFSPath(VFSPath p, std::string subent);
VFSPath(VFSPath p, VFSPath p2);
VFSPath GetParent();
VFSPath CollapseRelativeParents();
std::string GetFileName();
std::string ToString();
};
class VFS {
public:
virtual Tesses::Framework::Streams::Stream* OpenFile(VFSPath path, std::string mode)=0;
virtual void CreateDirectory(VFSPath path)=0;
virtual void DeleteDirectory(VFSPath path)=0;
virtual bool RegularFileExists(VFSPath path)=0;
virtual bool SymlinkExists(VFSPath path);
virtual bool CharacterDeviceExists(VFSPath path);
virtual bool BlockDeviceExists(VFSPath path);
virtual bool SocketFileExists(VFSPath path);
virtual bool FIFOFileExists(VFSPath path);
virtual bool FileExists(VFSPath path);
virtual bool SpecialFileExists(VFSPath path);
virtual void CreateSymlink(VFSPath existingFile, VFSPath symlinkFile);
virtual void CreateHardlink(VFSPath existingFile, VFSPath newName);
virtual bool DirectoryExists(VFSPath path)=0;
virtual void DeleteFile(VFSPath path)=0;
virtual void DeleteDirectoryRecurse(VFSPath path);
virtual void GetPaths(VFSPath path, std::vector<VFSPath>& paths)=0;
virtual void MoveFile(VFSPath src, VFSPath dest)=0;
virtual void MoveDirectory(VFSPath src, VFSPath dest);
virtual VFSPath ReadLink(VFSPath path);
virtual std::string VFSPathToSystem(VFSPath path)=0;
virtual VFSPath SystemToVFSPath(std::string path)=0;
virtual ~VFS();
};
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "../Common.hpp"
namespace Tesses::Framework::Http
{
class ContentDisposition {
public:
std::string filename;
std::string type;
std::string fieldName;
static bool TryParse(std::string str, ContentDisposition& cd);
std::string ToString();
};
}

View File

@ -0,0 +1,25 @@
#pragma once
#include "../Filesystem/VFS.hpp"
#include "HttpServer.hpp"
namespace Tesses::Framework::Http
{
class FileServer : public IHttpServer
{
Tesses::Framework::Filesystem::VFS* vfs;
bool ownsVFS;
bool SendFile(ServerContext& ctx,Tesses::Framework::Filesystem::VFSPath path);
public:
bool allowListing;
bool spa;
std::vector<std::string> defaultNames;
FileServer(std::filesystem::path path,bool allowListing,bool spa);
FileServer(std::filesystem::path path,bool allowListing, bool spa, std::vector<std::string> defaultNames);
FileServer(Tesses::Framework::Filesystem::VFS* fs, bool owns, bool allowListing, bool spa);
FileServer(Tesses::Framework::Filesystem::VFS* fs, bool owns, bool allowListing, bool spa, std::vector<std::string> defaultNames);
bool Handle(ServerContext& ctx);
~FileServer();
};
}

View File

@ -0,0 +1,63 @@
#pragma once
#include "../Streams/Stream.hpp"
#include "HttpUtils.hpp"
namespace Tesses::Framework::Http
{
class HttpRequestBody {
public:
virtual void HandleHeaders(HttpDictionary& dict);
virtual void Write(Tesses::Framework::Streams::Stream* strm)=0;
};
class StreamHttpRequestBody : public HttpRequestBody {
Tesses::Framework::Streams::Stream* strm;
bool owns;
std::string mimeType;
public:
StreamHttpRequestBody(Tesses::Framework::Streams::Stream* strm, bool owns, std::string mimeType);
void HandleHeaders(HttpDictionary& dict);
void Write(Tesses::Framework::Streams::Stream* strm);
~StreamHttpRequestBody();
};
class HttpRequest {
public:
HttpRequest();
std::string trusted_root_cert_bundle;
bool ignoreSSLErrors;
bool followRedirects;
std::string method;
std::string url;
HttpDictionary requestHeaders;
HttpRequestBody* body;
static Tesses::Framework::Streams::Stream* EstablishConnection(Uri uri,bool ignoreSSLErrors,std::string trusted_root_cert_bundle);
void SendRequest(Tesses::Framework::Streams::Stream* strm);
};
class HttpResponse {
private:
bool owns;
Tesses::Framework::Streams::Stream* handleStrm;
public:
HttpResponse(Tesses::Framework::Streams::Stream* strm, bool owns);
HttpResponse(HttpRequest& request);
std::string version;
StatusCode statusCode;
HttpDictionary responseHeaders;
std::string ReadAsString();
Tesses::Framework::Streams::Stream* ReadAsStream();
void CopyToStream(Tesses::Framework::Streams::Stream* strm);
~HttpResponse();
};
}

View File

@ -0,0 +1,70 @@
#pragma once
#include "../Streams/NetworkStream.hpp"
#include "HttpUtils.hpp"
#include "../Threading/Thread.hpp"
namespace Tesses::Framework::Http
{
class ServerContext {
bool sent;
Tesses::Framework::Streams::Stream* strm;
public:
HttpDictionary requestHeaders;
HttpDictionary responseHeaders;
HttpDictionary queryParams;
std::string path;
std::string originalPath;
std::string method;
StatusCode statusCode;
std::string ip;
uint16_t port;
std::string version;
bool encrypted;
ServerContext(Tesses::Framework::Streams::Stream* strm);
Tesses::Framework::Streams::Stream& GetStream();
std::string GetOriginalPathWithQuery();
std::string GetUrlWithQuery();
bool Sent();
bool NeedToParseFormData();
void ParseFormData(std::function<Tesses::Framework::Streams::Stream*(std::string mime, std::string filename, std::string name)> cb);
void ReadStream(Tesses::Framework::Streams::Stream& strm);
void ReadStream(Tesses::Framework::Streams::Stream* strm);
std::string ReadString();
void SendBytes(std::vector<uint8_t> buffer);
void SendText(std::string text);
void SendStream(Tesses::Framework::Streams::Stream& strm);
void SendStream(Tesses::Framework::Streams::Stream* strm);
void SendErrorPage(bool showPath);
void SendNotFound();
void SendBadRequest();
void SendException(std::exception& ex);
Tesses::Framework::Streams::Stream* OpenResponseStream();
Tesses::Framework::Streams::Stream* OpenRequestStream();
ServerContext& WithHeader(std::string key, std::string value);
ServerContext& WithSingleHeader(std::string key, std::string value);
ServerContext& WithMimeType(std::string mime);
ServerContext& WithContentDisposition(std::string filename, bool isInline);
ServerContext& WriteHeaders();
};
class IHttpServer {
public:
virtual bool Handle(ServerContext& ctx)=0;
virtual ~IHttpServer();
};
class HttpServer {
Tesses::Framework::Streams::TcpServer* server;
IHttpServer* http;
Tesses::Framework::Threading::Thread* thrd;
bool owns;
uint16_t port;
public:
HttpServer(uint16_t port, IHttpServer& http);
HttpServer(uint16_t port, IHttpServer* http, bool owns);
void StartAccepting();
static void Process(Tesses::Framework::Streams::Stream& strm, IHttpServer& server, std::string ip, uint16_t port, bool encrypted);
~HttpServer();
};
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "../Streams/Stream.hpp"
namespace Tesses::Framework::Http
{
class HttpStream : public Tesses::Framework::Streams::Stream {
Tesses::Framework::Streams::Stream* strm;
size_t offset;
size_t read;
int64_t length;
int64_t position;
bool owns;
bool recv;
bool http1_1;
bool done;
public:
HttpStream(Tesses::Framework::Streams::Stream* strm, bool owns, int64_t length, bool recv, bool http1_1);
HttpStream(Tesses::Framework::Streams::Stream& strm, int64_t length, bool recv,bool http1_1);
bool CanRead();
bool CanWrite();
bool EndOfStream();
int64_t GetLength();
int64_t GetPosition();
size_t Read(uint8_t* buffer, size_t len);
size_t Write(const uint8_t* buffer, size_t len);
~HttpStream();
};
}

View File

@ -0,0 +1,144 @@
#pragma once
#include "../Common.hpp"
namespace Tesses::Framework::Http
{
typedef enum StatusCode {
Continue=100,
SwitchingProtocols=101,
Processing=102,
EarlyHints=103,
OK=200,
Created=201,
Accepted=202,
NonAuthoritativeInformation=203,
NoContent=204,
ResetContent=205,
PartialContent=206,
MultiStatus=207,
AlreadyReported=208,
IMUsed=226,
MultipleChoices=300,
MovedPermanently=301,
Found=302,
SeeOther=303,
NotModified=304,
UseProxy=305,
TemporaryRedirect=307,
PermanentRedirect=308,
BadRequest=400,
Unauthorized=401,
PaymentRequired=402,
Forbidden=403,
NotFound=404,
MethodNotAllowed=405,
NotAcceptable=406,
ProxyAuthenticationRequired=407,
RequestTimeout=408,
Conflict=409,
Gone=410,
LengthRequired=411,
PreconditionFailed=412,
PayloadTooLarge=413,
URITooLong=414,
UnsupportedMediaType=415,
RangeNotSatisfiable=416,
ExpectationFailed=417,
ImATeapot=418,
MisdirectedRequest=421,
UnprocessableContent=422,
Locked=423,
FailedDependency=424,
TooEarly=425,
UpgradeRequired=426,
PreconditionRequired=428,
TooManyRequests=429,
RequestHeaderFieldsTooLarge=431,
UnavailableForLegalReasons=451,
InternalServerError=500,
NotImplemented=501,
BadGateway=502,
ServiceUnavailable=503,
GatewayTimeout=504,
HTTPVersionNotSupported=505,
VariantAlsoNegotiates=506,
InsufficientStorage=507,
LoopDetected=508,
NotExtended=510,
NetworkAuthenticationRequired=511
} StatusCode;
class HttpDictionary {
public:
std::map<std::string,std::vector<std::string>> kvp;
void Clear();
void Clear(std::string key, bool kvpExistsAfter);
void SetValue(std::string key, std::string value);
void SetValue(std::string key, std::vector<std::string> value);
template<typename Itterator>
void SetValue(std::string key, Itterator begin, Itterator end)
{
Clear(key,true);
AddValue(key, begin, end);
}
void AddValue(std::string key, std::string value);
void AddValue(std::string key, std::vector<std::string> value);
template<typename Itterator>
void AddValue(std::string key, Itterator begin, Itterator end)
{
auto& ls = kvp[key];
ls.insert(ls.end(), begin, end);
}
bool TryGetFirst(std::string key, std::string& value);
bool TryGetFirstInt(std::string key, int64_t& value);
bool TryGetFirstDouble(std::string key, double& value);
bool GetFirstBoolean(std::string key);
};
class Uri {
public:
std::string GetQuery();
std::string GetPathAndQuery();
uint16_t GetPort();
std::string HostPort();
bool Relative(std::string url, Uri& uri);
std::string ToString();
static bool TryParse(std::string url, Uri& uri);
std::string scheme;
std::string host;
uint16_t port;
std::string path;
HttpDictionary query;
std::string hash;
};
class HttpUtils
{
public:
static char NibbleToHex(uint8_t nibble);
static uint8_t HexToNibble(char c);
static std::string MimeType(std::filesystem::path p);
static bool Invalid(char c);
static std::string Sanitise(std::string text);
static void QueryParamsDecode(HttpDictionary& dict,std::string query);
static std::string Join(std::string joinStr, std::vector<std::string> ents);
static std::string QueryParamsEncode(HttpDictionary& dict);
static std::string UrlDecode(std::string v);
static std::string UrlEncode(std::string v);
static std::string UrlPathDecode(std::string v);
static std::string UrlPathEncode(std::string v, bool ignoreSpace=false);
static std::string HtmlEncode(std::string v);
static std::vector<std::string> SplitString(std::string text, std::string delimiter,std::size_t maxCnt = std::string::npos);
static std::string StatusCodeString(StatusCode code);
};
}

View File

@ -0,0 +1,27 @@
#pragma once
#include "Stream.hpp"
namespace Tesses::Framework::Streams
{
class BufferedStream : public Stream
{
private:
Stream* strm;
bool owns;
uint8_t* buffer;
size_t bufferSize;
size_t offset;
size_t read;
public:
BufferedStream(Stream* strm, bool owns, size_t bufferSize=1024);
BufferedStream(Stream& strm, size_t bufferSize=1024);
bool EndOfStream();
bool CanRead();
bool CanWrite();
size_t Read(uint8_t* buff, size_t sz);
size_t Write(const uint8_t* buff, size_t sz);
~BufferedStream();
};
}

View File

@ -0,0 +1,27 @@
#pragma once
#include "Stream.hpp"
#include <cstdio>
namespace Tesses::Framework::Streams
{
class FileStream : public Stream {
bool canRead;
bool canWrite;
bool canSeek;
bool owns;
FILE* f;
void SetMode(std::string mode);
public:
FileStream(std::filesystem::path p, std::string mode);
FileStream(FILE* f, bool owns, std::string mode , bool canSeek=true);
size_t Read(uint8_t* buff, size_t sz);
size_t Write(const uint8_t* buff, size_t sz);
bool CanRead();
bool CanWrite();
bool CanSeek();
int64_t GetPosition();
void Flush();
void Seek(int64_t pos, SeekOrigin whence);
~FileStream();
};
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "Stream.hpp"
namespace Tesses::Framework::Streams
{
class MemoryStream : public Stream {
std::vector<uint8_t> buffer;
size_t offset;
bool writable;
public:
MemoryStream(bool isWritable);
std::vector<uint8_t>& GetBuffer();
size_t Read(uint8_t* buff, size_t sz);
size_t Write(const uint8_t* buff, size_t sz);
bool CanRead();
bool CanWrite();
bool CanSeek();
int64_t GetLength();
int64_t GetPosition();
void Seek(int64_t pos, SeekOrigin whence);
};
}

View File

@ -0,0 +1,43 @@
#pragma once
#include "Stream.hpp"
namespace Tesses::Framework::Streams
{
class NetworkStream;
class TcpServer {
int32_t sock;
bool owns;
bool valid;
public:
TcpServer(int32_t sock,bool owns);
TcpServer(uint16_t port, int32_t backlog);
TcpServer(std::string ip, uint16_t port, int32_t backlog);
NetworkStream* GetStream(std::string& ip, uint16_t& port);
~TcpServer();
void Close();
};
class NetworkStream : public Stream {
int32_t sock;
bool owns;
bool success;
bool endOfStream;
public:
bool EndOfStream();
bool CanRead();
bool CanWrite();
NetworkStream(bool ipV6,bool datagram);
NetworkStream(std::string ipOrFqdn, uint16_t port, bool datagram,bool broadcast,bool supportIPv6);
NetworkStream(int32_t sock, bool owns);
void Listen(int32_t backlog);
void Bind(std::string ip, uint16_t port);
void SetBroadcast(bool bC);
NetworkStream* Accept(std::string& ip, uint16_t& port);
size_t Read(uint8_t* buff, size_t sz);
size_t Write(const uint8_t* buff, size_t sz);
size_t ReadFrom(uint8_t* buff, size_t sz, std::string& ip, uint16_t& port);
size_t WriteTo(const uint8_t* buff, size_t sz, std::string ip, uint16_t port);
static std::vector<std::pair<std::string,std::string>> GetIPs(bool ipV6=false);
~NetworkStream();
};
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "../Common.hpp"
namespace Tesses::Framework::Streams
{
enum class SeekOrigin : uint8_t {
Begin=0,
Current=1,
End=2
};
class Stream {
public:
int32_t ReadByte();
void WriteByte(uint8_t b);
virtual bool EndOfStream();
virtual size_t Read(uint8_t* buff, size_t sz);
virtual size_t Write(const uint8_t* buff, size_t sz);
size_t ReadBlock(uint8_t* buff, size_t sz);
void WriteBlock(const uint8_t* buff, size_t sz);
virtual bool CanRead();
virtual bool CanWrite();
virtual bool CanSeek();
virtual int64_t GetPosition();
virtual int64_t GetLength();
virtual void Flush();
virtual void Seek(int64_t pos, SeekOrigin whence);
void CopyTo(Stream* strm, size_t buffSize=1024);
void CopyTo(Stream& strm, size_t buffSize=1024);
virtual ~Stream();
};
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "Http/HttpServer.hpp"
#include "Http/HttpClient.hpp"
#include "Http/FileServer.hpp"
#include "Http/ContentDisposition.hpp"
#include "Streams/FileStream.hpp"
#include "Streams/MemoryStream.hpp"
#include "Streams/NetworkStream.hpp"
#include "Streams/BufferedStream.hpp"
#include "TextStreams/StreamReader.hpp"
#include "TextStreams/StreamWriter.hpp"
#include "Threading/Thread.hpp"
#include "Filesystem/LocalFS.hpp"
#include "Filesystem/SubdirFilesystem.hpp"
#include "Filesystem/NullFilesystem.hpp"
#include "Filesystem/MountableFilesystem.hpp"
#include "Crypto/ClientTLSStream.hpp"

View File

@ -0,0 +1,19 @@
#pragma once
#include "TextReader.hpp"
#include "../Streams/Stream.hpp"
namespace Tesses::Framework