Skip to content

Instantly share code, notes, and snippets.

@niwinz
Last active December 13, 2015 19:28
Show Gist options
  • Save niwinz/4962578 to your computer and use it in GitHub Desktop.
Save niwinz/4962578 to your computer and use it in GitHub Desktop.
libinet with c++11, some code style fixings and without global variables (first and inacurate refactor with a lot of bugs)
Compile with:
clang++ -std=c++11 -Wall -c -o sockets.o sockets.cpp
clang++ -std=c++11 -Wall -c -o example-socket.o example-socket.cpp
clang++ -std=c++11 -Wall sockets.o example-socket.o -o example-socket
clang++ -std=c++11 -Wall -c -o http.o http.cpp
clang++ -std=c++11 -Wall -c -o example-http.o example-http.cpp
clang++ -std=c++11 -Wall sockets.o http.o example-http.o -o example-http
#include <iostream>
#include <string>
#include <vector>
#include "../src/http.h"
using namespace std;
void
progress(size_t length, size_t received_length) {
cout << (received_length * 100) / length << "%" << endl;
}
int
main(int argc, char *argv[]) {
// Setup the connection.
libinet::http::Request request("www.google.com", 80);
request.add_header("User-Agent", "libinet++/0.1");
request.set_progress_callback(progress); // Optional
libinet::http::ResponsePtr response = request.request("GET", "/", "Test POST body");
cout << response->status_code << " " << response->status_message << endl;
// Print the headers.
for (size_t i = 0; i < response->headers.size(); i++) {
vector<string> header = response->headers.at(i);
cout << header.at(0) << ": " << header.at(1) << endl;
}
// Print body.
cout << endl << response->body << endl;
return 0;
}
#include <iostream>
#include <string>
#include "../src/sockets.h"
using namespace std;
bool
socket_data_callback(string data) {
// If something went wrong while you were parsing return false!
cout << data << endl;
return true;
}
int
main(int argc, char *argv[]) {
libinet::Socket socket("www.google.es", 80, socket_data_callback);
socket.connect();
// Send some information.
socket.send_data("GET / HTTP/1.1\r\n");
socket.send_data("Host: localhost\r\n\r\n");
// Get the response.
socket.receive();
return 0;
}
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include "http.h"
#include "sockets.h"
namespace libinet {
namespace http {
/**
* HTTP_Response class constructor.
*/
Response::Response() {}
void
Response::set_headers(Headers &&headers) {
this->headers = headers;
}
void
Response::set_status(std::string &message, unsigned int code) {
this->status_message = message;
this->status_code = code;
}
void
Response::set_body(std::string &body) {
this->body = body;
}
/**
* HTTP class constructor.
*
* \param _server Server URI.
* \param _port Server port.
*/
Request::Request(std::string server, unsigned int port):
is_set_progress_callback(false), server(server), port(port) {}
/**
* Adds a header to the header list.
*
* \param name Header name.
* \param value Header value.
*/
void
Request::add_header(std::string name, std::string value) {
std::vector<std::string> header = {name, value};
headers.push_back(header);
}
/**
* Request something from a server.
*
* \param location The location of something in a server.
* \return HTTP_Response instance.
*/
ResponsePtr
Request::request(std::string type, std::string location, std::string body) {
this->is_finished = false;
this->length = 0;
ResponsePtr response_ptr(new Response());
Callback my_callback = [this, response_ptr](std::string data) -> bool {
this->raw += data;
if (this->raw.find("\r\n\r\n") == std::string::npos) {
return true;
}
if (this->length == 0) {
size_t cnthead_pos = this->raw.find("Content-Length: ");
if (cnthead_pos != std::string::npos) {
cnthead_pos = cnthead_pos + strlen("Content-Length: ");
std::string str_content_length = this->raw.substr(cnthead_pos);
str_content_length = str_content_length.substr(0, str_content_length.find("\r\n"));
this->length = atoi(str_content_length.c_str());
}
}
std::string body = this->raw.substr(this->raw.find("\r\n\r\n") + 4);
this->received = body.length();
if (this->is_set_progress_callback) {
this->progress_callback(this->length, body.length());
}
if (this->received == this->length and this->received != 0) {
unsigned int status_code = atoi(this->raw.substr(this->raw.find(" ") + 1, 3).c_str());
std::string status_message = this->raw.substr(this->raw.find(" ") + 5);
// Populate the HTTP_Response object.
response_ptr->set_body(body);
response_ptr->set_status(status_message, status_code);
response_ptr->set_headers(this->parse_headers());
this->is_finished = true;
}
return true;
};
// Setup the socket and connect.
Socket socket(server, port, my_callback);
socket.connect();
// Just a normal HTTP request.
socket.send_data(type + " " + location + " HTTP/1.1\r\n");
socket.send_data("Host: " + server + "\r\n");
for (size_t i = 0; i < headers.size(); i++) {
socket.send_data(headers.at(i).at(0) + ": " + headers.at(i).at(1) + "\r\n");
}
if (body.empty()) {
socket.send_data("\r\n");
} else {
std::stringstream stream;
stream << "Content-Length: " << body.length() << "\r\n\r\n";
socket.send_data(stream.str());
socket.send_data(body);
socket.send_data("\r\n\r\n");
}
socket.receive();
// Block until ready
while (!this->is_finished) {
usleep(2000);
}
return response_ptr;
}
/**
* Parse HTTP headers.
*
* \return Vector of string vectors (name, value).
*/
Headers
Request::parse_headers() {
std::string str_headers = this->raw.substr(this->raw.find("\r\n") + 2);
str_headers = str_headers.substr(0, this->raw.find("\r\n\r\n"));
Headers response_headers;
while (str_headers.find("\r\n") != std::string::npos) {
std::string curr_header = str_headers.substr(0, str_headers.find("\r\n"));
// Just making sure...
if (curr_header != "\r\n" && curr_header.find(": ") != std::string::npos) {
std::string name = curr_header.substr(0, curr_header.find(": "));
std::string value = curr_header.substr(curr_header.find(": ") + 2);
std::vector<std::string> header;
header.push_back(name);
header.push_back(value);
response_headers.push_back(header);
}
// Remove the used header.
str_headers = str_headers.substr(str_headers.find("\r\n") + 2);
}
return response_headers;
}
void
Request::set_progress_callback(ProgressCallback callback) {
this->progress_callback = callback;
this->is_set_progress_callback = true;
}
}; };
#ifndef LIBINET_HTTP_H_
#define LIBINET_HTTP_H_
#include <string>
#include <vector>
#include <memory>
#include "sockets.h"
namespace libinet {
namespace http {
typedef std::function<void(size_t, size_t)> ProgressCallback;
typedef std::vector<std::vector<std::string>> Headers;
class Response {
public:
std::string raw;
unsigned int length;
unsigned int status_code;
std::string status_message;
Headers headers;
std::string body;
Response();
void set_headers(Headers &&headers);
void set_body(std::string &);
void set_status(std::string &, unsigned int);
};
typedef std::shared_ptr<Response> ResponsePtr;
class Request {
private:
Socket socket;
ProgressCallback progress_callback;
bool is_set_progress_callback;
bool is_finished;
std::string raw;
unsigned int length;
unsigned int received;
public:
std::string server;
unsigned int port;
Headers headers;
Request();
Request(std::string server, unsigned int port);
void add_header(std::string name, std::string value);
void set_progress_callback(ProgressCallback callback);
Headers parse_headers();
ResponsePtr request(std::string type, std::string location, std::string body = "");
};
// End namespace
}; };
#endif
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "sockets.h"
#define MAXDATASIZE 1024 // Max number of bytes we can get at once.
namespace libinet {
/**
* Constructs an incomplete Socket object.
*/
Socket::Socket() {
this->connected = false;
}
/**
* Constructs the Socket object.
*
* \param _server Server location.
* \param _port Server port.
* \param _handler_callback Callback to parse the received information.
*/
Socket::Socket(std::string server, unsigned int port, Callback callback):
server(server), port(port) {
this->callback = callback;
this->connected = false;
}
/**
* Connects to the server.
*
* \param _server Server location.
* \param _port Server port.
* \param _handler_callback Callback to parse the received information.
*/
void
Socket::connect(std::string server, unsigned int port, Callback callback) {
this->server = server;
this->port = port;
this->callback = callback;
this->connect();
}
/**
* Connects to the server.
*/
void
Socket::connect() {
struct addrinfo hints, *servinfo;
int res;
// Empty the hints struct and setup.
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
// Setup the structs.
char cport[5];
sprintf(cport, "%d", port);
res = getaddrinfo(server.c_str(), cport, &hints, &servinfo);
if (res != 0) {
std::cerr << "getaddrinfo: " << gai_strerror(res) << std::endl;
exit(EXIT_FAILURE);
}
// Setup the socket.
socket_descriptor = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
if (socket_descriptor == -1) {
std::cerr << "Couldn't setup socket." << std::endl;
exit(EXIT_FAILURE);
}
// Connect.
if (::connect(socket_descriptor, servinfo->ai_addr, servinfo->ai_addrlen) == -1) {
close(socket_descriptor);
std::cerr << "Couldn't connect to host." << std::endl;
} else {
this->connected = true;
#ifdef DEBUG
cout << "Connected to \"" << server << ":" << port << "\"" << std::endl;
#endif
// Create the thread that will handle messages from the server
//pthread_create(&thread, NULL, &handle_recv_thread_helper, this);
}
// Free the server information.
freeaddrinfo(servinfo);
}
/**
* Closes the connection if it's connected.
*/
void
Socket::close_connection() {
if (this->connected) {
if (close(socket_descriptor) != 0) {
std::cerr << "An error occurred while trying to close the connection" << std::endl;
exit(EXIT_FAILURE);
}
//pthread_join(thread, NULL);
this->connected = false;
}
}
/**
* Send the data string to the server.
*
* \return Number of bytes sent.
*/
int
Socket::send_data(std::string data) {
const char *buffer = data.c_str();
int len = strlen(buffer);
int bytes_sent = send(socket_descriptor, buffer, len, 0);
if (bytes_sent == -1) {
if (!connected) {
std::cerr << "You need to be connected to send data." << std::endl;
} else {
std::cerr << "An error occurred while trying to send the data" << std::endl;
}
exit(EXIT_FAILURE);
}
#ifdef DEBUG
std::cout << "Sent " << bytes_sent << " bytes: \"" << data << "\"" << std::endl;
#endif
return bytes_sent;
}
/**
* Handles the information that was received from the server.
*/
void
Socket::receive() {
// recv some data.
int numbytes;
char buffer[MAXDATASIZE];
while (true) {
numbytes = recv(socket_descriptor, buffer, MAXDATASIZE - 1, 0);
buffer[numbytes] = '\0';
if (numbytes == 0) {
#ifdef DEBUG
cout << "Connection terminated" << std::endl;
#endif
this->connected = false;
this->close_connection();
return;
}
if (!this->callback(std::string(buffer))) {
connected = false;
close_connection();
return;
}
}
// Check if there was an error
if (numbytes == -1) {
std::cerr << "handle_recv Error" << std::endl;
exit(EXIT_FAILURE);
}
connected = false;
return;
}
// End namespace
};
#ifndef LIBINET_SOCKETS_H_
#define LIBINET_SOCKETS_H_
#include <string>
#include <functional>
namespace libinet {
typedef std::function<bool(std::string)> Callback;
class Socket {
private:
int socket_descriptor;
bool connected;
Callback callback;
std::string server;
unsigned int port;
public:
Socket();
Socket(std::string _server, unsigned int _port, Callback callback);
// Connectors?
void connect();
void connect(std::string server, unsigned int port, Callback callback);
void receive();
int send_data(std::string data);
void close_connection();
};
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment