Mi clase HTTP + winsock peticion HTTP ejemplo

Iniciado por patilanz, 26 Diciembre 2014, 18:08 PM

0 Miembros y 1 Visitante están viendo este tema.

patilanz

Hola acabo de hacer una clase para organizar mas o menos el header de una peticion HTTP. Como esto:

CitarHTTP/1.1 200 OK\r\n
Date: Mon, 23 May 2005 22:38:34 GMT\r\n
Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)\r\n
Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT\r\n
ETag: "3f80f-1b6-3e1cb03b"\r\n
Content-Type: text/html; charset=UTF-8\r\n
Content-Length: 131\r\n
Connection: close\r\n
\r\n

HTTP_class.hpp
Código (cpp) [Seleccionar]
#ifndef HTTP_CLASS_HPP
#define HTTP_CLASS_HPP

#include <string>
#include <ctime>
#include <vector>

using namespace std;


//CLASS STATE
#define SETUP_OK 1
#define SETUP_NEED 2
#define INVALID_HEADER 3


//HTTP CODES

//Informational
#define H_Continue 100
#define H_Switching_Protocols 101
#define H_Processing 102

//Success
#define H_OK 200
#define H_Created 201
#define H_Accepted 202
#define H_Non_Authoritative_Information 203
#define H_No_Content 204
#define H_Reset_Content 205
#define H_Partial_Content 206
#define H_Multi_Status 207
#define H_Already_Reported 208
#define H_IM_Used 226

//Redirection
#define H_Multiple_Choices 300
#define H_Moved_Permanently 301
#define H_Found 302
#define H_See_Other 303
#define H_Not_Modified 304
#define H_Use_Proxy 305
#define H_Switch_Proxy 306
#define H_Temporary_Redirect 307
#define H_Permanent_Redirect 308

//Client Error
#define H_Bad_Request 400
#define H_Unauthorized 401
#define H_Payment_Required 402
#define H_Forbidden 403
#define H_Not_Found 404
#define H_Method_Not_Allowed 405
#define H_Not_Acceptable 406
#define H_Proxy_Authentication_Required 407
#define H_Request_Timeout 408
#define H_Conflict 409
#define H_Gone 410
#define H_Length_Required 411
#define H_Precondition_Failed 412
#define H_Request_Entity_Too_Large 413
#define H_Request_URI_Too_Long 414
#define H_Unsupported_Media_Type 415
#define H_Requested_Range_Not_Satisfiable 416
#define H_Expectation_Failed 417
#define H_Im_a_teapot 418
#define H_Authentication_Timeout 419
#define H_Method_Failure 420
#define Enhance_Your_Calm 420
#define H_UnprocessableEntity 422
#define H_Locked 423
#define H_Failed_Dependency 424
#define H_Upgrade_Required 426
#define H_Precondition_Required 428
#define H_Too_Many_Requests 429
#define H_Request_Header_Fields_Too_Large 431
#define H_Login_Timeout 440
#define H_No_Response 444
#define H_Retry_With 449
#define H_Blocked_by_Windows_Parental_Controls 450
#define H_Unavailable_For_Legal_Reasons 451
#define H_Redirect 494
#define H_Request_Header_Too_Large 495
#define H_Cert_Error 496
#define H_No_Cert 497
#define H_HTTP_to_HTTPS 498
#define H_Token_expired_invalid 499
#define H_Client_Closed_Request 499
#define H_Token_required 499



//class ContentType{
//public:
// string type;
// string charset;
//};


class HTTP{
public:
HTTP();
HTTP(string header);
bool setup(string header);
int Code();//HTTP status
const int State();//class status
tm Date();
string Server();
tm Last_Modified();
string ETag();
string Content_Type();
int Content_Lenght();
string Connection();

string Other(string name);
vector<string> Other(int i);

//helpful functions
double HTTPVersion();
int Size();//Size of fields


private:
int state;//Already setup?

int code;
tm date;
string server;
tm last_modified;
string etag;
string content_type;
int content_length;
string header;
string connection;

vector < vector<string> > all; //all fields

//helpful data
double version;

//Config functions
tm getDate(string date);
void defaultConfig();
};


