Created
December 25, 2022 20:08
-
-
Save Lhy121125/2d6b0efa1f473baa92c512e002fa3b5a to your computer and use it in GitHub Desktop.
A http Client that Works Similar to wget
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
| // Because we are using POSIX features that aren't part of the C standard, we | |
| // need to #define this macro before #including standard library headers to be | |
| // granted access to POSIX-specific definitions such as struct addrinfo. | |
| #define _GNU_SOURCE | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| #include <sys/types.h> | |
| #include <sys/socket.h> | |
| #include <arpa/inet.h> | |
| #include <netdb.h> | |
| /* | |
| * Convenient error handler for printing errno-associated message and exit(1). | |
| */ | |
| static void die(char *msg) { | |
| perror(msg); | |
| exit(1); | |
| } | |
| int main(int argc, char **argv){ | |
| if(argc != 4){ | |
| fprintf(stderr, "Usage: %s <host-name> <port-number> <URI>\n ex) ./http-client www.example.com 80/index.html\n",argv[0]); | |
| exit(1); | |
| } | |
| char *server_ip = argv[1]; | |
| char *server_port = argv[2]; | |
| char *URI = argv[3]; | |
| //Obtain server address info using getaddrinfo() | |
| struct addrinfo hints;//the struct we receving the actual answer | |
| memset(&hints, 0, sizeof(hints)); | |
| // Specify what kind of connection we intend to make; these values tell | |
| // getaddrinfo() that we don't care about other kinds of addresses. | |
| hints.ai_family = AF_INET; // Only accept IPv4 addresses | |
| hints.ai_socktype = SOCK_STREAM; // stream socket for TCP connections | |
| hints.ai_protocol = IPPROTO_TCP; // TCP protocol | |
| // Define where getaddrinfo() will return the information it found. | |
| struct addrinfo *info; | |
| // Call getaddrinfo(), specifying the server IP address and port as strings. | |
| // getaddrinfo() will parse those for us and point info to the result. | |
| int addr_err; | |
| if ((addr_err = getaddrinfo(server_ip, server_port, &hints, &info)) != 0) { | |
| fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(addr_err)); | |
| exit(1); | |
| } | |
| /* | |
| * Create socket() and connect() it to server. | |
| */ | |
| // Create socket() according to the address family, socket type, and | |
| // protocol of the address info. | |
| int serv_fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol); | |
| if (serv_fd < 0) | |
| die("socket"); | |
| // Connect socket with server address; the IP address and port in | |
| // info->ai_addr should be the same address and port that getaddrinfo() | |
| // parsed from server_address and server_port. | |
| if (connect(serv_fd, info->ai_addr, info->ai_addrlen) < 0) | |
| die("connect"); | |
| freeaddrinfo(info); | |
| //Specific to this lab | |
| FILE *fpw = fdopen(serv_fd,"wb"); | |
| if (fpw == NULL) | |
| die("1: fdopen (for writing)"); | |
| setlinebuf(fpw); | |
| /*FILE *fpr = fdopen(dup(serv_fd),"rb"); | |
| if (fpr == NULL) | |
| die("2: fdopen (for reading)"); | |
| setlinebuf(fpr); | |
| */ | |
| //Send Http request | |
| //char* request; | |
| char buff[8192] = ""; | |
| char* request = strcat(buff,"GET "); | |
| request = strcat(buff,URI); | |
| request = strcat(buff," HTTP/1.0\r\nHost: "); | |
| request = strcat(buff,server_ip); | |
| request = strcat(buff,":"); | |
| request = strcat(buff,server_port); | |
| request = strcat(buff,"\r\n\r\n"); | |
| printf("%s",request); | |
| fwrite(request,1,8192,fpw); | |
| // send(serv_fd, request, 8192, 0); | |
| /* | |
| FILE *fpw = fdopen(serv_fd,"wb"); | |
| if (fpw == NULL) | |
| die("fdopen (for writing)") | |
| FILE *fpr = fdopen(dup(serv_fd),"rb"); | |
| if (fpr == NULL) | |
| die("fdopen (for reading)") | |
| */ | |
| FILE *fpr = fdopen(dup(serv_fd),"rb"); | |
| if (fpr == NULL) | |
| die("2: fdopen (for reading)"); | |
| setlinebuf(fpr); | |
| char buf[8192] = ""; | |
| char* first_line = fgets(buf,sizeof(buf),fpr); | |
| printf("%s",first_line); | |
| if(first_line == NULL){ | |
| printf("%s",buf); | |
| die("1: fget(the fist line)"); | |
| } | |
| //skip the http/1.0 and see whether the 9th char is 200 | |
| if(strncmp("200",buf+9,3) != 0){ | |
| printf("%s",buf); | |
| die("400 bad request"); | |
| } | |
| //get rid of header | |
| while(1){ | |
| if(fgets(buf,8192,fpr) == NULL){ | |
| die("No \r\n"); | |
| } | |
| if(strcmp(buf,"\r\n")==0){ | |
| break; | |
| } | |
| } | |
| //Output file | |
| FILE *fpo = fopen(strrchr(URI,'/')+1,"wb"); | |
| if (fpo == NULL) | |
| die("fopen (for writing)"); | |
| // FILE pointers are block-buffered by default; | |
| // configure fpw to be line-buffered. | |
| setlinebuf(fpo); | |
| //char buf[8192]; | |
| //Write the files from the response to the output file | |
| size_t line_size; | |
| while((line_size = fread(buf,1,8192,fpr))>0){ | |
| size_t write_size = fwrite(buf,1,line_size,fpo); | |
| if(write_size != line_size){ | |
| die("writing something not expected"); | |
| } | |
| } | |
| fclose(fpr); | |
| fclose(fpw); | |
| fclose(fpo); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment