Skip to content

Commit

Permalink
Merge pull request #16 from lil-skelly:client-http-post
Browse files Browse the repository at this point in the history
[REFACTOR] Improved the HTTP library
  • Loading branch information
lil-skelly authored Sep 6, 2024
2 parents 2ef948c + 180a319 commit e3365fa
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 63 deletions.
Binary file removed src/client/client
Binary file not shown.
161 changes: 130 additions & 31 deletions src/client/http.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
const char *CONTENT_LENGTH_HEADER = "Content-Length: ";
const char *GET_REQ_TEMPLATE =
"GET %s HTTP/1.1\r\nConnection: keep-alive\r\n\r\n";
const char *POST_REQ_TEMPLATE = "POST %s HTTP/1.1\r\nContent-Type: "
"%s\r\nContent-Length: %d\r\n%s\r\n\r\n";

/* Log a http_res_t */
void print_http_res(const http_res_t res) {
printf("--[ STATUS CODE: %i ]--\n", res.status_code);
printf("--[ REQUEST ]--\n%s\n--[ REQUEST ]--\n", res.request);
printf("%s\n", res.data);
puts("--[ END ]--");
puts("--[ END ]--\n");
}

/* Parse HTTP status code */
Expand Down Expand Up @@ -55,14 +57,126 @@ long parse_http_content_length(const char *buf) {
return content_length;
}

/* Parse HTTP response body */
int parse_http_body(int sfd, char *src, char *dest, long content_length, long total_bytes) {
const char *body_start;
long header_length, received_length, left_length;

body_start = strstr(src, "\r\n\r\n");
if (!body_start) {
perror("Header delimeter not found\n");
return -HTTP_INVALID_RESPONSE;
}
body_start += 4;

header_length = body_start - src;

received_length = MIN(total_bytes - header_length, content_length);
if (received_length < 0) { // in case God is against us
perror("Received length is negative\n");
return -HTTP_SOCKET_ERR;
}
memcpy(dest, body_start, received_length);

if (header_length + content_length > total_bytes) {
left_length = content_length - received_length;
if (recv_response(sfd, dest + received_length, left_length) < 0) {
perror("Failed to receive left over data\n");
return -HTTP_SOCKET_ERR;
}
}

return 0;
}

int http_post(int sfd, const char *path,
const char *content_type, const char *parameters,
http_res_t *res) {
long total_bytes, bytes_read, content_length, status_code;
size_t req_buf_len;
char req_buffer[HTTP_BUFFER_SIZE];
char buffer[HTTP_BUFFER_SIZE];

snprintf(req_buffer, HTTP_BUFFER_SIZE - 1, POST_REQ_TEMPLATE, path, content_type,
strlen(parameters), parameters);
req_buf_len = strlen(req_buffer);

res->request = malloc(req_buf_len);
if (res->request == NULL) {
free(res->data); // free previously allocated data
return -HTTP_OOM;
}
strncpy(res->request, req_buffer, req_buf_len - 1);

if (send_request(sfd, req_buffer) < 0) {
perror("Error: failed to send request\n");
return -HTTP_SOCKET_ERR;
}

if (HTTP_VERBOSE)
puts("Sent POST request");

/* Receive response from server */
total_bytes = 0;
while ((bytes_read = recv(sfd, buffer + total_bytes,
HTTP_BUFFER_SIZE - 1 - total_bytes, 0)) > 0) {
total_bytes += bytes_read;
// add temporary null terminator
buffer[total_bytes] = 0;
if (NULL != strstr(buffer + total_bytes - bytes_read, "\r\n\r\n")) {
// if we read all headers stop reading
break;
}

if (total_bytes >= HTTP_BUFFER_SIZE - 1) {
buffer[HTTP_BUFFER_SIZE - 1] = 0;
break;
}
}

/* Check if response starts with "HTTP" */
if (memcmp(buffer, "HTTP", 4)) {
return -HTTP_INVALID_RESPONSE;
}

/* Parse status code */
status_code = parse_http_status_code(buffer);
if (status_code < 0) {
return -HTTP_INVALID_RESPONSE;
}
res->status_code = (int)status_code;

/* Parse content length */
content_length = parse_http_content_length(buffer);
if (content_length < 0) {
return -HTTP_INVALID_RESPONSE;
}
res->size = (size_t)content_length;

/* Parse the response body */
res->data = malloc(res->size);
if (res->data == NULL) {
return -HTTP_OOM;
}

if (parse_http_body(sfd, buffer, res->data, content_length, total_bytes)) {
return -HTTP_INVALID_RESPONSE;
}

if (HTTP_VERBOSE)
puts("Parsed response");
if (HTTP_VERBOSE > 1)
print_http_res(*res);

return HTTP_SUCCESS;
}