vector<string> split(string full, string part); //helpful function


#endif


HTTP_class.cpp
Código (cpp) [Seleccionar]
#include "http_class.hpp"
#include <regex>

HTTP::HTTP(){
defaultConfig();
}

HTTP::HTTP(string header){
defaultConfig();
setup(header);
}


string HTTP::Other(string name){
for (int i = 0; i < all.size(); i++){
if (all[i][0] == name){
return all[i][1];
}
}
return "";
}
vector<string> HTTP::Other(int i){
if (i < all.size())
return all[i];
else
return vector<string>();
}


bool HTTP::setup(string header){
vector<string> parts = split(header, "\r\n");

//all setup
for (int i = 1; i < parts.size(); i++){
all.push_back(split(parts[i],": " ));
}
all.pop_back();          // remove last 2 \r\n of the end of http protocol
all.pop_back();          //
//end
if (all.size() < 1){ //Nothing founded
state = INVALID_HEADER;
return false;
}

//http setup
string v = parts[0].substr(5, 3);
version = atof(v.c_str());
code = atoi(parts[0].substr(8, 4).c_str());
//end
date = getDate(Other("Date"));
server = Other("Server");
last_modified = getDate(Other("Last-Modified"));
etag = Other("ETag");
content_type = Other("Content-Type");//Mejores en 2.0
content_length = atoi(Other("Content-Length").c_str());
connection = Other("Connection");
state = SETUP_OK;
return true;
}


int HTTP::Code(){
return code;
}

const int HTTP::State(){
return state;
}

tm HTTP::Date(){
//Example of check
if (date.tm_year != 0 && state == SETUP_OK){ // if == 0 no date in header
return date;
}
}
string HTTP::Server(){
return server;
}
tm HTTP::Last_Modified(){
return last_modified;
}
string HTTP::ETag(){
return etag;
}
string HTTP::Content_Type(){
return content_type;
}
int HTTP::Content_Lenght(){
return content_length;
}
string HTTP::Connection(){
return connection;
}


//Helpful functions
double HTTP::HTTPVersion(){
return version;
}
int HTTP::Size(){
return all.size();
}



