C++ File Download - DNS HTTP GET
#include <stdio.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <string>
#include <iostream>
#include <fstream>
using std::string;
using std::vector;
using std::cout;
using std::endl;
using std::ofstream;
using std::stringstream;
vector<string> dns_lookup(const string &host_name, int ipv=4); //ipv: default=4
bool is_ipv6_address(const string& str);
bool is_ipv4_address(const string& str);
int socket_connect(string ip_address, int port);
int http_get(const string& request, const string& ip_address, int port, const string& fname);
void download(const string& url, const string& filename);
int main(int argc, char* argv[])
{
download("http://code.jquery.com/jquery-1.11.1.js", "./jquery-1.11.1.js");
return 0;
}
//----------------------------------------------------------------------
vector<string> dns_lookup(const string &host_name, int ipv) //ipv: default=4
{
vector<string> output;
struct addrinfo hints, *res, *p;
int status, ai_family;
char ip_address[INET6_ADDRSTRLEN];
ai_family = ipv==6 ? AF_INET6 : AF_INET; //v4 vs v6?
ai_family = ipv==0 ? AF_UNSPEC : ai_family; // AF_UNSPEC (any), or chosen
memset(&hints, 0, sizeof hints);
hints.ai_family = ai_family;
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(host_name.c_str(), NULL, &hints, &res)) != 0) {
//cerr << "getaddrinfo: "<< gai_strerror(status) << endl;
return output;
}
//cout << "DNS Lookup: " << host_name << " ipv:" << ipv << endl;
for(p = res;p != NULL; p = p->ai_next) {
void *addr;
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
}
// convert the IP to a string
inet_ntop(p->ai_family, addr, ip_address, sizeof ip_address);
output.push_back(ip_address);
}
freeaddrinfo(res); // free the linked list
return output;
}
//----------------------------------------------------------------------
bool is_ipv6_address(const string& str)
{
struct sockaddr_in6 sa;
return inet_pton(AF_INET6, str.c_str(), &(sa.sin6_addr))!=0;
}
//----------------------------------------------------------------------
bool is_ipv4_address(const string& str)
{
struct sockaddr_in sa;
return inet_pton(AF_INET, str.c_str(), &(sa.sin_addr))!=0;
}
//----------------------------------------------------------------------
int socket_connect(string ip_address, int port)
{
int err=-1,sd=-1;
struct sockaddr_in sa;
memset (&sa, '\0', sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr (ip_address.c_str()); /* Server IP */
sa.sin_port = htons (port); /* Server Port number */
sd = ::socket(AF_INET, SOCK_STREAM, 0);
if (sd)
{
err = ::connect(sd, (struct sockaddr*) &sa, sizeof(sa));
}
if (err!=-1)//success
{
return sd;
}
cout << "socket connect error: " << endl;
return -1;
//if (errno==EINPROGRESS) { return 0; }//errno is a global set by connect
}
//----------------------------------------------------------------------
void download(const string& url, const string& filename)
{
int ipv,r,port;
string protocol, domain, path, query, url_port;
vector<string> ip_addresses;
int offset = 0;
size_t pos1,pos2,pos3,pos4;
offset = offset==0 && url.compare(0, 8, "https://")==0 ? 8 : offset;
offset = offset==0 && url.compare(0, 7, "http://" )==0 ? 7 : offset;
pos1 = url.find_first_of('/', offset+1 );
path = pos1==string::npos ? "" : url.substr(pos1);
domain = string( url.begin()+offset, pos1 != string::npos ? url.begin()+pos1 : url.end() );
path = (pos2 = path.find("#"))!=string::npos ? path.substr(0,pos2) : path;
url_port = (pos3 = domain.find(":"))!=string::npos ? domain.substr(pos3+1) : "";
domain = domain.substr(0, pos3!=string::npos ? pos3 : domain.length());
protocol = offset > 0 ? url.substr(0,offset-3) : "";
query = (pos4 = path.find("?"))!=string::npos ? path.substr(pos4+1) : "";
path = pos4!=string::npos ? path.substr(0,pos4) : path;
if (query.length()>0)
{
path.reserve( path.length() + 1 + query.length() );
path.append("?").append(query);
}
if (url_port.length()==0 && protocol.length()>0)
{
url_port = protocol=="http" ? "80" : "443";
}
if (domain.length()>0 && !is_ipv6_address(domain))
{
if (is_ipv4_address(domain))
{
ip_addresses.push_back(domain);
}
else //if (!is_ipv4_address(domain))
{
ip_addresses = dns_lookup(domain, ipv=4);
}
}
if (ip_addresses.size()>0)
{
stringstream(url_port) >> port;//string to int
stringstream request;
request << "GET " << path << " HTTP/1.1\r\n";
request << "Host: " << domain << "\r\n\r\n";
for(int i=0,r=0, ix=ip_addresses.size(); i<ix && r==0; i++)
{
r = http_get(request.str(), ip_addresses[i], port, filename);
}
}
}
//----------------------------------------------------------------------
string header_value(const string& full_header, const string& header_name)
{
size_t pos = full_header.find(header_name);//case sensitive but probably shouldn't be
string r;
if (pos!=string::npos)
{
size_t begin = full_header.find_first_not_of(": ", pos + header_name.length());
size_t until = full_header.find_first_of("\r\n\t ", begin + 1);
if (begin!=string::npos && until!=string::npos)
{
r = full_header.substr(begin,until-begin);
}
}
return r;
}
//----------------------------------------------------------------------
int http_get(const string& request, const string& ip_address, int port, const string& fname)
{
stringstream header;
char delim[] = "\r\n\r\n";
char buffer[16384];
int sd, bytes_received=-1,bytes_sofar=0,bytes_expected=-1,i,state=0;
ofstream fd(fname.c_str());
if (fd.good() && (sd = socket_connect(ip_address, port)) )
{
::send(sd, request.c_str(), request.length(), 0);
while (bytes_sofar!=bytes_expected && (bytes_received = ::recv(sd, buffer, sizeof(buffer), 0))>0)
{
if (state<sizeof(delim)-1)//read header
{
for(i=0; i<bytes_received && state<sizeof(delim)-1; i++)
{
header << buffer[i];
state = buffer[i]==delim[state] ? state+1 : 0;
}
bytes_received = state==sizeof(delim)-1 ? bytes_received-i : bytes_received;
}
if (bytes_expected==-1 && state==sizeof(delim)-1)//parse header
{
bytes_expected=-2;
string h = header.str();
stringstream(header_value(h, "Content-Length"))>>bytes_expected;
}
if (state==sizeof(delim)-1)//read body
{
bytes_sofar+=bytes_received;
fd.write(buffer, bytes_received);
}
}
//cout << header.str() << endl;//OUTPUT header
::close(sd);
fd.close();
}
return bytes_sofar;
}
//----------------------------------------------------------------------code snippets are licensed under Creative Commons CC-By-SA 3.0 (unless otherwise specified)
|
|
Toto
on
2017-08-21 15:23:43
Bonjour,
Merci pour ce code... |
|