Add server content data and mountableserver

This commit is contained in:
2025-01-12 19:32:19 -06:00
parent c80a5c503a
commit 4ce3526047
11 changed files with 265 additions and 19 deletions

View File

@ -6,6 +6,8 @@ set(CMAKE_CXX_STANDARD 20)
list(APPEND TESSESFRAMEWORK_SOURCE
src/Http/FileServer.cpp
src/Http/MountableServer.cpp
src/Http/CallbackServer.cpp
src/Http/HttpServer.cpp
src/Http/HttpUtils.cpp
src/Http/HttpClient.cpp

View File

@ -5,11 +5,24 @@ using namespace Tesses::Framework::Http;
using namespace Tesses::Framework::Streams;
using namespace Tesses::Framework::TextStreams;
using namespace Tesses::Framework::Threading;
class Johnny : public ServerContextData
{
public:
Johnny()
{
text = "Steve Ballmer";
}
std::string text;
~Johnny()
{
std::cout << "Destroying" << std::endl;
}
};
class MyWebServer : public IHttpServer {
public:
bool Handle(ServerContext& ctx)
{
std::cout << ctx.path << std::endl;
if(ctx.path == "/")
{
FileStream fs("index.html","rb");
@ -58,10 +71,23 @@ class MyWebServer : public IHttpServer {
return new FileStream(filename,"wb");
});
}
else if(ctx.path == "/steve")
{
Johnny* data = ctx.GetServerContentData<Johnny>("mytag");
data->text = "Demi Lovato";
}
return false;
}
bool Handle1(ServerContext& ctx)
};
class MyOtherWebServer : public IHttpServer
{
public:
bool Handle(ServerContext& ctx)
{
if(ctx.path == "/")
{
std::string name;
if(ctx.queryParams.TryGetFirst("name",name))
{
@ -74,6 +100,14 @@ class MyWebServer : public IHttpServer {
return true;
}
}
else if(ctx.path == "/mymount/steve")
{
Johnny* data = ctx.GetServerContentData<Johnny>("mytag");
ctx.SendText(data->text);
return true;
}
return false;
}
};
@ -81,20 +115,13 @@ class MyWebServer : public IHttpServer {
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();
}
MyOtherWebServer myo;
MyWebServer mws;
MountableServer mountable(myo);
mountable.Mount("/mymount/",mws);
HttpServer server(10001,mountable);
server.StartAccepting();
TF_RunEventLoop();
std::cout << "Closing server" << std::endl;
}

View File