int http_get(int sfd, const char *path, http_res_t *res) {
char request_buf[HTTP_BUFFER_SIZE]; // use separate buffer for the request
char buf[HTTP_BUFFER_SIZE];

const char *body_start;
int bytes_read;
long total_bytes, status_code, header_length, content_length, received_length,
left_length;
long total_bytes, status_code, content_length;
size_t req_buf_len;

/* send request */
Expand All @@ -73,20 +187,25 @@ int http_get(int sfd, const char *path, http_res_t *res) {
perror("Error: failed to send request\n");
return -HTTP_SOCKET_ERR;
}

if (HTTP_VERBOSE)
puts("Sent GET request");

/* receive response from server */
if (HTTP_VERBOSE)
puts("Receiving data");
res->request = malloc(req_buf_len);
if (res->request == NULL) {
free(res->data); // free previously allocated data
return -HTTP_OOM;
}
strncpy(res->request, request_buf, req_buf_len - 1);

/* receive response from server */
total_bytes = 0;
while ((bytes_read = recv(sfd, buf + total_bytes,
HTTP_BUFFER_SIZE - 1 - total_bytes, 0)) > 0) {
total_bytes += bytes_read;
// add temporary null terminator
buf[total_bytes] = 0;
if (NULL != strstr(buf + total_bytes - bytes_read, "\r\n\r\n")) {
if (strstr(buf + total_bytes - bytes_read, "\r\n\r\n")) {
// if we read all headers stop reading
break;
}
Expand Down Expand Up @@ -124,29 +243,9 @@ int http_get(int sfd, const char *path, http_res_t *res) {
return -HTTP_OOM;
}

body_start = strstr(buf, "\r\n\r\n") + 4;
header_length = body_start - buf;
received_length = MIN(total_bytes - header_length, content_length);

memcpy(res->data, body_start, received_length);

if (header_length + content_length > total_bytes) {
if (HTTP_VERBOSE)
puts("Receiving left over data");
left_length = content_length - received_length;
if (recv_response(sfd, res->data + received_length, left_length) < 0) {
perror("Failed to receive left over data\n");
free(res->data);
return -HTTP_SOCKET_ERR;
}
}

res->request = malloc(req_buf_len);
if (res->request == NULL) {
free(res->data); // free previously allocated data
return -HTTP_OOM;
if (parse_http_body(sfd, buf, res->data, content_length, total_bytes) < 0) {
return -HTTP_INVALID_RESPONSE;
}
strncpy(res->request, request_buf, req_buf_len - 1);

if (HTTP_VERBOSE)
puts("Parsed response");
Expand Down Expand Up @@ -179,7 +278,7 @@ int http_download_data_to_file(int sfd, const char *path, const char *f_path) {
perror("Error: Failed to write data to file");
fclose(file);
http_free(&res);
return -2;
return -2;
}

if (fclose(file) != 0) {
Expand Down
5 changes: 4 additions & 1 deletion src/client/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#define HTTP_INVALID_RESPONSE 2
#define HTTP_OOM 3

#define HTTP_VERBOSE 2
#define HTTP_VERBOSE 0

typedef struct {
int status_code;
Expand All @@ -26,7 +26,10 @@ void http_free(http_res_t *res);
void http_init(http_res_t *res);

int http_get(int sfd, const char *path, http_res_t *res);
int http_post(int sfd,const char* path,const char *content_type, const char* parameters, http_res_t *res);

int http_download_data_to_file(int sfd, const char *path, const char *f_path);
long parse_http_status_code(const char *buf);
long parse_http_content_length(const char *buf);

#endif // HTTP_H
35 changes: 24 additions & 11 deletions src/client/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ int main() {
int sfd; // socket file descriptor
char hostname[NI_MAXHOST];
http_res_t http_fraction_res;
http_res_t http_post_res;

/* Setup socket and initiate connection with the server */
setup_hints(&hints);
Expand All @@ -25,28 +26,26 @@ int main() {
if (h_getnameinfo(ainfo, hostname, sizeof(hostname)) != 0) {
return EXIT_FAILURE;
}

printf("Connecting to %s\n", hostname);
printf("Connecting to: %s\n", hostname);
sfd = create_sock_and_conn(ainfo);
if (sfd == -1) {
return EXIT_FAILURE;
}
freeaddrinfo(ainfo); // we don't need these anymore

/* Get the fraction links */
// http_init(&http_fraction_res);
if (http_get(sfd, "/", &http_fraction_res) != HTTP_SUCCESS) {
return EXIT_FAILURE;
goto err;
}
// Count number of links
int num_links = count_lines(http_fraction_res.data) + 1; // +1 for the last line if not ending with \n

// Allocate memory for fraction links
char **fraction_links = malloc(num_links * sizeof(char *));
if (fraction_links == NULL) {
fprintf(stderr, "malloc failed to allocate memory for fraction links\n");
close(sfd);
http_free(&http_fraction_res);
return EXIT_FAILURE;
goto err;
}

// Split the response data into lines
Expand All @@ -55,8 +54,7 @@ int main() {
if (lines_read < 0) {
http_free(&http_fraction_res);
free(fraction_links);
close(sfd);
return EXIT_FAILURE;
goto err;
}

// Print the fraction links
Expand All @@ -66,11 +64,26 @@ int main() {
free(fraction_links[i]); // Free allocated memory for each line
}

/* Tell the server that we successfully downloaded the fractions */
if (http_post(sfd, "/deadbeef", "plain/text", "{'downloaded':true}", &http_post_res) != HTTP_SUCCESS) {
http_free(&http_fraction_res);
http_free(&http_post_res);

free(fraction_links);

goto err;
}

/* Cleanup */
freeaddrinfo(ainfo);
close(sfd);
http_free(&http_fraction_res);
http_free(&http_post_res);

free(fraction_links);

close(sfd);
return EXIT_SUCCESS;

err:
close(sfd);
return EXIT_FAILURE;
}
2 changes: 1 addition & 1 deletion src/client/sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ int h_getaddrinfo(const char *ip, const char *port, struct addrinfo *hints,
return 0;
}


int h_getnameinfo(const struct addrinfo *ainfo, char buffer[], size_t buffer_size) {
int res;
res = getnameinfo(ainfo->ai_addr, ainfo->ai_addrlen, buffer, buffer_size, NULL, 0, 0);
Expand Down Expand Up @@ -89,6 +90,5 @@ int create_sock_and_conn(struct addrinfo *res) {
freeaddrinfo(res);
return -1;
}

return sfd;
}
43 changes: 27 additions & 16 deletions src/client/utils.c
Original file line number Diff line number Diff line change
@@ -1,34 +1,45 @@
#include "utils.h"

// if this dont work DeLuks is to blame :) - it worked, kinda (skelly) :D
// if this dont work Skelly is to blame ;)
int split_fraction_links(char *data, char *data_arr[], int maxlines) {
int lines_read = 0;
char *line;

char *tmp_str = strdup(data);
if (tmp_str == NULL) {
fprintf(stderr, "strdup failed to allocate memory\n");
return -1;
}

line = strtok(tmp_str, "\n");
// Use the input `data` directly, no need for strdup
line = strtok(data, "\n");
while (line != NULL && lines_read < maxlines) {
data_arr[lines_read] = malloc(strlen(line) + 1);
data_arr[lines_read] = strdup(line);
if (data_arr[lines_read] == NULL) {
fprintf(stderr, "malloc failed to allocate memory for data array\n");
free(tmp_str);
// Free previously allocated memory in case of failure
fprintf(stderr, "strdup failed to allocate memory\n");
// Free previously allocated lines in case of failure
for (int i = 0; i < lines_read; i++) {
free(data_arr[i]);
}
return -1;
}

strcpy(data_arr[lines_read], line);
line = strtok(NULL, "\n");
lines_read++;
line = strtok(NULL, "\n");
}

free(tmp_str);
return lines_read;
}


char *get_path_from_url(const char *url) {
const char *path_start = strstr(url, "://");
if (!path_start) {
perror("There was a error with the URL");
return NULL;
}

path_start += 3; // Skip past "://"

// Find the first '/' after the host part
char *path = strchr(path_start, '/');
if (!path) {
perror("No string found!");
return "";
}

return path;
}
Loading

0 comments on commit e3365fa

Please sign in to comment.