Skip to content

Commit

Permalink
webserver: refactor handling of HEAD requests and of caching and rang…
Browse files Browse the repository at this point in the history
…e HTTP headers
  • Loading branch information
Montellese committed Jan 24, 2015
1 parent 98c8445 commit 25b9db7
Show file tree
Hide file tree
Showing 17 changed files with 932 additions and 538 deletions.
904 changes: 534 additions & 370 deletions xbmc/network/WebServer.cpp

Large diffs are not rendered by default.

18 changes: 10 additions & 8 deletions xbmc/network/WebServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ namespace XFILE
}
class CDateTime;

typedef std::pair<int64_t, int64_t> HttpRange;
typedef std::vector<HttpRange> HttpRanges;

class CWebServer : public JSONRPC::ITransportLayer
{
public:
Expand All @@ -60,6 +57,8 @@ class CWebServer : public JSONRPC::ITransportLayer
static int GetRequestHeaderValues(struct MHD_Connection *connection, enum MHD_ValueKind kind, std::map<std::string, std::string> &headerValues);
static int GetRequestHeaderValues(struct MHD_Connection *connection, enum MHD_ValueKind kind, std::multimap<std::string, std::string> &headerValues);

static bool GetRequestedRanges(struct MHD_Connection *connection, uint64_t totalLength, CHttpRanges &ranges);

private:
struct MHD_Daemon* StartMHD(unsigned int flags, int port);
static int AskForAuthentication (struct MHD_Connection *connection);
Expand All @@ -74,6 +73,7 @@ class CWebServer : public JSONRPC::ITransportLayer
#else
static int ContentReaderCallback (void *cls, size_t pos, char *buf, int max);
#endif
static void ContentReaderFreeCallback(void *cls);

#if (MHD_VERSION >= 0x00040001)
static int AnswerToConnection (void *cls, struct MHD_Connection *connection,
Expand All @@ -95,11 +95,15 @@ class CWebServer : public JSONRPC::ITransportLayer
unsigned int size);
#endif
static int HandleRequest(IHTTPRequestHandler *handler);
static void ContentReaderFreeCallback (void *cls);
static int FinalizeRequest(IHTTPRequestHandler *handler, int responseStatus, struct MHD_Response *response);

static int CreateMemoryDownloadResponse(IHTTPRequestHandler *handler, struct MHD_Response *&response);
static int CreateRangedMemoryDownloadResponse(IHTTPRequestHandler *handler, struct MHD_Response *&response);

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, int &responseCode);
static int CreateFileDownloadResponse(IHTTPRequestHandler *handler, struct MHD_Response *&response);
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);
static int CreateMemoryDownloadResponse(struct MHD_Connection *connection, const void *data, size_t size, bool free, bool copy, struct MHD_Response *&response);

static int SendErrorResponse(struct MHD_Connection *connection, int errorType, HTTPMethod method);

Expand All @@ -110,8 +114,6 @@ class CWebServer : public JSONRPC::ITransportLayer
static std::string CreateMimeTypeFromExtension(const char *ext);

static int AddHeader(struct MHD_Response *response, const std::string &name, const std::string &value);
static int64_t ParseRangeHeader(const std::string &rangeHeaderValue, int64_t totalLength, HttpRanges &ranges, int64_t &firstPosition, int64_t &lastPosition);
static std::string GenerateMultipartBoundary();
static bool GetLastModifiedDateTime(XFILE::CFile *file, CDateTime &lastModified);

struct MHD_Daemon *m_daemon_ip6;
Expand Down
108 changes: 108 additions & 0 deletions xbmc/network/httprequesthandler/HTTPFileHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright (C) 2015 Team XBMC
* http://xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with XBMC; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
*/

#include "system.h"
#include "HTTPFileHandler.h"
#include "filesystem/File.h"
#include "utils/Mime.h"
#include "utils/StringUtils.h"
#include "utils/URIUtils.h"

CHTTPFileHandler::CHTTPFileHandler(const HTTPRequest &request)
: IHTTPRequestHandler(request),
m_url(),
m_canHandleRanges(true),
m_canBeCached(true),
m_lastModified()
{ }

int CHTTPFileHandler::HandleRequest()
{
return !m_url.empty() ? MHD_YES : MHD_NO;
}

bool CHTTPFileHandler::GetLastModifiedDate(CDateTime &lastModified) const
{
if (!m_lastModified.IsValid())
return false;

lastModified = m_lastModified;
return true;
}