@ -8,6 +8,25 @@
#include <map>
#include <vector>
class TextException : public std::exception {
std::string error_message;
public:
TextException(std::string ex)
{
error_message = "TextException: ";
error_message.append(ex);
}
const char * what() const noexcept override
{
return error_message.c_str();
}
};
namespace Tesses::Framework
{
void TF_Init();

View File

@ -0,0 +1,16 @@
#pragma once
#include "HttpServer.hpp"
namespace Tesses::Framework::Http
{
class CallbackServer : public IHttpServer
{
std::function<bool(ServerContext&)> cb;
std::function<void()> destroy;
public:
CallbackServer(std::function<bool(ServerContext&)> cb);
CallbackServer(std::function<bool(ServerContext&)> cb,std::function<void()> destroy);
bool Handle(ServerContext& ctx);
~CallbackServer();
};
}

View File

@ -3,12 +3,18 @@
#include "HttpUtils.hpp"
#include "../Threading/Thread.hpp"
#include <unordered_map>
namespace Tesses::Framework::Http
{
class ServerContextData {
public:
virtual ~ServerContextData();
};
class ServerContext {
bool sent;
Tesses::Framework::Streams::Stream* strm;
std::map<std::string,ServerContextData*> data;
public:
HttpDictionary requestHeaders;
HttpDictionary responseHeaders;
@ -22,6 +28,7 @@ namespace Tesses::Framework::Http
std::string version;
bool encrypted;
ServerContext(Tesses::Framework::Streams::Stream* strm);
~ServerContext();
Tesses::Framework::Streams::Stream& GetStream();
std::string GetOriginalPathWithQuery();
std::string GetUrlWithQuery();
@ -46,6 +53,18 @@ namespace Tesses::Framework::Http
ServerContext& WithMimeType(std::string mime);
ServerContext& WithContentDisposition(std::string filename, bool isInline);
ServerContext& WriteHeaders();
template<class T>
T* GetServerContentData(std::string tag)
{
std::string name = typeid(T).name();
name.push_back(' ');
name.append(tag);
if(data.count(name) > 0) return dynamic_cast<T*>(data[name]);
T* item = new T();
data[name] = item;
return item;
}
};
class IHttpServer {

View File

@ -0,0 +1,25 @@
#pragma once
#include "HttpServer.hpp"
#include "../Filesystem/VFSFix.hpp"
#include "../Filesystem/VFS.hpp"
namespace Tesses::Framework::Http
{
class MountableServer : public IHttpServer
{
IHttpServer* root;
bool owns;
std::vector<std::pair<std::string,std::pair<bool,IHttpServer*>>> servers;
std::string Subpath(Filesystem::VFSPath fullPath, Filesystem::VFSPath offsetPath);
bool StartsWith(Filesystem::VFSPath fullPath, Filesystem::VFSPath offsetPath);
public:
MountableServer();
MountableServer(IHttpServer* root, bool owns);
MountableServer(IHttpServer& root);
void Mount(std::string path, IHttpServer* server, bool owns);
void Mount(std::string path, IHttpServer& server);
void Unmount(std::string path);
bool Handle(ServerContext& ctx);
~MountableServer();
};
}

View File

@ -2,6 +2,8 @@
#include "Http/HttpServer.hpp"
#include "Http/HttpClient.hpp"
#include "Http/FileServer.hpp"
#include "Http/CallbackServer.hpp"
#include "Http/MountableServer.hpp"
#include "Http/ContentDisposition.hpp"
#include "Streams/FileStream.hpp"
#include "Streams/MemoryStream.hpp"

View File

@ -0,0 +1,22 @@
#include "TessesFramework/Http/CallbackServer.hpp"
namespace Tesses::Framework::Http
{
CallbackServer::CallbackServer(std::function<bool(ServerContext&)> cb) : CallbackServer(cb,[]()->void{})
{
}
CallbackServer::CallbackServer(std::function<bool(ServerContext&)> cb,std::function<void()> destroy)
{
this->cb = cb;
this->destroy=destroy;
}
bool CallbackServer::Handle(ServerContext& ctx)
{
return this->cb(ctx);
}
CallbackServer::~CallbackServer()
{
this->destroy();
}
}

View File

@ -379,10 +379,23 @@ namespace Tesses::Framework::Http
{
if(strm == nullptr) return;
SendStream(*strm);
}
ServerContext::~ServerContext()
{
for(auto item : this->data)
{
delete item.second;
}
}
ServerContextData::~ServerContextData()
{
}
void ServerContext::SendStream(Stream& strm)
{
if(sent) return;
if(!strm.CanRead()) throw TextException("Cannot read from stream");
if(strm.EndOfStream()) throw TextException("End of stream");
if(strm.CanSeek())
{
int64_t len=strm.GetLength();
@ -666,6 +679,11 @@ namespace Tesses::Framework::Http
{
ctx.SendException(ex);
}
catch(...)
{
TextException ex("An unknown error occurred");
ctx.SendException(ex);
}
if(ctx.version != "HTTP/1.1" ) return;

View File

@ -0,0 +1,85 @@
#include "TessesFramework/Http/MountableServer.hpp"
namespace Tesses::Framework::Http {
std::string MountableServer::Subpath(Filesystem::VFSPath fullPath, Filesystem::VFSPath offsetPath)
{
if(fullPath.path.size() < offsetPath.path.size()) return {}; //this shouldn't happen but here just in case
Filesystem::VFSPath p;
p.relative=false;
for(size_t i = offsetPath.path.size(); i < fullPath.path.size(); i++)
{
p.path.push_back(fullPath.path[i]);
}
return p.ToString();
}
bool MountableServer::StartsWith(Filesystem::VFSPath fullPath, Filesystem::VFSPath offsetPath)
{
if(fullPath.path.size() < offsetPath.path.size()) return false;
for(size_t i = 0; i < offsetPath.path.size(); i++)
{
if(fullPath.path[i] != offsetPath.path[i]) return false;
}
return true;
}
MountableServer::MountableServer() : MountableServer(nullptr,false)
{
}
MountableServer::MountableServer(IHttpServer* root, bool owns)
{
this->root = root;
this->owns = owns;
}
MountableServer::MountableServer(IHttpServer& root) : MountableServer(&root,false)
{
}
void MountableServer::Mount(std::string path, IHttpServer* server, bool owns)
{
this->servers.insert(this->servers.begin(), std::pair<std::string,std::pair<bool,IHttpServer*>>(path, std::pair<bool,IHttpServer*>(owns,server)));
}
void MountableServer::Mount(std::string path, IHttpServer& server)
{
Mount(path,&server,false);
}
void MountableServer::Unmount(std::string path)
{
for(auto i = this->servers.begin(); i != this->servers.end(); i++)
{
auto& item = *i;
if(item.first == path)
{
if(item.second.first) delete item.second.second;
this->servers.erase(i);
return;
}
}
}
bool MountableServer::Handle(ServerContext& ctx)
{
std::string oldPath = ctx.path;
for(auto item : this->servers)
{
if(StartsWith(oldPath, item.first))
{
ctx.path = Subpath(oldPath, item.first);
if(item.second.second->Handle(ctx))
{
ctx.path = oldPath;
return true;
}
ctx.path = oldPath;
break;
}
}
ctx.path=oldPath;
if(this->root != nullptr && this->root->Handle(ctx)) return true;
return false;
}
MountableServer::~MountableServer()
{
if(this->owns) delete this->root;
for(auto svr : this->servers) if(svr.second.first) delete svr.second.second;
}
}

View File

@ -49,10 +49,12 @@ namespace Tesses::Framework::Streams
}
size_t FileStream::Read(uint8_t* buff, size_t sz)
{
if(!CanRead()) throw TextException("Cannot read from stream");
return fread(buff,1, sz, this->f);
}
size_t FileStream::Write(const uint8_t* buff, size_t sz)
{
if(!CanWrite()) throw TextException("Cannot write to stream");
return fwrite(buff,1, sz, f);
}
bool FileStream::CanRead()
@ -69,11 +71,14 @@ namespace Tesses::Framework::Streams
}
bool FileStream::EndOfStream()
{
if(!f) return true;
return feof(this->f);
}
int64_t FileStream::GetPosition()
{
if(!f) return 0;
#if defined(_WIN32)
return (int64_t)_ftelli64(this->f);
#else
@ -82,10 +87,14 @@ namespace Tesses::Framework::Streams
}
void FileStream::Flush()
{
if(!f) return;
fflush(this->f);
}
void FileStream::Seek(int64_t pos, SeekOrigin whence)
{
if(!f) return;
#if defined(_WIN32)
_fseeki64(this->f,pos,whence == SeekOrigin::Begin ? SEEK_SET : whence == SeekOrigin::Current ? SEEK_CUR : SEEK_END);
#else
@ -94,6 +103,8 @@ namespace Tesses::Framework::Streams
}
FileStream::~FileStream()
{
if(!f) return;
if(this->owns)
fclose(this->f);
}