Inital Commit
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
*
|
||||
!src
|
||||
!src/**
|
||||
!run.sh
|
||||
!Makefile
|
||||
!.gitignore
|
68
src/network/Address.cpp
Normal file
68
src/network/Address.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <cstring>
|
||||
#include "Address.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
Address::Address() {
|
||||
|
||||
}
|
||||
|
||||
Address::Address(string addr) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
Address::Address(struct sockaddr_in *addr) {
|
||||
address = ntohl(addr->sin_addr.s_addr);
|
||||
}
|
||||
|
||||
|
||||
struct sockaddr_in Address::toStruct(unsigned short port)const {
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(address);
|
||||
addr.sin_port = htons(port);
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
string Address::toString() const {
|
||||
struct sockaddr_in addr = toStruct(0);
|
||||
struct in_addr ipAddr = addr.sin_addr;
|
||||
return inet_ntoa(ipAddr);
|
||||
}
|
||||
|
||||
bool Address::isLocal() {
|
||||
string a = toString();
|
||||
return a.find("127.0.0.") == 0;
|
||||
}
|
||||
|
||||
|
||||
ostream& operator<<(ostream &str, const Address &addr) {
|
||||
return str << addr.toString();
|
||||
}
|
||||
|
||||
string operator+(string &str, const Address &addr) {
|
||||
return str + addr.toString();
|
||||
}
|
||||
|
||||
string operator+(string &str, const Address *addr) {
|
||||
return str + addr->toString();
|
||||
}
|
||||
|
||||
string operator+(const Address &addr, string &str) {
|
||||
return addr.toString() + str;
|
||||
}
|
||||
|
||||
string operator+(const Address *addr, string &str) {
|
||||
return addr->toString() + str;
|
||||
}
|
||||
|
||||
|
||||
|
31
src/network/Address.h
Normal file
31
src/network/Address.h
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Necronda Web Server 3.0
|
||||
* HttpHeader.h - HttpHeader Class definition
|
||||
* Lorenz Stechauner, 2018-05-09
|
||||
*/
|
||||
|
||||
#ifndef NECRONDA_ADDRESS
|
||||
#define NECRONDA_ADDRESS
|
||||
|
||||
using namespace std;
|
||||
|
||||
class Address {
|
||||
private:
|
||||
unsigned int address;
|
||||
|
||||
public:
|
||||
Address();
|
||||
|
||||
explicit Address(string address);
|
||||
|
||||
explicit Address(struct sockaddr_in *address);
|
||||
|
||||
struct sockaddr_in toStruct(unsigned short port) const;
|
||||
|
||||
string toString() const;
|
||||
|
||||
bool isLocal();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
642
src/network/Socket.cpp
Normal file
642
src/network/Socket.cpp
Normal file
@ -0,0 +1,642 @@
|
||||
/**
|
||||
* Necronda Web Server 3.0
|
||||
* Socket.cpp - Socket Class methods
|
||||
* Lorenz Stechauner, 2018-05-09
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include <ctime>
|
||||
#include <poll.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <list>
|
||||
|
||||
#include "Address.h"
|
||||
#include "Socket.h"
|
||||
#include "../necronda-server.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
static void multi_ssl_init() {
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
ERR_load_crypto_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
}
|
||||
|
||||
|
||||
char *multi_ssl_get_error(SSL *ssl, int ret) {
|
||||
if (ret > 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned long ret2 = ERR_get_error();
|
||||
const char *err2 = strerror(errno);
|
||||
const char *err1 = ERR_reason_error_string(ret2);
|
||||
|
||||
switch (SSL_get_error(ssl, ret)) {
|
||||
case SSL_ERROR_NONE:
|
||||
return (char *) "none";
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
return (char *) "closed";
|
||||
case SSL_ERROR_WANT_READ:
|
||||
return (char *) "want_read";
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
return (char *) "want_write";
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
return (char *) "want_connect";
|
||||
case SSL_ERROR_WANT_ACCEPT:
|
||||
return (char *) "want_accept";
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
return (char *) "want_x509_lookup";
|
||||
case SSL_ERROR_SYSCALL:
|
||||
return (char *) ((ret2 == 0) ? (ret == 0) ? "protocol violation" : err2 : err1);
|
||||
case SSL_ERROR_SSL:
|
||||
return (char *) err1;
|
||||
default:
|
||||
return (char *) "unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
char *strerror_socket(int nr) {
|
||||
if (nr == EAGAIN || nr == EWOULDBLOCK) {
|
||||
return (char *) "timeout";
|
||||
} else if (nr == ECONNRESET) {
|
||||
return (char *) "closed";
|
||||
} else {
|
||||
return strerror(nr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Socket::Socket(int fd) {
|
||||
this->fd = fd;
|
||||
microsStart = getMicros();
|
||||
microsLast = microsStart;
|
||||
bytesSent = 0;
|
||||
bytesReceived = 0;
|
||||
enc = false;
|
||||
ssl = nullptr;
|
||||
ctx = nullptr;
|
||||
clients = false;
|
||||
servers = false;
|
||||
}
|
||||
|
||||
Socket::Socket() {
|
||||
fd = ::socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd == 0) {
|
||||
throw strerror(errno);
|
||||
}
|
||||
enc = false;
|
||||
microsStart = getMicros();
|
||||
microsLast = microsStart;
|
||||
bytesSent = 0;
|
||||
bytesReceived = 0;
|
||||
ssl = nullptr;
|
||||
ctx = nullptr;
|
||||
clients = false;
|
||||
servers = false;
|
||||
}
|
||||
|
||||
void Socket::setSocketOption(int option, bool value = true) {
|
||||
int val = value ? 1 : 0;
|
||||
|
||||
if (::setsockopt(fd, SOL_SOCKET, option, &val, sizeof(val)) != 0) {
|
||||
throw strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::bind(Address *address, unsigned short port) {
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = INADDR_ANY; // address.
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
if (::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0) {
|
||||
throw strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::bind(unsigned short port) {
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
if (::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0) {
|
||||
throw strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::listen(int num) {
|
||||
if (::listen(fd, num) != 0) {
|
||||
throw strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::connect(Address, unsigned short) {
|
||||
|
||||
}
|
||||
|
||||
Socket* Socket::accept() {
|
||||
int newfd = ::accept(fd, nullptr, nullptr);
|
||||
if (newfd < 0) {
|
||||
throw strerror(errno);
|
||||
}
|
||||
Socket *socket = new Socket(newfd);
|
||||
socket->servers = true;
|
||||
return socket;
|
||||
}
|
||||
|
||||
void Socket::close() {
|
||||
if (isSecured()) {
|
||||
//SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
SSL_CTX_free(ctx);
|
||||
}
|
||||
|
||||
if (::close(fd) != 0) {
|
||||
throw strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
Address *Socket::getPeerAddress() const {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
getpeername(fd, (struct sockaddr *) &addr, &len);
|
||||
struct sockaddr_in *s = (struct sockaddr_in *) &addr;
|
||||
return new Address(s);
|
||||
}
|
||||
|
||||
unsigned short Socket::getPeerPort() const {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
getpeername(fd, (struct sockaddr *) &addr, &len);
|
||||
return ntohs(((struct sockaddr_in *) &addr)->sin_port);
|
||||
}
|
||||
|
||||
Address *Socket::getSocketAddress() const {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
getsockname(fd, (struct sockaddr *) &addr, &len);
|
||||
struct sockaddr_in *s = (struct sockaddr_in *) &addr;
|
||||
return new Address(s);
|
||||
}
|
||||
|
||||
unsigned short Socket::getSocketPort() const {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
getsockname(fd, (struct sockaddr *) &addr, &len);
|
||||
return ntohs(((struct sockaddr_in *) &addr)->sin_port);
|
||||
}
|
||||
|
||||
|
||||
void Socket::setReuseAddress(bool value) {
|
||||
setSocketOption(SO_REUSEADDR, value);
|
||||
}
|
||||
|
||||
void Socket::setReusePort(bool value) {
|
||||
setSocketOption(SO_REUSEPORT, value);
|
||||
}
|
||||
|
||||
|
||||
string Socket::toString() const {
|
||||
return "{[Socket]" + getSocketAddress()->toString() + ":" + to_string(getSocketPort()) + "<->" +
|
||||
getPeerAddress()->toString() + ":" + to_string(getPeerPort()) + "}";
|
||||
}
|
||||
|
||||
long Socket::send(string *str) {
|
||||
return send(str->c_str(), str->length());
|
||||
}
|
||||
|
||||
long Socket::send(string str) {
|
||||
return send(str.c_str(), str.length());
|
||||
}
|
||||
|
||||
long Socket::send(const char *str, long length) {
|
||||
return send((void*) str, length);
|
||||
}
|
||||
|
||||
long Socket::send(const char *str) {
|
||||
return send(str, strlen(str));
|
||||
}
|
||||
|
||||
Socket::~Socket() {
|
||||
|
||||
}
|
||||
|
||||
long Socket::receive(void *buffer, int size) {
|
||||
long len;
|
||||
if (isSecured()) {
|
||||
len = SSL_read(ssl, buffer, size);
|
||||
if (len < 0) {
|
||||
throw multi_ssl_get_error(ssl, (int) len);
|
||||
}
|
||||
} else {
|
||||
len = recv(fd, buffer, (size_t) size, 0);
|
||||
if (len < 0) {
|
||||
throw strerror_socket(errno);
|
||||
}
|
||||
}
|
||||
bytesReceived += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
long Socket::peek(void *buffer, int size) {
|
||||
long len;
|
||||
if (isSecured()) {
|
||||
len = SSL_peek(ssl, buffer, size);
|
||||
if (len < 0) {
|
||||
throw multi_ssl_get_error(ssl, (int) len);
|
||||
}
|
||||
} else {
|
||||
len = recv(fd, buffer, (size_t) size, MSG_PEEK);
|
||||
if (len < 0) {
|
||||
throw strerror_socket(errno);
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
long Socket::send(void *buffer, int size) {
|
||||
long len;
|
||||
if (isSecured()) {
|
||||
if (size != 0) {
|
||||
len = SSL_write(ssl, buffer, size);
|
||||
if (len <= 0) {
|
||||
throw multi_ssl_get_error(ssl, (int) len);
|
||||
}
|
||||
} else {
|
||||
len = 0;
|
||||
}
|
||||
} else {
|
||||
len = ::send(fd, buffer, (size_t) size, 0);
|
||||
if (len < 0) {
|
||||
throw strerror_socket(errno);
|
||||
}
|
||||
}
|
||||
bytesSent += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
string Socket::receive() {
|
||||
string *str = new string();
|
||||
|
||||
char buffer[CHUNK];
|
||||
long len = 0;
|
||||
do {
|
||||
len = receive((void*) buffer, CHUNK);
|
||||
str->append(buffer, (unsigned) len);
|
||||
} while (len > 0 && len == CHUNK);
|
||||
|
||||
return *str;
|
||||
}
|
||||
|
||||
string Socket::receive(long length) {
|
||||
string *str = new string();
|
||||
|
||||
char buffer[CHUNK];
|
||||
long len = 0;
|
||||
long reclen = 0;
|
||||
do {
|
||||
len = receive((void*) buffer, CHUNK);
|
||||
reclen += len;
|
||||
str->append(buffer, (unsigned) len);
|
||||
} while (reclen < length);
|
||||
|
||||
return *str;
|
||||
}
|
||||
|
||||
string Socket::receive(string until) {
|
||||
string *str = new string();
|
||||
|
||||
struct pollfd ufds[1];
|
||||
ufds[0].fd = fd;
|
||||
ufds[0].events = POLLIN | POLLOUT;
|
||||
|
||||
char buffer[CHUNK];
|
||||
long len = 0;
|
||||
do {
|
||||
len = peek((void*) buffer, CHUNK);
|
||||
if (len != 0) {
|
||||
string s = string(buffer, (size_t) len);
|
||||
size_t found = s.find(until);
|
||||
long l = (found != string::npos) ? found + 1 : len;
|
||||
long l2 = (found != string::npos) ? found : len;
|
||||
str->append(buffer, (unsigned) l2);
|
||||
receive((void *) buffer, (int) l);
|
||||
if (found != string::npos) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (poll(ufds, 1, 0) < 0) {
|
||||
throw strerror_socket(errno);
|
||||
} else if ((ufds[0].revents & POLLIN) == 0) {
|
||||
if ((ufds[0].revents & POLLOUT) != 0) {
|
||||
throw (char *) "error";
|
||||
} else {
|
||||
throw (char *) "want_write";
|
||||
}
|
||||
} else if ((ufds[0].revents & POLLERR) != 0) {
|
||||
throw (char *) "error";
|
||||
} else if (ufds[0].revents & (POLLRDHUP | POLLHUP | POLLNVAL) != 0) {
|
||||
throw (char *) "closed";
|
||||
}
|
||||
} while (true);
|
||||
|
||||
return *str;
|
||||
}
|
||||
|
||||
string Socket::receive(const char *until) {
|
||||
return receive(until, (int) (strlen(until)));
|
||||
}
|
||||
|
||||
string Socket::receive(const char *until, unsigned long strlen) {
|
||||
return receive(string(until, strlen));
|
||||
}
|
||||
|
||||
void Socket::receive(FILE *file) {
|
||||
char buffer[CHUNK];
|
||||
long len = 0;
|
||||
do {
|
||||
len = receive((void*) buffer, CHUNK);
|
||||
fwrite(buffer, 1, CHUNK, file);
|
||||
} while (len > 0 && len == CHUNK);
|
||||
}
|
||||
|
||||
void Socket::receive(FILE *file, long size) {
|
||||
char buffer[CHUNK];
|
||||
long len = 0;
|
||||
long rec = 0;
|
||||
do {
|
||||
if (size - rec == 0) {
|
||||
break;
|
||||
}
|
||||
len = receive((void*) buffer, (CHUNK > (size - rec) && size >= 0)?(size - rec):CHUNK);
|
||||
fwrite(buffer, 1, CHUNK, file);
|
||||
rec += len;
|
||||
} while (len > 0);
|
||||
}
|
||||
|
||||
string Socket::receiveLine() {
|
||||
string str = receive("\n");
|
||||
if (str.length() > 0 && str.at(str.length() - 1) == '\r') {
|
||||
str = str.substr(0, str.length() - 1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
long Socket::getDuration() {
|
||||
return getMicros() - microsStart;
|
||||
}
|
||||
|
||||
|
||||
void Socket::setReceiveTimeout(unsigned long ms) {
|
||||
struct timeval timeout;
|
||||
if (ms == 0) {
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 1;
|
||||
} else {
|
||||
timeout.tv_sec = ms / 1000;
|
||||
timeout.tv_usec = (ms % 1000) * 1000;
|
||||
}
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout)) < 0) {
|
||||
throw strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
void Socket::setSendTimeout(unsigned long ms) {
|
||||
struct timeval timeout;
|
||||
if (ms == 0) {
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 1;
|
||||
} else {
|
||||
timeout.tv_sec = ms / 1000;
|
||||
timeout.tv_usec = (ms % 1000) * 1000;
|
||||
}
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *) &timeout, sizeof(timeout)) < 0) {
|
||||
throw strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
bool Socket::isServerSide() {
|
||||
return servers;
|
||||
}
|
||||
|
||||
bool Socket::isSecured() {
|
||||
return enc;
|
||||
}
|
||||
|
||||
bool Socket::isClientSide() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
void Socket::sslHandshake(map<string, KeyPair> sni) {
|
||||
/*if (isSecured()) {
|
||||
throw (char *) "Socket already secured";
|
||||
}
|
||||
|
||||
const SSL_METHOD *method;
|
||||
if (isServerSide()) {
|
||||
method = TLSv1_2_server_method();
|
||||
} else if (isClientSide()) {
|
||||
method = TLSv1_2_client_method();
|
||||
} else {
|
||||
method = TLSv1_2_method();
|
||||
}
|
||||
|
||||
SSL_CTX *ctx = SSL_CTX_new(method);
|
||||
SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
|
||||
SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4");
|
||||
SSL_CTX_set_ecdh_auto(ctx, 1);
|
||||
|
||||
const char *certfile = keypair.fullchain.c_str();
|
||||
const char *keyfile = keypair.privkey.c_str();
|
||||
|
||||
if (isServerSide()) {
|
||||
if (SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM) != 1) {
|
||||
throw (char *) ERR_reason_error_string(ERR_get_error());
|
||||
}
|
||||
|
||||
if (SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM) != 1) {
|
||||
throw (char *) ERR_reason_error_string(ERR_get_error());
|
||||
}
|
||||
}
|
||||
|
||||
SSL_CTX_set_tlsext_servername_callback
|
||||
|
||||
this->ctx = ctx;
|
||||
this->ssl = SSL_new(ctx);
|
||||
SSL_set_fd(ssl, fd);
|
||||
enc = true;
|
||||
|
||||
while (true) {
|
||||
int ret = 0;
|
||||
if (isServerSide()) {
|
||||
ret = SSL_accept(ssl);
|
||||
} else if (isClientSide()) {
|
||||
ret = SSL_connect(ssl);
|
||||
} else {
|
||||
ret = SSL_do_handshake(ssl);
|
||||
}
|
||||
|
||||
if (ret <= 0 && ((isServerSide() && SSL_get_error(ssl, ret) != SSL_ERROR_WANT_READ) ||
|
||||
(isClientSide() && SSL_get_error(ssl, ret) != SSL_ERROR_WANT_WRITE))) {
|
||||
throw multi_ssl_get_error(ssl, ret);
|
||||
} else if (ret == 1) {
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
void Socket::sslHandshake() {
|
||||
sslHandshake(KeyPair{"", ""});
|
||||
}
|
||||
|
||||
void Socket::sslHandshake(KeyPair keypair) {
|
||||
if (isSecured()) {
|
||||
throw (char *) "Socket already secured";
|
||||
}
|
||||
|
||||
const SSL_METHOD *method;
|
||||
if (isServerSide()) {
|
||||
method = TLSv1_2_server_method();
|
||||
} else if (isClientSide()) {
|
||||
method = TLSv1_2_client_method();
|
||||
} else {
|
||||
method = TLSv1_2_method();
|
||||
}
|
||||
|
||||
SSL_CTX *ctx = SSL_CTX_new(method);
|
||||
SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
|
||||
SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4");
|
||||
SSL_CTX_set_ecdh_auto(ctx, 1);
|
||||
|
||||
const char *certfile = keypair.fullchain.c_str();
|
||||
const char *keyfile = keypair.privkey.c_str();
|
||||
|
||||
if (isServerSide()) {
|
||||
if (SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM) != 1) {
|
||||
throw (char *) ERR_reason_error_string(ERR_get_error());
|
||||
}
|
||||
|
||||
if (SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM) != 1) {
|
||||
throw (char *) ERR_reason_error_string(ERR_get_error());
|
||||
}
|
||||
}
|
||||
|
||||
this->ctx = ctx;
|
||||
this->ssl = SSL_new(ctx);
|
||||
SSL_set_fd(ssl, fd);
|
||||
enc = true;
|
||||
|
||||
while (true) {
|
||||
int ret = 0;
|
||||
if (isServerSide()) {
|
||||
ret = SSL_accept(ssl);
|
||||
} else if (isClientSide()) {
|
||||
ret = SSL_connect(ssl);
|
||||
} else {
|
||||
ret = SSL_do_handshake(ssl);
|
||||
}
|
||||
|
||||
if (ret <= 0 && ((isServerSide() && SSL_get_error(ssl, ret) != SSL_ERROR_WANT_READ) ||
|
||||
(isClientSide() && SSL_get_error(ssl, ret) != SSL_ERROR_WANT_WRITE))) {
|
||||
throw multi_ssl_get_error(ssl, ret);
|
||||
} else if (ret == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Socket::sslHandshake(string privkey, string fullchain) {
|
||||
sslHandshake(KeyPair{std::move(privkey), std::move(fullchain)});
|
||||
}
|
||||
|
||||
long Socket::select(list<Socket> read, list<Socket> write, long millis) {
|
||||
fd_set readfd, writefd;
|
||||
int maxfd = 0;
|
||||
FD_ZERO(&readfd);
|
||||
FD_ZERO(&writefd);
|
||||
|
||||
for (Socket s : read) {
|
||||
if (s.fd > maxfd) {
|
||||
maxfd = s.fd;
|
||||
}
|
||||
FD_SET(s.fd, &readfd);
|
||||
}
|
||||
|
||||
for (Socket s : write) {
|
||||
if (s.fd > maxfd) {
|
||||
maxfd = s.fd;
|
||||
}
|
||||
FD_SET(s.fd, &writefd);
|
||||
}
|
||||
|
||||
struct timeval *tv = new struct timeval;
|
||||
if (millis < 0) {
|
||||
tv = nullptr;
|
||||
} else if (millis == 0) {
|
||||
tv->tv_sec = 0;
|
||||
tv->tv_usec = 1;
|
||||
} else {
|
||||
tv->tv_sec = millis / 1000;
|
||||
tv->tv_usec = (millis % 1000) * 1000;
|
||||
}
|
||||
|
||||
int ret = ::select(maxfd + 1, &readfd, &writefd, nullptr, tv);
|
||||
if (ret < 0) {
|
||||
throw (char *) strerror(errno);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
long Socket::select(list<Socket> read, list<Socket> write) {
|
||||
Socket::select(std::move(read), std::move(write), -1);
|
||||
}
|
||||
|
||||
unsigned long Socket::getBytesSent() {
|
||||
return bytesSent;
|
||||
}
|
||||
|
||||
unsigned long Socket::getBytesReceived() {
|
||||
return bytesReceived;
|
||||
}
|
||||
|
||||
|
||||
ostream &operator<<(ostream &str, const Socket &socket) {
|
||||
return str << socket.toString();
|
||||
}
|
||||
|
||||
ostream &operator<<(ostream &str, const Socket *socket) {
|
||||
return str << socket->toString();
|
||||
}
|
||||
|
||||
string operator+(string &str, const Socket &socket) {
|
||||
return str + socket.toString();
|
||||
}
|
||||
|
||||
string operator+(const Socket &socket, string &str) {
|
||||
return socket.toString() + str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
167
src/network/Socket.h
Normal file
167
src/network/Socket.h
Normal file
@ -0,0 +1,167 @@
|
||||
/**
|
||||
* Necronda Web Server 3.0
|
||||
* Socket.h - Socket Class definition
|
||||
* Lorenz Stechauner, 2018-05-09
|
||||
*/
|
||||
|
||||
#ifndef NECRONDA_SOCKET
|
||||
#define NECRONDA_SOCKET
|
||||
|
||||
#include <map>
|
||||
|
||||
typedef struct {
|
||||
string privkey;
|
||||
string fullchain;
|
||||
} KeyPair;
|
||||
|
||||
using namespace std;
|
||||
|
||||
class Socket {
|
||||
private:
|
||||
int fd;
|
||||
SSL *ssl;
|
||||
SSL_CTX *ctx;
|
||||
bool enc;
|
||||
bool servers;
|
||||
bool clients;
|
||||
unsigned long bytesSent;
|
||||
unsigned long bytesReceived;
|
||||
long microsStart;
|
||||
long microsLast;
|
||||
|
||||
void setSocketOption(int, bool);
|
||||
|
||||
long send(void *buffer, int size);
|
||||
|
||||
long receive(void *buffer, int size);
|
||||
|
||||
long peek(void *buffer, int size);
|
||||
|
||||
public:
|
||||
Socket();
|
||||
|
||||
explicit Socket(int filedescriptor);
|
||||
|
||||
~Socket();
|
||||
|
||||
void bind(Address *address, unsigned short port);
|
||||
|
||||
void bind(unsigned short port);
|
||||
|
||||
void listen(int count = 1);
|
||||
|
||||
void connect(Address address, unsigned short port);
|
||||
|
||||
Socket* accept();
|
||||
|
||||
void sslHandshake();
|
||||
|
||||
void sslHandshake(map<string, KeyPair> sni);
|
||||
|
||||
void sslHandshake(KeyPair keypair);
|
||||
|
||||
void sslHandshake(string privkey, string fullchain);
|
||||
|
||||
long send(string *str);
|
||||
|
||||
long send(string str);
|
||||
|
||||
long send(const char *str);
|
||||
|
||||
long send(const char *str, long length);
|
||||
|
||||
string receive();
|
||||
|
||||
string receive(long length);
|
||||
|
||||
string receive(string until);
|
||||
|
||||
string receive(const char *until, unsigned long strlen);
|
||||
|
||||
string receive(const char *until);
|
||||
|
||||
void receive(FILE *file);
|
||||
|
||||
string receiveLine();
|
||||
|
||||
void shutdown();
|
||||
|
||||
void close();
|
||||
|
||||
long getDuration();
|
||||
|
||||
Address *getSocketAddress() const;
|
||||
|
||||
unsigned short getSocketPort() const;
|
||||
|
||||
Address *getPeerAddress() const;
|
||||
|
||||
unsigned short getPeerPort() const;
|
||||
|
||||
string toString() const;
|
||||
|
||||
|
||||
bool isServerSide();
|
||||
|
||||
bool isClientSide();
|
||||
|
||||
bool isSecured();
|
||||
|
||||
|
||||
void setReuseAddress(bool value = true);
|
||||
|
||||
void setReusePort(bool value = true);
|
||||
|
||||
void setSendBufferSize(int value);
|
||||
|
||||
void setReceiveBufferSize(int value);
|
||||
|
||||
void setMinReceiveBytes(int value);
|
||||
|
||||
void setMinSendBytes(int value);
|
||||
|
||||
void setSendTimeout(unsigned long ms);
|
||||
|
||||
void setReceiveTimeout(unsigned long ms);
|
||||
|
||||
|
||||
bool getReuseAddress();
|
||||
|
||||
bool getReusePort();
|
||||
|
||||
int getSendBufferSize();
|
||||
|
||||
int getReceiveBufferSize();
|
||||
|
||||
int getMinReceiveBytes();
|
||||
|
||||
int getMinSendBytes();
|
||||
|
||||
long getSendTimeout();
|
||||
|
||||
long getReceiveTimeout();
|
||||
|
||||
unsigned long getBytesSent();
|
||||
|
||||
unsigned long getBytesReceived();
|
||||
|
||||
static long select(list<Socket> read, list<Socket> write, long millis);
|
||||
|
||||
static long select(list<Socket> read, list<Socket> write);
|
||||
|
||||
void receive(FILE *file, long size);
|
||||
};
|
||||
|
||||
Socket operator<<(Socket sock, const char *str);
|
||||
|
||||
Socket operator<<(Socket sock, string str);
|
||||
|
||||
ostream &operator<<(ostream &str, const Socket &socket);
|
||||
|
||||
ostream &operator<<(ostream &str, const Socket *socket);
|
||||
|
||||
string operator+(string &str, const Socket &socket);
|
||||
|
||||
string operator+(const Socket &socket, string &str);
|
||||
|
||||
#endif
|
193
src/network/http/HttpConnection.cpp
Normal file
193
src/network/http/HttpConnection.cpp
Normal file
@ -0,0 +1,193 @@
|
||||
|
||||
|
||||
#include <zlib.h>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
#include "HttpConnection.h"
|
||||
#include "../../necronda-server.h"
|
||||
#include "HttpStatusCode.h"
|
||||
|
||||
HttpConnection::HttpConnection(Socket *socket) {
|
||||
this->socket = socket;
|
||||
this->request = new HttpRequest(socket);
|
||||
this->response = new HttpResponse();
|
||||
microsStart = getMicros();
|
||||
response->setVersion("1.1");
|
||||
response->setField("Server", "Necronda/3.0");
|
||||
}
|
||||
|
||||
void HttpConnection::respond(int statuscode) {
|
||||
if (statuscode >= 400 && statuscode < 600) {
|
||||
respond(statuscode,
|
||||
"<!DOCTYPE html><html><head><title>" + to_string(statuscode) + " " +
|
||||
::getStatusCode(statuscode).message +
|
||||
"</title></head><body><center><h1>" + to_string(statuscode) + " " +
|
||||
::getStatusCode(statuscode).message +
|
||||
"</h1>" +
|
||||
((request->isExistingField("Host")) ?
|
||||
(request->isExistingField("Referer") &&
|
||||
request->getField("Referer").find(request->getField("Host")) != string::npos) ?
|
||||
"<p>Go back to the last page you visited: <a href=\"" + request->getField("Referer") + "\">" +
|
||||
request->getField("Referer") + "</a></p>" :
|
||||
"<p>Go back to the home page of <a href=\"//" +
|
||||
request->getField("Host") + "/\">" +
|
||||
request->getField("Host") +
|
||||
"</a></p>" : "") + "</center></body></html>\r\n"
|
||||
);
|
||||
} else {
|
||||
respond(statuscode, "");
|
||||
}
|
||||
}
|
||||
|
||||
void HttpConnection::respond(int statuscode, string payload) {
|
||||
response->setStatusCode(statuscode);
|
||||
response->setField("Date", getHttpDate());
|
||||
response->setField("Content-Length", to_string(payload.length()));
|
||||
response->sendHeader(socket);
|
||||
socket->send(std::move(payload));
|
||||
}
|
||||
|
||||
void HttpConnection::respond(int statuscode, FILE *file, bool compress, long start, long end) {
|
||||
response->setStatusCode(statuscode);
|
||||
response->setField("Transfer-Encoding", "chunked");
|
||||
response->setField("Date", getHttpDate());
|
||||
|
||||
long shouldTransfer;
|
||||
long transfered = 0;
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
long len = ftell(file);
|
||||
|
||||
if (start != -1 && end != -1) {
|
||||
fseek(file, start, SEEK_SET);
|
||||
response->setField("Content-Length", to_string(end - start + 1));
|
||||
shouldTransfer = end - start + 1;
|
||||
compress = false;
|
||||
} else {
|
||||
fseek(file, 0, SEEK_SET);
|
||||
shouldTransfer = len;
|
||||
if (len >= 0) {
|
||||
response->setField("Content-Length", to_string(len));
|
||||
}
|
||||
}
|
||||
|
||||
if (compress) {
|
||||
response->setField("Content-Encoding", "deflate");
|
||||
}
|
||||
|
||||
response->sendHeader(socket);
|
||||
|
||||
if (compress) {
|
||||
int level = 1;
|
||||
int ret, flush;
|
||||
unsigned have;
|
||||
z_stream strm;
|
||||
unsigned char in[CHUNK];
|
||||
unsigned char out[CHUNK];
|
||||
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
ret = deflateInit(&strm, level);
|
||||
if (ret != Z_OK) {
|
||||
throw (char *) "Unable to open file";
|
||||
}
|
||||
|
||||
do {
|
||||
strm.avail_in = (uInt) fread(in, 1, CHUNK, file);
|
||||
|
||||
if (ferror(file)) {
|
||||
(void) deflateEnd(&strm);
|
||||
throw (char *) strerror(errno);
|
||||
}
|
||||
flush = feof(file) ? Z_FINISH : Z_NO_FLUSH;
|
||||
strm.next_in = in;
|
||||
do {
|
||||
strm.avail_out = CHUNK;
|
||||
strm.next_out = out;
|
||||
ret = deflate(&strm, flush);
|
||||
assert(ret != Z_STREAM_ERROR);
|
||||
have = CHUNK - strm.avail_out;
|
||||
|
||||
if (have != 0) {
|
||||
char buffer[64];
|
||||
sprintf(buffer, "%X\r\n", have);
|
||||
socket->send(buffer);
|
||||
socket->send((const char *) out, have);
|
||||
socket->send("\r\n");
|
||||
}
|
||||
} while (strm.avail_out == 0);
|
||||
assert(strm.avail_in == 0);
|
||||
} while (flush != Z_FINISH);
|
||||
assert(ret == Z_STREAM_END);
|
||||
socket->send("0\r\n\r\n");
|
||||
deflateEnd(&strm);
|
||||
} else {
|
||||
char buffer[CHUNK];
|
||||
char buff[64];
|
||||
while (true) {
|
||||
unsigned long size = fread(buffer, 1, (size_t) ((CHUNK > (shouldTransfer - transfered) && shouldTransfer > 0) ? (shouldTransfer - transfered) : CHUNK), file);
|
||||
transfered += size;
|
||||
sprintf(buff, "%lX\r\n", size);
|
||||
socket->send(buff);
|
||||
socket->send((const char *) buffer, size);
|
||||
socket->send("\r\n");
|
||||
if (size == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string HttpConnection::getField(string index) {
|
||||
return request->getField(std::move(index));
|
||||
}
|
||||
|
||||
string HttpConnection::getPath() {
|
||||
return request->getPath();
|
||||
}
|
||||
|
||||
void HttpConnection::setField(string index, string data) {
|
||||
response->setField(std::move(index), std::move(data));
|
||||
}
|
||||
|
||||
bool HttpConnection::isExistingField(string index) {
|
||||
return request->isExistingField(std::move(index));
|
||||
}
|
||||
|
||||
string HttpConnection::getMethod() {
|
||||
return request->getMethod();
|
||||
}
|
||||
|
||||
long HttpConnection::getDuration() {
|
||||
return getMicros() - microsStart;
|
||||
}
|
||||
|
||||
HttpStatusCode HttpConnection::getStatusCode() {
|
||||
return response->getStatusCode();
|
||||
}
|
||||
|
||||
void HttpConnection::redirect(int statuscode, string location) {
|
||||
setField("Location", std::move(location));
|
||||
respond(statuscode, "");
|
||||
}
|
||||
|
||||
string HttpConnection::getResponseField(string index) {
|
||||
return response->getField(std::move(index));
|
||||
}
|
||||
|
||||
bool HttpConnection::isExistingResponseField(string index) {
|
||||
return response->isExistingField(std::move(index));
|
||||
}
|
||||
|
||||
string HttpConnection::cgiExport() {
|
||||
return request->cgiExport();
|
||||
}
|
||||
|
||||
void HttpConnection::removeField(string index) {
|
||||
response->removeField(std::move(index));
|
||||
}
|
||||
|
||||
|
||||
|
54
src/network/http/HttpConnection.h
Normal file
54
src/network/http/HttpConnection.h
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
|
||||
#ifndef NECRONDA_HTTP_CONNECTION
|
||||
#define NECRONDA_HTTP_CONNECTION
|
||||
|
||||
|
||||
#include "../Socket.h"
|
||||
#include "HttpResponse.h"
|
||||
#include "HttpRequest.h"
|
||||
#include "../../necronda-server.h"
|
||||
|
||||
|
||||
class HttpConnection {
|
||||
private:
|
||||
Socket *socket;
|
||||
HttpRequest *request;
|
||||
HttpResponse *response;
|
||||
long microsStart;
|
||||
|
||||
public:
|
||||
explicit HttpConnection(Socket *socket);
|
||||
|
||||
void respond(int statuscode);
|
||||
|
||||
void respond(int statuscode, string payload);
|
||||
|
||||
void respond(int statuscode, FILE *file, bool compress = false, long start = -1, long end = -1);
|
||||
|
||||
void redirect(int statuscode, string location);
|
||||
|
||||
bool isExistingField(string index);
|
||||
|
||||
bool isExistingResponseField(string index);
|
||||
|
||||
string getField(string index);
|
||||
|
||||
string getResponseField(string index);
|
||||
|
||||
string getPath();
|
||||
|
||||
string getMethod();
|
||||
|
||||
void setField(string index, string data);
|
||||
|
||||
long getDuration();
|
||||
|
||||
HttpStatusCode getStatusCode();
|
||||
|
||||
string cgiExport();
|
||||
|
||||
void removeField(string index);
|
||||
};
|
||||
|
||||
#endif
|
123
src/network/http/HttpHeader.cpp
Normal file
123
src/network/http/HttpHeader.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Necronda Web Server 3.0
|
||||
* HttpHeader.cpp - HttpHeader Class methods
|
||||
* Lorenz Stechauner, 2018-05-09
|
||||
*/
|
||||
|
||||
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include "../Socket.h"
|
||||
|
||||
#include "HttpHeader.h"
|
||||
#include "../../necronda-server.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
string to_cgi(string text) {
|
||||
for (auto & c: text) c = (char) toupper(c);
|
||||
long pos = 0;
|
||||
while ((pos = text.find('-', pos + 1)) != string::npos) {
|
||||
text.replace(pos, 1, 1, '_');
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
HttpHeader::HttpHeader() {
|
||||
fields = fields;
|
||||
}
|
||||
|
||||
HttpHeader::HttpHeader(Socket *socket) : HttpHeader::HttpHeader() {
|
||||
parse(socket);
|
||||
}
|
||||
|
||||
|
||||
void HttpHeader::parse(Socket *socket) {
|
||||
while (true) {
|
||||
string line = socket->receiveLine();
|
||||
if (line.length() == 0) {
|
||||
break;
|
||||
} else {
|
||||
unsigned long pos = line.find(':');
|
||||
if (pos == string::npos) {
|
||||
throw (char *) "Malformed header";
|
||||
}
|
||||
string index = line.substr(0, pos);
|
||||
string data = line.substr(pos + 1, line.length() - pos);
|
||||
while (index[0] == ' ') index.erase(index.begin() + 0);
|
||||
while (index[index.length() - 1] == ' ') index.erase(index.end() - 1);
|
||||
while (data[0] == ' ') data.erase(data.begin() + 0);
|
||||
while (data[data.length() - 1] == ' ') data.erase(data.end() - 1);
|
||||
setField(index, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Default Destructor
|
||||
*/
|
||||
HttpHeader::~HttpHeader() {
|
||||
fields.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets a field in the HTTP header
|
||||
* e.g. Content-Length: 42
|
||||
* @param index The field index
|
||||
* @param data The field data
|
||||
*/
|
||||
void HttpHeader::setField(string index, string data) {
|
||||
removeField(index);
|
||||
fields.insert(make_pair(index, data));
|
||||
}
|
||||
|
||||
void HttpHeader::removeField(string index) {
|
||||
fields.erase(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a field from the HTTP header
|
||||
* e.g. Content-Length: 42
|
||||
* @param index The field index
|
||||
* @return The field data
|
||||
*/
|
||||
string HttpHeader::getField(string index) {
|
||||
auto i = fields.find(index);
|
||||
if (i != fields.end()) {
|
||||
return fields.at(index);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool HttpHeader::isExistingField(string index) {
|
||||
auto i = fields.find(index);
|
||||
return i != fields.end();
|
||||
}
|
||||
|
||||
string HttpHeader::toString() {
|
||||
string header = "";
|
||||
for (auto it = fields.begin(); it != fields.end(); it++ ) {
|
||||
header += it->first + ": " + it->second + "\r\n";
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
string HttpHeader::cgiExport() {
|
||||
string header = "";
|
||||
for (auto it = fields.begin(); it != fields.end(); it++ ) {
|
||||
header += "HTTP_" + to_cgi(it->first) + "=" + cli_encode(it->second) + " ";
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
|
||||
|
53
src/network/http/HttpHeader.h
Normal file
53
src/network/http/HttpHeader.h
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Necronda Web Server 3.0
|
||||
* HttpHeader.h - HttpHeader Class definition
|
||||
* Lorenz Stechauner, 2018-05-09
|
||||
*/
|
||||
|
||||
#ifndef NECRONDA_HTTP_HEADER
|
||||
#define NECRONDA_HTTP_HEADER
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct comp {
|
||||
bool operator()(const std::string& lhs, const std::string& rhs) const {
|
||||
return strcasecmp(lhs.c_str(), rhs.c_str()) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores Key-Value Pairs for a HTTP header
|
||||
* e.g.
|
||||
* Content-Length: 64
|
||||
* Host: example.org
|
||||
*/
|
||||
class HttpHeader {
|
||||
private:
|
||||
map<string, string, comp> fields;
|
||||
|
||||
public:
|
||||
HttpHeader();
|
||||
|
||||
explicit HttpHeader(Socket *socket);
|
||||
|
||||
~HttpHeader();
|
||||
|
||||
void setField(string index, string data);
|
||||
|
||||
string getField(string index);
|
||||
|
||||
void removeField(string index);
|
||||
|
||||
bool isExistingField(string index);
|
||||
|
||||
void parse(Socket *socket);
|
||||
|
||||
string toString();
|
||||
|
||||
string cgiExport();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
107
src/network/http/HttpRequest.cpp
Normal file
107
src/network/http/HttpRequest.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
#include "../Socket.h"
|
||||
#include "HttpHeader.h"
|
||||
#include "HttpRequest.h"
|
||||
|
||||
|
||||
HttpRequest::HttpRequest() {
|
||||
this->header = HttpHeader();
|
||||
}
|
||||
|
||||
HttpRequest::HttpRequest(Socket *socket) : HttpRequest::HttpRequest() {
|
||||
parseHeader(socket);
|
||||
}
|
||||
|
||||
HttpRequest::HttpRequest(string method, string path, string version) : HttpRequest::HttpRequest() {
|
||||
this->method = std::move(method);
|
||||
this->path = std::move(path);
|
||||
this->version = std::move(version);
|
||||
}
|
||||
|
||||
void HttpRequest::parseHeader(Socket *socket) {
|
||||
string line = socket->receiveLine();
|
||||
|
||||
unsigned long pos1 = line.find(' ');
|
||||
unsigned long pos2;
|
||||
|
||||
bool invalid = false;
|
||||
|
||||
if (pos1 != string::npos) {
|
||||
pos2 = line.find(' ', pos1 + 1);
|
||||
if (pos2 != string::npos) {
|
||||
method = line.substr(0, pos1);
|
||||
for (auto &c: method) c = (char) toupper(c);
|
||||
path = line.substr(pos1 + 1, pos2 - pos1 - 1);
|
||||
version = line.substr(pos2 + 6, 3);
|
||||
} else {
|
||||
invalid = true;
|
||||
}
|
||||
} else {
|
||||
pos2 = string::npos;
|
||||
invalid = true;
|
||||
}
|
||||
|
||||
|
||||
if (!invalid && (line.substr(pos2 + 1, 5) != "HTTP/" || version[1] != '.' || path[0] != '/' || !(version[0] >= '0' && version[0] <= '9') || !(version[2] >= '0' && version[2] <= '9'))) {
|
||||
invalid = true;
|
||||
}
|
||||
|
||||
if (invalid) {
|
||||
method = "";
|
||||
path = "";
|
||||
version = "";
|
||||
throw (char *) "Malformed header";
|
||||
}
|
||||
|
||||
header.parse(socket);
|
||||
}
|
||||
|
||||
string HttpRequest::getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
string HttpRequest::getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
string HttpRequest::getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
void HttpRequest::setMethod(string method) {
|
||||
this->method = std::move(method);
|
||||
}
|
||||
|
||||
void HttpRequest::setPath(string path) {
|
||||
this->path = std::move(path);
|
||||
}
|
||||
|
||||
void HttpRequest::setVersion(string version) {
|
||||
this->version = std::move(version);
|
||||
}
|
||||
|
||||
string HttpRequest::getField(string index) {
|
||||
return header.getField(std::move(index));
|
||||
}
|
||||
|
||||
void HttpRequest::setField(string index, string data) {
|
||||
header.setField(std::move(index), std::move(data));
|
||||
}
|
||||
|
||||
bool HttpRequest::isExistingField(string index) {
|
||||
return header.isExistingField(std::move(index));
|
||||
}
|
||||
|
||||
string HttpRequest::cgiExport() {
|
||||
return header.cgiExport();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
52
src/network/http/HttpRequest.h
Normal file
52
src/network/http/HttpRequest.h
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Necronda Web Server 3.0
|
||||
* HttpHeader.h - HttpHeader Class definition
|
||||
* Lorenz Stechauner, 2018-05-09
|
||||
*/
|
||||
|
||||
#ifndef NECRONDA_HTTP_REQUEST
|
||||
#define NECRONDA_HTTP_REQUEST
|
||||
|
||||
using namespace std;
|
||||
|
||||
class HttpRequest {
|
||||
private:
|
||||
HttpHeader header;
|
||||
string method;
|
||||
string path;
|
||||
string version;
|
||||
|
||||
public:
|
||||
HttpRequest();
|
||||
|
||||
explicit HttpRequest(Socket *socket);
|
||||
|
||||
HttpRequest(string method, string path, string version = "1.1");
|
||||
|
||||
void parseHeader(Socket *socket);
|
||||
|
||||
void sendHeader(Socket *socket);
|
||||
|
||||
string getField(string index);
|
||||
|
||||
void setField(string index, string data);
|
||||
|
||||
bool isExistingField(string index);
|
||||
|
||||
string getMethod();
|
||||
|
||||
string getPath();
|
||||
|
||||
string getVersion();
|
||||
|
||||
void setMethod(string method);
|
||||
|
||||
void setPath(string path);
|
||||
|
||||
void setVersion(string version);
|
||||
|
||||
string cgiExport();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
70
src/network/http/HttpResponse.cpp
Normal file
70
src/network/http/HttpResponse.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
//
|
||||
// Created by lorenz on 5/17/18.
|
||||
//
|
||||
|
||||
#include "HttpResponse.h"
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
#include "HttpStatusCode.h"
|
||||
|
||||
|
||||
HttpResponse::HttpResponse() {
|
||||
this->header = HttpHeader();
|
||||
}
|
||||
|
||||
HttpResponse::HttpResponse(Socket *socket) : HttpResponse::HttpResponse() {
|
||||
this->parseHeader(socket);
|
||||
}
|
||||
|
||||
HttpResponse::HttpResponse(int statuscode, string version) : HttpResponse::HttpResponse(::getStatusCode(statuscode), std::move(version)) {
|
||||
}
|
||||
|
||||
HttpResponse::HttpResponse(HttpStatusCode statuscode, string version) : HttpResponse::HttpResponse() {
|
||||
this->statuscode = statuscode;
|
||||
this->version = std::move(version);
|
||||
}
|
||||
|
||||
void HttpResponse::sendHeader(Socket *socket) {
|
||||
socket->send("HTTP/" + version + " " + to_string(statuscode.code) + " " + statuscode.message + "\r\n" +
|
||||
header.toString() + "\r\n");
|
||||
}
|
||||
|
||||
string HttpResponse::getField(string index) {
|
||||
return header.getField(std::move(index));
|
||||
}
|
||||
|
||||
void HttpResponse::setField(string index, string data) {
|
||||
header.setField(std::move(index), std::move(data));
|
||||
}
|
||||
|
||||
bool HttpResponse::isExistingField(string index) {
|
||||
return header.isExistingField(std::move(index));
|
||||
}
|
||||
|
||||
HttpStatusCode HttpResponse::getStatusCode() {
|
||||
return statuscode;
|
||||
}
|
||||
|
||||
string HttpResponse::getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
void HttpResponse::setStatusCode(HttpStatusCode statuscode) {
|
||||
this->statuscode = statuscode;
|
||||
}
|
||||
|
||||
void HttpResponse::setStatusCode(int statuscode) {
|
||||
this->statuscode = ::getStatusCode(statuscode);
|
||||
}
|
||||
|
||||
void HttpResponse::setVersion(string version) {
|
||||
this->version = std::move(version);
|
||||
}
|
||||
|
||||
void HttpResponse::parseHeader(Socket *socket) {
|
||||
|
||||
}
|
||||
|
||||
void HttpResponse::removeField(string index) {
|
||||
header.removeField(std::move(index));
|
||||
}
|
50
src/network/http/HttpResponse.h
Normal file
50
src/network/http/HttpResponse.h
Normal file
@ -0,0 +1,50 @@
|
||||
|
||||
|
||||
#ifndef NECRONDA_HTTP_RESPONSE
|
||||
#define NECRONDA_HTTP_RESPONSE
|
||||
|
||||
|
||||
#include <string>
|
||||
#include "HttpHeader.h"
|
||||
#include "HttpStatusCode.h"
|
||||
#include "../Socket.h"
|
||||
|
||||
class HttpResponse {
|
||||
private:
|
||||
HttpHeader header;
|
||||
HttpStatusCode statuscode;
|
||||
string version;
|
||||
|
||||
public:
|
||||
HttpResponse();
|
||||
|
||||
explicit HttpResponse(Socket *socket);
|
||||
|
||||
explicit HttpResponse(int statuscode, string version = "1.1");
|
||||
|
||||
explicit HttpResponse(HttpStatusCode statuscode, string version = "1.1");
|
||||
|
||||
void parseHeader(Socket *socket);
|
||||
|
||||
void sendHeader(Socket *socket);
|
||||
|
||||
string getField(string index);
|
||||
|
||||
void setField(string index, string data);
|
||||
|
||||
bool isExistingField(string index);
|
||||
|
||||
HttpStatusCode getStatusCode();
|
||||
|
||||
string getVersion();
|
||||
|
||||
void setStatusCode(HttpStatusCode statuscode);
|
||||
|
||||
void setStatusCode(int statuscode);
|
||||
|
||||
void setVersion(string version);
|
||||
|
||||
void removeField(string index);
|
||||
};
|
||||
|
||||
#endif
|
65
src/network/http/HttpStatusCode.cpp
Normal file
65
src/network/http/HttpStatusCode.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
#include "HttpStatusCode.h"
|
||||
|
||||
/**
|
||||
* Necronda Web Server 3.0
|
||||
* HttpStatusCode.cpp - HTTP Status Code definition
|
||||
* Lorenz Stechauner, 2018-05-16
|
||||
* Reference: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||
*/
|
||||
|
||||
|
||||
HttpStatusCode httpStatusCodes[] = {
|
||||
HttpStatusCode{100, "Informational", "Continue", ""},
|
||||
HttpStatusCode{101, "Informational", "Switching Protocols", ""},
|
||||
|
||||
HttpStatusCode{200, "Success", "OK", ""},
|
||||
HttpStatusCode{201, "Success", "Created", ""},
|
||||
HttpStatusCode{202, "Success", "Accepted", ""},
|
||||
HttpStatusCode{203, "Success", "Non-Authoritative Information", ""},
|
||||
HttpStatusCode{204, "Success", "No Centent", ""},
|
||||
HttpStatusCode{205, "Success", "Reset Content", ""},
|
||||
HttpStatusCode{206, "Success", "Partial Content", ""},
|
||||
|
||||
HttpStatusCode{300, "Redirection", "Multiple Choices", ""},
|
||||
HttpStatusCode{301, "Redirection", "Moved Permanently", ""},
|
||||
HttpStatusCode{302, "Redirection", "Found", ""},
|
||||
HttpStatusCode{303, "Redirection", "See Other", ""},
|
||||
HttpStatusCode{304, "Redirection", "Not Modified", ""},
|
||||
HttpStatusCode{305, "Redirection", "Use Proxy", ""},
|
||||
HttpStatusCode{307, "Redirection", "Temporary Redirect", ""},
|
||||
|
||||
HttpStatusCode{400, "Client Error", "Bad Request", ""},
|
||||
HttpStatusCode{401, "Client Error", "Unauthorized", ""},
|
||||
HttpStatusCode{402, "Client Error", "Payment Required", ""},
|
||||
HttpStatusCode{403, "Client Error", "Forbidden", ""},
|
||||
HttpStatusCode{404, "Client Error", "Not Found", ""},
|
||||
HttpStatusCode{405, "Client Error", "Method Not Allowed", ""},
|
||||
HttpStatusCode{406, "Client Error", "Not Acceptable", ""},
|
||||
HttpStatusCode{407, "Client Error", "Proxy Authentication Required", ""},
|
||||
HttpStatusCode{408, "Client Error", "Request Timeout", ""},
|
||||
HttpStatusCode{409, "Client Error", "Conflict", ""},
|
||||
HttpStatusCode{410, "Client Error", "Gone", ""},
|
||||
HttpStatusCode{411, "Client Error", "Length Required", ""},
|
||||
HttpStatusCode{412, "Client Error", "Precondition Failed", ""},
|
||||
HttpStatusCode{413, "Client Error", "Request Entity Too Large", ""},
|
||||
HttpStatusCode{414, "Client Error", "Request-URI Too Long", ""},
|
||||
HttpStatusCode{415, "Client Error", "Unsupported Media Type", ""},
|
||||
HttpStatusCode{416, "Client Error", "Requested Range Not Satisfiable", ""},
|
||||
HttpStatusCode{417, "Client Error", "Expectation Failed", ""},
|
||||
|
||||
HttpStatusCode{500, "Server Error", "Internal Server Error", ""},
|
||||
HttpStatusCode{501, "Server Error", "Not Implemented", ""},
|
||||
HttpStatusCode{502, "Server Error", "Bad Gateway", ""},
|
||||
HttpStatusCode{503, "Server Error", "Service Unavailable", ""},
|
||||
HttpStatusCode{504, "Server Error", "Gateway Timeout", ""},
|
||||
HttpStatusCode{505, "Server Error", "HTTP Version Not Supported", ""},
|
||||
};
|
||||
|
||||
HttpStatusCode getStatusCode(int statuscode) {
|
||||
for (HttpStatusCode sc : httpStatusCodes) {
|
||||
if (sc.code == statuscode) {
|
||||
return sc;
|
||||
}
|
||||
}
|
||||
throw (char *) "Invalid status code";
|
||||
}
|
15
src/network/http/HttpStatusCode.h
Normal file
15
src/network/http/HttpStatusCode.h
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
#ifndef NECRONDA_HTTP_STATUSCODE
|
||||
#define NECRONDA_HTTP_STATUSCODE
|
||||
|
||||
typedef struct {
|
||||
short code; // The status code (e.g. 200)
|
||||
const char *type; // The status type type (e.g Success)
|
||||
const char *message; // The status code message (e.g. OK)
|
||||
const char *description; // The status code description (currently not used)
|
||||
} HttpStatusCode;
|
||||
|
||||
HttpStatusCode getStatusCode(int statuscode);
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user