void CHTTPFileHandler::SetFile(const std::string& file, int responseStatus)
{
m_url = file;
m_response.status = responseStatus;
if (m_url.empty())
return;

// translate the response status into the response type
if (m_response.status == MHD_HTTP_OK)
m_response.type = HTTPFileDownload;
else if (m_response.status == MHD_HTTP_FOUND)
m_response.type = HTTPRedirect;
else
m_response.type = HTTPError;

// try to determine some additional information if the file can be downloaded
if (m_response.type == HTTPFileDownload)
{
// determine the content type
std::string ext = URIUtils::GetExtension(m_url);
StringUtils::ToLower(ext);
m_response.contentType = CMime::GetMimeType(ext);

// determine the last modified date
XFILE::CFile fileObj;
if (!fileObj.Open(m_url, READ_NO_CACHE))
{
m_response.type = HTTPError;
m_response.status = MHD_HTTP_INTERNAL_SERVER_ERROR;
}
else
{
struct __stat64 statBuffer;
if (fileObj.Stat(&statBuffer) == 0)
{
struct tm *time;
#ifdef HAVE_LOCALTIME_R
struct tm result = { };
time = localtime_r((time_t*)&statBuffer.st_mtime, &result);
#else
time = localtime((time_t *)&statBuffer.st_mtime);
#endif
if (time != NULL)
m_lastModified = *time;
}
}
}

// disable ranges and caching if the file can't be downloaded
if (m_response.type != HTTPFileDownload)
{
m_canHandleRanges = false;
m_canBeCached = false;
}

// disable caching if the last modified date couldn't be read
if (!m_lastModified.IsValid())
m_canBeCached = false;
}
59 changes: 59 additions & 0 deletions xbmc/network/httprequesthandler/HTTPFileHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#pragma once
/*
* Copyright (C) 2015 Team XBMC
* http://xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with XBMC; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
*/

#include <string>

#include "XBDateTime.h"
#include "network/httprequesthandler/IHTTPRequestHandler.h"

class CHTTPFileHandler : public IHTTPRequestHandler
{
public:
virtual ~CHTTPFileHandler() { }

virtual int HandleRequest();

virtual bool CanHandleRanges() const { return m_canHandleRanges; }
virtual bool CanBeCached() const { return m_canBeCached; }
virtual bool GetLastModifiedDate(CDateTime &lastModified) const;

virtual std::string GetRedirectUrl() const { return m_url; }
virtual std::string GetResponseFile() const { return m_url; }

protected:
CHTTPFileHandler() { }
explicit CHTTPFileHandler(const HTTPRequest &request);

void SetFile(const std::string& file, int responseStatus);

void SetCanHandleRanges(bool canHandleRanges) { m_canHandleRanges = canHandleRanges; }
void SetCanBeCached(bool canBeCached) { m_canBeCached = canBeCached; }
void SetLastModifiedDate(CDateTime lastModified) { m_lastModified = lastModified; }

private:
std::string m_url;

bool m_canHandleRanges;
bool m_canBeCached;

CDateTime m_lastModified;

};
37 changes: 16 additions & 21 deletions xbmc/network/httprequesthandler/HTTPImageHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,35 +25,30 @@

using namespace std;

bool CHTTPImageHandler::CanHandleRequest(const HTTPRequest &request)
CHTTPImageHandler::CHTTPImageHandler(const HTTPRequest &request)
: CHTTPFileHandler(request)
{
return (request.url.find("/image/") == 0);
}
std::string file;
int responseStatus = MHD_HTTP_BAD_REQUEST;

int CHTTPImageHandler::HandleRequest()
{
// resolve the URL into a file path and a HTTP response status
if (m_request.url.size() > 7)
{
m_path = m_request.url.substr(7);
file = m_request.url.substr(7);

XFILE::CImageFile imageFile;
const CURL pathToUrl(m_path);
const CURL pathToUrl(file);
if (imageFile.Exists(pathToUrl))
{
m_responseCode = MHD_HTTP_OK;
m_responseType = HTTPFileDownload;
}
responseStatus = MHD_HTTP_OK;
else
{
m_responseCode = MHD_HTTP_NOT_FOUND;
m_responseType = HTTPError;
}
}
else
{
m_responseCode = MHD_HTTP_BAD_REQUEST;
m_responseType = HTTPError;
responseStatus = MHD_HTTP_NOT_FOUND;
}

return MHD_YES;
// set the file and the HTTP response status
SetFile(file, responseStatus);
}

