Last active
December 13, 2015 19:28
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
}; }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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