first commit

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

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);
};
}