Add date code

This commit is contained in:
2025-05-10 19:52:23 -05:00
parent 1cb9bc93ee
commit 21b0418926
31 changed files with 21614 additions and 78 deletions

View File

@ -5,6 +5,7 @@ project(TessesFramework VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 17)
list(APPEND TESSESFRAMEWORK_SOURCE
src/Date/Date.cpp
src/Http/FileServer.cpp
src/Http/MountableServer.cpp
src/Http/CallbackServer.cpp
@ -199,6 +200,7 @@ if(TESSESFRAMEWORK_ENABLE_EXAMPLES)
add_executable(printjsondecodedemoji examples/printjsondecodedemoji.cpp)
target_link_libraries(printjsondecodedemoji PUBLIC tessesframework)
endif()
if(TESSESFRAMEWORK_ENABLE_APPS)
@ -216,6 +218,10 @@ install(TARGETS tjsonpretty DESTINATION bin)
add_executable(tjsonunpretty apps/tjsonunpretty.cpp)
target_link_libraries(tjsonunpretty PUBLIC tessesframework)
install(TARGETS tjsonunpretty DESTINATION bin)
add_executable(ttime apps/ttime.cpp)
target_link_libraries(ttime PUBLIC tessesframework)
install(TARGETS ttime DESTINATION bin)
endif()
include(InstallRequiredSystemLibraries)

133
apps/ttime.cpp Normal file
View File

@ -0,0 +1,133 @@
#include "TessesFramework/TessesFramework.hpp"
using namespace Tesses::Framework;
using namespace Tesses::Framework::Date;
int main(int argc, char** argv)
{
TF_Init();
DateTime dt=DateTime::Now();
//dt = dt.ToLocal();
if(argc > 1)
{
if(strcmp(argv[1],"--help") == 0)
{
/*
case 'r':
{
auto hours = hour % 12;
if(hours == 0) hours=12;
text.append(Http::HttpUtils::LeftPad(std::to_string(hours),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0'));
text.append(hour >= 12 ? " PM" : " AM");
}
break;
case 'R':
{
text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0'));
}
break;
case 'T':
{
text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0'));
}
break;
case 'u':
{
int dow = weekday + 6;
dow %= 7;
text.append(std::to_string(dow+1));
}
break;
case 'w':
{
text.append(std::to_string(weekday));
}
break;
case 'c':
{
text.append(weekday_short[weekday]);
text.push_back(' ');
text.append(months_short[month]);
text.push_back(' ');
text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,'0'));
text.push_back(' ');
text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0'));
text.push_back(' ');
text.append(Http::HttpUtils::LeftPad(std::to_string(year),4,'0'));
}
break;
case 'C':
text.append(Http::HttpUtils::LeftPad(std::to_string(year / 100),2,'0'));
break;
case 'Y':
text.append(Http::HttpUtils::LeftPad(std::to_string(year),4,'0'));
break;
case 'p':
text.append(hour >= 12 ? "PM" : "AM");
break;
case 'I':
{
auto hours = hour % 12;
if(hours == 0) hours=12;
text.append(Http::HttpUtils::LeftPad(std::to_string(hours),2,'0'));
}
break;
case 'h':
case 'b':
text.append(months_short[month-1]);
break;
case 'B':
text.append(months_long[month-1]);
break;
case '%':
text.push_back('%');
break;
case 'n':
text.push_back('\n');
break;
case 't':
text.push_back('\t');
break;
*/
std::cout << argv[0] << " <fmt>" << std::endl;
std::cout << "The fmt is for DateTime::ToString(fmt) just like strftime but just these are supported:" << std::endl;
std::cout << "%a: weekday short eg " << dt.ToString("%a") << std::endl;
std::cout << "%A: weekday long eg " << dt.ToString("%A") << std::endl;
std::cout << "%S: seconds with leading zeros eg " << dt.ToString("%S") << std::endl;
std::cout << "%m: month with leading zeros eg " << dt.ToString("%m") << std::endl;
std::cout << "%d: day with leading zeros eg " << dt.ToString("%d") << std::endl;
std::cout << "%e: day with leading spaces eg " << dt.ToString("%e") << std::endl;
std::cout << "%M: minute with leading zeros eg " << dt.ToString("%M") << std::endl;
std::cout << "%H: 24 hour with leading zeros eg " << dt.ToString("%H") << std::endl;
std::cout << "%F: %Y-%m-%d like this " << dt.ToString("%F") << std::endl;
std::cout << "%D: %m/%d/%y like this " << dt.ToString("%D") << std::endl;
std::cout << "%y: year two digits eg " << dt.ToString("%y") << std::endl;
std::cout << "%Y: year four digits eg " << dt.ToString("%Y") << std::endl;
return 1;
}
std::cout << dt.ToString(argv[1]) << std::endl;
}
else
std::cout << dt.ToString() << std::endl;
return 0;
}

View File

@ -0,0 +1,66 @@
#pragma once
#include <string>
#include <chrono>
#include <cstdint>
namespace Tesses::Framework::Date
{
int GetTimeZone();
bool TimeZoneSupportDST();
class DateTime {
int year=1970;
int month=1;
int day=1;
int hour=0;
int minute=0;
int second=0;
bool isLocal=false;
int64_t ToEpochNoConvert();
void FromEpochNoConvert(int64_t gmt);
public:
DateTime();
DateTime(int year, int month, int day, int hour, int minute, int seconds, bool isLocal=true);
DateTime(int64_t epoch);
int Year();
int Month();
int Day();
int Hour();
int Minute();
int Second();
int DayOfWeek();
bool IsLocal();
int64_t ToEpoch();
DateTime ToLocal();
DateTime ToUTC();
void SetToLocal();
void SetToUTC();
void SetYear(int y);
void SetMonth(int m);
void SetDay(int d);
void SetHour(int h);
void SetMinute(int m);
void SetSecond(int s);
void SetLocal(bool local);
void Set(int64_t epoch);
void Set(int year, int month, int day, int hour, int minute, int seconds, bool isLocal=true);
void SetToNow();
void SetToNowUTC();
static DateTime Now();
static DateTime NowUTC();
std::string ToString();
std::string ToString(std::string fmt);
std::string ToHttpDate();
static bool TryParseHttpDate(std::string txt, DateTime& dt);
};
};

View File

@ -31,8 +31,10 @@ namespace Tesses::Framework::Filesystem
VFSPath ReadLink(VFSPath path);
std::string VFSPathToSystem(VFSPath path);
VFSPath SystemToVFSPath(std::string path);
void GetDate(VFSPath path, time_t& lastWrite, time_t& lastAccess);
void SetDate(VFSPath path, time_t lastWrite, time_t lastAccess);
};
void GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess);
void SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess);
};
extern LocalFilesystem LocalFS;
}

View File

@ -13,7 +13,7 @@ namespace Tesses::Framework::Filesystem
class MemoryFileData {
public:
MemoryFileData();
time_t lastWrite;
Date::DateTime lastWrite;
bool canAccess;
size_t readers;
@ -31,7 +31,7 @@ namespace Tesses::Framework::Filesystem
{
public:
MemoryDirectory();
time_t lastWrite;
Date::DateTime lastWrite;
std::vector<MemoryEntry*> entries;
~MemoryDirectory();
};
@ -39,7 +39,7 @@ namespace Tesses::Framework::Filesystem
class MemorySymlink : public MemoryEntry
{
public:
time_t lastWrite;
Date::DateTime lastWrite;
VFSPath linkedTo;
};
@ -92,8 +92,8 @@ namespace Tesses::Framework::Filesystem
VFSPath ReadLink(VFSPath path);
std::string VFSPathToSystem(VFSPath path);
VFSPath SystemToVFSPath(std::string path);
void GetDate(VFSPath path, time_t& lastWrite, time_t& lastAccess);
void SetDate(VFSPath path, time_t lastWrite, time_t lastAccess);
void GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess);
void SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess);
~MemoryFilesystem();
};
};

View File

@ -51,7 +51,7 @@ namespace Tesses::Framework::Filesystem
std::string VFSPathToSystem(VFSPath path);
VFSPath SystemToVFSPath(std::string path);
~MountableFilesystem();
void GetDate(VFSPath path, time_t& lastWrite, time_t& lastAccess);
void SetDate(VFSPath path, time_t lastWrite, time_t lastAccess);
void GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess);
void SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess);
};
}

View File

@ -36,8 +36,8 @@ namespace Tesses::Framework::Filesystem
std::string VFSPathToSystem(VFSPath path);
VFSPath SystemToVFSPath(std::string path);
~SubdirFilesystem();
void GetDate(VFSPath path, time_t& lastWrite, time_t& lastAccess);
void SetDate(VFSPath path, time_t lastWrite, time_t lastAccess);
void GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess);
void SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess);
};
}

View File

@ -3,6 +3,7 @@
#include "../Streams/Stream.hpp"
#include <functional>
#include <memory>
#include "../Date/Date.hpp"
#include "VFSFix.hpp"
namespace Tesses::Framework::Filesystem
@ -130,8 +131,8 @@ namespace Tesses::Framework::Filesystem
virtual VFSPath SystemToVFSPath(std::string path)=0;
virtual void GetDate(VFSPath path, time_t& lastWrite, time_t& lastAccess);
virtual void SetDate(VFSPath path, time_t lastWrite, time_t lastAccess);
virtual void GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess);
virtual void SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess);
virtual ~VFS();
};

View File

