mirror of https://github.com/JAJames/Jupiter.git
JustinAJ
9 years ago
4 changed files with 850 additions and 0 deletions
@ -0,0 +1,841 @@ |
|||||
|
/**
|
||||
|
* Copyright (C) 2015 Justin James. |
||||
|
* |
||||
|
* Permission to use, copy, modify, and/or distribute this software for any |
||||
|
* purpose with or without fee is hereby granted, provided that the above |
||||
|
* copyright notice and this permission notice appear in all copies. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
||||
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
||||
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
|
* |
||||
|
* Written by Justin James <justin.aj@hotmail.com> |
||||
|
*/ |
||||
|
|
||||
|
#include <ctime> |
||||
|
#include <chrono> |
||||
|
#include "String.h" |
||||
|
#include "CString.h" |
||||
|
#include "Reference_String.h" |
||||
|
#include "TCPSocket.h" |
||||
|
#include "ArrayList.h" |
||||
|
#include "HTTP.h" |
||||
|
#include "HTTP_Server.h" |
||||
|
|
||||
|
using namespace Jupiter::literals; |
||||
|
|
||||
|
static const Jupiter::ReferenceString HTTP_REQUEST_ENDING = "\r\n\r\n"_jrs; |
||||
|
|
||||
|
// HTTPCommand
|
||||
|
|
||||
|
enum HTTPCommand |
||||
|
{ |
||||
|
GET, |
||||
|
HEAD, |
||||
|
UNKNOWN, |
||||
|
NONE_SPECIFIED |
||||
|
}; |
||||
|
|
||||
|
// HTTPVersion
|
||||
|
|
||||
|
enum HTTPVersion |
||||
|
{ |
||||
|
HTTP_1_0, |
||||
|
HTTP_1_1, |
||||
|
HTTP_Unsupported |
||||
|
}; |
||||
|
|
||||
|
// HTTP::Server::Content
|
||||
|
|
||||
|
Jupiter::HTTP::Server::Content::Content(const Jupiter::ReadableString &in_name, Jupiter::HTTP::Server::HTTPFunction in_function) : name(in_name) |
||||
|
{ |
||||
|
Jupiter::HTTP::Server::Content::function = in_function; |
||||
|
Jupiter::HTTP::Server::Content::name_checksum = Jupiter::HTTP::Server::Content::name.calcChecksum(); // switch to calcChecksumi to make case-insensitive
|
||||
|
} |
||||
|
|
||||
|
Jupiter::ReadableString *Jupiter::HTTP::Server::Content::execute(const Jupiter::ReadableString ¶meters) |
||||
|
{ |
||||
|
return Jupiter::HTTP::Server::Content::function(parameters); |
||||
|
} |
||||
|
|
||||
|
// HTTP::Server::Directory
|
||||
|
|
||||
|
Jupiter::HTTP::Server::Directory::Directory(const Jupiter::ReadableString &in_name) : name(in_name) |
||||
|
{ |
||||
|
name_checksum = Jupiter::HTTP::Server::Directory::name.calcChecksumi(); |
||||
|
} |
||||
|
|
||||
|
Jupiter::HTTP::Server::Directory::~Directory() |
||||
|
{ |
||||
|
Jupiter::HTTP::Server::Directory::directories.emptyAndDelete(); |
||||
|
Jupiter::HTTP::Server::Directory::content.emptyAndDelete(); |
||||
|
} |
||||
|
|
||||
|
// host/dir/content
|
||||
|
// .hook(host, "dir/content")
|
||||
|
|
||||
|
void Jupiter::HTTP::Server::Directory::hook(const Jupiter::ReadableString &in_name, Content *in_content) |
||||
|
{ |
||||
|
Jupiter::ReferenceString in_name_ref = in_name; |
||||
|
in_name_ref.shiftRight(in_name_ref.span('/')); |
||||
|
|
||||
|
size_t index = in_name_ref.find('/'); |
||||
|
if (index == Jupiter::INVALID_INDEX) // Hook content
|
||||
|
Jupiter::HTTP::Server::Directory::content.add(in_content); |
||||
|
else |
||||
|
{ |
||||
|
Jupiter::ReferenceString dir_name(in_name_ref.ptr(), index); |
||||
|
in_name_ref.shiftRight(dir_name.size()); |
||||
|
Jupiter::HTTP::Server::Directory *directory; |
||||
|
unsigned int dir_name_checksum = dir_name.calcChecksum(); |
||||
|
index = Jupiter::HTTP::Server::Directory::directories.size(); |
||||
|
while (index != 0) |
||||
|
{ |
||||
|
directory = Jupiter::HTTP::Server::Directory::directories.get(--index); |
||||
|
if (directory->name_checksum == dir_name_checksum && directory->name.equals(dir_name)) |
||||
|
return directory->hook(dir_name, in_content); |
||||
|
} |
||||
|
|
||||
|
// create directories
|
||||
|
directory = new Jupiter::HTTP::Server::Directory(dir_name); |
||||
|
Jupiter::HTTP::Server::Directory::directories.add(directory); |
||||
|
|
||||
|
directory_add_loop: |
||||
|
index = in_name_ref.find('/'); |
||||
|
if (index != Jupiter::INVALID_INDEX) |
||||
|
{ |
||||
|
// add directory
|
||||
|
directory->directories.add(new Jupiter::HTTP::Server::Directory(in_name_ref.substring(0U, index))); |
||||
|
directory = directory->directories.get(directories.size() - 1); |
||||
|
goto directory_add_loop; |
||||
|
} |
||||
|
// add content
|
||||
|
directory->content.add(in_content); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool Jupiter::HTTP::Server::Directory::remove(const Jupiter::ReadableString &in_name) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
bool Jupiter::HTTP::Server::Directory::has(const Jupiter::ReadableString &in_name) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
Jupiter::HTTP::Server::Content *Jupiter::HTTP::Server::Directory::find(const Jupiter::ReadableString &in_name) |
||||
|
{ |
||||
|
Jupiter::ReferenceString in_name_ref = in_name; |
||||
|
in_name_ref.shiftRight(in_name_ref.span("/"_jrs)); |
||||
|
|
||||
|
size_t index = in_name_ref.find('/'); |
||||
|
if (index == Jupiter::INVALID_INDEX) // Search content
|
||||
|
{ |
||||
|
unsigned int content_name_checksum = in_name_ref.calcChecksum(); |
||||
|
index = Jupiter::HTTP::Server::Directory::content.size(); |
||||
|
Jupiter::HTTP::Server::Content *content_itr; |
||||
|
while (index != 0) |
||||
|
{ |
||||
|
content_itr = Jupiter::HTTP::Server::Directory::content.get(--index); |
||||
|
if (content_itr->name_checksum == content_name_checksum && content_itr->name.equals(in_name_ref)) |
||||
|
return content_itr; |
||||
|
} |
||||
|
return nullptr; // No such content
|
||||
|
} |
||||
|
|
||||
|
Jupiter::ReferenceString dir_name(in_name_ref.ptr(), index); |
||||
|
in_name_ref.shiftRight(dir_name.size()); |
||||
|
Jupiter::HTTP::Server::Directory *directory; |
||||
|
unsigned int dir_name_checksum = dir_name.calcChecksum(); |
||||
|
index = Jupiter::HTTP::Server::Directory::directories.size(); |
||||
|
while (index != 0) |
||||
|
{ |
||||
|
directory = Jupiter::HTTP::Server::Directory::directories.get(--index); |
||||
|
if (directory->name_checksum == dir_name_checksum && directory->name.equals(dir_name)) |
||||
|
return directory->find(in_name_ref); |
||||
|
} |
||||
|
|
||||
|
return nullptr; // No such directory
|
||||
|
} |
||||
|
|
||||
|
Jupiter::ReadableString *Jupiter::HTTP::Server::Directory::execute(const Jupiter::ReadableString &in_name, const Jupiter::ReadableString ¶meters) |
||||
|
{ |
||||
|
Jupiter::HTTP::Server::Content *content = Jupiter::HTTP::Server::Directory::find(in_name); |
||||
|
if (content == nullptr) |
||||
|
return nullptr; |
||||
|
return content->execute(parameters); |
||||
|
} |
||||
|
|
||||
|
// HTTP::Server::Host
|
||||
|
|
||||
|
Jupiter::HTTP::Server::Host::Host(const Jupiter::ReadableString &in_name) : Directory(in_name) |
||||
|
{ |
||||
|
name_checksum = Jupiter::HTTP::Server::Host::name.calcChecksumi(); |
||||
|
} |
||||
|
|
||||
|
// HTTPSession struct
|
||||
|
|
||||
|
struct HTTPSession |
||||
|
{ |
||||
|
Jupiter::Socket sock; |
||||
|
Jupiter::String request; |
||||
|
bool keep_alive = false; |
||||
|
Jupiter::HTTP::Server::Host *host = nullptr; |
||||
|
HTTPVersion version = HTTPVersion::HTTP_1_0; |
||||
|
std::chrono::steady_clock::time_point last_active = std::chrono::steady_clock::now(); |
||||
|
HTTPSession(Jupiter::Socket &&in_sock); |
||||
|
~HTTPSession(); |
||||
|
}; |
||||
|
|
||||
|
HTTPSession::HTTPSession(Jupiter::Socket &&in_sock) : sock(std::move(in_sock)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
HTTPSession::~HTTPSession() |
||||
|
{ |
||||
|
HTTPSession::sock.closeSocket(); |
||||
|
} |
||||
|
|
||||
|
// Server::Data struct
|
||||
|
|
||||
|
struct Jupiter::HTTP::Server::Data |
||||
|
{ |
||||
|
/** Data */ |
||||
|
Jupiter::ArrayList<Jupiter::HTTP::Server::Host> hosts; |
||||
|
Jupiter::ArrayList<Socket> ports; |
||||
|
Jupiter::ArrayList<HTTPSession> sessions; |
||||
|
std::chrono::milliseconds session_timeout = std::chrono::milliseconds(30000); // TODO: Config variable
|
||||
|
std::chrono::milliseconds keep_alive_session_timeout = std::chrono::milliseconds(30000); // TODO: Config variable
|
||||
|
size_t max_request_size = 1024; // TODO: Config variable
|
||||
|
bool permit_keept_alive = true; // TODO: Config variable
|
||||
|
|
||||
|
/** Foward functions */ |
||||
|
void hook(const Jupiter::ReadableString &host, const Jupiter::ReadableString &path, Content *in_content); |
||||
|
bool remove(const Jupiter::ReadableString &hostname); |
||||
|
bool remove(const Jupiter::ReadableString &hostname, const Jupiter::ReadableString &name); |
||||
|
bool has(const Jupiter::ReadableString &hostname); |
||||
|
bool has(const Jupiter::ReadableString &hostname, const Jupiter::ReadableString &name); |
||||
|
Jupiter::HTTP::Server::Host *find_host(const Jupiter::ReadableString &name); |
||||
|
Content *find(const Jupiter::ReadableString &name); |
||||
|
Content *find(const Jupiter::ReadableString &hostname, const Jupiter::ReadableString &name); |
||||
|
Jupiter::ReadableString *execute(const Jupiter::ReadableString &name, const Jupiter::ReadableString ¶meters); |
||||
|
Jupiter::ReadableString *execute(const Jupiter::ReadableString &hostname, const Jupiter::ReadableString &name, const Jupiter::ReadableString ¶meters); |
||||
|
|
||||
|
int process_request(HTTPSession &session); |
||||
|
|
||||
|
/** Constructors */ |
||||
|
Data(); |
||||
|
Data(const Data &source) = delete; |
||||
|
Data(Data &&source) = delete; |
||||
|
|
||||
|
/** Destructor */ |
||||
|
~Data(); |
||||
|
}; |
||||
|
|
||||
|
// Data constructor
|
||||
|
|
||||
|
Jupiter::HTTP::Server::Data::Data() |
||||
|
{ |
||||
|
// hosts[0] is always the "global" namespace.
|
||||
|
Jupiter::HTTP::Server::Data::hosts.add(new Jupiter::HTTP::Server::Host(Jupiter::HTTP::Server::global_namespace)); |
||||
|
} |
||||
|
|
||||
|
// Data destructor
|
||||
|
|
||||
|
Jupiter::HTTP::Server::Data::~Data() |
||||
|
{ |
||||
|
Jupiter::HTTP::Server::Data::hosts.emptyAndDelete(); |
||||
|
Jupiter::HTTP::Server::Data::sessions.emptyAndDelete(); |
||||
|
Jupiter::HTTP::Server::Data::ports.emptyAndDelete(); |
||||
|
} |
||||
|
|
||||
|
// Data functions
|
||||
|
|
||||
|
void Jupiter::HTTP::Server::Data::hook(const Jupiter::ReadableString &hostname, const Jupiter::ReadableString &in_path, Content *in_content) |
||||
|
{ |
||||
|
Jupiter::ReferenceString path = in_path; |
||||
|
Jupiter::ReferenceString dir_name; |
||||
|
Jupiter::HTTP::Server::Host *host = Jupiter::HTTP::Server::Data::find_host(hostname); |
||||
|
Jupiter::HTTP::Server::Directory *dir; |
||||
|
|
||||
|
if (host == nullptr) |
||||
|
{ |
||||
|
host = new Jupiter::HTTP::Server::Host(hostname); |
||||
|
Jupiter::HTTP::Server::Data::hosts.add(host); |
||||
|
// OPTIMIZE: create directory tree and return.
|
||||
|
} |
||||
|
|
||||
|
dir = host; |
||||
|
|
||||
|
path.shiftRight(path.span('/')); |
||||
|
if (path.isEmpty()) |
||||
|
host->content.add(in_content); |
||||
|
|
||||
|
if (path.isNotEmpty()) |
||||
|
{ |
||||
|
dir_name = path.getToken(0, '/'); |
||||
|
|
||||
|
size_t index = dir->directories.size(); |
||||
|
Jupiter::HTTP::Server::Directory *t_dir; |
||||
|
|
||||
|
dir_search_loop: |
||||
|
if (index != 0) |
||||
|
{ |
||||
|
t_dir = dir->directories.get(--index); |
||||
|
if (t_dir->name.equalsi(dir_name) == false) |
||||
|
goto dir_search_loop; |
||||
|
dir = t_dir; |
||||
|
} |
||||
|
else // directory doesn't exist
|
||||
|
{ |
||||
|
t_dir = new Jupiter::HTTP::Server::Directory(dir_name); |
||||
|
dir->directories.add(t_dir); |
||||
|
dir = t_dir; |
||||
|
// OPTIMIZE: create directory tree and return.
|
||||
|
} |
||||
|
// end dir_search_loop
|
||||
|
|
||||
|
path.shiftRight(dir_name.size()); |
||||
|
path.shiftRight(path.span('/')); |
||||
|
} |
||||
|
dir->hook(path, in_content); |
||||
|
//dir->content.add(in_content);
|
||||
|
|
||||
|
// path is empty -- insert content into dir
|
||||
|
} |
||||
|
|
||||
|
bool Jupiter::HTTP::Server::Data::remove(const Jupiter::ReadableString &hostname) |
||||
|
{ |
||||
|
unsigned int name_checksum = hostname.calcChecksumi(); |
||||
|
size_t index = Jupiter::HTTP::Server::Data::hosts.size(); |
||||
|
Jupiter::HTTP::Server::Host *host; |
||||
|
while (index != 0) |
||||
|
{ |
||||
|
host = Jupiter::HTTP::Server::Data::hosts.get(--index); |
||||
|
if (name_checksum == host->name_checksum && host->name.equalsi(hostname)) |
||||
|
{ |
||||
|
delete Jupiter::HTTP::Server::Data::hosts.remove(index); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// name: path/to/resource OR path/
|
||||
|
bool Jupiter::HTTP::Server::Data::remove(const Jupiter::ReadableString &hostname, const Jupiter::ReadableString &name) |
||||
|
{ |
||||
|
/*unsigned int name_checksum = hostname.calcChecksumi();
|
||||
|
size_t index = Jupiter::HTTP::Server::Data::hosts.size(); |
||||
|
Jupiter::HTTP::Server::Host *host; |
||||
|
while (index != 0) |
||||
|
{ |
||||
|
host = Jupiter::HTTP::Server::Data::hosts.get(--index); |
||||
|
if (name_checksum == host->name_checksum && host->name.equalsi(hostname)) |
||||
|
{ |
||||
|
name_checksum = name.calcChecksum(); // switch to equalsi to make case-insensitive
|
||||
|
index = host->functions.size(); |
||||
|
Jupiter::HTTP::Server::Content *content; |
||||
|
while (index != 0) |
||||
|
{ |
||||
|
content = host->functions.get(--index); |
||||
|
if (name_checksum == content->name_checksum && content->name.equals(name)) // switch to equalsi to make case-insensitive
|
||||
|
{ |
||||
|
delete host->functions.remove(index); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
}*/ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
bool Jupiter::HTTP::Server::Data::has(const Jupiter::ReadableString &hostname) |
||||
|
{ |
||||
|
unsigned int name_checksum = hostname.calcChecksumi(); |
||||
|
size_t index = Jupiter::HTTP::Server::Data::hosts.size(); |
||||
|
Jupiter::HTTP::Server::Host *host; |
||||
|
while (index != 0) |
||||
|
{ |
||||
|
host = Jupiter::HTTP::Server::Data::hosts.get(--index); |
||||
|
if (name_checksum == host->name_checksum && host->name.equalsi(hostname)) |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
bool Jupiter::HTTP::Server::Data::has(const Jupiter::ReadableString &hostname, const Jupiter::ReadableString &name) |
||||
|
{ |
||||
|
unsigned int name_checksum = hostname.calcChecksumi(); |
||||
|
size_t index = Jupiter::HTTP::Server::Data::hosts.size(); |
||||
|
Jupiter::HTTP::Server::Host *host; |
||||
|
while (index != 0) |
||||
|
{ |
||||
|
host = Jupiter::HTTP::Server::Data::hosts.get(--index); |
||||
|
if (name_checksum == host->name_checksum && host->name.equalsi(hostname)) |
||||
|
{ |
||||
|
name_checksum = name.calcChecksum(); // switch to equalsi to make case-insensitive
|
||||
|
index = host->content.size(); |
||||
|
Jupiter::HTTP::Server::Content *content; |
||||
|
while (index != 0) |
||||
|
{ |
||||
|
content = host->content.get(--index); |
||||
|
if (name_checksum == content->name_checksum && content->name.equals(name)) // switch to equalsi to make case-insensitive
|
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
Jupiter::HTTP::Server::Host *Jupiter::HTTP::Server::Data::find_host(const Jupiter::ReadableString &name) |
||||
|
{ |
||||
|
unsigned int name_checksum = name.calcChecksumi(); |
||||
|
size_t index = Jupiter::HTTP::Server::Data::hosts.size(); |
||||
|
Jupiter::HTTP::Server::Host *host; |
||||
|
while (index != 0) |
||||
|
{ |
||||
|
host = Jupiter::HTTP::Server::Data::hosts.get(--index); |
||||
|
if (name_checksum == host->name_checksum && host->name.equalsi(name)) |
||||
|
return host; |
||||
|
} |
||||
|
return nullptr; |
||||
|
} |
||||
|
|
||||
|
Jupiter::HTTP::Server::Content *Jupiter::HTTP::Server::Data::find(const Jupiter::ReadableString &name) |
||||
|
{ |
||||
|
return Jupiter::HTTP::Server::Data::hosts.get(0)->find(name); |
||||
|
} |
||||
|
|
||||
|
Jupiter::HTTP::Server::Content *Jupiter::HTTP::Server::Data::find(const Jupiter::ReadableString &hostname, const Jupiter::ReadableString &name) |
||||
|
{ |
||||
|
Jupiter::HTTP::Server::Host *host = Jupiter::HTTP::Server::Data::find_host(hostname); |
||||
|
if (host == nullptr) |
||||
|
return nullptr; |
||||
|
|
||||
|
return host->find(name); |
||||
|
} |
||||
|
|
||||
|
Jupiter::ReadableString *Jupiter::HTTP::Server::Data::execute(const Jupiter::ReadableString &name, const Jupiter::ReadableString ¶meters) |
||||
|
{ |
||||
|
Jupiter::HTTP::Server::Content *content = Jupiter::HTTP::Server::Data::find(name); |
||||
|
if (content == nullptr) |
||||
|
return nullptr; |
||||
|
return content->execute(parameters); |
||||
|
} |
||||
|
|
||||
|
Jupiter::ReadableString *Jupiter::HTTP::Server::Data::execute(const Jupiter::ReadableString &hostname, const Jupiter::ReadableString &name, const Jupiter::ReadableString ¶meters) |
||||
|
{ |
||||
|
Jupiter::HTTP::Server::Content *content = Jupiter::HTTP::Server::Data::find(hostname, name); |
||||
|
if (content == nullptr) |
||||
|
return nullptr; |
||||
|
return content->execute(parameters); |
||||
|
} |
||||
|
|
||||
|
char *html_time() |
||||
|
{ |
||||
|
time_t rawtime = time(0); |
||||
|
char *rtime = new char[64]; |
||||
|
strftime(rtime, 64, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&rawtime)); |
||||
|
return rtime; |
||||
|
} |
||||
|
|
||||
|
int Jupiter::HTTP::Server::Data::process_request(HTTPSession &session) |
||||
|
{ |
||||
|
Jupiter::ReadableString::TokenizeResult<Jupiter::Reference_String> lines = Jupiter::ReferenceString::tokenize(session.request, STRING_LITERAL_AS_REFERENCE(ENDL)); |
||||
|
HTTPCommand command = HTTPCommand::NONE_SPECIFIED; |
||||
|
Content *content = nullptr; |
||||
|
Jupiter::ReferenceString content_parameters; |
||||
|
Jupiter::ReferenceString first_token; |
||||
|
size_t index = 0; |
||||
|
size_t span; |
||||
|
|
||||
|
auto get_line_offset = [&session, &lines](size_t index) |
||||
|
{ |
||||
|
size_t offset = 0; |
||||
|
while (index != 0) |
||||
|
offset += lines.tokens[--index].size() + 2; |
||||
|
return offset; |
||||
|
}; |
||||
|
|
||||
|
while (index != lines.token_count) |
||||
|
{ |
||||
|
Jupiter::ReferenceString &line = lines.tokens[index++]; |
||||
|
|
||||
|
// trim front-end spaces.
|
||||
|
span = line.span(" "_jrs); |
||||
|
if (span != 0) |
||||
|
line.shiftRight(span); |
||||
|
|
||||
|
if (line.isEmpty()) // end of http request
|
||||
|
{ |
||||
|
Jupiter::String result(256); |
||||
|
switch (command) |
||||
|
{ |
||||
|
case HTTPCommand::GET: |
||||
|
case HTTPCommand::HEAD: |
||||
|
if (content != nullptr) |
||||
|
{ |
||||
|
/*
|
||||
|
HTTP/1.1 200 OK |
||||
|
Date: Mon, 05 Oct 2015 01:50:08 GMT |
||||
|
Server: Apache/2.4.7 (Ubuntu) |
||||
|
Last-Modified: Tue, 18 Aug 2015 03:53:28 GMT |
||||
|
ETag: "0-51d8ddc61160f" |
||||
|
Accept-Ranges: bytes |
||||
|
Content-Length: 0 |
||||
|
Connection: close |
||||
|
Content-Type: text/html |
||||
|
*/ |
||||
|
Jupiter::ReadableString *content_result = content->execute(content_parameters); |
||||
|
|
||||
|
switch (session.version) |
||||
|
{ |
||||
|
default: |
||||
|
case HTTPVersion::HTTP_1_0: |
||||
|
result = "HTTP/1.0 200 OK"_jrs ENDL; |
||||
|
break; |
||||
|
case HTTPVersion::HTTP_1_1: |
||||
|
result = "HTTP/1.1 200 OK"_jrs ENDL; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
char *time_header = html_time(); |
||||
|
result += "Date: "_jrs ENDL; |
||||
|
result += time_header; |
||||
|
delete[] time_header; |
||||
|
|
||||
|
result += "Server: " JUPITER_VERSION ENDL; |
||||
|
|
||||
|
result += Jupiter::StringS::Format("Content-Length: %u" ENDL, content_result->size()); |
||||
|
|
||||
|
switch (session.version) |
||||
|
{ |
||||
|
default: |
||||
|
case HTTPVersion::HTTP_1_0: |
||||
|
result += "Connection: close"_jrs ENDL; |
||||
|
break; |
||||
|
case HTTPVersion::HTTP_1_1: |
||||
|
result += "Connection: keep-alive"_jrs ENDL; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
result += "Content-Type: "_jrs; |
||||
|
if (content->type == nullptr) |
||||
|
result += Jupiter::HTTP::Content::Type::Text::PLAIN; |
||||
|
else |
||||
|
result += *content->type; |
||||
|
if (content->charset != nullptr) |
||||
|
{ |
||||
|
result += "; charset="_jrs; |
||||
|
result += *content->charset; |
||||
|
} |
||||
|
result += ENDL; |
||||
|
|
||||
|
if (content->language != nullptr) |
||||
|
{ |
||||
|
result += "Content-Language"_jrs; |
||||
|
result += *content->language; |
||||
|
} |
||||
|
|
||||
|
result += ENDL ENDL; |
||||
|
if (command == HTTPCommand::GET) |
||||
|
result += *content_result; |
||||
|
|
||||
|
delete content_result; |
||||
|
|
||||
|
session.sock.send(result); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// 404
|
||||
|
/*
|
||||
|
HTTP/1.1 404 Not Found |
||||
|
Date: Mon, 05 Oct 2015 01:46:01 GMT |
||||
|
Server: Apache/2.4.7 (Ubuntu) |
||||
|
Content-Length: 281 |
||||
|
Connection: close |
||||
|
Content-Type: text/html; charset=iso-8859-1 |
||||
|
*/ |
||||
|
|
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if (session.keep_alive == false) // not keep-alive -- will be destroyed on return
|
||||
|
break; |
||||
|
if (index == lines.token_count) // end of packet
|
||||
|
session.request.erase(); |
||||
|
else // end of request -- another request is following
|
||||
|
session.request.shiftRight(get_line_offset(index)); |
||||
|
|
||||
|
if (session.request.find(HTTP_REQUEST_ENDING) != Jupiter::INVALID_INDEX) // there's another full request already received
|
||||
|
return Jupiter::HTTP::Server::Data::process_request(session); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
// Not empty
|
||||
|
first_token = line.getToken(0, ' '); |
||||
|
|
||||
|
if (first_token.get(first_token.size() - 1) == ':') // header field
|
||||
|
{ |
||||
|
first_token.truncate(1); // trim trailing ':'
|
||||
|
if (first_token.equalsi("HOST"_jrs)) |
||||
|
session.host = Jupiter::HTTP::Server::Data::find_host(line.getWord(1, " ")); |
||||
|
else if (first_token.equalsi("CONNECTION"_jrs)) |
||||
|
{ |
||||
|
Jupiter::ReferenceString connection_type = line.getWord(1, " "); |
||||
|
if (connection_type.equalsi("keep-alive"_jrs)) |
||||
|
session.keep_alive = true; |
||||
|
} |
||||
|
} |
||||
|
else // command
|
||||
|
{ |
||||
|
if (first_token.equals("GET"_jrs)) |
||||
|
{ |
||||
|
command = HTTPCommand::GET; |
||||
|
|
||||
|
content_parameters = line.getWord(1, " "); |
||||
|
span = content_parameters.find('?'); // repurposing 'span'
|
||||
|
if (span == Jupiter::INVALID_INDEX) |
||||
|
{ |
||||
|
content = Jupiter::HTTP::Server::Data::find(content_parameters); |
||||
|
content_parameters.erase(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
content = Jupiter::HTTP::Server::Data::find(content_parameters.substring(0U, span)); |
||||
|
content_parameters.shiftRight(span + 1); |
||||
|
// decode content_parameters here
|
||||
|
} |
||||
|
|
||||
|
Jupiter::ReferenceString protocol_str = line.getWord(2, " "); |
||||
|
if (protocol_str.equalsi("http/1.0"_jrs)) |
||||
|
session.version = HTTPVersion::HTTP_1_0; |
||||
|
else if (protocol_str.equalsi("http/1.1"_jrs)) |
||||
|
{ |
||||
|
session.version = HTTPVersion::HTTP_1_1; |
||||
|
session.keep_alive = true; |
||||
|
} |
||||
|
} |
||||
|
else if (first_token.equals("HEAD"_jrs)) |
||||
|
{ |
||||
|
command = HTTPCommand::HEAD; |
||||
|
|
||||
|
content_parameters = line.getWord(1, " "); |
||||
|
span = content_parameters.find('?'); // repurposing 'span'
|
||||
|
if (span == Jupiter::INVALID_INDEX) |
||||
|
{ |
||||
|
content = Jupiter::HTTP::Server::Data::find(content_parameters); |
||||
|
content_parameters.erase(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
content = Jupiter::HTTP::Server::Data::find(content_parameters.substring(0U, span)); |
||||
|
content_parameters.shiftRight(span + 1); |
||||
|
} |
||||
|
|
||||
|
Jupiter::ReferenceString protocol_str = line.getWord(2, " "); |
||||
|
if (protocol_str.equalsi("http/1.0"_jrs)) |
||||
|
session.version = HTTPVersion::HTTP_1_0; |
||||
|
else if (protocol_str.equalsi("http/1.1"_jrs)) |
||||
|
{ |
||||
|
session.version = HTTPVersion::HTTP_1_1; |
||||
|
session.keep_alive = true; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
command = HTTPCommand::UNKNOWN; |
||||
|
} |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/** HTTP::Server */ |
||||
|
|
||||
|
// Server constructors
|
||||
|
|
||||
|
Jupiter::HTTP::Server::Server() |
||||
|
{ |
||||
|
Jupiter::HTTP::Server::data_ = new Jupiter::HTTP::Server::Data(); |
||||
|
} |
||||
|
|
||||
|
Jupiter::HTTP::Server::Server(Jupiter::HTTP::Server &&source) |
||||
|
{ |
||||
|
Jupiter::HTTP::Server::data_ = source.data_; |
||||
|
source.data_ = new Jupiter::HTTP::Server::Data(); |
||||
|
} |
||||
|
|
||||
|
Jupiter::HTTP::Server::~Server() |
||||
|
{ |
||||
|
delete Jupiter::HTTP::Server::data_; |
||||
|
} |
||||
|
|
||||
|
// Server functions
|
||||
|
|
||||
|
void Jupiter::HTTP::Server::hook(const Jupiter::ReadableString &host, const Jupiter::ReadableString &name, Content *content) |
||||
|
{ |
||||
|
return Jupiter::HTTP::Server::data_->hook(host, name, content); |
||||
|
} |
||||
|
|
||||
|
bool Jupiter::HTTP::Server::remove(const Jupiter::ReadableString &host) |
||||
|
{ |
||||
|
return Jupiter::HTTP::Server::data_->remove(host); |
||||
|
} |
||||
|
|
||||
|
bool Jupiter::HTTP::Server::remove(const Jupiter::ReadableString &host, const Jupiter::ReadableString &name) |
||||
|
{ |
||||
|
return Jupiter::HTTP::Server::data_->remove(host, name); |
||||
|
} |
||||
|
|
||||
|
bool Jupiter::HTTP::Server::has(const Jupiter::ReadableString &host) |
||||
|
{ |
||||
|
return Jupiter::HTTP::Server::data_->has(host); |
||||
|
} |
||||
|
|
||||
|
bool Jupiter::HTTP::Server::has(const Jupiter::ReadableString &host, const Jupiter::ReadableString &name) |
||||
|
{ |
||||
|
return Jupiter::HTTP::Server::data_->has(host, name); |
||||
|
} |
||||
|
|
||||
|
Jupiter::HTTP::Server::Content *Jupiter::HTTP::Server::find(const Jupiter::ReadableString &name) |
||||
|
{ |
||||
|
return Jupiter::HTTP::Server::data_->find(name); |
||||
|
} |
||||
|
|
||||
|
Jupiter::HTTP::Server::Content *Jupiter::HTTP::Server::find(const Jupiter::ReadableString &host, const Jupiter::ReadableString &name) |
||||
|
{ |
||||
|
return Jupiter::HTTP::Server::data_->find(host, name); |
||||
|
} |
||||
|
|
||||
|
Jupiter::ReadableString *Jupiter::HTTP::Server::execute(const Jupiter::ReadableString &name, const Jupiter::ReadableString ¶meters) |
||||
|
{ |
||||
|
return Jupiter::HTTP::Server::data_->execute(name, parameters); |
||||
|
} |
||||
|
|
||||
|
Jupiter::ReadableString *Jupiter::HTTP::Server::execute(const Jupiter::ReadableString &host, const Jupiter::ReadableString &name, const Jupiter::ReadableString ¶meters) |
||||
|
{ |
||||
|
return Jupiter::HTTP::Server::data_->execute(host, name, parameters); |
||||
|
} |
||||
|
|
||||
|
bool Jupiter::HTTP::Server::bind(const Jupiter::ReadableString &hostname, uint16_t port) |
||||
|
{ |
||||
|
Jupiter::TCPSocket *socket = new Jupiter::TCPSocket(); |
||||
|
if (socket->bind(Jupiter::CStringS(hostname).c_str(), port, true)) |
||||
|
{ |
||||
|
socket->setBlocking(false); |
||||
|
Jupiter::HTTP::Server::data_->ports.add(socket); |
||||
|
return true; |
||||
|
} |
||||
|
delete socket; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
bool Jupiter::HTTP::Server::tls_bind(const Jupiter::ReadableString &hostname, uint16_t port) |
||||
|
{ |
||||
|
Jupiter::SecureTCPSocket *socket = new Jupiter::SecureTCPSocket(); |
||||
|
if (socket->bind(Jupiter::CStringS(hostname).c_str(), port, true)) |
||||
|
{ |
||||
|
Jupiter::HTTP::Server::data_->ports.add(socket); |
||||
|
return true; |
||||
|
} |
||||
|
delete socket; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
int Jupiter::HTTP::Server::think() |
||||
|
{ |
||||
|
Jupiter::Socket *socket; |
||||
|
HTTPSession *session; |
||||
|
|
||||
|
// Process existing clients
|
||||
|
size_t index = Jupiter::HTTP::Server::data_->sessions.size(); |
||||
|
while (index != 0) |
||||
|
{ |
||||
|
session = Jupiter::HTTP::Server::data_->sessions.get(--index); |
||||
|
std::chrono::steady_clock::now(); |
||||
|
if ((std::chrono::steady_clock::now() > session->last_active + Jupiter::HTTP::Server::data_->keep_alive_session_timeout) |
||||
|
|| (session->keep_alive == false && std::chrono::steady_clock::now() > session->last_active + Jupiter::HTTP::Server::data_->session_timeout)) |
||||
|
delete Jupiter::HTTP::Server::data_->sessions.remove(index); |
||||
|
else if (session->sock.recv() > 0) |
||||
|
{ |
||||
|
const Jupiter::ReadableString &sock_buffer = session->sock.getBuffer(); |
||||
|
if (session->request.size() + sock_buffer.size() <= Jupiter::HTTP::Server::data_->max_request_size) // accept
|
||||
|
{ |
||||
|
session->request += sock_buffer; |
||||
|
if (session->request.find(HTTP_REQUEST_ENDING) != Jupiter::INVALID_INDEX) // completed request
|
||||
|
{ |
||||
|
session->last_active = std::chrono::steady_clock::now(); |
||||
|
Jupiter::HTTP::Server::data_->process_request(*session); |
||||
|
if (session->keep_alive == false) // remove completed session
|
||||
|
delete Jupiter::HTTP::Server::data_->sessions.remove(index); |
||||
|
} |
||||
|
else if (session->request.size() == Jupiter::HTTP::Server::data_->max_request_size) // reject (full buffer)
|
||||
|
delete Jupiter::HTTP::Server::data_->sessions.remove(index); |
||||
|
} |
||||
|
else // reject
|
||||
|
delete Jupiter::HTTP::Server::data_->sessions.remove(index); |
||||
|
} |
||||
|
else if (session->sock.getLastError() != 10035) |
||||
|
delete Jupiter::HTTP::Server::data_->sessions.remove(index); |
||||
|
} |
||||
|
|
||||
|
// Process incoming clients
|
||||
|
index = Jupiter::HTTP::Server::data_->ports.size(); |
||||
|
while (index != 0) |
||||
|
{ |
||||
|
socket = Jupiter::HTTP::Server::data_->ports.get(--index); |
||||
|
socket = socket->accept(); |
||||
|
if (socket != nullptr) |
||||
|
{ |
||||
|
socket->setBlocking(false); |
||||
|
session = new HTTPSession(std::move(*socket)); |
||||
|
if (session->sock.recv()) // data received
|
||||
|
{ |
||||
|
const Jupiter::ReadableString &sock_buffer = session->sock.getBuffer(); |
||||
|
if (sock_buffer.size() < Jupiter::HTTP::Server::data_->max_request_size) // accept
|
||||
|
{ |
||||
|
session->request = session->sock.getBuffer(); |
||||
|
if (sock_buffer.find(HTTP_REQUEST_ENDING) != Jupiter::INVALID_INDEX) // completed request
|
||||
|
{ |
||||
|
Jupiter::HTTP::Server::data_->process_request(*session); |
||||
|
if (session->keep_alive) // session will live for 30 seconds.
|
||||
|
Jupiter::HTTP::Server::data_->sessions.add(session); |
||||
|
else // session completed
|
||||
|
delete session; |
||||
|
} |
||||
|
else // store for more processing
|
||||
|
Jupiter::HTTP::Server::data_->sessions.add(session); |
||||
|
} |
||||
|
else if (sock_buffer.size() == Jupiter::HTTP::Server::data_->max_request_size) |
||||
|
{ |
||||
|
if (sock_buffer.find(HTTP_REQUEST_ENDING) == Jupiter::INVALID_INDEX) // reject (too large)
|
||||
|
delete session; |
||||
|
else // accept (max size, completed request)
|
||||
|
{ |
||||
|
session->request = session->sock.getBuffer(); |
||||
|
Jupiter::HTTP::Server::data_->process_request(*session); |
||||
|
if (session->keep_alive) // session will live for 30 seconds.
|
||||
|
Jupiter::HTTP::Server::data_->sessions.add(session); |
||||
|
else // session completed
|
||||
|
delete session; |
||||
|
} |
||||
|
} |
||||
|
else // reject (too large)
|
||||
|
delete session; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
const Jupiter::ReadableString &Jupiter::HTTP::Server::global_namespace = Jupiter::ReferenceString::empty; |
||||
|
const Jupiter::ReadableString &Jupiter::HTTP::Server::server_string = "Jupiter"_jrs; |
Binary file not shown.
Loading…
Reference in new issue