tm HTTP::getDate(string _date){ //Get date in tm format for example in Date or Last-Modified
tm date = tm();
if (_date.size() > 0){
const static string months[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
smatch match;
if (_date.find("GMT") != -1){ // New format
if (regex_search(_date, match, regex("^\\w{3,9},\\s(\\d{2})[\\s\\-](\\w{3})[\\s\\-](\\d{2,4})\\s(\\d{2}):(\\d{2}):(\\d{2}) GMT$"))){
date.tm_mday = atoi(match[1].str().c_str());
int month;
for (int i = 0; i < sizeof(months); i++){
if (months[i] == match[2].str()){
month = i;
break;
}
}
date.tm_mon = month;
int year = atoi(match[3].str().c_str());
if (year > 1900)
year -= 1900;
date.tm_year = year;
date.tm_hour = atoi(match[4].str().c_str());
date.tm_min = atoi(match[5].str().c_str());
date.tm_sec = atoi(match[6].str().c_str());
}
}
else{ //Old ANSI format
if (regex_search(_date, match, regex("^\\w{3} (\\w{3})  (\\d{1,2}) (\\d{2}):(\\d{2}):(\\d{2}) (\\d{4})$"))){
int month;
for (int i = 0; i < sizeof(months); i++){
if (months[i] == match[1].str()){
month = i;
break;
}
}
date.tm_mday = atoi(match[2].str().c_str());
date.tm_hour = atoi(match[3].str().c_str());
date.tm_min = atoi(match[4].str().c_str());
date.tm_sec = atoi(match[5].str().c_str());
date.tm_year = atoi(match[6].str().c_str());
}
}
}
return date;
}


void HTTP::defaultConfig(){ //Set some values help to don't check state in every function
state = SETUP_NEED;
code = 0;
version = 0;
content_length = 0;

}



//split string in vector

vector<string> split(string full, string part){
vector<string> parts;
int last = 0;
int p;
while ((p = full.find(part, last)) != -1){
parts.push_back(full.substr(last, p - last));
last = p + part.size();
}
parts.push_back(full.substr(last, full.size()));
return parts;
}


Ejemplo conexión http con win sockets
Código (cpp) [Seleccionar]
#include <iostream>
#include <string>
#include <vector>
#include <WinSock2.h>
#include "http_class.hpp"

#pragma comment(lib,"ws2_32.lib")

using namespace std;

#define SIZE 1024

int main(){
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
char buffer[SIZE];



vector<vector<string>>links = { //Random urls
{ "foro.elhacker.net", "/throw 403 error" },
{ "foro.elhacker.net", "/throw_302_error" },
{ "www.google.es", "/" },
{ "foro.elhacker.net", "/ingenieria_inversa-b26.0/" }
};


hostent * host;
in_addr ip;
sockaddr_in data;
string request;

for (int i = 0; i < links.size(); i++){
host = gethostbyname(links[i][0].c_str());
ip.s_addr = *(long * )host->h_addr_list[0];


SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET){
cout << "Invalid socket";
return 1;
}

memset(&data, 0, sizeof(sockaddr_in));
data.sin_addr = ip;
data.sin_family = AF_INET;
data.sin_port = htons(80);

if(connect(sock, (sockaddr*)&data, sizeof(sockaddr_in))){
cout << "Connection with " << inet_ntoa(ip) << " failed.";
return 1;
}


request = "GET " + links[i][1] + " HTTP/1.0\r\n";
request += "Host: " + links[i][0] + "\r\n";
request += "\r\n";

if (send(sock, request.c_str(), request.size(), 0) != request.size()){
cout << "Send error";
return 1;
}

//Accept
char buffer[SIZE];
int lastBytes = SIZE - 1;//bytes recerved
string response;
while (lastBytes > 0){
lastBytes = recv(sock, buffer, sizeof(buffer), 0);
if (lastBytes > 0)
response += string(buffer).substr(0, lastBytes);
}
string header = response.substr(0, response.find("\r\n\r\n") + 4);

HTTP http(header);
//    or
//http.setup(header);
cout << "Host: " << links[i][0] << endl;
cout << "HTTP version: " << http.HTTPVersion() << endl;
if (http.Code() == H_Not_Found){
cout << "Path: " << links[i][1] << " wasn't founded";
}
else if (http.Code() == H_Forbidden){
cout << "Server Forbidden message";
if (http.Other("Location").size() > 0){//Location field
cout << " to " << http.Other("Location");
}
}
else{
cout << "Status code: " << http.Code();
}
cout << "\n\nAll fields: \n";
for (int i = 0; i < http.Size(); i++){
cout << http.Other(i)[0] << ": " << http.Other(i)[1] << endl;
}
cout << endl << endl;

}


fflush(stdin);
getchar();
return 0;
}


Descargar ejemplo: http://pruebasdephp.hol.es/things/HTTP_class.rar
Probado en visual studio 2013



Si me pueden dar ideas de que puedo mejorar o si tienen alguna duda  :D

Saludos

ivancea96

Ahora haz para que todo el proceso de winsock lo lleve la clase (u otra clase/función aparte)

En vez de define, tal vez te interesa más hacer un enum :o

patilanz

Hola gracias por tu respuesta. Ya creo que tengo lo que me dijiste.

http_class.cpp
Código (cpp) [Seleccionar]
//HTTP_class v2.0
//Autor: patilanz

#ifndef HTTP_CLASS_HPP
#define HTTP_CLASS_HPP

#include <string>
#include <ctime>
#include <vector>
#include "http_config.hpp"

using namespace std;


//CLASS STATE
enum class_state{ SETUP_OK, SETUP_NEED, INVALID_HEADER, INVALID_URL, SOCK_ERROR, CONNECTION_TIME_OUT, CONNECTION_REFUSED, UNKNOW_ERROR, SEND_ERROR, INVALID_RESPONSE };

