Compare commits
No commits in common. "04abd8b152edf2f198bf88bc3c98400c14c24e3d" and "16481ec708ff41a51d6fc1d77144223c1fc9fba7" have entirely different histories.
04abd8b152
...
16481ec708
@ -1,7 +1,7 @@
|
|||||||
TARGET=http
|
TARGET=http
|
||||||
CC=gcc
|
CC=gcc
|
||||||
|
|
||||||
OBJ = main.o tcp.o http.o file.o
|
OBJ = main.o tcp.o http.o
|
||||||
|
|
||||||
$(TARGET): $(OBJ)
|
$(TARGET): $(OBJ)
|
||||||
mkdir -p ../build
|
mkdir -p ../build
|
||||||
@ -10,8 +10,7 @@ $(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 include/file.h
|
http.o: include/http.h
|
||||||
file.o: include/file.h
|
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
|
71
src/file.c
71
src/file.c
@ -1,71 +0,0 @@
|
|||||||
#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,7 +3,22 @@
|
|||||||
#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)
|
||||||
{
|
{
|
||||||
@ -29,161 +44,7 @@ const char* http_method_to_str(http_method_t input)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* http_status_code_to_str(http_status_code_t code)
|
void parse_request_line(char** line, http_request* request)
|
||||||
{
|
|
||||||
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;
|
||||||
|
|
||||||
@ -242,7 +103,6 @@ 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");
|
||||||
@ -251,9 +111,7 @@ void parse_http_request(char** buffer, http_request* request)
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_request_line(&line, &r_line);
|
parse_request_line(&line, request);
|
||||||
|
|
||||||
request->request_line = r_line;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: start accepting the headers
|
* TODO: start accepting the headers
|
||||||
@ -274,24 +132,3 @@ 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";
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
#ifndef FILE_H
|
|
||||||
#define FILE_H
|
|
||||||
|
|
||||||
void get_file_from_uri(char** uri, char** buffer);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,334 +0,0 @@
|
|||||||
#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,5 +1,3 @@
|
|||||||
#include "headers.h"
|
|
||||||
|
|
||||||
#ifndef HTTP_H
|
#ifndef HTTP_H
|
||||||
#define HTTP_H
|
#define HTTP_H
|
||||||
|
|
||||||
@ -9,28 +7,6 @@ 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;
|
||||||
@ -40,44 +16,15 @@ 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);
|
||||||
|
|
||||||
const char* http_status_code_to_str(http_status_code_t code);
|
void parse_request_line(char** line, http_request* request);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
int recv_message(char** buffer, int socket);
|
void 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,40 +5,6 @@
|
|||||||
|
|
||||||
#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,7 +5,6 @@
|
|||||||
#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"
|
||||||
@ -43,7 +42,7 @@ void setup_socket(server_conn_t* conn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int recv_message(char** buffer, int socket)
|
void recv_message(char** buffer, int socket)
|
||||||
{
|
{
|
||||||
ulong current_size, bytes_recv;
|
ulong current_size, bytes_recv;
|
||||||
int status;
|
int status;
|
||||||
@ -71,135 +70,42 @@ int 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) {
|
||||||
// these signify that that were just no data to read, dw about this
|
if (errno != EAGAIN && errno != EWOULDBLOCK) // 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
|
recv_message(&buffer, client_socket);
|
||||||
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_http_request(&buffer, &request);
|
// parse
|
||||||
|
//
|
||||||
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