Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions example/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,26 @@ int main(int argc, char **argv) {
mochios::client::Connection connection;
connection.host = "expresso.aditjain.me";
connection.port = 80;
connection.timeout = 2; // this is in seconds

mochios::Client client(connection);
client.interceptors.request.use([](mochios::messages::Request &request) {
logger::info("Intercepting request!");
request.print();
});
mochios::messages::Response response;

mochios::messages::Request healthRequest("/health");
response = client.get(healthRequest);
logger::success(response.body);

mochios::messages::Request request("/about");
response = client.get(request);
logger::success(response.body.dumps(2));
try {
mochios::messages::Response response = client.get(request);
logger::success("Request to \"" + request.path +
"\" succeeded with status code " +
std::to_string(response.statusCode));
response.print();
} catch (const mochios::messages::Response &e) {
logger::error("Request to \"" + request.path +
"\" failed with status code " + std::to_string(e.statusCode));
e.print();
}

return EXIT_SUCCESS;
}
1 change: 1 addition & 0 deletions include/mochios/client/client.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <cstring>
#include <fcntl.h>
#include <netdb.h>

#include <brewtils/sys.h>
Expand Down
1 change: 1 addition & 0 deletions include/mochios/client/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace client {
typedef struct {
std::string host;
unsigned short port;
unsigned int timeout = 2;
} Connection;

} // namespace client
Expand Down
2 changes: 2 additions & 0 deletions include/mochios/helpers/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ buildRequest(mochios::messages::Request &request);
void buildResponse(mochios::messages::Response &res,
std::stringstream &response);

void buildDefaultResponse(mochios::messages::Response &res, int statusCode);

mochios::messages::Response send(mochios::messages::Request &request,
const int &socket);

Expand Down
2 changes: 1 addition & 1 deletion include/mochios/messages/message.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Message {
void set(const std::string &key, const std::string &value);
const std::string get(const std::string &key) const;

virtual void print() = 0;
virtual const void print() const = 0;
};

} // namespace messages
Expand Down
2 changes: 1 addition & 1 deletion include/mochios/messages/request.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Request : public Message {
std::string path;
mochios::enums::method method;

void print() override;
const void print() const override;
};

} // namespace messages
Expand Down
2 changes: 1 addition & 1 deletion include/mochios/messages/response.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Response : public Message {
int statusCode;
std::string statusText;

void print() override;
const void print() const override;
};

} // namespace messages
Expand Down
51 changes: 49 additions & 2 deletions src/client/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,61 @@ void mochios::Client::connect() {
logger::error("Failed to create socket", "void mochios::Client::connect()");
}

if (::connect(this->socket, this->server->ai_addr, this->server->ai_addrlen) <
0) {
int flags = fcntl(this->socket, F_GETFL, 0);
fcntl(this->socket, F_SETFL, flags | O_NONBLOCK);

int result =
::connect(this->socket, this->server->ai_addr, this->server->ai_addrlen);
if (result < 0 && errno != EINPROGRESS) {
freeaddrinfo(this->server);
close(this->socket);
this->socket = -1;
logger::error("Failed to connect to server at " + connection.host,
"void mochios::Client::connect()");
return;
}

if (result != 0) {
fd_set writefds;
FD_ZERO(&writefds);
FD_SET(this->socket, &writefds);

struct timeval timeout {};
timeout.tv_sec = this->connection.timeout;
timeout.tv_usec = 0;

int sel = select(this->socket + 1, nullptr, &writefds, nullptr, &timeout);
if (sel <= 0) {
close(this->socket);
this->socket = -1;
freeaddrinfo(this->server);
logger::error("Connection timed out", "mochios::Client::connect()");
return;
}

int so_error;
socklen_t len = sizeof(so_error);
getsockopt(this->socket, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error != 0) {
close(this->socket);
this->socket = -1;
freeaddrinfo(this->server);
logger::error("Connection failed: " + std::to_string(so_error),
"mochios::Client::connect()");
return;
}
}

fcntl(this->socket, F_SETFL, flags);

struct timeval timeout;
timeout.tv_sec = this->connection.timeout;
timeout.tv_usec = 0;
setsockopt(this->socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));

timeout.tv_sec = this->connection.timeout;
timeout.tv_usec = 0;
setsockopt(this->socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
}

mochios::messages::Response
Expand Down
151 changes: 114 additions & 37 deletions src/helpers/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ void mochios::helpers::client::buildResponse(mochios::messages::Response &res,
res.set(key, value);
}

