Compare commits
10 Commits
16481ec708
...
04abd8b152
Author | SHA1 | Date | |
---|---|---|---|
![]() |
04abd8b152 | ||
![]() |
8d810650e5 | ||
![]() |
71a197c9cb | ||
![]() |
e1f9046db0 | ||
![]() |
18579c7981 | ||
![]() |
97d970c0cd | ||
![]() |
986290f6de | ||
![]() |
14cc5ef574 | ||
![]() |
e04f9b6d95 | ||
![]() |
aaeac2ad7f |
@ -1,7 +1,7 @@
|
||||
TARGET=http
|
||||
CC=gcc
|
||||
|
||||
OBJ = main.o tcp.o http.o
|
||||
OBJ = main.o tcp.o http.o file.o
|
||||
|
||||
$(TARGET): $(OBJ)
|
||||
mkdir -p ../build
|
||||
@ -10,7 +10,8 @@ $(TARGET): $(OBJ)
|
||||
|
||||
main.o: include/tcp.h
|
||||
tcp.o: include/tcp.h include/http.h
|
||||
http.o: include/http.h
|
||||
http.o: include/http.h include/file.h
|
||||
file.o: include/file.h
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
71
src/file.c
Normal file
71
src/file.c
Normal file
@ -0,0 +1,71 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "include/file.h"
|
||||
|
||||
void get_file_from_uri(char** uri, char** buffer)
|
||||
{
|
||||
char* tok;
|
||||
FILE* f;
|
||||
int err, i;
|
||||
size_t bufsize, newlen;
|
||||
char* filepath;
|
||||
|
||||
|
||||
|
||||
|
||||
// this is assuming absPath, NOT abs uri
|
||||
//
|
||||
// absURI is ONLY generate them in requests to proxies, otherwise
|
||||
// will be an abs path
|
||||
|
||||
/**
|
||||
* TODO: if no file is specified, append index.html
|
||||
*/
|
||||
|
||||
asprintf(&filepath, "./%s", *uri);
|
||||
if (filepath[strlen(filepath) - 1] == '/')
|
||||
asprintf(&filepath, "%sindex.html", filepath);
|
||||
|
||||
|
||||
f = fopen(filepath, "r");
|
||||
if (f == NULL) {
|
||||
perror("fopen");
|
||||
return; // TODO: 404
|
||||
}
|
||||
|
||||
err = fseek(f, 0L, SEEK_END); // go to the end of the file
|
||||
if (err != 0) {
|
||||
perror("fseek");
|
||||
fclose(f);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
bufsize = ftell(f); // lets get the length of the file here
|
||||
if (bufsize == -1) {
|
||||
perror("ftell");
|
||||
fclose(f);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
(*buffer) = malloc(sizeof(char) * (bufsize + 1));
|
||||
if (*buffer == NULL) {
|
||||
perror("malloc");
|
||||
fclose(f);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
rewind(f); // go back to the beginning of the file
|
||||
|
||||
newlen = fread(*buffer, sizeof(char), bufsize, f);
|
||||
if (ferror(f) != 0) {
|
||||
perror("fread");
|
||||
fclose(f);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
(*buffer)[newlen++] = '\0'; // this is just to be safe
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
199
src/http.c
199
src/http.c
@ -3,22 +3,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "include/http.h"
|
||||
|
||||
// what we want to do here is start parsing the http message
|
||||
|
||||
// GET / HTTP/1.1
|
||||
// Host: localhost:8080
|
||||
// User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:134.0) Gecko/20100101 Firefox/134.0
|
||||
// Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
||||
// Accept-Language: en-US,en;q=0.5
|
||||
// Accept-Encoding: gzip, deflate, br, zstd
|
||||
// Connection: keep-alive
|
||||
// Upgrade-Insecure-Requests: 1
|
||||
// Sec-Fetch-Dest: document
|
||||
// Sec-Fetch-Mode: navigate
|
||||
// Sec-Fetch-Site: none
|
||||
// Sec-Fetch-User: ?1
|
||||
// Priority: u=0, i
|
||||
#include "include/file.h"
|
||||
|
||||
http_method_t str_to_http_method(char* input)
|
||||
{
|
||||
@ -44,7 +29,161 @@ const char* http_method_to_str(http_method_t input)
|
||||
}
|
||||
}
|
||||
|
||||
void parse_request_line(char** line, http_request* request)
|
||||
const char* http_status_code_to_str(http_status_code_t code)
|
||||
{
|
||||
switch(code) {
|
||||
case HTTP100:
|
||||
return "Continue";
|
||||
case HTTP101:
|
||||
return "Switching Protocols";
|
||||
case HTTP200:
|
||||
return "OK";
|
||||
case HTTP201:
|
||||
return "Created";
|
||||
case HTTP202:
|
||||
return "Accepted";
|
||||
case HTTP203:
|
||||
return "Non-Authoritative Information";
|
||||
case HTTP204:
|
||||
return "No Content";
|
||||
case HTTP205:
|
||||
return "Reset Content";
|
||||
case HTTP206:
|
||||
return "Partial Content";
|
||||
case HTTP300:
|
||||
return "Multiple Choices";
|
||||
case HTTP301:
|
||||
return "Moved Permanently";
|
||||
case HTTP302:
|
||||
return "Found";
|
||||
case HTTP303:
|
||||
return "See Other";
|
||||
case HTTP304:
|
||||
return "Not Modified";
|
||||
case HTTP305:
|
||||
return "Use Proxy";
|
||||
case HTTP307:
|
||||
return "Temporary Redirect";
|
||||
case HTTP400:
|
||||
return "Bad Request";
|
||||
case HTTP401:
|
||||
return "Unauthorized";
|
||||
case HTTP402:
|
||||
return "Payment Required";
|
||||
case HTTP403:
|
||||
return "Forbidden";
|
||||
case HTTP404:
|
||||
return "Not Found";
|
||||
case HTTP405:
|
||||
return "Method Not Allowed";
|
||||
case HTTP406:
|
||||
return "Not Acceptable";
|
||||
case HTTP407:
|
||||
return "Proxy Authentication Required";
|
||||
case HTTP408:
|
||||
return "Request Time-out";
|
||||
case HTTP409:
|
||||
return "Conflict";
|
||||
case HTTP410:
|
||||
return "Gone";
|
||||
case HTTP411:
|
||||
return "Length Required";
|
||||
case HTTP412:
|
||||
return "Precondition Failed";
|
||||
case HTTP413:
|
||||
return "Request Entity Too Large";
|
||||
case HTTP414:
|
||||
return "Request-URI Too Large";
|
||||
case HTTP415:
|
||||
return "Unsupported Media Type";
|
||||
case HTTP416:
|
||||
return "Requested range not satisfiable";
|
||||
case HTTP417:
|
||||
return "Expectation Failed";
|
||||
case HTTP500:
|
||||
return "Internal Server Error";
|
||||
case HTTP501:
|
||||
return "Not Implemented";
|
||||
case HTTP502:
|
||||
return "Bad Gateway";
|
||||
case HTTP503:
|
||||
return "Service Unavailable";
|
||||
case HTTP504:
|
||||
return "Gateway Time-out";
|
||||
case HTTP505:
|
||||
return "HTTP Version not supported";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void init_general_headers(struct general_headers* headers)
|
||||
{
|
||||
headers->cache_control = "";
|
||||
headers->connection = "";
|
||||
headers->date = "";
|
||||
headers->pragma = "";
|
||||
headers->trailer = "";
|
||||
headers->transfer_encoding = "";
|
||||
headers->upgrade = "";
|
||||
headers->via = "";
|
||||
headers->warning = "";
|
||||
|
||||
}
|
||||
|
||||
void init_http_request(http_request* request)
|
||||
{
|
||||
init_general_headers(&(request->general_headers));
|
||||
|
||||
request->request_headers.accept = "";
|
||||
request->request_headers.accept_charset = "";
|
||||
request->request_headers.accept_encoding = "";
|
||||
request->request_headers.accept_language = "";
|
||||
request->request_headers.authorization = "";
|
||||
request->request_headers.expect = "";
|
||||
request->request_headers.from = "";
|
||||
request->request_headers.host = "";
|
||||
request->request_headers.if_match = "";
|
||||
request->request_headers.if_modified_since = "";
|
||||
request->request_headers.if_none_match = "";
|
||||
request->request_headers.if_range = "";
|
||||
request->request_headers.if_unmodified_since = "";
|
||||
request->request_headers.max_forwards = "";
|
||||
request->request_headers.proxy_authorization = "";
|
||||
request->request_headers.range = "";
|
||||
request->request_headers.referer = "";
|
||||
request->request_headers.te = "";
|
||||
request->request_headers.user_agent = "";
|
||||
}
|
||||
|
||||
void init_http_response(http_response* response)
|
||||
{
|
||||
init_general_headers(&(response->general_headers));
|
||||
|
||||
response->response_headers.content_length = 0;
|
||||
|
||||
response->response_headers.accept_ranges = "";
|
||||
response->response_headers.age = "";
|
||||
response->response_headers.etag = "";
|
||||
response->response_headers.location = "";
|
||||
response->response_headers.proxy_authenticate = "";
|
||||
response->response_headers.retry_after = "";
|
||||
response->response_headers.server = "";
|
||||
response->response_headers.vary = "";
|
||||
response->response_headers.www_authenticate = "";
|
||||
}
|
||||
|
||||
void free_http_request(http_request* request)
|
||||
{
|
||||
free(request->request_line.request_uri);
|
||||
}
|
||||
|
||||
void free_http_response(http_response* response)
|
||||
{
|
||||
free(response->message_body);
|
||||
}
|
||||
|
||||
void parse_request_line(char** line, http_request_line* request)
|
||||
{
|
||||
char *tok, *ver, *num;
|
||||
|
||||
@ -103,6 +242,7 @@ void parse_http_request(char** buffer, http_request* request)
|
||||
|
||||
// first lets split each line
|
||||
char* line;
|
||||
http_request_line r_line;
|
||||
|
||||
// parse the first line
|
||||
line = strtok(*buffer, "\n");
|
||||
@ -111,7 +251,9 @@ void parse_http_request(char** buffer, http_request* request)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
parse_request_line(&line, request);
|
||||
parse_request_line(&line, &r_line);
|
||||
|
||||
request->request_line = r_line;
|
||||
|
||||
/**
|
||||
* TODO: start accepting the headers
|
||||
@ -132,3 +274,24 @@ void parse_http_request(char** buffer, http_request* request)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void create_http_response(http_request* request, http_response* response)
|
||||
{
|
||||
get_file_from_uri(&(request->request_line.request_uri), &(response->message_body));
|
||||
|
||||
init_http_response(response);
|
||||
|
||||
response->response_headers.content_length = strlen(response->message_body);
|
||||
|
||||
// HTTP/1.1
|
||||
response->status.version.major = 1;
|
||||
response->status.version.minor = 1;
|
||||
|
||||
// TODO: obviouslly add something to determine the status code to use
|
||||
response->status.status_code = HTTP200;
|
||||
response->status.reason_phrase = http_status_code_to_str(response->status.status_code);
|
||||
|
||||
response->response_headers.content_type = "text/html";
|
||||
|
||||
|
||||
}
|
||||
|
6
src/include/file.h
Normal file
6
src/include/file.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef FILE_H
|
||||
#define FILE_H
|
||||
|
||||
void get_file_from_uri(char** uri, char** buffer);
|
||||
|
||||
#endif
|
334
src/include/headers.h
Normal file
334
src/include/headers.h
Normal file
@ -0,0 +1,334 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef HEADERS_H
|
||||
#define HEADERS_H
|
||||
|
||||
struct general_headers {
|
||||
/**
|
||||
* Cache-Control: ...
|
||||
*
|
||||
* Used to control the cache
|
||||
*
|
||||
* TODO: implement cache,
|
||||
* for no we will be using "no-cache" for response
|
||||
*/
|
||||
const char* cache_control;
|
||||
|
||||
/**
|
||||
* Connection: ...
|
||||
*
|
||||
* requests will often have "keep-alive", but since we don't
|
||||
* support maintain constant connections, every header must have
|
||||
* "close"
|
||||
*
|
||||
* TODO: implmenet constant connections?
|
||||
*/
|
||||
const char* connection;
|
||||
|
||||
/**
|
||||
* Date: HTTP-date
|
||||
*
|
||||
* must always be included unless the status code is 100 or 101
|
||||
* or 500/503 if its inconvenient/impossible to generate a valid Date
|
||||
*/
|
||||
const char* date;
|
||||
|
||||
/**
|
||||
* Pragma: ...
|
||||
*
|
||||
* no-cache control directive should be paired with Pragma: "no-cache"
|
||||
*/
|
||||
const char* pragma;
|
||||
|
||||
/**
|
||||
* Trailer: fieldnames...
|
||||
*
|
||||
* This indicates that there will be a trailing header with the provided
|
||||
* field names
|
||||
*
|
||||
* If there is no trailer, this field should not include anything
|
||||
*
|
||||
* can not include the following header fields:
|
||||
* transfer-encoding
|
||||
* content-length
|
||||
* trailer
|
||||
*/
|
||||
const char* trailer;
|
||||
|
||||
/**
|
||||
* Transfer-Encoding: ...
|
||||
*
|
||||
* Indicates what type of transformation has been applied to
|
||||
* the message body
|
||||
*/
|
||||
const char* transfer_encoding;
|
||||
|
||||
/**
|
||||
* Upgrade: ...
|
||||
*
|
||||
* Allows the client to specify what additional protocols
|
||||
* it supports and would like to use
|
||||
*
|
||||
* Must use this in the 101 (Switching Protocols) response
|
||||
*/
|
||||
const char* upgrade;
|
||||
|
||||
/**
|
||||
* Via: ...
|
||||
*
|
||||
* MUST be used by gateways and proxies to indicate
|
||||
* intermediate protocols and receipients
|
||||
*/
|
||||
const char* via;
|
||||
|
||||
/**
|
||||
* Warning: ...
|
||||
*
|
||||
* carries additional info about the status or transformation
|
||||
* of a message, which might not be reflected in the message
|
||||
*
|
||||
* may carry more than one header TODO: implement this i suppose
|
||||
*/
|
||||
const char* warning;
|
||||
};
|
||||
|
||||
struct response_headers {
|
||||
/**
|
||||
* Content-Length: ...
|
||||
*
|
||||
* length of the content
|
||||
*/
|
||||
uint content_length;
|
||||
|
||||
/**
|
||||
* Accept-Ranges: ...
|
||||
*
|
||||
* If we should accept a range request for a resource, defaults to NONE
|
||||
*
|
||||
*
|
||||
*/
|
||||
const char* accept_ranges;
|
||||
|
||||
/**
|
||||
* Age: ...
|
||||
*
|
||||
* represents time in seconds
|
||||
*
|
||||
* time since the response was generated, this is for cached responses,
|
||||
* if we are cacheing, this header is REQUIRED
|
||||
*/
|
||||
const char* age;
|
||||
|
||||
/**
|
||||
* ETag: ...
|
||||
*
|
||||
* Current value of the entity tag for the requested tag
|
||||
* only useful if we're sending an entity body+header aswell
|
||||
*/
|
||||
const char* etag;
|
||||
|
||||
/**
|
||||
* Location: absoluteURI
|
||||
*
|
||||
* For 201 (created), this should be the new source that was created
|
||||
* for 3xx this should indicate the servers preferred URI for automatic redirection
|
||||
*/
|
||||
const char* location;
|
||||
|
||||
/**
|
||||
* Proxy-Authenticate: ...
|
||||
*
|
||||
* For 407 this is a required field
|
||||
*/
|
||||
const char* proxy_authenticate;
|
||||
|
||||
/**
|
||||
* Retry-After: (HTTP-date | delta-seconds)
|
||||
*
|
||||
* can be used with a 503 to indicate how long its expeccted to be unavailable
|
||||
* may be used with 3xx to tell how long to wait before redirecting
|
||||
*/
|
||||
const char* retry_after;
|
||||
|
||||
/**
|
||||
* Server: product/version ...
|
||||
*
|
||||
* Information about the HTTP server
|
||||
*/
|
||||
const char* server;
|
||||
|
||||
/**
|
||||
* Vary: (* | field-name)
|
||||
*
|
||||
* This is for a cacheable (or non cacheable reponse)
|
||||
* that is subject to server-driven negotiation
|
||||
*/
|
||||
const char* vary;
|
||||
|
||||
/**
|
||||
* WWW-Authenticate: ...
|
||||
*
|
||||
* required for 401
|
||||
*/
|
||||
const char* www_authenticate;
|
||||
|
||||
/**
|
||||
* Content-Type: ...
|
||||
*
|
||||
* type of the content
|
||||
*/
|
||||
const char* content_type;
|
||||
};
|
||||
|
||||
struct request_headers {
|
||||
/**
|
||||
* Accept: ...
|
||||
*
|
||||
* Specify all mediatypes which are acceptable for the response
|
||||
*
|
||||
*/
|
||||
const char* accept;
|
||||
|
||||
/**
|
||||
* Accept-Charset: ...
|
||||
*
|
||||
* Sppeicgy the character sets that are acceptable for the response
|
||||
*/
|
||||
const char* accept_charset;
|
||||
|
||||
/**
|
||||
* Accept-Encoding: ...
|
||||
*
|
||||
* restricts the content-codings that are acceptable in the response
|
||||
*/
|
||||
const char* accept_encoding;
|
||||
|
||||
/**
|
||||
* Accept-Language: ...
|
||||
*
|
||||
* restricts the set of langauges that are preferred as a response to the request
|
||||
*/
|
||||
const char* accept_language;
|
||||
|
||||
/**
|
||||
* Authorization: ...
|
||||
*
|
||||
* Used to auth itself with the server
|
||||
*/
|
||||
const char* authorization;
|
||||
|
||||
/**
|
||||
* Expect: ...
|
||||
*
|
||||
* Used to indicate the server behaviour required by the client
|
||||
*
|
||||
* A server that doeesn't understand/unable to compy, must respond
|
||||
* with the appropriate error status
|
||||
* If the expectations cant be met, respond with 417, otherwise, some 4xx
|
||||
*/
|
||||
const char* expect;
|
||||
|
||||
/**
|
||||
* From: mailbox
|
||||
*
|
||||
* If given, SHOULD contain an internet email address for the
|
||||
* user that controls the requesting user agent
|
||||
*/
|
||||
const char* from;
|
||||
|
||||
/**
|
||||
* Host: ...
|
||||
*
|
||||
* Contains the host and port of the resource being requested
|
||||
*/
|
||||
const char* host;
|
||||
|
||||
/**
|
||||
* If-Match: ...
|
||||
*
|
||||
* used with a method to make it conditional
|
||||
* uses entity tags..
|
||||
*/
|
||||
const char* if_match;
|
||||
|
||||
/**
|
||||
* If-Modified-Since: ...
|
||||
*
|
||||
* used with a method to make it conditional
|
||||
* uses entity tags..
|
||||
*/
|
||||
const char* if_modified_since;
|
||||
|
||||
/**
|
||||
* If-None-Match: ...
|
||||
*
|
||||
* used with a method to make it conditional
|
||||
* uses entity tags..
|
||||
*/
|
||||
const char* if_none_match;
|
||||
|
||||
/**
|
||||
* If-Range: ...
|
||||
*
|
||||
* used with a method to make it conditional
|
||||
* uses entity tags..
|
||||
*/
|
||||
const char* if_range;
|
||||
|
||||
/**
|
||||
* If-Unmodified-Since: ...
|
||||
*
|
||||
* used with a method to make it conditional
|
||||
* uses entity tags..
|
||||
*/
|
||||
const char* if_unmodified_since;
|
||||
|
||||
/**
|
||||
* Max-Forwards: ...
|
||||
*
|
||||
* Provided with TRACE and OPTIONS method to limit
|
||||
* the number of proxies or gateways that can forward the request
|
||||
*/
|
||||
const char* max_forwards;
|
||||
|
||||
/**
|
||||
* Proxy-Authorization: ...
|
||||
*
|
||||
* Allows the client to identify itself to a proxy which requires auth
|
||||
*/
|
||||
const char* proxy_authorization;
|
||||
|
||||
/**
|
||||
* Range: ...
|
||||
*
|
||||
* Specify range...
|
||||
*/
|
||||
const char* range;
|
||||
|
||||
/**
|
||||
* Referer: ...
|
||||
*
|
||||
* Specify the address from which the request-uri was obtained
|
||||
*/
|
||||
const char* referer;
|
||||
|
||||
/**
|
||||
* TE: ...
|
||||
*
|
||||
* what transfer-encodings it is willing to accept in the response
|
||||
* and whether or not it is willing to accept trailer fields
|
||||
* in a chunked transfer-coding
|
||||
*/
|
||||
const char* te;
|
||||
|
||||
/**
|
||||
* User-Agent: ...
|
||||
*
|
||||
* The useragent of the accessing client
|
||||
*
|
||||
*/
|
||||
const char* user_agent;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -1,3 +1,5 @@
|
||||
#include "headers.h"
|
||||
|
||||
#ifndef HTTP_H
|
||||
#define HTTP_H
|
||||
|
||||
@ -7,6 +9,28 @@ typedef enum {
|
||||
UNKNOWN,
|
||||
} http_method_t;
|
||||
|
||||
typedef enum {
|
||||
HTTP100 = 100,
|
||||
HTTP101,
|
||||
HTTP200 = 200,
|
||||
HTTP201, HTTP202, HTTP203,
|
||||
HTTP204, HTTP205, HTTP206,
|
||||
HTTP300 = 300,
|
||||
HTTP301, HTTP302, HTTP303,
|
||||
HTTP304, HTTP305,
|
||||
HTTP307 = 307,
|
||||
HTTP400 = 400,
|
||||
HTTP401, HTTP402, HTTP403,
|
||||
HTTP404, HTTP405, HTTP406,
|
||||
HTTP407, HTTP408, HTTP409,
|
||||
HTTP410, HTTP411, HTTP412,
|
||||
HTTP413, HTTP414, HTTP415,
|
||||
HTTP416, HTTP417,
|
||||
HTTP500 = 500,
|
||||
HTTP501, HTTP502, HTTP503,
|
||||
HTTP504, HTTP505
|
||||
} http_status_code_t;
|
||||
|
||||
struct http_version {
|
||||
int major;
|
||||
int minor;
|
||||
@ -16,15 +40,44 @@ typedef struct {
|
||||
http_method_t method;
|
||||
char* request_uri;
|
||||
struct http_version version;
|
||||
} http_request_line;
|
||||
|
||||
typedef struct {
|
||||
struct http_version version;
|
||||
http_status_code_t status_code;
|
||||
const char* reason_phrase;
|
||||
} http_status_line;
|
||||
|
||||
typedef struct {
|
||||
http_request_line request_line;
|
||||
struct general_headers general_headers;
|
||||
struct request_headers request_headers;
|
||||
} http_request;
|
||||
|
||||
typedef struct {
|
||||
http_status_line status;
|
||||
struct general_headers general_headers;
|
||||
struct response_headers response_headers;
|
||||
char* message_body;
|
||||
} http_response;
|
||||
|
||||
|
||||
http_method_t str_to_http_method(char* input);
|
||||
const char* http_method_to_str(http_method_t input);
|
||||
|
||||
void parse_request_line(char** line, http_request* request);
|
||||
const char* http_status_code_to_str(http_status_code_t code);
|
||||
|
||||
void parse_request_line(char** line, http_request_line* request);
|
||||
|
||||
void init_general_headers(struct general_headers* headers);
|
||||
void init_http_request(http_request* request);
|
||||
void init_http_response(http_response* response);
|
||||
|
||||
void free_http_request(http_request* request);
|
||||
void free_http_response(http_response* response);
|
||||
|
||||
void parse_http_request(char** buffer, http_request* request);
|
||||
|
||||
void create_http_response(http_request* request, http_response* response);
|
||||
|
||||
#endif
|
||||
|
@ -16,7 +16,7 @@ typedef struct {
|
||||
|
||||
void setup_socket(server_conn_t* conn);
|
||||
|
||||
void recv_message(char** buffer, int socket);
|
||||
int recv_message(char** buffer, int socket);
|
||||
|
||||
/// Threaded function to handle http requests
|
||||
/// takes client socket as an argument
|
||||
|
34
src/main.c
34
src/main.c
@ -5,6 +5,40 @@
|
||||
|
||||
#include "include/tcp.h"
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
*
|
||||
* we need to parse the request headers and put them into a struct
|
||||
*
|
||||
* also need to implement the features the headers are requesting
|
||||
*
|
||||
* for now we're just using the request line and the status line for basic info
|
||||
* but eventually i would like to start implementing headers
|
||||
*
|
||||
* file config for port, file location, etc
|
||||
*
|
||||
* send INTERNAL SERVER ERROR for failures instead of exiting the thread
|
||||
*
|
||||
* send 404 for if the file couldnt be found (use errno for this)
|
||||
*
|
||||
* setup the http_request parser to fill the request struct
|
||||
*
|
||||
* set the init_response to fill the required default headers (e.g. date)
|
||||
*
|
||||
* TODO: see where content-length is supposed to be ?
|
||||
* TODO: first thing we want to do is start sending a basic response
|
||||
* aka, make the status line, fill content-length and fill the message body,
|
||||
* then send it
|
||||
*
|
||||
* cleanup, make sure everything is being free'd and properly cleaned up
|
||||
*
|
||||
* final:
|
||||
* would like to have a cache at some point
|
||||
* make sure everything is fully rfc compliant
|
||||
* segment the code even more to make stuff as resuable as possible
|
||||
*/
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
server_conn_t server;
|
||||
|
122
src/tcp.c
122
src/tcp.c
@ -5,6 +5,7 @@
|
||||
#include <netinet/in.h>
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "include/tcp.h"
|
||||
#include "include/http.h"
|
||||
@ -42,7 +43,7 @@ void setup_socket(server_conn_t* conn)
|
||||
}
|
||||
}
|
||||
|
||||
void recv_message(char** buffer, int socket)
|
||||
int recv_message(char** buffer, int socket)
|
||||
{
|
||||
ulong current_size, bytes_recv;
|
||||
int status;
|
||||
@ -70,42 +71,135 @@ void recv_message(char** buffer, int socket)
|
||||
// lets recv BUFFER_INC_LEN amount of bytes
|
||||
status = recv(socket, *buffer + bytes_recv, BUFFER_INC_LEN, MSG_DONTWAIT);
|
||||
if (status < 0) {
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK) // these signify that that were just no data to read, dw about this
|
||||
// these signify that that were just no data to read, dw about this
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||||
perror("recv");
|
||||
break; // don't want to crash on a recv failure, lets just break
|
||||
}
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
bytes_recv += status;
|
||||
} while (status == BUFFER_INC_LEN); // break when we stop receiving, status < BUFFER_INC_LEN means no more data after this last call
|
||||
|
||||
// break when we stop receiving, status < BUFFER_INC_LEN
|
||||
// means no more data after this last call
|
||||
} while (status == BUFFER_INC_LEN);
|
||||
(*buffer)[bytes_recv] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void add_header_to_string(char** msg_to_send, const char* headers[][2], size_t len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (strcmp(headers[i][0], "") == 0) // if its empty we don't want this header
|
||||
continue;
|
||||
(*msg_to_send) = realloc(*msg_to_send, sizeof(char) *
|
||||
(strlen(*msg_to_send) +
|
||||
strlen(headers[i][0]) +
|
||||
strlen(headers[i][1])
|
||||
));
|
||||
strcat(*msg_to_send, headers[i][1]);
|
||||
strcat(*msg_to_send, headers[i][0]);
|
||||
}
|
||||
}
|
||||
|
||||
void send_message(int socket, http_response* response)
|
||||
{
|
||||
int err;
|
||||
size_t msg_len;
|
||||
|
||||
char* msg_to_send = NULL;
|
||||
|
||||
asprintf(&msg_to_send, "HTTP/%d.%d %d %s", response->status.version.major,
|
||||
response->status.version.minor, response->status.status_code,
|
||||
response->status.reason_phrase);
|
||||
|
||||
const char* general_headers[][2] = {
|
||||
{response->general_headers.cache_control, "\nCache-Control: "},
|
||||
{response->general_headers.connection, "\nConnection: "},
|
||||
{response->general_headers.date, "\nDate: "},
|
||||
{response->general_headers.pragma, "\nPragma: "},
|
||||
{response->general_headers.trailer, "\nTrailer: "},
|
||||
{response->general_headers.transfer_encoding, "\nTransfer-Encoding: "},
|
||||
{response->general_headers.upgrade, "\nUpgrade: "},
|
||||
{response->general_headers.via, "\nVia: "},
|
||||
{response->general_headers.warning, "\nWarning: "},
|
||||
};
|
||||
|
||||
add_header_to_string(&msg_to_send, general_headers, 9);
|
||||
|
||||
const char* response_headers[][2] = {
|
||||
{response->response_headers.accept_ranges, "\nAccept-Ranges: "},
|
||||
{response->response_headers.age, "\nAge: "},
|
||||
{response->response_headers.etag, "\nETag: "},
|
||||
{response->response_headers.location, "\nLocation: "},
|
||||
{response->response_headers.proxy_authenticate, "\nProxy-Authenticate: "},
|
||||
{response->response_headers.retry_after, "\nRetry-After: "},
|
||||
{response->response_headers.server, "\nServer: "},
|
||||
{response->response_headers.vary, "\nVary: "},
|
||||
{response->response_headers.www_authenticate, "\nWWW-Authenticate: "},
|
||||
{response->response_headers.content_type, "\nContent-Type: "},
|
||||
|
||||
};
|
||||
|
||||
add_header_to_string(&msg_to_send, response_headers, 9);
|
||||
|
||||
asprintf(&msg_to_send, "%s\nContent-Length: %d\n\n", msg_to_send, response->response_headers.content_length);
|
||||
asprintf(&msg_to_send, "%s%s\n", msg_to_send, response->message_body);
|
||||
|
||||
|
||||
msg_len = strlen(msg_to_send);
|
||||
err = send(socket, msg_to_send, msg_len, 0);
|
||||
if (err < 0)
|
||||
perror("send");
|
||||
|
||||
free(msg_to_send);
|
||||
|
||||
}
|
||||
|
||||
void* connection_handler(void* arg)
|
||||
{
|
||||
int client_socket = *(int*) arg;
|
||||
http_request request;
|
||||
http_response response;
|
||||
//http_request_line request;
|
||||
char* buffer = NULL;
|
||||
int err;
|
||||
|
||||
recv_message(&buffer, client_socket);
|
||||
// recv message
|
||||
err = recv_message(&buffer, client_socket);
|
||||
if (err == -1) { // if we had a recv error lets throw away the result
|
||||
free(buffer);
|
||||
close(client_socket);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
puts(buffer);
|
||||
|
||||
parse_http_request(&buffer, &request);
|
||||
|
||||
printf("%s %s %1.1f\n", http_method_to_str(request.method), request.request_uri, request.version);
|
||||
// puts(buffer);
|
||||
|
||||
/**
|
||||
* Handle HTTP request here :)
|
||||
*/
|
||||
|
||||
// recv message
|
||||
|
||||
// parse
|
||||
//
|
||||
parse_http_request(&buffer, &request);
|
||||
|
||||
printf("%s %s %d.%d\n", http_method_to_str(request.request_line.method),
|
||||
request.request_line.request_uri, request.request_line.version.major,
|
||||
request.request_line.version.minor);
|
||||
|
||||
// create payload
|
||||
//
|
||||
|
||||
create_http_response(&request, &response);
|
||||
free_http_request(&request);
|
||||
|
||||
|
||||
// send payload
|
||||
|
||||
send_message(client_socket, &response);
|
||||
free_http_response(&response);
|
||||
|
||||
|
||||
free(buffer);
|
||||
close(client_socket);
|
||||
|
Loading…
x
Reference in New Issue
Block a user