bool CHTTPImageHandler::CanHandleRequest(const HTTPRequest &request)
{
return request.url.find("/image/") == 0;
}
15 changes: 3 additions & 12 deletions xbmc/network/httprequesthandler/HTTPImageHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@

#include <string>

#include "network/httprequesthandler/IHTTPRequestHandler.h"
#include "network/httprequesthandler/HTTPFileHandler.h"

class CHTTPImageHandler : public IHTTPRequestHandler
class CHTTPImageHandler : public CHTTPFileHandler
{
public:
CHTTPImageHandler() { }
Expand All @@ -32,17 +32,8 @@ class CHTTPImageHandler : public IHTTPRequestHandler
virtual IHTTPRequestHandler* Create(const HTTPRequest &request) { return new CHTTPImageHandler(request); }
virtual bool CanHandleRequest(const HTTPRequest &request);

virtual int HandleRequest();

virtual std::string GetResponseFile() const { return m_path; }

virtual int GetPriority() const { return 2; }

protected:
CHTTPImageHandler(const HTTPRequest &request)
: IHTTPRequestHandler(request)
{ }

private:
std::string m_path;
explicit CHTTPImageHandler(const HTTPRequest &request);
};
28 changes: 19 additions & 9 deletions xbmc/network/httprequesthandler/HTTPJsonRpcHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ int CHTTPJsonRpcHandler::HandleRequest()
if (!contentType.empty() && contentType.compare("application/json-rpc") != 0 &&
contentType.compare("application/json") != 0 && contentType.compare("application/jsonrequest") != 0)
{
m_responseType = HTTPError;
m_responseCode = MHD_HTTP_UNSUPPORTED_MEDIA_TYPE;
m_response.type = HTTPError;
m_response.status = MHD_HTTP_UNSUPPORTED_MEDIA_TYPE;
return MHD_YES;
}

Expand All @@ -70,25 +70,35 @@ int CHTTPJsonRpcHandler::HandleRequest()
}

if (isRequest)
m_response = CJSONRPC::MethodCall(m_requestData, m_request.webserver, &client);
m_responseData = CJSONRPC::MethodCall(m_requestData, m_request.webserver, &client);
else
{
// get the whole output of JSONRPC.Introspect
CVariant result;
CJSONServiceDescription::Print(result, m_request.webserver, &client);
m_response = CJSONVariantWriter::Write(result, false);
m_responseData = CJSONVariantWriter::Write(result, false);
}

m_responseHeaderFields.insert(pair<string, string>(MHD_HTTP_HEADER_CONTENT_TYPE, "application/json"));

m_requestData.clear();

m_responseType = HTTPMemoryDownloadNoFreeCopy;
m_responseCode = MHD_HTTP_OK;

m_responseRange.SetData(m_responseData.c_str(), m_responseData.size());

m_response.type = HTTPMemoryDownloadNoFreeCopy;
m_response.status = MHD_HTTP_OK;
m_response.contentType = "application/json";
m_response.totalLength = m_responseData.size();

return MHD_YES;
}

HttpResponseRanges CHTTPJsonRpcHandler::GetResponseData() const
{
HttpResponseRanges ranges;
ranges.push_back(m_responseRange);

return ranges;
}

#if (MHD_VERSION >= 0x00040001)
bool CHTTPJsonRpcHandler::appendPostData(const char *data, size_t size)
#else
Expand Down
8 changes: 4 additions & 4 deletions xbmc/network/httprequesthandler/HTTPJsonRpcHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,12 @@ class CHTTPJsonRpcHandler : public IHTTPRequestHandler

virtual int HandleRequest();

virtual void* GetResponseData() const { return (void *)m_response.c_str(); };
virtual size_t GetResponseDataLength() const { return m_response.size(); }
virtual HttpResponseRanges GetResponseData() const;

virtual int GetPriority() const { return 2; }

protected:
CHTTPJsonRpcHandler(const HTTPRequest &request)
explicit CHTTPJsonRpcHandler(const HTTPRequest &request)
: IHTTPRequestHandler(request)
{ }

Expand All @@ -53,7 +52,8 @@ class CHTTPJsonRpcHandler : public IHTTPRequestHandler

private:
std::string m_requestData;
std::string m_response;
std::string m_responseData;
CHttpResponseRange m_responseRange;

class CHTTPClient : public JSONRPC::IClient
{
Expand Down
Loading

0 comments on commit 25b9db7

Please sign in to comment.