// Body
std::stringstream body;
while (std::getline(response, line) && line != "\r") {
body << line;
Expand All @@ -68,48 +67,126 @@ void mochios::helpers::client::buildResponse(mochios::messages::Response &res,
return;
}

void mochios::helpers::client::buildDefaultResponse(
mochios::messages::Response &res, int statusCode) {
res.statusCode = statusCode;
json::object responseBody;

switch (statusCode) {
case 400:
responseBody["error"] = "Bad Request";
responseBody["message"] =
"The request could not be understood by the server.";
break;
case 404:
responseBody["error"] = "Not Found";
responseBody["message"] = "The requested resource could not be found.";
break;
case 408:
responseBody["error"] = "Request Timeout";
responseBody["message"] = "The server timed out waiting for the request.";
break;
case 500:
responseBody["error"] = "Internal Server Error";
responseBody["message"] = "An unexpected error occurred on the server.";
break;
default:
responseBody["error"] = "Unknown Error";
responseBody["message"] = "An unknown error occurred.";
break;
}

res.body = responseBody.dumps(0);
res.set("Content-Type", "application/json");
res.set("Connection", "close");
res.set("Content-Length", std::to_string(res.body.size()));
return;
}

mochios::messages::Response
mochios::helpers::client::send(mochios::messages::Request &request,
const int &socket) {
std::pair<std::string, std::string> requestString =
mochios::helpers::client::buildRequest(request);
if (brewtils::sys::send(socket, requestString.first.c_str(),
requestString.first.size(), 0) < 0) {
logger::error(
"Error sending request headers",
"mochios::messages::Response "
"mochios::helpers::client::send(mochios::messages::Request &request, "
"const mochios::enums::method &method, const int &socket)");
}
if (requestString.second.size() > 0) {
if (brewtils::sys::send(socket, requestString.second.c_str(),
requestString.second.size(), 0) < 0) {
logger::error(
"Error sending request body",
"mochios::messages::Response "
"mochios::helpers::client::send(mochios::messages::Request &request, "
"const mochios::enums::method &method, const int &socket)");
mochios::messages::Response res;
bool socketClosed = false;
try {
std::pair<std::string, std::string> requestString =
mochios::helpers::client::buildRequest(request);
if (brewtils::sys::send(socket, requestString.first.c_str(),
requestString.first.size(), 0) < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
mochios::helpers::client::buildDefaultResponse(res, 408);
throw res;
} else {
logger::error("Error sending request headers",
"mochios::messages::Response "
"mochios::helpers::client::send(mochios::messages::"
"Request &request, const mochios::enums::method &method, "
"const int &socket)");
}
}
if (requestString.second.size() > 0) {
if (brewtils::sys::send(socket, requestString.second.c_str(),
requestString.second.size(), 0) < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
mochios::helpers::client::buildDefaultResponse(res, 408);
throw res;
} else {
if (brewtils::sys::send(socket, requestString.second.c_str(),
requestString.second.size(), 0) < 0) {
logger::error("Error sending request body",
"mochios::messages::Response "
"mochios::helpers::client::send(mochios::messages::"
"Request &request, const mochios::enums::method "
"&method, const int &socket)");
}
}
}
}
}

std::stringstream oss;
char buffer[4096];
int bytesRead;
while ((bytesRead = brewtils::sys::recv(socket, buffer, 4096, 0)) > 0) {
oss << buffer;
memset(buffer, 0, 4096);
}
close(socket);
std::stringstream oss;
char buffer[4096];
int bytesRead;
while ((bytesRead = brewtils::sys::recv(socket, buffer, 4096, 0)) > 0) {
oss.write(buffer, bytesRead);
memset(buffer, 0, 4096);
}
close(socket);
socketClosed = true;

mochios::messages::Response res;
std::string line;
std::getline(oss, line);
std::vector<std::string> parts = brewtils::string::split(line, " ");
res.statusCode = std::stoi(parts[1]);
if (parts.size() > 2) {
res.statusText = parts[2];
if (bytesRead < 0) {
if (oss.str().empty() || errno == EAGAIN || errno == EWOULDBLOCK) {
mochios::helpers::client::buildDefaultResponse(res, 408);
throw res;
} else {
logger::error("Receive failed: " + std::string(strerror(errno)),
"mochios::messages::Response "
"mochios::helpers::client::send(mochios::messages::"
"Request &request, const mochios::enums::method &method, "
"const int &socket)");
}
}

std::string line;
std::getline(oss, line);
std::vector<std::string> parts = brewtils::string::split(line, " ");
res.statusCode = std::stoi(brewtils::string::trim(parts[1]));
if (parts.size() > 2) {
res.statusText = brewtils::string::trim(parts[2]);
}
mochios::helpers::client::buildResponse(res, oss);
} catch (const mochios::messages::Response &e) {
} catch (const std::exception &e) {
logger::error("Exception occurred: " + std::string(e.what()));
if (!socketClosed) {
close(socket);
socketClosed = true;
}
mochios::helpers::client::buildDefaultResponse(res, 500);
}

mochios::helpers::client::buildResponse(res, oss);
return res;
if (res.statusCode >= 200 && res.statusCode < 300) {
return res;
} else {
throw res;
}
}
2 changes: 1 addition & 1 deletion src/messages/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ mochios::messages::Request::Request(const std::string &path) : path(path) {

mochios::messages::Request::~Request() { return; }

void mochios::messages::Request::print() {
const void mochios::messages::Request::print() const {
logger::debug("Request:");
logger::debug(" path: " + this->path);
logger::debug(" method: " + this->method);
Expand Down
2 changes: 1 addition & 1 deletion src/messages/response.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mochios::messages::Response::Response() { return; }

mochios::messages::Response::~Response() { return; }

void mochios::messages::Response::print() {
const void mochios::messages::Response::print() const {
logger::debug("Response:");
logger::debug(" statusCode: " + std::to_string(this->statusCode));
logger::debug(" statusText: " + this->statusText);
Expand Down