@ -3,6 +3,7 @@
#include "HttpUtils.hpp"
#include "../Threading/Thread.hpp"
#include "../Date/Date.hpp"
#include <unordered_map>
namespace Tesses::Framework::Http
{
@ -79,6 +80,7 @@ namespace Tesses::Framework::Http
void SendException(std::exception& ex);
Tesses::Framework::Streams::Stream* OpenResponseStream();
Tesses::Framework::Streams::Stream* OpenRequestStream();
ServerContext& WithLastModified(Date::DateTime time);
ServerContext& WithHeader(std::string key, std::string value);
ServerContext& WithSingleHeader(std::string key, std::string value);
ServerContext& WithMimeType(std::string mime);
@ -110,11 +112,19 @@ namespace Tesses::Framework::Http
Tesses::Framework::Streams::TcpServer* server;
IHttpServer* http;
Tesses::Framework::Threading::Thread* thrd;
bool owns;
uint16_t port;
bool ownsTCP;
bool ownsHttp;
bool showIPs;
public:
HttpServer(uint16_t port, IHttpServer& http);
HttpServer(uint16_t port, IHttpServer* http, bool owns);
HttpServer(Tesses::Framework::Streams::TcpServer& tcpServer, IHttpServer& http, bool showIPs=true);
HttpServer(Tesses::Framework::Streams::TcpServer* tcpServer, bool ownsTCP, IHttpServer& http, bool showIPs=true);
HttpServer(Tesses::Framework::Streams::TcpServer& tcpServer, IHttpServer* http, bool ownsHttpServer, bool showIPs=true);
HttpServer(Tesses::Framework::Streams::TcpServer* tcpServer, bool ownsTCP, IHttpServer* http, bool ownsHttpServer, bool showIPs=true);
HttpServer(uint16_t port, IHttpServer& http, bool showIPs=true);
HttpServer(uint16_t port, IHttpServer* http, bool owns, bool showIPs=true);
uint16_t GetPort();
void StartAccepting();
static void Process(Tesses::Framework::Streams::Stream& strm, IHttpServer& server, std::string ip, uint16_t port, bool encrypted);
~HttpServer();

View File

@ -1,5 +1,6 @@
#pragma once
#include "../Common.hpp"
#include "../Date/Date.hpp"
#include <algorithm>
namespace Tesses::Framework::Http
@ -78,6 +79,9 @@ struct CaseInsensitiveLess {
void Clear();
void Clear(std::string key, bool kvpExistsAfter);
void SetValue(std::string key, std::string value);
void SetValue(std::string key, int64_t v);
void SetValue(std::string key, double v);
void SetValue(std::string key, Date::DateTime v);
void SetValue(std::string key, std::vector<std::string> value);
template<typename Itterator>
void SetValue(std::string key, Itterator begin, Itterator end)
@ -86,6 +90,9 @@ struct CaseInsensitiveLess {
AddValue(key, begin, end);
}
void AddValue(std::string key, std::string value);
void AddValue(std::string key, int64_t v);
void AddValue(std::string key, double v);
void AddValue(std::string key, Date::DateTime v);
void AddValue(std::string key, std::vector<std::string> value);
template<typename Itterator>
@ -100,6 +107,7 @@ struct CaseInsensitiveLess {
bool TryGetFirstInt(std::string key, int64_t& value);
bool TryGetFirstDouble(std::string key, double& value);
bool TryGetFirstDate(std::string key, Date::DateTime& value);
bool GetFirstBoolean(std::string key);
@ -140,10 +148,11 @@ struct CaseInsensitiveLess {
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 Replace(std::string str, std::string find, std::string replace);
static std::string StatusCodeString(StatusCode code);
static std::string ToLower(std::string str);
static std::string ToUpper(std::string str);
static std::string LeftPad(std::string text, int count, char c);
};

View File

@ -10,10 +10,12 @@ namespace Tesses::Framework::Streams
bool owns;
bool valid;
public:
TcpServer(int32_t backlog);
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);
uint16_t GetPort();
~TcpServer();
bool IsValid();
void Close();
@ -30,6 +32,7 @@ namespace Tesses::Framework::Streams
NetworkStream(bool ipV6,bool datagram);
NetworkStream(std::string ipOrFqdn, uint16_t port, bool datagram,bool broadcast,bool supportIPv6);
NetworkStream(int32_t sock, bool owns);
uint16_t GetPort();
void Listen(int32_t backlog);
void Bind(std::string ip, uint16_t port);
void SetBroadcast(bool bC);

View File

@ -1,4 +1,5 @@
#pragma once
#include "Date/Date.hpp"
#include "Http/HttpServer.hpp"
#include "Http/HttpClient.hpp"
#include "Http/FileServer.hpp"

720
src/Date/Date.cpp Normal file
View File

@ -0,0 +1,720 @@
#include "TessesFramework/Date/Date.hpp"
#include "TessesFramework/Http/HttpUtils.hpp"
#include "../HowardHinnant_date/date.h"
#include <iomanip>
#include <iostream>
#include <sstream>
using namespace std::chrono;
using namespace date;
namespace Tesses::Framework::Date
{
int GetTimeZone()
{
#if defined(__SWITCH__) || defined(_WIN32)
return (int)(-_timezone);
#else
return (int)(-timezone);
#endif
}
bool TimeZoneSupportDST()
{
#if defined(__SWITCH__) || defined(_WIN32)
return _daylight == 1;
#else
return daylight == 1;
#endif
}
DateTime::DateTime()
{
}
static int64_t div_floor(int64_t a, int64_t b)
{
int64_t v = a / b;
if(a < 0 && (a % b) != 0) v--;
return v;
}
void DateTime::FromEpochNoConvert(int64_t gmt)
{
auto epoch = date::sys_days{date::January/1/1970};
epoch += date::days(div_floor(gmt,86400));
//date::days<int64_t> sys_days_since_epoch = date::days<int64_t>(gmt);
// Convert sys_days to year_month_day
date::year_month_day ymd = date::year_month_day(epoch);
int64_t secs = gmt % 86400;
if(secs < 0) secs += 86400;
second = secs % 60;
secs /= 60;
minute = secs % 60;
secs /= 60;
hour = secs % 24;
day = (int)(uint32_t)ymd.day();
month = (int)(uint32_t)ymd.month();
year = (int32_t)ymd.year();
}
DateTime::DateTime(int64_t epoch)
{
this->isLocal=false;
this->FromEpochNoConvert(epoch);
}
void DateTime::Set(int64_t epoch)
{
this->isLocal=false;
this->FromEpochNoConvert(epoch);
}
void DateTime::Set(int year, int month, int day, int hour, int minute, int seconds, bool isLocal)
{
this->year = year;
this->month = month;
this->day = day;
this->hour = hour;
this->minute = minute;
this->second = seconds;
this->isLocal = isLocal;
}
DateTime::DateTime(int year, int month, int day, int hour, int minute, int seconds, bool isLocal)
{
this->year = year;
this->month = month;
this->day = day;
this->hour = hour;
this->minute = minute;
this->second = seconds;
this->isLocal = isLocal;
}
int DateTime::Year()
{
return this->year;
}
int DateTime::Month()
{
return this->month;
}
int DateTime::Day()
{
return this->day;
}
int DateTime::Hour()
{
return this->hour;
}
int DateTime::Minute()
{
return this->minute;
}
int DateTime::Second()
{
return this->second;
}
bool DateTime::IsLocal()
{
return this->isLocal;
}
int DateTime::DayOfWeek()
{
date::year_month_day ymd(date::year(year),date::month((uint32_t)month),date::day((uint32_t)day));
date::sys_days d = ymd;
date::year_month_weekday ymw(d);
return ymw.weekday().c_encoding() % 7;
}
void DateTime::SetToLocal()
{
if(this->isLocal) return;
auto local = this->ToEpochNoConvert();
local += GetTimeZone();
if(TimeZoneSupportDST())
{
auto epoch = date::sys_days{date::January/1/1970};
epoch += date::days(div_floor(local,86400));
bool isDST = false;
date::year_month_day ymd(epoch);
auto month = (uint32_t)ymd.month();
if(month > 3 && month < 11)
{
isDST=true;
}
else if(month == 3)
{
auto day = (uint32_t)ymd.day();
if(day > 14) isDST=true;
else if(day >= 8 && day <= 14)
{
date::year_month_weekday ymw(epoch);
auto dow=ymw.weekday().c_encoding();
auto secondSunday = day - dow;
if(secondSunday < 8) secondSunday+=7;
if(day > secondSunday) isDST=true;
else if(day == secondSunday)
{
int64_t secs = local % 86400;
if(secs < 0) secs += 86400;
secs /= 3600;
auto _hours = secs % 24;
if(_hours >= 2) isDST=true;
}
}
}
else if(month == 11)
{
auto day = (uint32_t)ymd.day();
if(day >= 1 && day <= 7)
{
date::year_month_weekday ymw(epoch);
auto dow=ymw.weekday().c_encoding();
int32_t firstSunday = (int32_t)day - (int32_t)dow;
if(firstSunday < 1) firstSunday+=7;
if(day < firstSunday) isDST=true;
else if(day == firstSunday)
{
int64_t secs = local % 86400;
if(secs < 0) secs += 86400;
secs /= 3600;
auto _hours = secs % 24;
if(_hours <=1) isDST=true;
}
}
}
if(isDST) local += 3600;
}
this->isLocal=true;
this->FromEpochNoConvert(local);
}
DateTime DateTime::ToLocal()
{
DateTime dt = *this;
dt.SetToLocal();
return dt;
}
void DateTime::SetToUTC()
{
if(!this->isLocal) return;
auto local = this->ToEpochNoConvert();
local -= GetTimeZone();
if(TimeZoneSupportDST())
{
{
auto epoch = date::sys_days{date::January/1/1970};
epoch += date::days(div_floor(local,86400));
bool isDST = false;
date::year_month_day ymd(epoch);
auto month = (uint32_t)ymd.month();
if(month > 3 && month < 11)
{
isDST=true;
}
else if(month == 3)
{
auto day = (uint32_t)ymd.day();
if(day > 14) isDST=true;
else if(day >= 8 && day <= 14)
{
date::year_month_weekday ymw(epoch);
auto dow=ymw.weekday().c_encoding();
auto secondSunday = day - dow;
if(secondSunday < 8) secondSunday+=7;
if(day > secondSunday) isDST=true;
else if(day == secondSunday)
{
int64_t secs = local % 86400;
if(secs < 0) secs += 86400;
secs /= 3600;
auto _hours = secs % 24;
if(_hours >= 2) isDST=true;
}
}
}
else if(month == 11)
{
auto day = (uint32_t)ymd.day();
if(day >= 1 && day <= 7)
{
date::year_month_weekday ymw(epoch);
auto dow=ymw.weekday().c_encoding();
int32_t firstSunday = (int32_t)day - (int32_t)dow;
if(firstSunday < 1) firstSunday+=7;
if(day < firstSunday) isDST=true;
else if(day == firstSunday)
{
int64_t secs = local % 86400;
if(secs < 0) secs += 86400;
secs /= 3600;
auto _hours = secs % 24;
if(_hours <=1) isDST=true;
}
}
}
if(isDST) local -= 3600;
}
}
this->isLocal=false;
this->FromEpochNoConvert(local);
}
DateTime DateTime::ToUTC()
{
DateTime dt = *this;
dt.SetToUTC();
return dt;
}
int64_t DateTime::ToEpoch()
{
if(this->isLocal)
{
DateTime dt = this->ToUTC();
return dt.ToEpochNoConvert();
}
return this->ToEpochNoConvert();
}
int64_t DateTime::ToEpochNoConvert()
{
date::year y = (date::year)year;
date::month m = (date::month)month;
date::day d = (date::day)day;
date::year_month_day ymd(y,m,d);
std::chrono::duration hr = hours(this->hour) + minutes(this->minute) + seconds(this->second);
sys_days sd = ymd;
auto res = sd - date::sys_days{date::January/1/1970};
return (res.count() * 86400) + hr.count(); //bad
}
DateTime DateTime::NowUTC()
{
DateTime theTime((int64_t)time(NULL));
return theTime;
}
void DateTime::SetToNowUTC()
{
this->Set((int64_t)time(NULL));
}
void DateTime::SetYear(int y)
{
this->year = year;
}
void DateTime::SetMonth(int m)
{
this->month = m;
}
void DateTime::SetDay(int d)
{
this->day = d;
}
void DateTime::SetHour(int h)
{
this->hour = h;
}
void DateTime::SetMinute(int m)
{
this->minute = m;
}
void DateTime::SetSecond(int s)
{
this->second = s;
}
void DateTime::SetLocal(bool local)
{
this->isLocal=local;
}
void DateTime::SetToNow()
{
int64_t local = (int64_t)time(NULL);
local += GetTimeZone();
if(TimeZoneSupportDST())
{
auto epoch = date::sys_days{date::January/1/1970};
epoch += date::days(div_floor(local,86400));
bool isDST = false;
date::year_month_day ymd(epoch);
auto month = (uint32_t)ymd.month();
if(month > 3 && month < 11)
{
isDST=true;
}
else if(month == 3)
{
auto day = (uint32_t)ymd.day();
if(day > 14) isDST=true;
else if(day >= 8 && day <= 14)
{
date::year_month_weekday ymw(epoch);
auto dow=ymw.weekday().c_encoding();
auto secondSunday = day - dow;
if(secondSunday < 8) secondSunday+=7;
if(day > secondSunday) isDST=true;
else if(day == secondSunday)
{
int64_t secs = local % 86400;
if(secs < 0) secs += 86400;
secs /= 3600;
auto _hours = secs % 24;
if(_hours >= 2) isDST=true;
}
}
}
else if(month == 11)
{
auto day = (uint32_t)ymd.day();
if(day >= 1 && day <= 7)
{
date::year_month_weekday ymw(epoch);
auto dow=ymw.weekday().c_encoding();
int32_t firstSunday = (int32_t)day - (int32_t)dow;
if(firstSunday < 1) firstSunday+=7;
if(day < firstSunday) isDST=true;
else if(day == firstSunday)
{
int64_t secs = local % 86400;
if(secs < 0) secs += 86400;
secs /= 3600;
auto _hours = secs % 24;
if(_hours <=1) isDST=true;
}
}
}
if(isDST) local += 3600;
}
this->isLocal=true;
this->FromEpochNoConvert(local);
}
DateTime DateTime::Now()
{
DateTime dt;
dt.SetToNow();
return dt;
}
const char* weekday_short[] = {
"Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat"
};
const char* months_short[] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
};
const char* weekday_long[] = {
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
};
const char* months_long[] = {
"January",
"Febuary",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
bool DateTime::TryParseHttpDate(std::string txt, DateTime& dt)
{
//Mon, 24 Jul 2018 11:00:00 GMT
auto split = Http::HttpUtils::SplitString(txt,", ",2);
if(split.size() != 2) return false;
bool validDay = false;
for(size_t i = 0; i < 7; i++)
{
std::string_view d = weekday_short[i];
if(d == split[0]) validDay=true;
}
if(!validDay) return false;
//DAY MON YEAR HH:MM:SS GMT
split = Http::HttpUtils::SplitString(split[1]," ",5);
if(split.size() != 5) return false;
if(split[4] != "GMT") return false;
int day=0;
int mon=0;
int year=0;
int hour=0;
int minute=0;
int second=0;
try {
day = std::stoi(split[0]);
if(day < 1 && day > 31) return false;
} catch(std::exception& ex) {
return false;
}
for(int i = 0; i < 12; mon++)
{
std::string_view d = months_short[i];
if(d == split[1])
{
mon = i + 1;
break;
}
}
if(mon == 0) return false;
try {
year = std::stoi(split[2]);
} catch(std::exception& ex) {
return false;
}
split = Http::HttpUtils::SplitString(split[3],":",3);
if(split.size() != 3) return false;
try {
hour = std::stoi(split[0]);
minute = std::stoi(split[1]);
second = std::stoi(split[2]);
} catch(std::exception& ex) {
return false;
}
dt.day = day;
dt.month = mon;
dt.year = year;
dt.hour = hour;
dt.minute = minute;
dt.second = second;
return true;
}
std::string DateTime::ToHttpDate()
{
auto utc=this->ToUTC();
std::string weekday=weekday_short[utc.DayOfWeek()];
std::string month = months_short[utc.month-1];
std::stringstream strm(std::ios_base::out);
strm << weekday << ", " << std::setfill('0') << std::setw(2) << utc.day;
strm << " " << month << " " << std::setfill('0') << std::setw(4) << utc.year;
strm << " " << std::setfill('0') << std::setw(2) << utc.hour;
strm << ":" << std::setfill('0') << std::setw(2) << utc.minute;
strm << ":" << std::setfill('0') << std::setw(2) << utc.second;
strm << " GMT";
return strm.str();
}
std::string DateTime::ToString()
{
return ToString("%Y/%m/%d %H:%M:%S");
}
std::string DateTime::ToString(std::string fmt)
{
auto weekday = this->DayOfWeek();
std::string text = "";
for(size_t i = 0; i < fmt.size(); i++)
{
if(fmt[i]=='%')
{
i++;
if(i < fmt.size())
switch(fmt[i])
{
case 'a':
text.append(weekday_short[weekday]);
break;
case 'A':
text.append(weekday_long[weekday]);
break;
case 'S':
text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0'));
break;
case 'm':
text.append(Http::HttpUtils::LeftPad(std::to_string(month),2,'0'));
break;
case 'd':
text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,'0'));
break;
case 'e':
text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,' '));
break;
case 'M':
text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0'));
break;
case 'H':
text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0'));
break;
case 'F':
text.append(Http::HttpUtils::LeftPad(std::to_string(year),4,'0'));
text.push_back('-');
text.append(Http::HttpUtils::LeftPad(std::to_string(month),2,'0'));
text.push_back('-');
text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,'0'));
break;
case 'D':
text.append(Http::HttpUtils::LeftPad(std::to_string(month),2,'0'));
text.push_back('/');
text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,'0'));
text.push_back('/');
text.append(Http::HttpUtils::LeftPad(std::to_string(year % 100),2,'0'));
break;
case 'y':
text.append(Http::HttpUtils::LeftPad(std::to_string(year % 100),2,'0'));
break;
case 'r':
{
auto hours = hour % 12;
if(hours == 0) hours=12;
text.append(Http::HttpUtils::LeftPad(std::to_string(hours),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0'));
text.append(hour >= 12 ? " PM" : " AM");
}
break;
case 'R':
{
text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0'));
}
break;
case 'T':
{
text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0'));
}
break;
case 'u':
{
int dow = weekday + 6;
dow %= 7;
text.append(std::to_string(dow+1));
}
break;
case 'w':
{
text.append(std::to_string(weekday));
}
break;
case 'c':
{
text.append(weekday_short[weekday]);
text.push_back(' ');
text.append(months_short[month]);
text.push_back(' ');
text.append(Http::HttpUtils::LeftPad(std::to_string(day),2,'0'));
text.push_back(' ');
text.append(Http::HttpUtils::LeftPad(std::to_string(hour),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(minute),2,'0'));
text.push_back(':');
text.append(Http::HttpUtils::LeftPad(std::to_string(second),2,'0'));
text.push_back(' ');
text.append(Http::HttpUtils::LeftPad(std::to_string(year),4,'0'));
}
break;
case 'C':
text.append(Http::HttpUtils::LeftPad(std::to_string(year / 100),2,'0'));
break;
case 'Y':
text.append(Http::HttpUtils::LeftPad(std::to_string(year),4,'0'));
break;
case 'p':
text.append(hour >= 12 ? "PM" : "AM");
break;
case 'I':
{
auto hours = hour % 12;
if(hours == 0) hours=12;
text.append(Http::HttpUtils::LeftPad(std::to_string(hours),2,'0'));
}
break;
case 'h':
case 'b':
text.append(months_short[month-1]);
break;
case 'B':
text.append(months_long[month-1]);
break;
case '%':
text.push_back('%');
break;
case 'n':
text.push_back('\n');
break;
case 't':
text.push_back('\t');
break;
}
}
else
{
text.push_back(fmt[i]);
}
}
return text;
}
}

