Mercurial > projects > hoofbaby
view deps/Platinum/Source/Core/PltHttpServer.cpp @ 0:3425707ddbf6
Initial import (hopefully this mercurial stuff works...)
author | fraserofthenight |
---|---|
date | Mon, 06 Jul 2009 08:06:28 -0700 |
parents | |
children |
line wrap: on
line source
/***************************************************************** | | Platinum - HTTP Server | | Copyright (c) 2004-2008, Plutinosoft, LLC. | All rights reserved. | http://www.plutinosoft.com | | 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 | of the License, or (at your option) any later version. | | OEMs, ISVs, VARs and other distributors that combine and | distribute commercially licensed software with Platinum software | and do not wish to distribute the source code for the commercially | licensed software under version 2, or (at your option) any later | version, of the GNU General Public License (the "GPL") must enter | into a commercial license agreement with Plutinosoft, LLC. | | 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 this program; see the file LICENSE.txt. If not, write to | the Free Software Foundation, Inc., | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | http://www.gnu.org/licenses/gpl-2.0.html | ****************************************************************/ /*---------------------------------------------------------------------- | includes +---------------------------------------------------------------------*/ #include "PltTaskManager.h" #include "PltHttpServer.h" #include "PltHttp.h" #include "PltVersion.h" NPT_SET_LOCAL_LOGGER("platinum.core.http.server") /*---------------------------------------------------------------------- | PLT_HttpServer::PLT_HttpServer +---------------------------------------------------------------------*/ PLT_HttpServer::PLT_HttpServer(unsigned int port, bool port_rebind /* = false */, NPT_Cardinal max_clients /* = 0 */, bool reuse_address /* = false */) : m_TaskManager(new PLT_TaskManager(max_clients)), m_Port(port), m_PortRebind(port_rebind), m_ReuseAddress(reuse_address), m_HttpListenTask(NULL) { } /*---------------------------------------------------------------------- | PLT_HttpServer::~PLT_HttpServer +---------------------------------------------------------------------*/ PLT_HttpServer::~PLT_HttpServer() { delete m_TaskManager; } /*---------------------------------------------------------------------- | PLT_HttpServer::Start +---------------------------------------------------------------------*/ NPT_Result PLT_HttpServer::Start() { NPT_Result res = NPT_FAILURE; // if we're given a port for our http server, try it if (m_Port) { res = SetListenPort(m_Port, m_ReuseAddress); // return right away on failure if told not to try to rebind randomly if (NPT_FAILED(res) && !m_PortRebind) { NPT_CHECK_SEVERE(res); } } // try random port now if (!m_Port || NPT_FAILED(res)) { // randomly try a port for our http server int retries = 100; do { int random = NPT_System::GetRandomInteger(); int port = (unsigned short)(50000 + (random % 15000)); if (NPT_SUCCEEDED(SetListenPort(port, m_ReuseAddress))) { break; } } while (--retries > 0); if (retries == 0) NPT_CHECK_SEVERE(NPT_FAILURE); } m_Port = m_Config.m_ListenPort; // start a task to listen m_HttpListenTask = new PLT_HttpServerListenTask(this, &m_Socket, false); m_TaskManager->StartTask(m_HttpListenTask, NULL, false); NPT_SocketInfo info; m_Socket.GetInfo(info); NPT_LOG_INFO_2("HttpServer listening on %s:%d", (const char*)info.local_address.GetIpAddress().ToString(), m_Port); return NPT_SUCCESS; } /*---------------------------------------------------------------------- | PLT_HttpServer::Stop +---------------------------------------------------------------------*/ NPT_Result PLT_HttpServer::Stop() { if (m_HttpListenTask) { m_HttpListenTask->Kill(); m_HttpListenTask = NULL; } // stop all other pending tasks m_TaskManager->StopAllTasks(); return NPT_SUCCESS; } /*---------------------------------------------------------------------- | PLT_HttpServer::ProcessHttpRequest +---------------------------------------------------------------------*/ NPT_Result PLT_HttpServer::ProcessHttpRequest(NPT_HttpRequest& request, const NPT_HttpRequestContext& context, NPT_HttpResponse*& response, bool& headers_only) { NPT_LOG_INFO_3("Received %s Request from %s for %s", (const char*) request.GetMethod(), (const char*) context.GetRemoteAddress().GetIpAddress().ToString(), (const char*) request.GetUrl().GetPath()); PLT_LOG_HTTP_MESSAGE(NPT_LOG_LEVEL_FINE, &request); NPT_HttpRequestHandler* handler = FindRequestHandler(request); if (handler == NULL) { response = new NPT_HttpResponse(404, "Not Found", NPT_HTTP_PROTOCOL_1_1); } else { // create a repsones object response = new NPT_HttpResponse(200, "OK", NPT_HTTP_PROTOCOL_1_1); response->GetHeaders().SetHeader("Server", "Platinum/" PLT_PLATINUM_VERSION_STRING); // prepare the response response->SetEntity(new NPT_HttpEntity()); // ask the handler to setup the response handler->SetupResponse(request, context, *response); // set headers_only flag headers_only = (request.GetMethod()==NPT_HTTP_METHOD_HEAD); } return NPT_SUCCESS; } /*---------------------------------------------------------------------- | PLT_FileServer::GetMimeType +---------------------------------------------------------------------*/ const char* PLT_FileServer::GetMimeType(const NPT_String& filename) { int last_dot = filename.ReverseFind('.'); if (last_dot > 0) { NPT_String extension = filename.GetChars()+last_dot+1; extension.MakeLowercase(); for (unsigned int i=0; i<NPT_ARRAY_SIZE(NPT_HttpFileRequestHandler_DefaultFileTypeMap); i++) { if (extension == NPT_HttpFileRequestHandler_DefaultFileTypeMap[i].extension) { return NPT_HttpFileRequestHandler_DefaultFileTypeMap[i].mime_type; } } } return "application/octet-stream"; } /*---------------------------------------------------------------------- | PLT_FileServer::ServeFile +---------------------------------------------------------------------*/ NPT_Result PLT_FileServer::ServeFile(NPT_HttpResponse& response, NPT_String file_path, NPT_Position start, NPT_Position end, bool request_is_head) { NPT_LargeSize total_len; NPT_InputStreamReference stream; NPT_File file(file_path); NPT_Result result; // prevent hackers from accessing files outside of our root if ((file_path.Find("/..") >= 0) || (file_path.Find("\\..") >= 0)) { response.SetStatus(404, "File Not Found"); return NPT_SUCCESS; } if (NPT_FAILED(result = file.Open(NPT_FILE_OPEN_MODE_READ)) || NPT_FAILED(result = file.GetInputStream(stream)) || NPT_FAILED(result = stream->GetSize(total_len))) { // file didn't open response.SetStatus(404, "File Not Found"); return NPT_SUCCESS; } else { NPT_HttpEntity* entity = new NPT_HttpEntity(); entity->SetContentLength(total_len); response.SetEntity(entity); // set the content type if we can entity->SetContentType(GetMimeType(file_path)); // request is HEAD, returns without setting a body if (request_is_head) return NPT_SUCCESS; // see if it was a byte range request if (start != (NPT_Position)-1 || end != (NPT_Position)-1) { // we can only support a range from an offset to the end of the resource for now // due to the fact we can't limit how much to read from a stream yet NPT_Position start_offset = (NPT_Position)-1, end_offset = total_len - 1, len; if (start == (NPT_Position)-1 && end != (NPT_Position)-1) { // we are asked for the last N=end bytes // adjust according to total length if (end >= total_len) { start_offset = 0; } else { start_offset = total_len-end; } } else if (start != (NPT_Position)-1) { start_offset = start; // if the end is specified but incorrect // set the end_offset in order to generate a bad response if (end != (NPT_Position)-1 && end < start) { end_offset = (NPT_Position)-1; } } // in case the range request was invalid or we can't seek then respond appropriately if (start_offset == (NPT_Position)-1 || end_offset == (NPT_Position)-1 || start_offset > end_offset || NPT_FAILED(stream->Seek(start_offset))) { response.SetStatus(416, "Requested range not satisfiable"); } else { len = end_offset - start_offset + 1; response.SetStatus(206, "Partial Content"); PLT_HttpHelper::SetContentRange(response, start_offset, end_offset, total_len); entity->SetInputStream(stream); entity->SetContentLength(len); } } else { entity->SetInputStream(stream); } return NPT_SUCCESS; } }