Add server content data and mountableserver
This commit is contained in:
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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();
|
||||
|
||||
16
include/TessesFramework/Http/CallbackServer.hpp
Normal file
16
include/TessesFramework/Http/CallbackServer.hpp
Normal 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();
|
||||
};
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
25
include/TessesFramework/Http/MountableServer.hpp
Normal file
25
include/TessesFramework/Http/MountableServer.hpp
Normal 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();
|
||||
};
|
||||
}
|
||||
@ -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"
|
||||
|
||||
22
src/Http/CallbackServer.cpp
Normal file
22
src/Http/CallbackServer.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
|
||||
85
src/Http/MountableServer.cpp
Normal file
85
src/Http/MountableServer.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user