View File

@ -19,25 +19,26 @@ namespace Tesses::Framework::Filesystem
pft->dwHighDateTime = time_value.HighPart;
}
#endif
void LocalFilesystem::GetDate(VFSPath path, time_t& lastWrite, time_t& lastAccess)
void LocalFilesystem::GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess)
{
std::string s = VFSPathToSystem(path);
struct stat st;
if(stat(s.c_str(),&st) == 0)
{
lastAccess = st.st_atime;
lastWrite = st.st_mtime;
lastAccess = Date::DateTime((int64_t)st.st_atime);
lastWrite = Date::DateTime((int64_t)st.st_mtime);
}
}
void LocalFilesystem::SetDate(VFSPath path, time_t lastWrite, time_t lastAccess)
void LocalFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess)
{
std::string s = VFSPathToSystem(path);
#if defined(TESSESFRAMEWORK_ENABLE_SETDATE)
#if defined(_WIN32)
FILETIME lastWriteF;
FILETIME lastAccessF;
TimetToFileTime(lastWrite,&lastWriteF);
TimetToFileTime(lastAccess,&lastAccessF);
TimetToFileTime((time_t)lastWrite.ToEpoch(),&lastWriteF);
TimetToFileTime((time_t)lastAccess.ToEpoch(),&lastAccessF);
HANDLE hFile = CreateFileA(
s.c_str(),
FILE_WRITE_ATTRIBUTES,
@ -59,8 +60,8 @@ namespace Tesses::Framework::Filesystem
}
#else
struct utimbuf utim;
utim.actime = lastAccess;
utim.modtime = lastWrite;
utim.actime = (time_t)lastAccess.ToEpoch();
utim.modtime = (time_t)lastWrite.ToEpoch();
utime(s.c_str(),&utim);
#endif
#endif

View File

@ -202,7 +202,7 @@ namespace Tesses::Framework::Filesystem
}
file->data->canAccess=false;
file->data->readers++;
if(truncate) {file->data->file.clear(); file->data->lastWrite=time(NULL);}
if(truncate) {file->data->file.clear(); file->data->lastWrite=Date::DateTime::NowUTC();}
mtx->Unlock();
return new MemoryFilesystemStream(mtx,file->data,canRead,canWrite,canSeek);
@ -303,7 +303,7 @@ namespace Tesses::Framework::Filesystem
}
thefile->data->canAccess=false;
thefile->data->readers++;
if(truncate) {thefile->data->file.clear(); thefile->data->lastWrite=time(NULL);}
if(truncate) {thefile->data->file.clear(); thefile->data->lastWrite=Date::DateTime::NowUTC();}
mtx->Unlock();
return new MemoryFilesystemStream(mtx,thefile->data,canRead,canWrite,canSeek);
}
@ -350,10 +350,10 @@ namespace Tesses::Framework::Filesystem
MemoryDirectory* dir2 = new MemoryDirectory();
dir2->name = part;
dir2->lastWrite=time(NULL);
dir2->lastWrite=Date::DateTime::NowUTC();
dir->entries.push_back(dir2);
dir->lastWrite=time(NULL);
dir->lastWrite=Date::DateTime::NowUTC();
dir=dir2;
}
@ -387,7 +387,7 @@ namespace Tesses::Framework::Filesystem
delete item;
dir->entries.erase(index);
dir->lastWrite=time(NULL);
dir->lastWrite=Date::DateTime::NowUTC();
}
break;
}
@ -450,7 +450,7 @@ namespace Tesses::Framework::Filesystem
{
delete item;
dir->entries.erase(index);
dir->lastWrite=time(NULL);
dir->lastWrite=Date::DateTime::NowUTC();
}
break;
}
@ -482,7 +482,7 @@ namespace Tesses::Framework::Filesystem
if(p != nullptr)
{
p->linkedTo = existingFile;
p->lastWrite = time(NULL);
p->lastWrite = Date::DateTime::NowUTC();
}
mtx->Unlock();
return;
@ -491,9 +491,9 @@ namespace Tesses::Framework::Filesystem
MemorySymlink* symlink = new MemorySymlink();
symlink->name = fname;
symlink->linkedTo = existingFile;
symlink->lastWrite = time(NULL);
symlink->lastWrite = Date::DateTime::NowUTC();
dir->entries.push_back(symlink);
dir->lastWrite = time(NULL);
dir->lastWrite = Date::DateTime::NowUTC();
mtx->Unlock();
}
@ -558,7 +558,7 @@ namespace Tesses::Framework::Filesystem
memFile->name = fname;
memFile->data = existing->data;
dir->entries.push_back(memFile);
dir->lastWrite=time(NULL);
dir->lastWrite=Date::DateTime::NowUTC();
mtx->Unlock();
}
@ -601,7 +601,7 @@ namespace Tesses::Framework::Filesystem
{
return path;
}
void MemoryFilesystem::GetDate(VFSPath path, time_t& lastWrite, time_t& lastAccess)
void MemoryFilesystem::GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess)
{
mtx->Lock();
@ -618,7 +618,7 @@ namespace Tesses::Framework::Filesystem
mtx->Unlock();
lastAccess = lastWrite;
}
void MemoryFilesystem::SetDate(VFSPath path, time_t lastWrite, time_t lastAccess)
void MemoryFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess)
{
mtx->Lock();
auto node = GetEntry(path,false);
@ -652,7 +652,7 @@ namespace Tesses::Framework::Filesystem
}
MemoryDirectory::MemoryDirectory()
{
this->lastWrite = time(NULL);
this->lastWrite = Date::DateTime::NowUTC();
}
MemoryDirectory::~MemoryDirectory()
{
@ -660,7 +660,7 @@ namespace Tesses::Framework::Filesystem
}
MemoryFileData::MemoryFileData()
{
this->lastWrite = time(NULL);
this->lastWrite = Date::DateTime::NowUTC();
this->canAccess=true;
this->readers=0;

View File

@ -288,7 +288,7 @@ namespace Tesses::Framework::Filesystem
vfs->DeleteFile(destPath);
}
void MountableFilesystem::GetDate(VFSPath path, time_t& lastWrite, time_t& lastAccess)
void MountableFilesystem::GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess)
{
path = path.CollapseRelativeParents();
@ -302,7 +302,7 @@ namespace Tesses::Framework::Filesystem
if(vfs != nullptr)
vfs->GetDate(destPath,lastWrite,lastAccess);
}
void MountableFilesystem::SetDate(VFSPath path, time_t lastWrite, time_t lastAccess)
void MountableFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess)
{
path = path.CollapseRelativeParents();

View File

@ -107,11 +107,11 @@ namespace Tesses::Framework::Filesystem
delete enumerator;
});
}
void SubdirFilesystem::GetDate(VFSPath path, time_t& lastWrite, time_t& lastAccess)
void SubdirFilesystem::GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess)
{
this->parent->GetDate(ToParent(path),lastWrite,lastAccess);
}
void SubdirFilesystem::SetDate(VFSPath path, time_t lastWrite, time_t lastAccess)
void SubdirFilesystem::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess)
{
this->parent->SetDate(ToParent(path),lastWrite,lastAccess);
}

