Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

webserver: improved file caching #1366

Merged
merged 6 commits into from

2 participants

@Montellese
Owner

These commits contain some additional code to improve client-side caching of files exposed through xbmc's webserver. I have discussed these things with @da-anda at devcon. The changes are:

  • raise the value of the "Expires" header to 1 year in the future (currently it's 1 day)
  • provide the "Last-Modified" header for any "physical" file
  • check for "If-Modified-Since" request header and return HTTP 304 "Not Modified" instead of the whole file

All these changes should speed up retrieval of files through xbmc's webserver, especially for webinterfaces that show a lot of images.

@Montellese Montellese was assigned
xbmc/network/WebServer.cpp
((41 lines not shown))
CDateTime expiryTime = CDateTime::GetCurrentDateTime();
- expiryTime += CDateTimeSpan(1, 0, 0, 0);
+ expiryTime += CDateTimeSpan(365, 0, 0, 0);
@da-anda Collaborator
da-anda added a note

could we add a condition to only set the expiration time to 1 year on files that are no HTML sites? I'd like to prevent that addon html files probably get cached that long by some browsers/clients - just to be really sure. So something like:

c_int expiryDuration = CDateTimeSpan(356, 0, 0, 0);

if (ext.c_str() == "htm" || ext.c_str() == "html") {
 expiryDuration = CDateTimeSpan(1, 0, 0, 0);
}

expiryTime += expiryDuration;

ofc only if this is the right place for it

@Montellese Owner

So caching HTML pages for a day and non-HTML pages for a year? Shouldn't be a problem.

@da-anda Collaborator
da-anda added a note

Yes. HTML pages are the only thing a addon editor cannot link versioned (at least not the index.html) - so probably better don't let browsers cache them that long just to be really sure updated addons are loaded by the browsers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Montellese Montellese merged commit c55558e into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
38 xbmc/XBDateTime.cpp
@@ -29,6 +29,9 @@
#define SECONDS_PER_MINUTE 60UL
#define SECONDS_TO_FILETIME 10000000UL
+static const char *DAY_NAMES[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+static const char *MONTH_NAMES[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
/////////////////////////////////////////////////
//
// CDateTimeSpan
@@ -994,6 +997,38 @@ void CDateTime::SetFromDBTime(const CStdString &time)
SetTime(hour, minute, second);
}
+void CDateTime::SetFromRFC1123DateTime(const CStdString &dateTime)
+{
+ CStdString date = dateTime;
+ date.Trim();
+
+ if (date.size() != 29)
+ return;
+
+ int day = strtol(date.Mid(5, 2).c_str(), NULL, 10);
+
+ CStdString strMonth = date.Mid(8, 3);
+ int month = 0;
+ for (unsigned int index = 0; index < 12; index++)
+ {
+ if (strMonth.Equals(MONTH_NAMES[index]))
+ {
+ month = index + 1;
+ break;
+ }
+ }
+
+ if (month < 1)
+ return;
+
+ int year = strtol(date.Mid(12, 4).c_str(), NULL, 10);
+ int hour = strtol(date.Mid(17, 2).c_str(), NULL, 10);
+ int min = strtol(date.Mid(20, 2).c_str(), NULL, 10);
+ int sec = strtol(date.Mid(23, 2).c_str(), NULL, 10);
+
+ SetDateTime(year, month, day, hour, min, sec);
+}
+
CStdString CDateTime::GetAsLocalizedTime(const CStdString &format, bool withSeconds) const
{
CStdString strOut;
@@ -1313,9 +1348,6 @@ CDateTime CDateTime::GetAsUTCDateTime() const
return time;
}
-static const char *DAY_NAMES[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
-static const char *MONTH_NAMES[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
-
CStdString CDateTime::GetAsRFC1123DateTime() const
{
CDateTime time(GetAsUTCDateTime());
View
1  xbmc/XBDateTime.h
@@ -172,6 +172,7 @@ class CDateTime : public IArchivable
void SetFromW3CDate(const CStdString &date);
void SetFromUTCDateTime(const CDateTime &dateTime);
void SetFromUTCDateTime(const time_t &dateTime);
+ void SetFromRFC1123DateTime(const CStdString &dateTime);
/*! \brief set from a database datetime format YYYY-MM-DD HH:MM:SS
\sa GetAsDBDateTime()
View
71 xbmc/network/WebServer.cpp
@@ -275,6 +275,7 @@ int CWebServer::HandleRequest(IHTTPRequestHandler *handler, const HTTPRequest &r
}
struct MHD_Response *response = NULL;
+ int responseCode = handler->GetHTTPResonseCode();
switch (handler->GetHTTPResponseType())
{
case HTTPNone:
@@ -286,7 +287,7 @@ int CWebServer::HandleRequest(IHTTPRequestHandler *handler, const HTTPRequest &r
break;
case HTTPFileDownload:
- ret = CreateFileDownloadResponse(request.connection, handler->GetHTTPResponseFile(), request.method, response);
+ ret = CreateFileDownloadResponse(request.connection, handler->GetHTTPResponseFile(), request.method, response, responseCode);
break;
case HTTPMemoryDownloadNoFreeNoCopy:
@@ -324,7 +325,7 @@ int CWebServer::HandleRequest(IHTTPRequestHandler *handler, const HTTPRequest &r
for (multimap<string, string>::const_iterator it = header.begin(); it != header.end(); it++)
MHD_add_response_header(response, it->first.c_str(), it->second.c_str());
- MHD_queue_response(request.connection, handler->GetHTTPResonseCode(), response);
+ MHD_queue_response(request.connection, responseCode, response);
MHD_destroy_response(response);
delete handler;
@@ -354,15 +355,43 @@ int CWebServer::CreateRedirect(struct MHD_Connection *connection, const string &
return MHD_NO;
}
-int CWebServer::CreateFileDownloadResponse(struct MHD_Connection *connection, const string &strURL, HTTPMethod methodType, struct MHD_Response *&response)
+int CWebServer::CreateFileDownloadResponse(struct MHD_Connection *connection, const string &strURL, HTTPMethod methodType, struct MHD_Response *&response, int &responseCode)
{
CFile *file = new CFile();
if (file->Open(strURL, READ_NO_CACHE))
{
+ bool getData = true;
if (methodType != HEAD)
{
- response = MHD_create_response_from_callback ( file->GetLength(),
+ if (methodType == GET)
+ {
+ string ifModifiedSince = GetRequestHeaderValue(connection, MHD_HEADER_KIND, "If-Modified-Since");
+ if (!ifModifiedSince.empty())
+ {
+ CDateTime ifModifiedSinceDate;
+ ifModifiedSinceDate.SetFromRFC1123DateTime(ifModifiedSince);
+
+ struct __stat64 statBuffer;
+ if (file->Stat(&statBuffer) == 0)
+ {
+ struct tm *time = localtime((time_t *)&statBuffer.st_mtime);
+ if (time != NULL)
+ {
+ CDateTime lastModified = *time;
+ if (lastModified.GetAsUTCDateTime() <= ifModifiedSinceDate)
+ {
+ getData = false;
+ response = MHD_create_response_from_data (0, NULL, MHD_NO, MHD_NO);
+ responseCode = MHD_HTTP_NOT_MODIFIED;
+ }
+ }
+ }
+ }
+ }
+
+ if (getData)
+ response = MHD_create_response_from_callback(file->GetLength(),
2048,
&CWebServer::ContentReaderCallback, file,
&CWebServer::ContentReaderFreeCallback);
@@ -371,26 +400,54 @@ int CWebServer::CreateFileDownloadResponse(struct MHD_Connection *connection, co
}
else
{
+ getData = false;
+
CStdString contentLength;
contentLength.Format("%I64d", file->GetLength());
- file->Close();
- delete file;
response = MHD_create_response_from_data (0, NULL, MHD_NO, MHD_NO);
if (response == NULL)
+ {
+ file->Close();
+ delete file;
return MHD_NO;
+ }
MHD_add_response_header(response, "Content-Length", contentLength);
}
+ // set the Content-Type header
CStdString ext = URIUtils::GetExtension(strURL);
ext = ext.ToLower();
const char *mime = CreateMimeTypeFromExtension(ext.c_str());
if (mime)
MHD_add_response_header(response, "Content-Type", mime);
+ // set the Last-Modified header
+ struct __stat64 statBuffer;
+ if (file->Stat(&statBuffer) == 0)
+ {
+ struct tm *time = localtime((time_t *)&statBuffer.st_mtime);
+ if (time != NULL)
+ {
+ CDateTime lastModified = *time;
+ MHD_add_response_header(response, "Last-Modified", lastModified.GetAsRFC1123DateTime());
+ }
+ }
+
+ // set the Expires header
CDateTime expiryTime = CDateTime::GetCurrentDateTime();
- expiryTime += CDateTimeSpan(1, 0, 0, 0);
+ if (mime && strncmp(mime, "text/html", 9) == 0)
+ expiryTime += CDateTimeSpan(1, 0, 0, 0);
+ else
+ expiryTime += CDateTimeSpan(365, 0, 0, 0);
MHD_add_response_header(response, "Expires", expiryTime.GetAsRFC1123DateTime());
+
+ // only close the CFile instance if libmicrohttpd doesn't have to grab the data of the file
+ if (!getData)
+ {
+ file->Close();
+ delete file;
+ }
}
else
{
View
2  xbmc/network/WebServer.h
@@ -92,7 +92,7 @@ class CWebServer : public JSONRPC::ITransportLayer
static int HandleRequest(IHTTPRequestHandler *handler, const HTTPRequest &request);
static void ContentReaderFreeCallback (void *cls);
static int CreateRedirect(struct MHD_Connection *connection, const std::string &strURL, struct MHD_Response *&response);
- static int CreateFileDownloadResponse(struct MHD_Connection *connection, const std::string &strURL, HTTPMethod methodType, struct MHD_Response *&response);
+ static int CreateFileDownloadResponse(struct MHD_Connection *connection, const std::string &strURL, HTTPMethod methodType, struct MHD_Response *&response, int &responseCode);
static int CreateErrorResponse(struct MHD_Connection *connection, int responseType, HTTPMethod method, struct MHD_Response *&response);
static int CreateMemoryDownloadResponse(struct MHD_Connection *connection, void *data, size_t size, bool free, bool copy, struct MHD_Response *&response);
Something went wrong with that request. Please try again.