Skip to content

Instantly share code, notes, and snippets.

@nolim1t
Created June 10, 2009 03:14
Show Gist options
  • Save nolim1t/126991 to your computer and use it in GitHub Desktop.
Save nolim1t/126991 to your computer and use it in GitHub Desktop.
HTTP Request in C using low level write to socket functionality
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
int socket_connect(char *host, in_port_t port){
struct hostent *hp;
struct sockaddr_in addr;
int on = 1, sock;
if((hp = gethostbyname(host)) == NULL){
herror("gethostbyname");
exit(1);
}
bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&on, sizeof(int));
if(sock == -1){
perror("setsockopt");
exit(1);
}
if(connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) == -1){
perror("connect");
exit(1);
}
return sock;
}
#define BUFFER_SIZE 1024
int main(int argc, char *argv[]){
int fd;
char buffer[BUFFER_SIZE];
if(argc < 3){
fprintf(stderr, "Usage: %s <hostname> <port>\n", argv[0]);
exit(1);
}
fd = socket_connect(argv[1], atoi(argv[2]));
write(fd, "GET /\r\n", strlen("GET /\r\n")); // write(fd, char[]*, len);
bzero(buffer, BUFFER_SIZE);
while(read(fd, buffer, BUFFER_SIZE - 1) != 0){
fprintf(stderr, "%s", buffer);
bzero(buffer, BUFFER_SIZE);
}
shutdown(fd, SHUT_RDWR);
close(fd);
return 0;
}
@sebitoelcheater
Copy link

nice work! how can I go to for example, localhost:3000/something ?

@TylerTemp
Copy link

@sebitoelcheater

gcc socket.c -o soc
./soc https://www.google.com 80

@dimuthnc
Copy link

dimuthnc commented Dec 1, 2016

I tried to call localhost:8080/services from this. It fails in every way. Can you please help me?

@fxfactorial
Copy link

@dimuthnc change the code to give 127.0.0.1 for whenever host is localhost

@Matrixbirds
Copy link

why use PF_INET for create sock.. addr structure be set by AF_INET..

@intlr
Copy link

intlr commented Feb 28, 2017

@sebitoelcheater and @dimuthnc you need to modify the request header https://gist.github.com/nolim1t/126991#file-socket-c-L53 to something such as GET /something HTTP/1.1\r\n, maybe create a #define if you want to use it everytime OR change the code so it accept a third argument (or parse it from the complete URI): the suffix URI - the requested page.

The GET /\r\n in this Gist will ask for the index page (/) if any.

Reminder: a minimal header may look like:

GET /something HTTP/1.1\r\n
Host: www.42.fr\r\n
\r\n

which, if successfully sent, will trigger a request to a web server located at www.42.fr to get the /something page using the GET HTTP method. Note the double \r\n line ending, ending the header.

I hope this will help you!
Happy coding everyone

@minhnv-viosoft
Copy link

I've used your code to get a text file ( about 70KB)
It run through the while loop about 25 times,
but the last loop it seem very slow , I think it's been blocking somewhere ( maybe in read function). please help me to explain .

example the file :
http://super-pomoyka.us.to//trash/ttv-list/ttv.m3u

@RossignolVincent
Copy link