View File

@ -490,11 +490,11 @@ namespace Tesses::Framework::Filesystem
}
DeleteDirectory(path);
}
void VFS::GetDate(VFSPath path, time_t& lastWrite, time_t& lastAccess)
void VFS::GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess)
{
}
void VFS::SetDate(VFSPath path, time_t lastWrite, time_t lastAccess)
void VFS::SetDate(VFSPath path, Date::DateTime lastWrite, Date::DateTime lastAccess)
{
}

View File

@ -0,0 +1,34 @@
#ifndef CHRONO_IO_H
#define CHRONO_IO_H
// The MIT License (MIT)
//
// Copyright (c) 2016, 2017 Howard Hinnant
//
// 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.
//
// Our apologies. When the previous paragraph was written, lowercase had not yet
// been invented (that would involve another several millennia of evolution).
// We did not mean to shout.
// This functionality has moved to "date.h"
#include "date.h"
#endif // CHRONO_IO_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
//
// ios.h
// DateTimeLib
//
// The MIT License (MIT)
//
// Copyright (c) 2016 Alexander Kormanovsky
//
// 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.
#ifndef ios_hpp
#define ios_hpp
#if __APPLE__
# include <TargetConditionals.h>
# if TARGET_OS_IPHONE
# include <string>
namespace date
{
namespace iOSUtils
{
std::string get_tzdata_path();
std::string get_current_timezone();
} // namespace iOSUtils
} // namespace date
# endif // TARGET_OS_IPHONE
#else // !__APPLE__
# define TARGET_OS_IPHONE 0
#endif // !__APPLE__
#endif // ios_hpp

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,952 @@
#ifndef PTZ_H
#define PTZ_H
// The MIT License (MIT)
//
// Copyright (c) 2017 Howard Hinnant
//
// 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.
// This header allows Posix-style time zones as specified for TZ here:
// http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
//
// Posix::time_zone can be constructed with a posix-style string and then used in
// a zoned_time like so:
//
// zoned_time<system_clock::duration, Posix::time_zone> zt{"EST5EDT,M3.2.0,M11.1.0",
// system_clock::now()};
// or:
//
// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"};
// zoned_time<system_clock::duration, Posix::time_zone> zt{tz, system_clock::now()};
//
// In C++17 CTAD simplifies this to:
//
// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"};
// zoned_time zt{tz, system_clock::now()};
//
// Extension to the Posix rules to allow a constant daylight saving offset:
//
// If the rule set is missing (everything starting with ','), then
// there must be exactly one abbreviation (std or daylight) with
// length 3 or greater, and that will be used as the constant offset. If
// there are two, the std abbreviation is silently set to "", and the
// result is constant daylight saving. If there are zero abbreviations
// with no rule set, an exception is thrown.
//
// Example:
// "EST5" yields a constant offset of -5h with 0h save and "EST abbreviation.
// "5EDT" yields a constant offset of -4h with 1h save and "EDT" abbreviation.
// "EST5EDT" and "5EDT4" are both equal to "5EDT".
//
// Note, Posix-style time zones are not recommended for all of the reasons described here:
// https://stackoverflow.com/tags/timezone/info
//
// They are provided here as a non-trivial custom time zone example, and if you really
// have to have Posix time zones, you're welcome to use this one.
#include "date/tz.h"
#include <algorithm>
#include <cctype>
#include <ostream>
#include <string>
namespace Posix
{
namespace detail
{
#if HAS_STRING_VIEW
using string_t = std::string_view;
#else // !HAS_STRING_VIEW
using string_t = std::string;
#endif // !HAS_STRING_VIEW
class rule;
void throw_invalid(const string_t& s, unsigned i, const string_t& message);
unsigned read_date(const string_t& s, unsigned i, rule& r);
unsigned read_name(const string_t& s, unsigned i, std::string& name);
unsigned read_signed_time(const string_t& s, unsigned i, std::chrono::seconds& t);
unsigned read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t);
unsigned read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u,
const string_t& message = string_t{});
class rule
{
enum {off, J, M, N};
date::month m_;
date::weekday wd_;
unsigned short n_ : 14;
unsigned short mode_ : 2;
std::chrono::duration<std::int32_t> time_ = std::chrono::hours{2};
public:
rule() : mode_(off) {}
bool ok() const {return mode_ != off;}
date::local_seconds operator()(date::year y) const;
std::string to_string() const;
friend std::ostream& operator<<(std::ostream& os, const rule& r);
friend unsigned read_date(const string_t& s, unsigned i, rule& r);
friend bool operator==(const rule& x, const rule& y);
};
inline
bool
operator==(const rule& x, const rule& y)
{
if (x.mode_ != y.mode_)
return false;
switch (x.mode_)
{
case rule::J:
case rule::N:
return x.n_ == y.n_;
case rule::M:
return x.m_ == y.m_ && x.n_ == y.n_ && x.wd_ == y.wd_;
default:
return true;
}
}
inline
bool
operator!=(const rule& x, const rule& y)
{
return !(x == y);
}
inline
date::local_seconds
rule::operator()(date::year y) const
{
using date::local_days;
using date::January;
using date::days;
using date::last;
using sec = std::chrono::seconds;
date::local_seconds t;
switch (mode_)
{
case J:
t = local_days{y/January/0} + days{n_ + (y.is_leap() && n_ > 59)} + sec{time_};
break;
case M:
t = (n_ == 5 ? local_days{y/m_/wd_[last]} : local_days{y/m_/wd_[n_]}) + sec{time_};
break;
case N:
t = local_days{y/January/1} + days{n_} + sec{time_};
break;
default:
assert(!"rule called with bad mode");
}
return t;
}
inline
std::string
rule::to_string() const
{
using namespace std::chrono;
auto print_offset = [](seconds off)
{
std::string nm;
if (off != hours{2})
{
date::hh_mm_ss<seconds> offset{off};
nm = '/';
nm += std::to_string(offset.hours().count());
if (offset.minutes() != minutes{0} || offset.seconds() != seconds{0})
{
nm += ':';
if (offset.minutes() < minutes{10})
nm += '0';
nm += std::to_string(offset.minutes().count());
if (offset.seconds() != seconds{0})
{
nm += ':';
if (offset.seconds() < seconds{10})
nm += '0';
nm += std::to_string(offset.seconds().count());
}
}
}
return nm;
};
std::string nm;
switch (mode_)
{
case rule::J:
nm = 'J';
nm += std::to_string(n_);
break;
case rule::M:
nm = 'M';
nm += std::to_string(static_cast<unsigned>(m_));
nm += '.';
nm += std::to_string(n_);
nm += '.';
nm += std::to_string(wd_.c_encoding());
break;
case rule::N:
nm = std::to_string(n_);
break;
default:
break;
}
nm += print_offset(time_);
return nm;
}
inline
std::ostream&
operator<<(std::ostream& os, const rule& r)
{
switch (r.mode_)
{
case rule::J:
os << 'J' << r.n_ << date::format(" %T", r.time_);
break;
case rule::M:
if (r.n_ == 5)
os << r.m_/r.wd_[date::last];
else
os << r.m_/r.wd_[r.n_];
os << date::format(" %T", r.time_);
break;
case rule::N:
os << r.n_ << date::format(" %T", r.time_);
break;
default:
break;
}
return os;
}
} // namespace detail
class time_zone
{
std::string std_abbrev_;
std::string dst_abbrev_ = {};
std::chrono::seconds offset_;
std::chrono::seconds save_ = std::chrono::hours{1};
detail::rule start_rule_;
detail::rule end_rule_;
public:
explicit time_zone(const detail::string_t& name);
template <class Duration>
date::sys_info get_info(date::sys_time<Duration> st) const;
template <class Duration>
date::local_info get_info(date::local_time<Duration> tp) const;
template <class Duration>
date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
to_sys(date::local_time<Duration> tp) const;
template <class Duration>
date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
to_sys(date::local_time<Duration> tp, date::choose z) const;
template <class Duration>
date::local_time<typename std::common_type<Duration, std::chrono::seconds>::type>
to_local(date::sys_time<Duration> tp) const;
friend std::ostream& operator<<(std::ostream& os, const time_zone& z);
const time_zone* operator->() const {return this;}
std::string name() const;
friend bool operator==(const time_zone& x, const time_zone& y);
private:
date::sys_seconds get_start(date::year y) const;
date::sys_seconds get_prev_start(date::year y) const;
date::sys_seconds get_next_start(date::year y) const;
date::sys_seconds get_end(date::year y) const;
date::sys_seconds get_prev_end(date::year y) const;
date::sys_seconds get_next_end(date::year y) const;
date::sys_info contant_offset() const;
};
inline
date::sys_seconds
time_zone::get_start(date::year y) const
{
return date::sys_seconds{(start_rule_(y) - offset_).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_prev_start(date::year y) const
{
return date::sys_seconds{(start_rule_(--y) - offset_).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_next_start(date::year y) const
{
return date::sys_seconds{(start_rule_(++y) - offset_).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_end(date::year y) const
{
return date::sys_seconds{(end_rule_(y) - (offset_ + save_)).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_prev_end(date::year y) const
{
return date::sys_seconds{(end_rule_(--y) - (offset_ + save_)).time_since_epoch()};
}
inline
date::sys_seconds
time_zone::get_next_end(date::year y) const
{
return date::sys_seconds{(end_rule_(++y) - (offset_ + save_)).time_since_epoch()};
}
inline
date::sys_info
time_zone::contant_offset() const
{
using date::year;
using date::sys_info;
using date::sys_days;
using date::January;
using date::December;
using date::last;
using date::days;
using std::chrono::minutes;
sys_info r;
r.begin = sys_days{year::min()/January/1};
r.end = sys_days{year::max()/December/last} + days{1} - std::chrono::seconds{1};
if (std_abbrev_.size() > 0)
{
r.abbrev = std_abbrev_;
r.offset = offset_;
r.save = {};
}
else
{
r.abbrev = dst_abbrev_;
r.offset = offset_ + save_;
r.save = date::ceil<minutes>(save_);
}
return r;
}
inline
time_zone::time_zone(const detail::string_t& s)
{
using detail::read_name;
using detail::read_signed_time;
using detail::throw_invalid;
auto i = read_name(s, 0, std_abbrev_);
auto std_name_i = i;
auto abbrev_name_i = i;
i = read_signed_time(s, i, offset_);
offset_ = -offset_;
if (i != s.size())
{
i = read_name(s, i, dst_abbrev_);
abbrev_name_i = i;
if (i != s.size())
{
if (s[i] != ',')
{
i = read_signed_time(s, i, save_);
save_ = -save_ - offset_;
}
if (i != s.size())
{
if (s[i] != ',')
throw_invalid(s, i, "Expecting end of string or ',' to start rule");
++i;
i = read_date(s, i, start_rule_);
if (i == s.size() || s[i] != ',')
throw_invalid(s, i, "Expecting ',' and then the ending rule");
++i;
i = read_date(s, i, end_rule_);
if (i != s.size())
throw_invalid(s, i, "Found unexpected trailing characters");
}
}
}
if (start_rule_.ok())
{
if (std_abbrev_.size() < 3)
throw_invalid(s, std_name_i, "Zone with rules must have a std"
" abbreviation of length 3 or greater");
if (dst_abbrev_.size() < 3)
throw_invalid(s, abbrev_name_i, "Zone with rules must have a daylight"
" abbreviation of length 3 or greater");
}
else
{
if (dst_abbrev_.size() >= 3)
{
std_abbrev_.clear();
}
else if (std_abbrev_.size() < 3)
{
throw_invalid(s, std_name_i, "Zone must have at least one abbreviation"
" of length 3 or greater");
}
else
{
dst_abbrev_.clear();
save_ = {};
}
}
}
template <class Duration>
date::sys_info
time_zone::get_info(date::sys_time<Duration> st) const
{
using date::sys_info;
using date::year_month_day;
using date::sys_days;
using date::floor;
using date::ceil;
using date::days;
using date::year;
using date::January;
using date::December;
using date::last;
using std::chrono::minutes;
sys_info r{};
r.offset = offset_;
if (start_rule_.ok())
{
auto y = year_month_day{floor<days>(st)}.year();
if (st >= get_next_start(y))
++y;
else if (st < get_prev_end(y))
--y;
auto start = get_start(y);
auto end = get_end(y);
if (start <= end) // (northern hemisphere)
{
if (start <= st && st < end)
{
r.begin = start;
r.end = end;
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
}
else if (st < start)
{
r.begin = get_prev_end(y);
r.end = start;
r.abbrev = std_abbrev_;
}
else // st >= end
{
r.begin = end;
r.end = get_next_start(y);
r.abbrev = std_abbrev_;
}
}
else // end < start (southern hemisphere)
{
if (end <= st && st < start)
{
r.begin = end;
r.end = start;
r.abbrev = std_abbrev_;
}
else if (st < end)
{
r.begin = get_prev_start(y);
r.end = end;
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
}
else // st >= start
{
r.begin = start;
r.end = get_next_end(y);
r.offset += save_;
r.save = ceil<minutes>(save_);
r.abbrev = dst_abbrev_;
}
}
}
else
r = contant_offset();
using seconds = std::chrono::seconds;
assert(r.begin <= floor<seconds>(st) && floor<seconds>(st) <= r.end);
return r;
}
template <class Duration>
date::local_info
time_zone::get_info(date::local_time<Duration> tp) const
{
using date::local_info;
using date::year_month_day;
using date::days;
using date::sys_days;
using date::sys_seconds;
using date::year;
using date::ceil;
using date::January;
using date::December;
using date::last;
using std::chrono::seconds;
using std::chrono::minutes;
local_info r{};
using date::floor;
if (start_rule_.ok())
{
auto y = year_month_day{floor<days>(tp)}.year();
auto start = get_start(y);
auto end = get_end(y);
auto utcs = sys_seconds{floor<seconds>(tp - offset_).time_since_epoch()};
auto utcd = sys_seconds{floor<seconds>(tp - (offset_ + save_)).time_since_epoch()};
auto northern = start <= end;
if ((utcs < start) != (utcd < start))
{
if (northern)
r.first.begin = get_prev_end(y);
else
r.first.begin = end;
r.first.end = start;
r.first.offset = offset_;
r.first.abbrev = std_abbrev_;
r.second.begin = start;
if (northern)
r.second.end = end;
else
r.second.end = get_next_end(y);
r.second.abbrev = dst_abbrev_;
r.second.offset = offset_ + save_;
r.second.save = ceil<minutes>(save_);
r.result = save_ > seconds{0} ? local_info::nonexistent
: local_info::ambiguous;
}
else if ((utcs < end) != (utcd < end))
{
if (northern)
r.first.begin = start;
else
r.first.begin = get_prev_start(y);
r.first.end = end;
r.first.offset = offset_ + save_;
r.first.save = ceil<minutes>(save_);
r.first.abbrev = dst_abbrev_;
r.second.begin = end;
if (northern)
r.second.end = get_next_start(y);
else
r.second.end = start;
r.second.abbrev = std_abbrev_;
r.second.offset = offset_;
r.result = save_ > seconds{0} ? local_info::ambiguous
: local_info::nonexistent;
}
else
r.first = get_info(utcs);
}
else
r.first = contant_offset();
return r;
}
template <class Duration>
date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
time_zone::to_sys(date::local_time<Duration> tp) const
{
using date::local_info;
using date::sys_time;
using date::ambiguous_local_time;
using date::nonexistent_local_time;
auto i = get_info(tp);
if (i.result == local_info::nonexistent)
throw nonexistent_local_time(tp, i);
else if (i.result == local_info::ambiguous)
throw ambiguous_local_time(tp, i);
return sys_time<Duration>{tp.time_since_epoch()} - i.first.offset;
}
template <class Duration>
date::sys_time<typename std::common_type<Duration, std::chrono::seconds>::type>
time_zone::to_sys(date::local_time<Duration> tp, date::choose z) const
{
using date::local_info;
using date::sys_time;
using date::choose;
auto i = get_info(tp);
if (i.result == local_info::nonexistent)
{
return i.first.end;
}
else if (i.result == local_info::ambiguous)
{
if (z == choose::latest)
return sys_time<Duration>{tp.time_since_epoch()} - i.second.offset;
}
return sys_time<Duration>{tp.time_since_epoch()} - i.first.offset;
}
template <class Duration>
date::local_time<typename std::common_type<Duration, std::chrono::seconds>::type>
time_zone::to_local(date::sys_time<Duration> tp) const
{
using date::local_time;
using std::chrono::seconds;
using LT = local_time<typename std::common_type<Duration, seconds>::type>;
auto i = get_info(tp);
return LT{(tp + i.offset).time_since_epoch()};
}
inline
std::ostream&
operator<<(std::ostream& os, const time_zone& z)
{
using date::operator<<;
os << '{';
os << z.std_abbrev_ << ", " << z.dst_abbrev_ << date::format(", %T, ", z.offset_)
<< date::format("%T, [", z.save_) << z.start_rule_ << ", " << z.end_rule_ << ")}";
return os;
}
inline
std::string
time_zone::name() const
{
using namespace date;
using namespace std::chrono;
auto print_abbrev = [](std::string const& nm)
{
if (std::any_of(nm.begin(), nm.end(),
[](char c)
{
return !std::isalpha(c);
}))
{
return '<' + nm + '>';
}
return nm;
};
auto print_offset = [](seconds off)
{
std::string nm;
date::hh_mm_ss<seconds> offset{-off};
if (offset.is_negative())
nm += '-';
nm += std::to_string(offset.hours().count());
if (offset.minutes() != minutes{0} || offset.seconds() != seconds{0})
{
nm += ':';
if (offset.minutes() < minutes{10})
nm += '0';
nm += std::to_string(offset.minutes().count());
if (offset.seconds() != seconds{0})
{
nm += ':';
if (offset.seconds() < seconds{10})
nm += '0';
nm += std::to_string(offset.seconds().count());
}
}
return nm;
};
auto nm = print_abbrev(std_abbrev_);
nm += print_offset(offset_);
if (!dst_abbrev_.empty())
{
nm += print_abbrev(dst_abbrev_);
if (save_ != hours{1})
nm += print_offset(offset_+save_);
if (start_rule_.ok())
{
nm += ',';
nm += start_rule_.to_string();
nm += ',';
nm += end_rule_.to_string();
}
}
return nm;
}
inline
bool
operator==(const time_zone& x, const time_zone& y)
{
return x.std_abbrev_ == y.std_abbrev_ &&
x.dst_abbrev_ == y. dst_abbrev_ &&
x.offset_ == y.offset_ &&
x.save_ == y.save_ &&
x.start_rule_ == y.start_rule_ &&
x.end_rule_ == y.end_rule_;
}
inline
bool
operator!=(const time_zone& x, const time_zone& y)
{
return !(x == y);
}
namespace detail
{
inline
void
throw_invalid(const string_t& s, unsigned i, const string_t& message)
{
throw std::runtime_error(std::string("Invalid time_zone initializer.\n") +
std::string(message) + ":\n" +
std::string(s) + '\n' +
"\x1b[1;32m" +
std::string(i, '~') + '^' +
std::string(i < s.size() ? s.size()-i-1 : 0, '~') +
"\x1b[0m");
}
inline
unsigned
read_date(const string_t& s, unsigned i, rule& r)
{
using date::month;
using date::weekday;
if (i == s.size())
throw_invalid(s, i, "Expected rule but found end of string");
if (s[i] == 'J')
{
++i;
unsigned n;
i = read_unsigned(s, i, 3, n, "Expected to find the Julian day [1, 365]");
if (!(1 <= n && n <= 365))
throw_invalid(s, i-1, "Expected Julian day to be in the range [1, 365]");
r.mode_ = rule::J;
r.n_ = n;
}
else if (s[i] == 'M')
{
++i;
unsigned m;
i = read_unsigned(s, i, 2, m, "Expected to find month [1, 12]");
if (!(1 <= m && m <= 12))
throw_invalid(s, i-1, "Expected month to be in the range [1, 12]");
if (i == s.size() || s[i] != '.')
throw_invalid(s, i, "Expected '.' after month");
++i;
unsigned n;
i = read_unsigned(s, i, 1, n, "Expected to find week number [1, 5]");
if (!(1 <= n && n <= 5))
throw_invalid(s, i-1, "Expected week number to be in the range [1, 5]");
if (i == s.size() || s[i] != '.')
throw_invalid(s, i, "Expected '.' after weekday index");
++i;
unsigned wd;
i = read_unsigned(s, i, 1, wd, "Expected to find day of week [0, 6]");
if (wd > 6)
throw_invalid(s, i-1, "Expected day of week to be in the range [0, 6]");
r.mode_ = rule::M;
r.m_ = month{m};
r.wd_ = weekday{wd};
r.n_ = n;
}
else if (std::isdigit(s[i]))
{
unsigned n;
i = read_unsigned(s, i, 3, n);
if (n > 365)
throw_invalid(s, i-1, "Expected Julian day to be in the range [0, 365]");
r.mode_ = rule::N;
r.n_ = n;
}
else
throw_invalid(s, i, "Expected 'J', 'M', or a digit to start rule");
if (i != s.size() && s[i] == '/')
{
++i;
std::chrono::seconds t;
i = read_unsigned_time(s, i, t);
r.time_ = t;
}
return i;
}
inline
unsigned
read_name(const string_t& s, unsigned i, std::string& name)
{
if (i == s.size())
throw_invalid(s, i, "Expected a name but found end of string");
if (s[i] == '<')
{
++i;
while (true)
{
if (i == s.size())
throw_invalid(s, i,
"Expected to find closing '>', but found end of string");
if (s[i] == '>')
break;
name.push_back(s[i]);
++i;
}
++i;
}
else
{
while (i != s.size() && std::isalpha(s[i]))
{
name.push_back(s[i]);
++i;
}
}
return i;
}
inline
unsigned
read_signed_time(const string_t& s, unsigned i,
std::chrono::seconds& t)
{
if (i == s.size())
throw_invalid(s, i, "Expected to read signed time, but found end of string");
bool negative = false;
if (s[i] == '-')
{
negative = true;
++i;
}
else if (s[i] == '+')
++i;
i = read_unsigned_time(s, i, t);
if (negative)
t = -t;
return i;
}
inline
unsigned
read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t)
{
using std::chrono::seconds;
using std::chrono::minutes;
using std::chrono::hours;
if (i == s.size())
throw_invalid(s, i, "Expected to read unsigned time, but found end of string");
unsigned x;
i = read_unsigned(s, i, 2, x, "Expected to find hours [0, 24]");
if (x > 24)
throw_invalid(s, i-1, "Expected hours to be in the range [0, 24]");
t = hours{x};
if (i != s.size() && s[i] == ':')
{
++i;
i = read_unsigned(s, i, 2, x, "Expected to find minutes [0, 59]");
if (x > 59)
throw_invalid(s, i-1, "Expected minutes to be in the range [0, 59]");
t += minutes{x};
if (i != s.size() && s[i] == ':')
{
++i;
i = read_unsigned(s, i, 2, x, "Expected to find seconds [0, 59]");
if (x > 59)
throw_invalid(s, i-1, "Expected seconds to be in the range [0, 59]");
t += seconds{x};
}
}
return i;
}
inline
unsigned
read_unsigned(const string_t& s, unsigned i, unsigned limit, unsigned& u,
const string_t& message)
{
if (i == s.size() || !std::isdigit(s[i]))
throw_invalid(s, i, message);
u = static_cast<unsigned>(s[i] - '0');
unsigned count = 1;
for (++i; count < limit && i != s.size() && std::isdigit(s[i]); ++i, ++count)
u = u * 10 + static_cast<unsigned>(s[i] - '0');
return i;
}
} // namespace detail
} // namespace Posix
namespace date
{
template <>
struct zoned_traits<Posix::time_zone>
{
#if HAS_STRING_VIEW
static
Posix::time_zone
locate_zone(std::string_view name)
{
return Posix::time_zone{name};
}
#else // !HAS_STRING_VIEW
static
Posix::time_zone
locate_zone(const std::string& name)
{
return Posix::time_zone{name};
}
static
Posix::time_zone
locate_zone(const char* name)
{
return Posix::time_zone{name};
}
#endif // !HAS_STRING_VIEW
};
} // namespace date
#endif // PTZ_H

File diff suppressed because it is too large Load Diff

View File

@ -43,7 +43,9 @@ namespace Tesses::Framework::Http
bool retVal = false;
if(strm != nullptr)
{
ctx.WithMimeType(HttpUtils::MimeType(path.GetFileName())).SendStream(strm);
Date::DateTime lw,la;
this->vfs->GetDate(path,lw,la);
ctx.WithLastModified(lw).WithMimeType(HttpUtils::MimeType(path.GetFileName())).SendStream(strm);
retVal = true;
}

View File

@ -507,18 +507,42 @@ namespace Tesses::Framework::Http
while(parseSection(this, ct, cb));
}
HttpServer::HttpServer(uint16_t port, IHttpServer* http, bool owns)
{
this->server = new TcpServer(port, 10);
this->http = http;
this->owns = owns;
this->thrd=nullptr;
this->port = port;
}
HttpServer::HttpServer(uint16_t port, IHttpServer& http) : HttpServer(port,&http,false)
HttpServer::HttpServer(Tesses::Framework::Streams::TcpServer& tcpServer, IHttpServer& http, bool showIPs) : HttpServer(&tcpServer,false,&http,false,showIPs)
{
}
HttpServer::HttpServer(Tesses::Framework::Streams::TcpServer* tcpServer, bool ownsTCP, IHttpServer& http, bool showIPs) : HttpServer(tcpServer,ownsTCP,&http,false,showIPs)
{
}
HttpServer::HttpServer(Tesses::Framework::Streams::TcpServer& tcpServer, IHttpServer* http, bool ownsHttpServer, bool showIPs) : HttpServer(&tcpServer,false,http,ownsHttpServer,showIPs)
{
}
HttpServer::HttpServer(Tesses::Framework::Streams::TcpServer* tcpServer, bool ownsTCP, IHttpServer* http, bool ownsHttpServer, bool showIPs)
{
this->server = tcpServer;
this->ownsTCP = ownsTCP;
this->http = http;
this->ownsHttp = ownsHttpServer;
this->showIPs = showIPs;
this->thrd=nullptr;
}
HttpServer::HttpServer(uint16_t port, IHttpServer* http, bool owns, bool showIPs) : HttpServer(new TcpServer(port,10),true,http,owns,showIPs)
{
}
HttpServer::HttpServer(uint16_t port, IHttpServer& http,bool showIPs) : HttpServer(port,&http,false,showIPs)
{
}
uint16_t HttpServer::GetPort()
{
if(server != nullptr)
return server->GetPort();
return 0;
}
Stream* ServerContext::OpenResponseStream()
{
@ -543,7 +567,7 @@ namespace Tesses::Framework::Http
void HttpServer::StartAccepting()
{
fflush(stdout);
if(http == nullptr) return;
if(http == nullptr || server == nullptr) return;
auto svr=this->server;
auto http = this->http;
TF_LOG("Before Creating Thread");
@ -574,31 +598,37 @@ namespace Tesses::Framework::Http
TF_LOG("After attach");
}
});
TF_LOG("Before printing interfaces");
std::cout << "\x1B[34mInterfaces:\n";
for(auto _ip : NetworkStream::GetIPs())
if(this->showIPs)
{
std::cout << "\x1B[32m";
std::cout << _ip.first << ": ";
std::cout << "\x1B[35mhttp://";
std::cout << _ip.second << ":" << std::to_string(this->port) << "/\n";
TF_LOG("Before printing interfaces");
std::cout << "\x1B[34mInterfaces:\n";
for(auto _ip : NetworkStream::GetIPs())
{
std::cout << "\x1B[32m";
std::cout << _ip.first << ": ";
std::cout << "\x1B[35mhttp://";
std::cout << _ip.second << ":" << std::to_string(this->GetPort()) << "/\n";
}
if(!svr->IsValid()) std::cout << "\x1B[31mError, we failed to bind or something\x1B[39m\n" << std::endl;
std::cout << "\x1B[31mAlmost Ready to Listen\x1B[39m\n";
}
if(!svr->IsValid()) std::cout << "\x1B[31mError, we failed to bind or something\x1B[39m\n" << std::endl;
std::cout << "\x1B[31mAlmost Ready to Listen\x1B[39m\n";
TF_LOG("After printing interfaces");
}
HttpServer::~HttpServer()
{
auto port = this->GetPort();
this->server->Close();
TF_ConnectToSelf(this->port);
TF_ConnectToSelf(port);
if(this->thrd != nullptr)
{
this->thrd->Join();
delete this->thrd;
}
if(this->owns)
if(this->ownsHttp)
delete http;
if(this->ownsTCP)
delete this->server;
}
IHttpServer::~IHttpServer()
@ -622,6 +652,12 @@ namespace Tesses::Framework::Http
strm.GetBuffer() = buff;
SendStream(strm);
}
ServerContext& ServerContext::WithLastModified(Date::DateTime dt)
{
this->responseHeaders.SetValue("Last-Modified",dt);
return *this;
}
void ServerContext::SendText(std::string text)
{
MemoryStream strm(false);

View File

@ -1,5 +1,7 @@
#include "TessesFramework/Http/HttpUtils.hpp"
#include "TessesFramework/Filesystem/VFS.hpp"
#include <iostream>
#include <algorithm>
using VFSPath = Tesses::Framework::Filesystem::VFSPath;
namespace Tesses::Framework::Http {
@ -158,6 +160,38 @@ namespace Tesses::Framework::Http {
uri.append(this->GetPathAndQuery());
return uri;
}
std::string HttpUtils::Replace(std::string text, std::string find, std::string replace)
{
std::string dest;
while(text.length() > 0)
{
std::size_t index= text.find(find);
if(index == std::string::npos)
{
dest.append(text);
break;
}
else
{
std::string left = text.substr(0,index);
text = text.substr(index+find.size());
dest.append(left);
dest.append(replace);
}
}
return dest;
}
std::string HttpUtils::LeftPad(std::string text, int count, char c)
{
std::stringstream strm(std::ios_base::out);
strm << std::setfill(c) << std::setw(count) << text;
return strm.str();
}
char HttpUtils::NibbleToHex(uint8_t b)
{
b %= 16;
@ -672,6 +706,18 @@ namespace Tesses::Framework::Http {
{
kvp[key] = {value};
}
void HttpDictionary::SetValue(std::string key, int64_t value)
{
kvp[key] = {std::to_string(value)};
}
void HttpDictionary::SetValue(std::string key, double value)
{
kvp[key] = {std::to_string(value)};
}
void HttpDictionary::SetValue(std::string key, Date::DateTime value)
{
kvp[key] = {value.ToHttpDate()};
}
void HttpDictionary::SetValue(std::string key, std::vector<std::string> value)
{
kvp[key] = value;
@ -680,6 +726,20 @@ namespace Tesses::Framework::Http {
{
kvp[key].push_back(value);
}
void HttpDictionary::AddValue(std::string key, int64_t value)
{
kvp[key].push_back(std::to_string(value));
}
void HttpDictionary::AddValue(std::string key, double value)
{
kvp[key].push_back(std::to_string(value));
}
void HttpDictionary::AddValue(std::string key, Date::DateTime value)
{
kvp[key].push_back(value.ToHttpDate());
}
void HttpDictionary::AddValue(std::string key, std::vector<std::string> value)
{
auto& ls = kvp[key];
@ -711,7 +771,12 @@ namespace Tesses::Framework::Http {
}
return true;
}
bool HttpDictionary::TryGetFirstDate(std::string key, Date::DateTime& dt)
{
std::string val;
if(!TryGetFirst(key,val)) return false;
return Date::DateTime::TryParseHttpDate(val,dt);
}
bool HttpDictionary::TryGetFirstDouble(std::string key, double& value)
{
std::string val;

View File

@ -2,6 +2,7 @@
#include "TessesFramework/Http/HttpUtils.hpp"
#include <iostream>
using HttpUtils = Tesses::Framework::Http::HttpUtils;
#if defined(TESSESFRAMEWORK_ENABLE_NETWORKING)
@ -11,6 +12,7 @@ using HttpUtils = Tesses::Framework::Http::HttpUtils;
#endif
#if defined(GEKKO) && !(defined(TESSESFRAMEWORK_USE_WII_SOCKET) && defined(HW_RVL))
#include <network.h>
#define NETWORK_GETSOCKNAME net_getsockname
#define NETWORK_RECV net_recv
#define sockaddr_storage sockaddr_in
#error "Not supported yet"
@ -53,6 +55,7 @@ extern "C" uint32_t if_config( char *local_ip, char *netmask, char *gateway,bool
#define NETWORK_ACCEPT accept
#define NETWORK_GETADDRINFO getaddrinfo
#define NETWORK_FREEADDRINFO freeaddrinfo
#define NETWORK_GETSOCKNAME getsockname
#if defined(_WIN32)
#define NETWORK_CLOSE closesocket
#else
@ -76,7 +79,41 @@ namespace Tesses::Framework::Streams {
char gateway[16];
if_config(localIp,netmask, gateway, true, 1);
ipConfig.push_back(std::pair<std::string,std::string>("net",localIp));
#elif defined(_WIN32) || defined(__ANDROID__) || defined(__SWITCH__)
#elif defined(_WIN32)
//Thanks https://www.youtube.com/watch?v=K446bcFeE3s
ULONG family = ipV6 ? 0 : AF_INET;
ULONG flags = 0;
ULONG size = 15000;
PIP_ADAPTER_ADDRESSES addresses = NULL;
addresses = (PIP_ADAPTER_ADDRESSES)malloc(size);
int retval = GetAdapterAddresses(family, flags, 0, addresses, &size);
if(retval != 0) {
free(addresses);
return {};
}
PIP_ADAPTER_ADDRESSES addrPtr = addresses;
while(addrPtr != NULL)
{
auto fname = addrPtr->FriendlyName;
size_t len = WideCharToMultiByte(CP_UTF8, 0,fname,-1,NULL, 0, NULL, NULL);
std::string name(len, 0);
WideCharToMultiByte(CP_UTF8, 0,fname,-1,name.data(), len, NULL, NULL);
auto ip = addrPtr->FirstUnicastAddress;
while(ip != NULL)
{
ipConfig.push_back(std::pair<std::string,std::string>(name, StringifyIP(ip->Address.lpSockaddr)));
ip = ip->Next;
}
addrPtr = addrPtr->Next;
}
free(addresses);
#elif defined(__ANDROID__) || defined(__SWITCH__)
#else
struct ifaddrs *ifAddrStruct = NULL;
@ -119,7 +156,7 @@ namespace Tesses::Framework::Streams {
#endif
}
uint16_t GetPort(struct sockaddr* addr)
static uint16_t getPort(struct sockaddr* addr)
{
if(addr->sa_family == AF_INET)
{
@ -223,10 +260,16 @@ namespace Tesses::Framework::Streams {
return "";
}
typedef union {
in_addr_t addr;
uint8_t addr_parts[4];
} my_addr_t;
TcpServer::TcpServer(uint16_t port, int32_t backlog)
{
this->owns=true;
this->sock = NETWORK_SOCKET(AF_INET, SOCK_STREAM, 0);
this->sock = NETWORK_SOCKET(AF_INET, SOCK_STREAM, 0);
if(this->sock < 0)
{
std::cout << "FAILED TO CREATE SOCKET FOR SOME REASON" << std::endl;
@ -238,6 +281,8 @@ namespace Tesses::Framework::Streams {
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if(port > 0)
{
int on=1;
#if defined(SO_REUSEPORT)
NETWORK_SETSOCKOPT(this->sock,SOL_SOCKET,SO_REUSEPORT,(const char*)&on, (socklen_t)sizeof(on));
@ -245,6 +290,16 @@ namespace Tesses::Framework::Streams {
#if defined(SO_REUSEADDR)
NETWORK_SETSOCKOPT(this->sock,SOL_SOCKET,SO_REUSEADDR,(const char*)&on, (socklen_t)sizeof(on));
#endif
}
else
{
my_addr_t addr2;
addr2.addr_parts[0] = 127;
addr2.addr_parts[1] = 0;
addr2.addr_parts[2] = 0;
addr2.addr_parts[3] = 1;
addr.sin_addr.s_addr=addr2.addr;
}
if(NETWORK_BIND(this->sock, (const sockaddr*)&addr, (socklen_t)sizeof(addr)) != 0)
{
std::cout << "FAILED TO BIND FOR SOME REASON" << std::endl;
@ -260,6 +315,46 @@ namespace Tesses::Framework::Streams {
}
this->valid = true;
}
TcpServer::TcpServer(int32_t backlog) : TcpServer(0U, backlog)
{
}
uint16_t TcpServer::GetPort()
{
struct sockaddr_storage addr;
memset(&addr, 0, sizeof(addr));
socklen_t len = sizeof(addr);
NETWORK_GETSOCKNAME(this->sock, (struct sockaddr*)&addr, &len);
if(addr.ss_family == AF_INET)
{
return (uint16_t)ntohs(((struct sockaddr_in*)&addr)->sin_port);
}
#if defined(AF_INET6)
if(addr.ss_family == AF_INET6)
{
return (uint16_t)ntohs(((struct sockaddr_in6*)&addr)->sin6_port);
}
#endif
return 0;
}
uint16_t NetworkStream::GetPort()
{
struct sockaddr_storage addr;
memset(&addr, 0, sizeof(addr));
socklen_t len = sizeof(addr);
NETWORK_GETSOCKNAME(this->sock, (struct sockaddr*)&addr, &len);
if(addr.ss_family == AF_INET)
{
return (uint16_t)(((struct sockaddr_in*)&addr)->sin_port);
}
#if defined(AF_INET6)
if(addr.ss_family == AF_INET6)
{
return (uint16_t)(((struct sockaddr_in6*)&addr)->sin6_port);
}
#endif
return 0;
}
bool TcpServer::IsValid()
{
return this->valid;
@ -333,7 +428,7 @@ namespace Tesses::Framework::Streams {
}
ip = StringifyIP((struct sockaddr*)&storage);
port = GetPort((struct sockaddr*)&storage);
port = getPort((struct sockaddr*)&storage);
return new NetworkStream(s,true);
}
@ -491,7 +586,7 @@ namespace Tesses::Framework::Streams {
}
ip = StringifyIP((struct sockaddr*)&storage);
port = GetPort((struct sockaddr*)&storage);
port = getPort((struct sockaddr*)&storage);
return new NetworkStream(s,true);
}
@ -529,7 +624,7 @@ namespace Tesses::Framework::Streams {
socklen_t addrlen=(socklen_t)sizeof(storage);
auto r = NETWORK_RECVFROM(this->sock,(char*)buff,sz,0, (struct sockaddr*)&storage, (socklen_t*)&addrlen);
ip = StringifyIP((struct sockaddr*)&storage);
port = GetPort((struct sockaddr*)&storage);
port = getPort((struct sockaddr*)&storage);
if(r < 0) return 0;
return (size_t)r;

View File

@ -115,6 +115,7 @@ namespace Tesses::Framework
}
void TF_Init()
{
tzset();
#if defined(_WIN32)
system(" ");
#endif