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
|
TARGET=http
|
||||||
CC=gcc
|
CC=gcc
|
||||||
|
|
||||||
OBJ = main.o tcp.o http.o
|
OBJ = main.o tcp.o http.o file.o
|
||||||
|
|
||||||
$(TARGET): $(OBJ)
|
$(TARGET): $(OBJ)
|
||||||
mkdir -p ../build
|
mkdir -p ../build
|
||||||
@ -10,7 +10,8 @@ $(TARGET): $(OBJ)
|
|||||||
|
|
||||||
main.o: include/tcp.h
|
main.o: include/tcp.h
|
||||||
tcp.o: include/tcp.h include/http.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
|
.PHONY: clean
|
||||||
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 <stdio.h>
|
||||||
|
|
||||||
#include "include/http.h"
|
#include "include/http.h"
|
||||||
|
#include "include/file.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
|
|
||||||
|
|
||||||
http_method_t str_to_http_method(char* input)
|
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;
|
char *tok, *ver, *num;
|
||||||
|
|
||||||
@ -103,6 +242,7 @@ void parse_http_request(char** buffer, http_request* request)
|
|||||||
|
|
||||||
// first lets split each line
|
// first lets split each line
|
||||||
char* line;
|
char* line;
|
||||||
|
http_request_line r_line;
|
||||||
|
|
||||||
// parse the first line
|
// parse the first line
|
||||||
line = strtok(*buffer, "\n");
|
line = strtok(*buffer, "\n");
|
||||||
@ -111,7 +251,9 @@ void parse_http_request(char** buffer, http_request* request)
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_request_line(&line, request);
|
parse_request_line(&line, &r_line);
|
||||||
|
|
||||||
|
request->request_line = r_line;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: start accepting the headers
|
* 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
|
#ifndef HTTP_H
|
||||||
#define HTTP_H
|
#define HTTP_H
|
||||||
|
|
||||||
@ -7,6 +9,28 @@ typedef enum {
|
|||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
} http_method_t;
|
} 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 {
|
struct http_version {
|
||||||
int major;
|
int major;
|
||||||
int minor;
|
int minor;
|
||||||
@ -16,15 +40,44 @@ typedef struct {
|
|||||||
http_method_t method;
|
http_method_t method;
|
||||||
char* request_uri;
|
char* request_uri;
|
||||||
struct http_version version;
|
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;
|
} 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);
|
http_method_t str_to_http_method(char* input);
|
||||||
const char* http_method_to_str(http_method_t 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 parse_http_request(char** buffer, http_request* request);
|
||||||
|
|
||||||
|
void create_http_response(http_request* request, http_response* response);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -16,7 +16,7 @@ typedef struct {
|
|||||||
|
|
||||||
void setup_socket(server_conn_t* conn);
|
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
|
/// Threaded function to handle http requests
|
||||||
/// takes client socket as an argument
|
/// takes client socket as an argument
|
||||||
|
34
src/main.c
34
src/main.c
@ -5,6 +5,40 @@
|
|||||||
|
|
||||||
#include "include/tcp.h"
|
#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)
|
int main(void)
|
||||||
{
|
{
|
||||||
server_conn_t server;
|
server_conn_t server;
|
||||||
|
122
src/tcp.c
122
src/tcp.c
@ -5,6 +5,7 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "include/tcp.h"
|
#include "include/tcp.h"
|
||||||
#include "include/http.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;
|
ulong current_size, bytes_recv;
|
||||||
int status;
|
int status;
|
||||||
@ -70,43 +71,136 @@ void recv_message(char** buffer, int socket)
|
|||||||
// lets recv BUFFER_INC_LEN amount of bytes
|
// lets recv BUFFER_INC_LEN amount of bytes
|
||||||
status = recv(socket, *buffer + bytes_recv, BUFFER_INC_LEN, MSG_DONTWAIT);
|
status = recv(socket, *buffer + bytes_recv, BUFFER_INC_LEN, MSG_DONTWAIT);
|
||||||
if (status < 0) {
|
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");
|
perror("recv");
|
||||||
break; // don't want to crash on a recv failure, lets just break
|
}
|
||||||
|
return -1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes_recv += status;
|
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';
|
(*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)
|
void* connection_handler(void* arg)
|
||||||
{
|
{
|
||||||
int client_socket = *(int*) arg;
|
int client_socket = *(int*) arg;
|
||||||
http_request request;
|
http_request request;
|
||||||
|
http_response response;
|
||||||
|
//http_request_line request;
|
||||||
char* buffer = NULL;
|
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);
|
// puts(buffer);
|
||||||
|
|
||||||
parse_http_request(&buffer, &request);
|
|
||||||
|
|
||||||
printf("%s %s %1.1f\n", http_method_to_str(request.method), request.request_uri, request.version);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle HTTP request here :)
|
* 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 payload
|
||||||
//
|
|
||||||
|
create_http_response(&request, &response);
|
||||||
|
free_http_request(&request);
|
||||||
|
|
||||||
|
|
||||||
// send payload
|
// send payload
|
||||||
|
|
||||||
|
send_message(client_socket, &response);
|
||||||
|
free_http_response(&response);
|
||||||
|
|
||||||
|
|
||||||
free(buffer);
|
free(buffer);
|
||||||
close(client_socket);
|
close(client_socket);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user