Hello, i try to run this code on the Star Wars API (https://swapi.co/) but it doesn't work. I've tried multiple configurations :

./api swapi.co/ 80
gethostbyname: Unknown host
./api http://swapi.co/api/people/1/ 80
gethostbyname: Unknown host

Even with you example with google.com, i've got the same error message. Do you know why ?

@Cannedfood
Copy link

@RossignolVincent Have you tried www.swapi.co?

@mudiadamz
Copy link

what if the response is 302 redirection,
how to make a request to new location?

@ajvuik
Copy link

ajvuik commented Sep 19, 2017

Thank for this. I used this in conjuction with Arduino Json to get the local weather from http://openweathermap.org. Works like a treat 👍

@wb-towa
Copy link

wb-towa commented Sep 30, 2017

gethostbyname() isn't very futureproof. gethostbyname2() will at least let you opt for ipv4 or ipv6

As alr-lab pointed out the headers are incomplete.

I think most of the problems people are experiencing is down to not understanding the code (i.e. including the http and ending slash rather than just www.google.com) so it may have been nicer to include some examples or comments. Of course this is 8 years old too so understandably it may not hold up as well now.

It is a good pointer in the right direction but learners will need to work on it bit.

@hebrerillo
Copy link

hebrerillo commented Nov 26, 2017

When I try to get this url "http://www.cplusplus.com/" the webserver performs a 301 redirection to the same url.

Why? What am I doing wrong?

This is the command line I am executing:

./a.out www.cplusplus.com 80

Thanks a lot!

@hebrerillo
Copy link

Hi everyone!

I found the solution, the get request should be like this :

"GET / HTTP/1.0\r\nHost: www.cplusplus.com\r\n\r\n\r\n"

All the best!

Copy link

ghost commented Jan 31, 2019

It looks like the GET request is missing a second \r\n. HTTP Requests require a blank line at the end. If you do a raw nc to a web server, and send "GET /" and press enter, you wont get a response. Once hitting enter again, however, you will get a response. This is due to that second \r\n being needed. write(fd, "GET /\r\n", strlen("GET /\r\n")); // write(fd, char[], len); should actually be write(fd, "GET /\r\n\r\n", strlen("GET /\r\n\r\n")); // write(fd, char[], len);

@vaaino
Copy link

vaaino commented Feb 9, 2019

This doesn't work on Mac. I keep getting an error:

Undefined symbols for architecture x86_64:
  "_copy", referenced from:
      _socket_connect in msgario-a206e2.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

@vaaino
Copy link

vaaino commented Feb 10, 2019

This code snippet should use bcopy() instead of copy(). copy() doesn't work.

@SirDrope
Copy link

try with -> ./soc www.google.com 80

@Andermutu
Copy link

Hi;

I am trying to send a Telegram message using HTTP GET. However, I don't know how to pass the required parameters. I have try this:

"GET / HTTP/1.1\r\nHost: https://api.telegram.org/botmybottokenhere/sendMessage?chat_id=mychatidhere&text=mymessagehere\r\nConnection: close\r\n\r\n"

but does not work. I get a 400 bad request error. But if I copy that url and paste it in Chrome the message is sent, so the url is correct. It also works in Python3.7.

Thanks.

@Penniman
Copy link

Penniman commented Nov 12, 2019

I am trying to send a Telegram message using HTTP GET. However, I don't know how to pass the required parameters. I have try this:

"GET / HTTP/1.1\r\nHost: https://api.telegram.org/botmybottokenhere/sendMessage?chat_id=mychatidhere&text=mymessagehere\r\nConnection: close\r\n\r\n"

but does not work. I get a 400 bad request error. But if I copy that url and paste it in Chrome the message is sent, so the url is correct. It also works in Python3.7.

You're getting the 400 error because you are sending an HTTP request to an HTTPS-encrypted endpoint. For what it's worth, I stumbled upon your comment because I am running into this exact same issue and I am trying to figure out how to send an HTTPS-based request using the low-level socket_*() functions. If I figure this out then I'll try to pop back in here and see if I can share my knowledge.

The response I receive in my own attempts are as follows:

400 The plain HTTP request was sent to HTTPS port
400 Bad Request
The plain HTTP request was sent to HTTPS port

@nolim1t
Copy link
Author

nolim1t commented Nov 13, 2019

This bit of code only relates to non-SSL secured endpoints. For SSL you would have to add in SSL/TLS related negotiation code too.

@nikita8118
Copy link

nikita8118 commented Sep 26, 2020

i have same error like Cannedfood's.
what is the solution? please...
./soc https://www.cplusplus.com 80

@Andermutu
Copy link

Andermutu commented Sep 26, 2020

I am trying to send a Telegram message using HTTP GET. However, I don't know how to pass the required parameters. I have try this:
"GET / HTTP/1.1\r\nHost: https://api.telegram.org/botmybottokenhere/sendMessage?chat_id=mychatidhere&text=mymessagehere\r\nConnection: close\r\n\r\n"
but does not work. I get a 400 bad request error. But if I copy that url and paste it in Chrome the message is sent, so the url is correct. It also works in Python3.7.

You're getting the 400 error because you are sending an HTTP request to an HTTPS-encrypted endpoint. For what it's worth, I stumbled upon your comment because I am running into this exact same issue and I am trying to figure out how to send an HTTPS-based request using the low-level socket_*() functions. If I figure this out then I'll try to pop back in here and see if I can share my knowledge.

The response I receive in my own attempts are as follows:

400 The plain HTTP request was sent to HTTPS port
400 Bad Request
The plain HTTP request was sent to HTTPS port

I think you need to add something like this to add TLS :

int client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TLS_1_2);
enum {
NONE = 0,
OPTIONAL = 1,
REQUIRED = 2,
};

int verify = OPTIONAL;

err = setsockopt(client_fd, SOL_TLS, TLS_PEER_VERIFY, &verify, sizeof(verify));

@naushadckk
Copy link

Error - IPPROTO_TLS_1_2 is not defined..........

@renatoathaydes
Copy link

@Andermutu it has been a while , but in case you're still interested :)

This is not valid HTTP:

"GET / HTTP/1.1\r\nHost: [https://api.telegram.org/botmybottokenhere/sendMessage?chat_id=mychatidhere&text=mymessagehere\r\nConnection](https://api.telegram.org/botmybottokenhere/sendMessage?chat_id=mychatidhere&text=mymessagehere%5Cr%5CnConnection): close\r\n\r\n"

The host header should contain only the host. You've given it a full URL!

And your request path is just / so it would never work.

Also, you're trying to send a https request which means the socket needs to be encrypted. And notice that the protocol doesn't go in the HTTP request itself as that refers to the transport being used.

So, your request should look like this (\r\n replaced with simple new-lines for readability, you still need them in a real request):

GET /botmybottokenhere/sendMessage?chat_id=mychatidhere&text=mymessagehere HTTP/1.1
Host: api.telegram.org
Connection: close

@0x2a94b5
Copy link

0x2a94b5 commented Aug 17, 2022

@dimuthnc

localhost:8080/services

write(fd, "GET /services HTTP/1.1\r\n\r\n", strlen("GET /services HTTP/1.1\r\n\r\n"));

Run command:

gcc socket.c -o socket
./socket localhost 8080

@rilysh
Copy link

rilysh commented Sep 1, 2022

You may want to use bcopy instead of copy at line 22

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment