22// SPDX-License-Identifier: MIT
33#include " HttpWebServer.h"
44#include " ../FileSystem/FileSystem.h"
5- #include " ../Time/Time.h"
65#include " Internal/HttpStringIterator.h"
76#include < stdio.h>
7+ #include < time.h>
8+ #if SC_PLATFORM_WINDOWS
9+ #define WIN32_LEAN_AND_MEAN
10+ #include < Windows.h>
11+ #include < sys/timeb.h>
12+ #else
13+ #include < math.h>
14+ #endif
815
916struct SC ::HttpWebServer::Internal
1017{
11- static Result writeGMTHeaderTime (StringSpan headerName, HttpResponse& response,
12- const Time::Absolute::ParseResult& local);
18+ static Result writeGMTHeaderTime (StringSpan headerName, HttpResponse& response, int64_t millisecondsSinceEpoch);
1319 static Result readFile (StringSpan initialDirectory, HttpRequest& request, HttpResponse& response);
20+ static Result formatHttpDate (int64_t millisecondsSinceEpoch, char * buffer, size_t bufferSize, size_t & outLength);
1421
22+ static int64_t getCurrentTimeMilliseconds ();
1523 static StringSpan getContentType (const StringSpan extension);
1624};
1725
@@ -58,32 +66,13 @@ SC::Result SC::HttpWebServer::Internal::readFile(StringSpan directory, HttpReque
5866 SC_TRY (response.addHeader (" Connection" , " Closed" ));
5967 SC_TRY (response.addHeader (" Content-Type" , Internal::getContentType (extension)));
6068 SC_TRY (response.addHeader (" Server" , " SC" ));
61- Time::Absolute::ParseResult local;
62- SC_TRY (Time::Realtime::now ().parseUTC (local));
63- SC_TRY (Internal::writeGMTHeaderTime (" Date" , response, local));
64- SC_TRY (Time::Realtime (fileStat.modifiedTime ).parseUTC (local));
65- SC_TRY (Internal::writeGMTHeaderTime (" Last-Modified" , response, local));
69+ SC_TRY (Internal::writeGMTHeaderTime (" Date" , response, Internal::getCurrentTimeMilliseconds ()));
70+ SC_TRY (Internal::writeGMTHeaderTime (" Last-Modified" , response, fileStat.modifiedTime .milliseconds ));
6671 SC_TRY (response.end (data.toSpanConst ()));
6772 }
6873 return Result (true );
6974}
7075
71- SC::Result SC::HttpWebServer::Internal::writeGMTHeaderTime (StringSpan headerName, HttpResponse& response,
72- const Time::Absolute::ParseResult& local)
73- {
74- char bufferData[128 ];
75- size_t len = static_cast <size_t >(::snprintf (bufferData, sizeof (bufferData), " %s, %02u %s %04u %02u:%02u:%02u GMT" ,
76- local.getDay (), local.dayOfMonth , local.getMonth (), local.year ,
77- local.hour , local.minutes , local.seconds ));
78- if (len == 0 or len >= sizeof (bufferData))
79- {
80- return Result::Error (" Failed to format time" );
81- }
82-
83- SC_TRY (response.addHeader (headerName, {{bufferData, len}, true , StringEncoding::Ascii}));
84- return Result (true );
85- }
86-
8776SC::StringSpan SC::HttpWebServer::Internal::getContentType (const StringSpan extension)
8877{
8978 if (extension == " htm" or extension == " html" )
@@ -108,3 +97,60 @@ SC::StringSpan SC::HttpWebServer::Internal::getContentType(const StringSpan exte
10897 }
10998 return " text/html" ;
11099}
100+
101+ SC::int64_t SC::HttpWebServer::Internal::getCurrentTimeMilliseconds ()
102+ {
103+ #if SC_PLATFORM_WINDOWS
104+ struct _timeb t;
105+ _ftime_s (&t);
106+ return static_cast <int64_t >(t.time ) * 1000 + t.millitm ;
107+ #else
108+ struct timespec nowTimeSpec;
109+ clock_gettime (CLOCK_REALTIME, &nowTimeSpec);
110+ return static_cast <int64_t >(round (nowTimeSpec.tv_nsec / 1.0e6 ) + nowTimeSpec.tv_sec * 1000 );
111+ #endif
112+ }
113+
114+ SC::Result SC::HttpWebServer::Internal::formatHttpDate (int64_t millisecondsSinceEpoch, char * buffer, size_t bufferSize,
115+ size_t & outLength)
116+ {
117+ const time_t seconds = static_cast <time_t >(millisecondsSinceEpoch / 1000 );
118+ struct tm parsedTm;
119+ #if SC_PLATFORM_WINDOWS
120+ if (_gmtime64_s (&parsedTm, &seconds) != 0 )
121+ {
122+ return Result::Error (" Failed to convert time" );
123+ }
124+ #else
125+ if (gmtime_r (&seconds, &parsedTm) == nullptr )
126+ {
127+ return Result::Error (" Failed to convert time" );
128+ }
129+ #endif
130+
131+ // Format as HTTP date: "Wed, 21 Oct 2015 07:28:00 GMT"
132+ const char * days[] = {" Sun" , " Mon" , " Tue" , " Wed" , " Thu" , " Fri" , " Sat" };
133+ const char * months[] = {" Jan" , " Feb" , " Mar" , " Apr" , " May" , " Jun" , " Jul" , " Aug" , " Sep" , " Oct" , " Nov" , " Dec" };
134+
135+ outLength = static_cast <size_t >(
136+ snprintf (buffer, bufferSize, " %s, %02d %s %04d %02d:%02d:%02d GMT" , days[parsedTm.tm_wday ], parsedTm.tm_mday ,
137+ months[parsedTm.tm_mon ], parsedTm.tm_year + 1900 , parsedTm.tm_hour , parsedTm.tm_min , parsedTm.tm_sec ));
138+
139+ if (outLength == 0 || outLength >= bufferSize)
140+ {
141+ return Result::Error (" Failed to format time" );
142+ }
143+
144+ return Result (true );
145+ }
146+
147+ SC::Result SC::HttpWebServer::Internal::writeGMTHeaderTime (StringSpan headerName, HttpResponse& response,
148+ int64_t millisecondsSinceEpoch)
149+ {
150+ char bufferData[128 ];
151+ size_t len;
152+ SC_TRY (formatHttpDate (millisecondsSinceEpoch, bufferData, sizeof (bufferData), len));
153+
154+ SC_TRY (response.addHeader (headerName, {{bufferData, len}, true , StringEncoding::Ascii}));
155+ return Result (true );
156+ }
0 commit comments