I'm trying to do an HTTP request using sockets on Linux, and my function works now, but only with simple domains as www.google.com
or webpage.000webhostapp.com
. When I try to add a path and query like webpage.000webhostapp.com/folder/file.php?parameter=value
, it starts failing.
This is my code:
#include <iostream>
#include <cstring>
#include <string>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
int main(){
int socket_desc;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[4096];
std::string url = "www.exampleweb.com/folder/file.php";
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if(socket_desc < 0){
std::cout<<"failed to create socket"<<std::endl;
}
server = gethostbyname(url.c_str());
if(server==NULL){std::cout<<"could Not resolve hostname :("<<std::endl;}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(80);
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
if(connect(socket_desc, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){
std::cout<<"connection failed :("<<std::endl;
}
std::string request = "GET / HTTP/1.1\r\nHost: " url "\r\nConnection: close\r\n\r\n";
if(send(socket_desc, request.c_str(), strlen(request.c_str()) 1, 0) < 0){
std::cout<<"failed to send request..."<<std::endl;
}
int n;
std::string raw_site;
while((n = recv(socket_desc, buffer, sizeof(buffer) 1, 0)) > 0){
int i = 0;
while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r'){
raw_site =buffer[i];
i = 1;
}
}
std::cout<<raw_site<<std::endl;
return 0;
}
Using www.google.com
, it works perfectly for the request.
Using www.example.com/folder/file.php?parameter=value¶meter2=value2
, it fails and returns "could not resolve hostname"
.
Any idea on how to fix it?
I'm not using curl and can't use it for this project.
CodePudding user response:
gethostbyname()
(which BTW is deprecated, you should be using getaddrinfo()
instead) does not work with URLs, only with host names.
Given a URL like http://www.exampleweb.com/folder/file.php?parameter=value¶meter2=value2
, you need to first break it up into its constituent pieces (read RFC 3986), eg:
- the scheme
http
- the host name
www.exampleweb.com
- the resource path
/folder/file.php
- the query
?parameter=value¶meter2=value2
You can then resolve the IP address for the host, connect to that IP address on port 80 (the default port for HTTP) since the URL doesn't specify a different port, and finally send a GET
request for the resource and query.
Also, note that the Host
header in the GET
request must specify only the host name (and port, if different than the default), not the whole URL. And also that HTTP requests are not null-terminated strings, so do not send the null terminator. Read RFC 2616.
Try this instead:
#include <iostream>
#include <cstring>
#include <string>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
int main(){
int socket_desc;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[4096];
std::string host = "www.exampleweb.com";
std::string port = "80";
std::string resource = "/folder/file.php";
std::string query = "?parameter=value¶meter2=value2";
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc < 0){
std::cout << "failed to create socket" << std::endl;
return 0;
}
server = gethostbyname(host.c_str());
if (server == NULL){
std::cout << "could Not resolve hostname :(" << std::endl;
close(socket_desc);
return 0;
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(std::stoi(port));
bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
if (connect(socket_desc, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){
std::cout << "connection failed :(" << std::endl;
close(socket_desc);
return 0;
}
std::string request = "GET " resource query " HTTP/1.1\r\nHost: " host "\r\nConnection: close\r\n\r\n";
if (send(socket_desc, request.c_str(), request.size(), 0) < 0){
std::cout << "failed to send request..." << std::endl;
close(socket_desc);
return 0;
}
int n;
std::string raw_site;
while ((n = recv(socket_desc, buffer, sizeof(buffer), 0)) > 0){
raw_site.append(buffer, n);
}
close(socket_desc);
std::cout << raw_site << std::endl;
return 0;
}