719 lines
19 KiB
C++
719 lines
19 KiB
C++
|
/*
|
||
|
* 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.
|
||
|
*
|
||
|
* 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 Library General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
|
*
|
||
|
* HttpServer.cpp
|
||
|
* Ola HTTP class
|
||
|
* Copyright (C) 2005-2008 Simon Newton
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <fstream>
|
||
|
#include <iostream>
|
||
|
#include <map>
|
||
|
#include <set>
|
||
|
#include <string>
|
||
|
#include <vector>
|
||
|
#include "olad/HttpServer.h"
|
||
|
#include "ola/Logging.h"
|
||
|
|
||
|
namespace ola {
|
||
|
|
||
|
using std::ifstream;
|
||
|
using std::pair;
|
||
|
using std::set;
|
||
|
using std::string;
|
||
|
using ola::network::UnmanagedSocket;
|
||
|
|
||
|
const char HttpServer::CONTENT_TYPE_PLAIN[] = "text/plain";
|
||
|
const char HttpServer::CONTENT_TYPE_HTML[] = "text/html";
|
||
|
const char HttpServer::CONTENT_TYPE_GIF[] = "image/gif";
|
||
|
const char HttpServer::CONTENT_TYPE_PNG[] = "image/png";
|
||
|
const char HttpServer::CONTENT_TYPE_CSS[] = "text/css";
|
||
|
const char HttpServer::CONTENT_TYPE_JS[] = "text/javascript";
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Called by MHD_get_connection_values to add headers to a request obect.
|
||
|
* @param cls a pointer to an HttpRequest object.
|
||
|
* @param key the header name
|
||
|
* @param value the header value
|
||
|
*/
|
||
|
static int AddHeaders(void *cls, enum MHD_ValueKind kind, const char *key,
|
||
|
const char *value) {
|
||
|
HttpRequest *request = reinterpret_cast<HttpRequest*>(cls);
|
||
|
string key_string = key;
|
||
|
string value_string = value;
|
||
|
request->AddHeader(key, value);
|
||
|
return MHD_YES;
|
||
|
(void) kind;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Called by MHD_create_post_processor to iterate over the post form data
|
||
|
* @param request_cls a pointer to a HttpRequest object
|
||
|
* @param key the header name
|
||
|
* @param value the header value
|
||
|
*/
|
||
|
int IteratePost(void *request_cls, enum MHD_ValueKind kind, const char *key,
|
||
|
const char *filename, const char *content_type,
|
||
|
const char *transfer_encoding, const char *data, uint64_t off,
|
||
|
size_t size) {
|
||
|
// libmicrohttpd has a bug where the zie isn't set correctly.
|
||
|
HttpRequest *request = reinterpret_cast<HttpRequest*>(request_cls);
|
||
|
string value(data);
|
||
|
request->AddPostParameter(key, value);
|
||
|
return MHD_YES;
|
||
|
(void) content_type;
|
||
|
(void) filename;
|
||
|
(void) kind;
|
||
|
(void) transfer_encoding;
|
||
|
(void) off;
|
||
|
(void) size;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Called whenever a new request is made. This sets up HttpRequest &
|
||
|
* HttpResponse objects and then calls DispatchRequest.
|
||
|
*/
|
||
|
static int HandleRequest(void *http_server_ptr,
|
||
|
struct MHD_Connection *connection,
|
||
|
const char *url,
|
||
|
const char *method,
|
||
|
const char *version,
|
||
|
const char *upload_data,
|
||
|
size_t *upload_data_size,
|
||
|
void **ptr) {
|
||
|
HttpServer *http_server = reinterpret_cast<HttpServer*>(http_server_ptr);
|
||
|
HttpRequest *request;
|
||
|
|
||
|
// on the first call ptr is null
|
||
|
if (*ptr == NULL) {
|
||
|
request = new HttpRequest(url, method, version, connection);
|
||
|
if (!request)
|
||
|
return MHD_NO;
|
||
|
|
||
|
if (!request->Init()) {
|
||
|
delete request;
|
||
|
return MHD_NO;
|
||
|
}
|
||
|
*ptr = reinterpret_cast<void*>(request);
|
||
|
return MHD_YES;
|
||
|
}
|
||
|
|
||
|
request = reinterpret_cast<HttpRequest*>(*ptr);
|
||
|
|
||
|
if (request->InFlight())
|
||
|
// don't dispatch more than once
|
||
|
return MHD_YES;
|
||
|
|
||
|
if (request->Method() == MHD_HTTP_METHOD_GET) {
|
||
|
HttpResponse *response = new HttpResponse(connection);
|
||
|
request->SetInFlight();
|
||
|
return http_server->DispatchRequest(request, response);
|
||
|
|
||
|
} else if (request->Method() == MHD_HTTP_METHOD_POST) {
|
||
|
if (*upload_data_size != 0) {
|
||
|
request->ProcessPostData(upload_data, upload_data_size);
|
||
|
*upload_data_size = 0;
|
||
|
return MHD_YES;
|
||
|
}
|
||
|
request->SetInFlight();
|
||
|
HttpResponse *response = new HttpResponse(connection);
|
||
|
return http_server->DispatchRequest(request, response);
|
||
|
}
|
||
|
return MHD_NO;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Called when a request completes. This deletes the associated HttpRequest
|
||
|
* object.
|
||
|
*/
|
||
|
void RequestCompleted(void *cls,
|
||
|
struct MHD_Connection *connection,
|
||
|
void **request_cls,
|
||
|
enum MHD_RequestTerminationCode toe) {
|
||
|
HttpRequest *request = reinterpret_cast<HttpRequest*>(*request_cls);
|
||
|
|
||
|
if (!request)
|
||
|
return;
|
||
|
|
||
|
delete request;
|
||
|
*request_cls = NULL;
|
||
|
(void) cls;
|
||
|
(void) connection;
|
||
|
(void) toe;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* HttpRequest object
|
||
|
* Setup the header callback and the post processor if needed.
|
||
|
*/
|
||
|
HttpRequest::HttpRequest(const string &url,
|
||
|
const string &method,
|
||
|
const string &version,
|
||
|
struct MHD_Connection *connection):
|
||
|
m_url(url),
|
||
|
m_method(method),
|
||
|
m_version(version),
|
||
|
m_connection(connection),
|
||
|
m_processor(NULL),
|
||
|
m_in_flight(false) {
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Initialize this request
|
||
|
* @return true if succesful, false otherwise.
|
||
|
*/
|
||
|
bool HttpRequest::Init() {
|
||
|
MHD_get_connection_values(m_connection, MHD_HEADER_KIND, AddHeaders, this);
|
||
|
|
||
|
if (m_method == MHD_HTTP_METHOD_POST) {
|
||
|
m_processor = MHD_create_post_processor(m_connection,
|
||
|
K_POST_BUFFER_SIZE,
|
||
|
IteratePost,
|
||
|
reinterpret_cast<void*>(this));
|
||
|
return m_processor;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Cleanup this request object
|
||
|
*/
|
||
|
HttpRequest::~HttpRequest() {
|
||
|
if (m_processor)
|
||
|
MHD_destroy_post_processor(m_processor);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Add a header to the request object.
|
||
|
* @param key the header name
|
||
|
* @param value the value of the header
|
||
|
*/
|
||
|
void HttpRequest::AddHeader(const string &key, const string &value) {
|
||
|
std::pair<string, string> pair(key, value);
|
||
|
m_headers.insert(pair);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Add a post parameter. This can be called multiple times and the values will
|
||
|
* be appended.
|
||
|
* @param key the parameter name
|
||
|
* @param value the value
|
||
|
*/
|
||
|
void HttpRequest::AddPostParameter(const string &key, const string &value) {
|
||
|
map<string, string>::iterator iter = m_post_params.find(key);
|
||
|
|
||
|
if (iter == m_post_params.end()) {
|
||
|
std::pair<string, string> pair(key, value);
|
||
|
m_post_params.insert(pair);
|
||
|
} else {
|
||
|
iter->second.append(value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Process post data
|
||
|
*/
|
||
|
void HttpRequest::ProcessPostData(const char *data, size_t *data_size) {
|
||
|
MHD_post_process(m_processor, data, *data_size);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Return the value of the header sent with this request
|
||
|
* @param key the name of the header
|
||
|
* @returns the value of the header or empty string if it doesn't exist.
|
||
|
*/
|
||
|
const string HttpRequest::GetHeader(const string &key) const {
|
||
|
map<string, string>::const_iterator iter = m_headers.find(key);
|
||
|
|
||
|
if (iter == m_headers.end())
|
||
|
return "";
|
||
|
else
|
||
|
return iter->second;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Return the value of a url parameter
|
||
|
* @param key the name of the parameter
|
||
|
* @return the value of the parameter
|
||
|
*/
|
||
|
const string HttpRequest::GetParameter(const string &key) const {
|
||
|
const char *value = MHD_lookup_connection_value(m_connection,
|
||
|
MHD_GET_ARGUMENT_KIND,
|
||
|
key.data());
|
||
|
if (value)
|
||
|
return string(value);
|
||
|
else
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Lookup a post parameter in this request
|
||
|
* @param key the name of the parameter
|
||
|
* @return the value of the parameter or the empty string if it doesn't exist
|
||
|
*/
|
||
|
const string HttpRequest::GetPostParameter(const string &key) const {
|
||
|
map<string, string>::const_iterator iter = m_post_params.find(key);
|
||
|
|
||
|
if (iter == m_post_params.end())
|
||
|
return "";
|
||
|
else
|
||
|
return iter->second;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Set the content-type header
|
||
|
* @param type, the content type
|
||
|
* @return true if the header was set correctly, false otherwise
|
||
|
*/
|
||
|
void HttpResponse::SetContentType(const string &type) {
|
||
|
SetHeader(MHD_HTTP_HEADER_CONTENT_TYPE, type);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Set a header in the response
|
||
|
* @param key the header name
|
||
|
* @param value the header value
|
||
|
* @return true if the header was set correctly, false otherwise
|
||
|
*/
|
||
|
void HttpResponse::SetHeader(const string &key, const string &value) {
|
||
|
std::pair<string, string> pair(key, value);
|
||
|
m_headers.insert(pair);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Send the HTTP response
|
||
|
* @returns true on success, false on error
|
||
|
*/
|
||
|
int HttpResponse::Send() {
|
||
|
map<string, string>::const_iterator iter;
|
||
|
struct MHD_Response *response = MHD_create_response_from_data(
|
||
|
m_data.length(),
|
||
|
reinterpret_cast<void*>(const_cast<char*>(m_data.data())),
|
||
|
MHD_NO,
|
||
|
MHD_YES);
|
||
|
for (iter = m_headers.begin(); iter != m_headers.end(); ++iter)
|
||
|
MHD_add_response_header(response,
|
||
|
iter->first.c_str(),
|
||
|
iter->second.c_str());
|
||
|
int ret = MHD_queue_response(m_connection, m_status_code, response);
|
||
|
MHD_destroy_response(response);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Setup the HTTP server.
|
||
|
* @param port the port to listen on
|
||
|
* @param data_dir the directory to serve static content from
|
||
|
*/
|
||
|
HttpServer::HttpServer(unsigned int port, const string &data_dir)
|
||
|
: OlaThread(),
|
||
|
m_httpd(NULL),
|
||
|
m_select_server(NULL),
|
||
|
m_default_handler(NULL),
|
||
|
m_port(port),
|
||
|
m_data_dir(data_dir) {
|
||
|
if (m_data_dir.empty())
|
||
|
m_data_dir = HTTP_DATA_DIR;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Destroy this object
|
||
|
*/
|
||
|
HttpServer::~HttpServer() {
|
||
|
Stop();
|
||
|
|
||
|
if (m_select_server)
|
||
|
delete m_select_server;
|
||
|
|
||
|
if (m_httpd)
|
||
|
MHD_stop_daemon(m_httpd);
|
||
|
|
||
|
map<string, BaseHttpClosure*>::const_iterator iter;
|
||
|
for (iter = m_handlers.begin(); iter != m_handlers.end(); ++iter)
|
||
|
delete iter->second;
|
||
|
|
||
|
if (m_default_handler) {
|
||
|
delete m_default_handler;
|
||
|
m_default_handler = NULL;
|
||
|
}
|
||
|
|
||
|
m_handlers.clear();
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Setup the HTTP server
|
||
|
* @return true on success, false on failure
|
||
|
*/
|
||
|
bool HttpServer::Init() {
|
||
|
if (m_httpd || m_select_server) {
|
||
|
OLA_INFO << "Non null pointers found, Init() was probably called twice";
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
m_httpd = MHD_start_daemon(MHD_NO_FLAG,
|
||
|
m_port,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
&HandleRequest,
|
||
|
this,
|
||
|
MHD_OPTION_NOTIFY_COMPLETED,
|
||
|
RequestCompleted,
|
||
|
NULL,
|
||
|
MHD_OPTION_END);
|
||
|
|
||
|
if (m_httpd) {
|
||
|
m_select_server = new ola::network::SelectServer();
|
||
|
m_select_server->RunInLoop(NewClosure(this, &HttpServer::UpdateSockets));
|
||
|
}
|
||
|
|
||
|
return m_httpd ? true : false;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* The entry point into the new thread
|
||
|
*/
|
||
|
void *HttpServer::Run() {
|
||
|
if (!(m_httpd || m_select_server)) {
|
||
|
OLA_WARN << "HttpServer::Run called but the server wasn't setup.";
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
m_select_server->Run();
|
||
|
|
||
|
// clean up any remaining sockets
|
||
|
set<UnmanagedSocket*, unmanaged_socket_lt>::iterator iter =
|
||
|
m_sockets.begin();
|
||
|
for (; iter != m_sockets.end(); ++iter) {
|
||
|
m_select_server->RemoveSocket(*iter);
|
||
|
m_select_server->UnRegisterWriteSocket(*iter);
|
||
|
delete *iter;
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Stop the HTTP server
|
||
|
*/
|
||
|
void HttpServer::Stop() {
|
||
|
if (IsRunning()) {
|
||
|
OLA_INFO << "Notifying HTTP server thread to stop";
|
||
|
m_select_server->Terminate();
|
||
|
OLA_INFO << "Waiting for HTTP server thread to exit";
|
||
|
Join();
|
||
|
OLA_INFO << "HTTP server thread exited";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* This is run every loop iteration to update the list of sockets in the
|
||
|
* SelectServer from MHD.
|
||
|
*/
|
||
|
void HttpServer::UpdateSockets() {
|
||
|
// We always call MHD_run so we send any queued responses. This isn't
|
||
|
// inefficient because the only thing that can wake up the select server is
|
||
|
// activity on a http socket or the client socket. The latter almost always
|
||
|
// results in a change to HTTP state.
|
||
|
if (MHD_run(m_httpd) == MHD_NO) {
|
||
|
OLA_WARN << "MHD run failed";
|
||
|
}
|
||
|
|
||
|
fd_set r_set, w_set, e_set;
|
||
|
int max_fd = 0;
|
||
|
FD_ZERO(&r_set);
|
||
|
FD_ZERO(&w_set);
|
||
|
|
||
|
if (MHD_YES != MHD_get_fdset(m_httpd, &r_set, &w_set, &e_set, &max_fd)) {
|
||
|
OLA_WARN << "Failed to get a list of the file descriptors for MHD";
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
set<UnmanagedSocket*, unmanaged_socket_lt>::iterator iter =
|
||
|
m_sockets.begin();
|
||
|
|
||
|
// This isn't the best plan, talk to the MHD devs about exposing the list of
|
||
|
// FD in a more suitable way
|
||
|
int i = 0;
|
||
|
while (iter != m_sockets.end() && i <= max_fd) {
|
||
|
if ((*iter)->ReadDescriptor() < i) {
|
||
|
// this socket is no longer required so remove it
|
||
|
OLA_DEBUG << "Removing unsed socket " << (*iter)->ReadDescriptor();
|
||
|
m_select_server->RemoveSocket(*iter);
|
||
|
m_select_server->UnRegisterWriteSocket(*iter);
|
||
|
delete *iter;
|
||
|
m_sockets.erase(iter++);
|
||
|
} else if ((*iter)->ReadDescriptor() == i) {
|
||
|
// this socket may need to be updated
|
||
|
if (FD_ISSET(i, &r_set))
|
||
|
m_select_server->AddSocket(*iter);
|
||
|
else
|
||
|
m_select_server->RemoveSocket(*iter);
|
||
|
|
||
|
if (FD_ISSET(i, &w_set))
|
||
|
m_select_server->RegisterWriteSocket(*iter);
|
||
|
else
|
||
|
m_select_server->UnRegisterWriteSocket(*iter);
|
||
|
iter++;
|
||
|
i++;
|
||
|
} else {
|
||
|
// this is a new socket
|
||
|
if (FD_ISSET(i, &r_set) || FD_ISSET(i, &w_set)) {
|
||
|
OLA_DEBUG << "Adding new socket " << i;
|
||
|
UnmanagedSocket *socket = NewSocket(&r_set, &w_set, i);
|
||
|
m_sockets.insert(socket);
|
||
|
}
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while (iter != m_sockets.end()) {
|
||
|
OLA_DEBUG << "Removing " << (*iter)->ReadDescriptor() <<
|
||
|
" as it's not longer needed";
|
||
|
m_select_server->UnRegisterWriteSocket(*iter);
|
||
|
m_select_server->RemoveSocket(*iter);
|
||
|
delete *iter;
|
||
|
m_sockets.erase(iter++);
|
||
|
}
|
||
|
|
||
|
for (;i <= max_fd; i++) {
|
||
|
// add the remaining sockets to the SS
|
||
|
if (FD_ISSET(i, &r_set) || FD_ISSET(i, &w_set)) {
|
||
|
OLA_DEBUG << "Adding " << i << " as a new socket";
|
||
|
UnmanagedSocket *socket = NewSocket(&r_set, &w_set, i);
|
||
|
m_sockets.insert(socket);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Called when there is HTTP IO activity to deal with. This is a noop as
|
||
|
* MHD_run is called in UpdateSockets above.
|
||
|
*/
|
||
|
void HttpServer::HandleHTTPIO() {}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Call the appropriate handler.
|
||
|
*/
|
||
|
int HttpServer::DispatchRequest(const HttpRequest *request,
|
||
|
HttpResponse *response) {
|
||
|
map<string, BaseHttpClosure*>::iterator iter =
|
||
|
m_handlers.find(request->Url());
|
||
|
|
||
|
if (iter != m_handlers.end())
|
||
|
return iter->second->Run(request, response);
|
||
|
|
||
|
map<string, static_file_info>::iterator file_iter =
|
||
|
m_static_content.find(request->Url());
|
||
|
|
||
|
if (file_iter != m_static_content.end())
|
||
|
return ServeStaticContent(&(file_iter->second), response);
|
||
|
|
||
|
if (m_default_handler)
|
||
|
return m_default_handler->Run(request, response);
|
||
|
|
||
|
return ServeNotFound(response);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Register a handler
|
||
|
* @param path the url to respond on
|
||
|
* @param handler the Closure to call for this request. These will be freed
|
||
|
* once the HttpServer is destroyed.
|
||
|
*/
|
||
|
bool HttpServer::RegisterHandler(const string &path, BaseHttpClosure *handler) {
|
||
|
map<string, BaseHttpClosure*>::const_iterator iter = m_handlers.find(path);
|
||
|
if (iter != m_handlers.end())
|
||
|
return false;
|
||
|
pair<string, BaseHttpClosure*> pair(path, handler);
|
||
|
m_handlers.insert(pair);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Register a static file
|
||
|
* @param path the path to serve on
|
||
|
* @param file the path to the file to serve
|
||
|
*/
|
||
|
bool HttpServer::RegisterFile(const string &path,
|
||
|
const string &file,
|
||
|
const string &content_type) {
|
||
|
map<string, static_file_info>::const_iterator file_iter = (
|
||
|
m_static_content.find(path));
|
||
|
|
||
|
if (file_iter != m_static_content.end())
|
||
|
return false;
|
||
|
|
||
|
static_file_info file_info;
|
||
|
file_info.file_path = file;
|
||
|
file_info.content_type = content_type;
|
||
|
|
||
|
pair<string, static_file_info> pair(path, file_info);
|
||
|
m_static_content.insert(pair);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Set the default handler.
|
||
|
* @param handler the default handler to call. This will be freed when the
|
||
|
* HttpServer is destroyed.
|
||
|
*/
|
||
|
void HttpServer::RegisterDefaultHandler(BaseHttpClosure *handler) {
|
||
|
m_default_handler = handler;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Return a list of all handlers registered
|
||
|
*/
|
||
|
vector<string> HttpServer::Handlers() const {
|
||
|
vector<string> handlers;
|
||
|
map<string, BaseHttpClosure*>::const_iterator iter;
|
||
|
for (iter = m_handlers.begin(); iter != m_handlers.end(); ++iter)
|
||
|
handlers.push_back(iter->first);
|
||
|
|
||
|
map<string, static_file_info>::const_iterator file_iter;
|
||
|
for (file_iter = m_static_content.begin();
|
||
|
file_iter != m_static_content.end(); ++file_iter)
|
||
|
handlers.push_back(file_iter->first);
|
||
|
return handlers;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Serve an error.
|
||
|
* @param response the reponse to use.
|
||
|
* @param details the error description
|
||
|
*/
|
||
|
int HttpServer::ServeError(HttpResponse *response, const string &details) {
|
||
|
response->SetStatus(MHD_HTTP_INTERNAL_SERVER_ERROR);
|
||
|
response->SetContentType(CONTENT_TYPE_HTML);
|
||
|
response->Append("<b>500 Server Error</b>");
|
||
|
if (!details.empty()) {
|
||
|
response->Append("<p>");
|
||
|
response->Append(details);
|
||
|
response->Append("</p>");
|
||
|
}
|
||
|
int r = response->Send();
|
||
|
delete response;
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Serve a 404
|
||
|
* @param response the response to use
|
||
|
*/
|
||
|
int HttpServer::ServeNotFound(HttpResponse *response) {
|
||
|
response->SetStatus(MHD_HTTP_NOT_FOUND);
|
||
|
response->SetContentType(CONTENT_TYPE_HTML);
|
||
|
response->SetStatus(404);
|
||
|
response->Append("<b>404 Not Found</b>");
|
||
|
int r = response->Send();
|
||
|
delete response;
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Serve static content.
|
||
|
* @param file_info details on the file to server
|
||
|
* @param response the response to use
|
||
|
*/
|
||
|
int HttpServer::ServeStaticContent(static_file_info *file_info,
|
||
|
HttpResponse *response) {
|
||
|
char *data;
|
||
|
unsigned int length;
|
||
|
string file_path = m_data_dir;
|
||
|
file_path.append("/");
|
||
|
file_path.append(file_info->file_path);
|
||
|
ifstream i_stream(file_path.data());
|
||
|
|
||
|
if (!i_stream.is_open()) {
|
||
|
OLA_WARN << "Missing file: " << file_path;
|
||
|
return ServeNotFound(response);
|
||
|
}
|
||
|
|
||
|
i_stream.seekg(0, std::ios::end);
|
||
|
length = i_stream.tellg();
|
||
|
i_stream.seekg(0, std::ios::beg);
|
||
|
|
||
|
data = reinterpret_cast<char*>(malloc(length * sizeof(char)));
|
||
|
|
||
|
i_stream.read(data, length);
|
||
|
i_stream.close();
|
||
|
|
||
|
struct MHD_Response *mhd_response = MHD_create_response_from_data(
|
||
|
length,
|
||
|
reinterpret_cast<void*>(const_cast<char*>(data)),
|
||
|
MHD_YES,
|
||
|
MHD_NO);
|
||
|
|
||
|
if (!file_info->content_type.empty())
|
||
|
MHD_add_response_header(mhd_response,
|
||
|
MHD_HTTP_HEADER_CONTENT_TYPE,
|
||
|
file_info->content_type.data());
|
||
|
|
||
|
int ret = MHD_queue_response(response->Connection(),
|
||
|
MHD_HTTP_OK,
|
||
|
mhd_response);
|
||
|
MHD_destroy_response(mhd_response);
|
||
|
delete response;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
UnmanagedSocket *HttpServer::NewSocket(fd_set *r_set,
|
||
|
fd_set *w_set,
|
||
|
int fd) {
|
||
|
UnmanagedSocket *socket = new UnmanagedSocket(fd);
|
||
|
socket->SetOnData(NewClosure(this, &HttpServer::HandleHTTPIO));
|
||
|
socket->SetOnWritable(NewClosure(this, &HttpServer::HandleHTTPIO));
|
||
|
|
||
|
if (FD_ISSET(fd, r_set))
|
||
|
m_select_server->AddSocket(socket);
|
||
|
|
||
|
if (FD_ISSET(fd, w_set))
|
||
|
m_select_server->RegisterWriteSocket(socket);
|
||
|
return socket;
|
||
|
}
|
||
|
} // ola
|