//HTTP CODES

//Informational
#define H_Continue 100
#define H_Switching_Protocols 101
#define H_Processing 102
//... no lo pongo para no ocupar espacio en el foro. Es como antes



class HTTP{
public:
HTTP();
HTTP(string url, string request = "", int port = 80);
HTTP(string url, HTTP_config config, int port = 80);

bool get(string url, string request = "", int port = 80);
bool get(string url, HTTP_config config, int port = 80);
bool get(string request = "", int port = 80);

bool setHeader(string header);
void setContent(string content);
bool setUrl(string url);

const int State();//class status

string Header();
string Content();

int Code();//HTTP status
tm Date();
string Server();
tm Last_Modified();
string ETag();
string Content_Type();
int Content_Lenght();
string Connection();

string getHost();
string getPath();
string getUrl();



string Other(string name);
vector<string> Other(int i);

//helpful functions
double HTTPVersion();
int Size();//Size of fields


private:
int state;//Already setup?

int code;
tm date;
string server;
tm last_modified;
string etag;
string content_type;
int content_length;
string connection;
double version;

string host;
string path;
string url;

string header;
string content;

vector < vector<string> > all; //all fields

const int recv_size = 1024;


//helpful functions
void WSAStart();
vector<string> split(string full, string part);

//Config functions
tm getDate(string date);
void defaultConfig();
};



#endif


http_class.cpp
Código (cpp) [Seleccionar]
#include "http_class.hpp"
#include <regex>
#include <WinSock2.h>

HTTP::HTTP(){
defaultConfig();
}

HTTP::HTTP(string url, string request, int port){
defaultConfig();
get(url, request, port);
}

HTTP::HTTP(string url, HTTP_config config, int port){
get(url, config, port);
}




string HTTP::Other(string name){
for (int i = 0; i < all.size(); i++){
if (all[i][0] == name){
return all[i][1];
}
}
return "";
}
vector<string> HTTP::Other(int i){
if (i < all.size())
return all[i];
else
return vector<string>();
}


bool HTTP::get(string _url, string _request, int port){
smatch match;
if (regex_search(_url, match, regex("(http|https)?:?(\\/\\/)?([^/]{5,})([^\\t\\n\\v\\r]*)"))){
url = _url;
host = match[3];
path = match[4];
if (path.size() < 1)
path = "/";

hostent * _host;
in_addr ip;
sockaddr_in data;
string request;


_host = gethostbyname(host.c_str());
ip.s_addr = *(long*)_host->h_addr_list[0];

SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET){
state = SOCK_ERROR;
return false;
}
memset(&data, 0, sizeof(sockaddr_in));
data.sin_addr = ip;
data.sin_family = AF_INET;
data.sin_port = htons(port);

if (connect(sock, (sockaddr*)&data, sizeof(sockaddr_in))){
if (WSAGetLastError() == WSAETIMEDOUT){
state = CONNECTION_TIME_OUT;
}
else if (WSAGetLastError() == WSAECONNREFUSED){
state = CONNECTION_REFUSED;
}
else{
state = UNKNOW_ERROR;
}
return false;
}
if (_request.size() > 0)
request = _request;
else{
request += "GET " + path + " HTTP/1.1\r\n";
request += "Host: " + host + "\r\n";
request += "Connection: close\r\n";
request += "\r\n";
}
if(send(sock, request.c_str(), request.size(), 0) != request.size()){
state = SEND_ERROR;
return false;
}
//Accept
char *buffer = new char[recv_size]; // No se me ocurrio otra manera de usar una variable const int de la clase
int lastBytes = recv_size - 1;
string response;

while (lastBytes > 0){
lastBytes = recv(sock, buffer, recv_size, 0);
if (lastBytes > 0)
response += string(buffer).substr(0, lastBytes);
}
if (response.find("\r\n\r\n") == -1){
state = INVALID_RESPONSE;
return false;
}
setHeader(response.substr(0, response.find("\r\n\r\n") + 4));
setContent(response.substr(response.find("\r\n\r\n") + 4, response.size()));





delete[recv_size] buffer;
return true;

}else{
state = INVALID_URL;
return false;
}
}

