Add working http client
This commit is contained in:
parent
69612815b6
commit
2f552e5ad4
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,3 +1,7 @@
|
||||
.vscode/
|
||||
*.out
|
||||
*.o
|
||||
*.html
|
||||
!public/*.html
|
||||
http/client
|
||||
http/server
|
||||
|
@ -21,7 +21,7 @@ install:
|
||||
cp $(TARGET_2) /usr/local/bin/myhttp-$(TARGET_2)
|
||||
|
||||
clean:
|
||||
$(RM) $(TARGET_1).o
|
||||
$(RM) $(TARGET_1)
|
||||
$(RM) $(TARGET_2).o
|
||||
$(RM) $(TARGET_2)
|
||||
$(RM) $(TARGET_2)
|
||||
$(RM) *.o
|
||||
$(RM) *.html
|
142
http/client.c
142
http/client.c
@ -28,6 +28,8 @@
|
||||
#include "shared/http.h"
|
||||
|
||||
static struct addrinfo* get_host_info(const char *hostname, char *port);
|
||||
static int create_socket(struct addrinfo* config);
|
||||
static void process_response(char reply[sizeof(int32_t)], char *ofile, char *dir);
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
check_opts_number(argc);
|
||||
@ -79,13 +81,41 @@ int main(int argc, char *argv[]) {
|
||||
url = argv[optind];
|
||||
}
|
||||
|
||||
const char *full_url = strstr(url, "http://") + 7; //< the full url starts after http://
|
||||
const char *path = strstr(full_url, "/");
|
||||
const char *hostname = "google.com"; //< TODO: Change this later
|
||||
char *base_url = strstr(url, "http://") + 7; //< the base url starts after http://
|
||||
|
||||
struct addrinfo* res = get_host_info(hostname, port); //< Check if the address is resolvable
|
||||
const char *path = strstr(base_url, "/");
|
||||
if(path == NULL) path = "/"; //< Set the root path if there is none
|
||||
|
||||
freeaddrinfo(res); //< Free the addrinfo struct
|
||||
const char *hostname = strtok(base_url, ";/?:@=&"); //< Get the FQDN of the remote
|
||||
|
||||
struct addrinfo* config = get_host_info(hostname, port); //< Check if the address is resolvable
|
||||
|
||||
char header[strlen(base_url) + 100]; //< Create a variable to store the header
|
||||
|
||||
snprintf(header, sizeof header, "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", path, hostname); //< Build the header
|
||||
|
||||
int sockfd = create_socket(config);
|
||||
|
||||
if (connect(sockfd, config -> ai_addr, config -> ai_addrlen) == -1) { //< Connect to the host
|
||||
(void) fprintf(stderr, "ERROR: Failure connecting to the server.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if((send(sockfd, header, strlen(header), 0)) < 0) { //< Send the GET request
|
||||
(void) fprintf(stderr, "ERROR: Failure sending the request to the server.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char reply[sizeof(int32_t)]; //< Set the variable to hold the server response. Set to a big length
|
||||
|
||||
if((recv(sockfd, reply, strlen(reply) - 1, 0)) < 0) { //< Receive response from the remote (-1 for the trailing terminating char)
|
||||
(void) fprintf(stderr, "ERROR: Did not receive response from the remote.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
process_response(reply, ofile, dir);
|
||||
|
||||
freeaddrinfo(config); //< Free the addrinfo struct
|
||||
}
|
||||
|
||||
static void print_usage(void) {
|
||||
@ -115,7 +145,7 @@ static void check_opts_number(int argc) {
|
||||
/**
|
||||
* @brief Function to check if the address is resolvable by the client
|
||||
* @details Checks if the address is resolvable by the client.
|
||||
* If the address is resolvable, it writes the result to res
|
||||
* If the address is resolvable, it returns the result
|
||||
* If the address is not resolvable, it exits with an error
|
||||
* @param
|
||||
* hostname - The address to be resolved
|
||||
@ -138,7 +168,6 @@ static struct addrinfo* get_host_info(const char *hostname, char *port) {
|
||||
(void) fprintf(stderr, "ERROR: %s\n", gai_strerror(err));
|
||||
exit(EXIT_FAILURE); //< Exit if there is an error
|
||||
}
|
||||
|
||||
if(res == NULL) {
|
||||
(void) fprintf(stderr, "ERROR: Could not resolve address %s\n", hostname);
|
||||
exit(EXIT_FAILURE); //< Exit if the address is not resolved
|
||||
@ -146,3 +175,102 @@ static struct addrinfo* get_host_info(const char *hostname, char *port) {
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function to create a socker using the supplied configuration
|
||||
* @details Creates a socket using the supplied configuration
|
||||
* If there was no error, it returns the socket
|
||||
* If there was an error, it exits and displays an error message
|
||||
* @param config - The the configuration to be used for the socket creation
|
||||
* @return sockfd - The socket obtained from the socket creation
|
||||
**/
|
||||
static int create_socket(struct addrinfo* config) {
|
||||
int sockfd = socket(config->ai_family, config->ai_socktype, config->ai_protocol);
|
||||
|
||||
if(sockfd < 0) {
|
||||
(void) fprintf(stderr, "ERROR: Socket creation failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function to process the response of the remote host
|
||||
* @details Processes the reply according to its' content and the ofile and dir parameters
|
||||
* The function checks the response code does the following:
|
||||
* - if the reponse code is invalid, it exits with an error
|
||||
* - if the response code is different from 200, it prints the whole response and exits with status 3
|
||||
* - if the response code is 200, it prints it to the console or saves it as a file
|
||||
* If there was an error, it exits and displays an error message
|
||||
* @param config - The the configuration to be used for the socket creation
|
||||
* @return null
|
||||
**/
|
||||
static void process_response(char reply[sizeof(int32_t)], char *ofile, char *dir) {
|
||||
char *http_version = "HTTP/1.1";
|
||||
if(strncmp(reply, http_version, strlen(http_version)) == 0) { //< Check if the remote gives a correct response header
|
||||
char cresponse_code[3]; //< Array for the response code. Response codes are always 3 digits long
|
||||
strncpy(cresponse_code ,strstr(reply, "HTTP/1.1 ") + sizeof(http_version) + 1, 3); //< Get the response code as string.
|
||||
|
||||
int response_code = strtol(cresponse_code, NULL, 10); //< Get the response code as integer
|
||||
|
||||
if(response_code == 0) {
|
||||
fprintf(stderr, "ERROR: Protocol error.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (response_code != 200) {
|
||||
printf("%s\n", strtok(reply, "\r\n")); //< Get the first line
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
char *body = strstr(reply, "\r\n\r\n") + 4;
|
||||
if(ofile == NULL && dir == NULL) printf("%s", body); //< Print the body of the response
|
||||
else {
|
||||
if(ofile != NULL) {
|
||||
FILE *out = NULL;
|
||||
out = fopen(ofile, "w");
|
||||
|
||||
if(out == NULL) {
|
||||
(void) fprintf(stderr, "ERROR: Could not open ./%s for writing.", ofile);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
fprintf(out, "%s", body);
|
||||
fclose(out);
|
||||
printf("File ./%s successfully written.\n", ofile);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
} else if(dir != NULL) {
|
||||
FILE *out = NULL;
|
||||
|
||||
char path[sizeof(dir) + 100];
|
||||
if(dir[strlen(dir) - 1] == '/')
|
||||
snprintf(path, sizeof path, "%sresult.html", dir);
|
||||
else
|
||||
snprintf(path, sizeof path, "%s/result.html", dir);
|
||||
|
||||
out = fopen(path, "w"); //< Open <path>/result.html for writing
|
||||
|
||||
if(out == NULL) {
|
||||
if(dir[strlen(dir) - 1] == '/')
|
||||
(void) fprintf(stderr, "ERROR: Could not open %sresult.html for writing.\n", dir);
|
||||
else
|
||||
(void) fprintf(stderr, "ERROR: Could not open %s/result.html for writing.\n", dir);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
fprintf(out, "%s", body);
|
||||
fclose(out);
|
||||
|
||||
if(dir[strlen(dir) - 1] == '/')
|
||||
printf("File %sresult.html successfully written.\n", dir);
|
||||
else
|
||||
printf("File %s/result.html successfully written.\n", dir);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: Protocol error.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user