bool HTTP::get(string url,HTTP_config config, int port){
setUrl(url);
string request = config.getGet() + "\r\n";
for (int i = 0; i < config.size(); i++){
request += config[i][0] + ": " + config[i][1] + "\r\n";
}
request += "\r\n";
return get(request, port);
}

bool HTTP::get(string request, int port){
return get(url, request, port);
}



bool HTTP::setHeader(string _header){
vector<string> parts = split(_header, "\r\n");

//all setup
for (int i = 1; i < parts.size(); i++){
all.push_back(split(parts[i],": " ));
}
all.pop_back();          // remove last 2 \r\n of the end of http protocol
all.pop_back();          //
//end
if (all.size() < 1){ //Nothing founded
state = INVALID_HEADER;
return false;
}
header = _header;


//http setup
string v = parts[0].substr(5, 3);
version = atof(v.c_str());
code = atoi(parts[0].substr(8, 4).c_str());
//end
date = getDate(Other("Date"));
server = Other("Server");
last_modified = getDate(Other("Last-Modified"));
etag = Other("ETag");
content_type = Other("Content-Type");//Mejores en 2.0
content_length = atoi(Other("Content-Length").c_str());
connection = Other("Connection");
state = SETUP_OK;
return true;
}

void HTTP::setContent(string c){
content = c;
}

bool HTTP::setUrl(string _url){
if (regex_match(_url, regex("(http|https)?:?(\\/\\/)?([^/]{5,})([^\\t\\n\\v\\r]*)"))){
url = _url;
return true;
}
return false;
}

int HTTP::Code(){
return code;
}

const int HTTP::State(){
return state;
}

tm HTTP::Date(){
//Example of check
if (date.tm_year != 0 && state == SETUP_OK){ // if == 0 no date in header
return date;
}
}
string HTTP::Server(){
return server;
}
tm HTTP::Last_Modified(){
return last_modified;
}
string HTTP::ETag(){
return etag;
}
string HTTP::Content_Type(){
return content_type;
}
int HTTP::Content_Lenght(){
return content_length;
}
string HTTP::Connection(){
return connection;
}


//Gets

string HTTP::getHost(){
return host;
}
string HTTP::getPath(){
return path;
}
string HTTP::getUrl(){
return url;
}


string HTTP::Header(){
return header;
}
string HTTP::Content(){
return content;
}



//Helpful functions
double HTTP::HTTPVersion(){
return version;
}
int HTTP::Size(){
return all.size();
}





tm HTTP::getDate(string _date){ //Get date in tm format for example in Date or Last-Modified
tm date = tm();
if (_date.size() > 0){
const static string months[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
smatch match;
if (_date.find("GMT") != -1){ // New format
if (regex_search(_date, match, regex("^\\w{3,9},\\s(\\d{2})[\\s\\-](\\w{3})[\\s\\-](\\d{2,4})\\s(\\d{2}):(\\d{2}):(\\d{2}) GMT$"))){
date.tm_mday = atoi(match[1].str().c_str());
int month;
for (int i = 0; i < sizeof(months); i++){
if (months[i] == match[2].str()){
month = i;
break;
}
}
date.tm_mon = month;
int year = atoi(match[3].str().c_str());
if (year > 1900)
year -= 1900;
date.tm_year = year;
date.tm_hour = atoi(match[4].str().c_str());
date.tm_min = atoi(match[5].str().c_str());
date.tm_sec = atoi(match[6].str().c_str());
}
}
else{ //Old ANSI format
if (regex_search(_date, match, regex("^\\w{3} (\\w{3})  (\\d{1,2}) (\\d{2}):(\\d{2}):(\\d{2}) (\\d{4})$"))){
int month;
for (int i = 0; i < sizeof(months); i++){
if (months[i] == match[1].str()){
month = i;
break;
}
}
date.tm_mday = atoi(match[2].str().c_str());
date.tm_hour = atoi(match[3].str().c_str());
date.tm_min = atoi(match[4].str().c_str());
date.tm_sec = atoi(match[5].str().c_str());
date.tm_year = atoi(match[6].str().c_str());
}
}
}
return date;
}


void HTTP::defaultConfig(){ //Set some values help to don't check state in every function
WSAStart();
state = SETUP_NEED;
code = 0;
version = 0;
content_length = 0;

}



void HTTP::WSAStart(){
if (socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED){
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
}
}








//split string in vector

vector<string> HTTP::split(string full, string part){
vector<string> parts;
int last = 0;
int p;
while ((p = full.find(part, last)) != -1){
parts.push_back(full.substr(last, p - last));
last = p + part.size();
}
parts.push_back(full.substr(last, full.size()));
return parts;
}



http_config.hpp
Código (cpp) [Seleccionar]
#ifndef HTTP_CONFIG_HPP
#define HTTP_CONFIG_HPP

#include <string>
#include <vector>
using namespace std;

class HTTP_config{
public:
HTTP_config();
HTTP_config(string get);
HTTP_config(string get, vector < vector <string > >);
bool add(string name, string value);
bool remove(string name);
bool edit(string name, string value);
const int size();
void setGet(string);
string getGet();

vector<string> operator [](const int);
private:
vector < vector<string> > data;
string get;
};



#endif


http_config.cpp
Código (cpp) [Seleccionar]
#include "http_config.hpp"

HTTP_config::HTTP_config(){}
HTTP_config::HTTP_config(string _get){
get = _get;
}
HTTP_config::HTTP_config(string _get, vector < vector<string> > _data){
get = _get;
data = _data;
}



bool HTTP_config::add(string name, string value){
for (int i = 0; i < data.size(); i++){
if (data[i][0] == name)
return false;
}
vector < string > a = {
name, value
};
data.push_back(a);
return true;
}
bool HTTP_config::remove(string name){
for (int i = 0; i < data.size(); i++){
if (data[i][0] == name){
data.erase(data.begin() + i);
return true;
}
}
return false;
}
bool HTTP_config::edit(string name, string value){
for (int i = 0; i < data.size(); i++){
if (data[i][0] == name){
data[i][1] = value;
return true;
}
}
return false;
}

vector<string> HTTP_config::operator[](const int i){
if (i < data.size()){
return data[i];
}
else{
return vector<string>();
}
}


const int HTTP_config::size(){
return data.size();
}

string HTTP_config::getGet(){
return get;
}



main.cpp (Ejemplo)
Código (cpp) [Seleccionar]
#include <iostream>
#include <WinSock2.h>
#include <string>
#include <vector>
#include <regex>
#include "http_class.hpp"

using namespace std;

#pragma comment(lib,"ws2_32.lib")

int main(){
HTTP http;
http.setUrl("http://foro.elhacker.net/programacion_cc/mi_clase_http_winsock_peticion_http_ejemplo-t427014.0.html");
http.get();
cout << http.Code() << endl << endl;


HTTP http2("http://www.cplusplus.com/reference/");
cout << http2.getHost() <<  ": HTTP/" << http2.HTTPVersion() << endl << endl;

HTTP_config config("GET / HTTP/1.0", {
{ "Host", "www.google.es" },
{ "Accept-Encoding", "gzip, deflate, sdch" },
{ "Accept-Language", "es,en;q=0.8" }
});
HTTP http3("www.google.es", config);
cout << http3.Header();
getchar();
fflush(stdin);

HTTP http4("www.google.es", "", 21);
if (http4.State() == CONNECTION_TIME_OUT){
cout << "No answer from " << http4.getHost();
}
getchar();
return 0;
}


Descarga: http://pruebasdephp.hol.es/things/HTTP_class%20V2.0.rar


Cambie solo los class state por enums porque los otros eran muchos y no me apetece estar escribiendo uno a uno y mirar todos los números otra vez.


Algún fallo o recomendación ? Esta bien hecha?

ivancea96

Sobre el envío, ten en cuenta el envío Chuncked :O