Parsing of simple requests. HashMap.
This commit is contained in:
@@ -9,6 +9,8 @@ add_library(
|
|||||||
STATIC
|
STATIC
|
||||||
src/httpserver.h
|
src/httpserver.h
|
||||||
src/httpserver.c
|
src/httpserver.c
|
||||||
|
src/utils/hashmap.c
|
||||||
|
src/utils/hashmap.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(
|
add_executable(
|
||||||
|
|||||||
@@ -5,14 +5,15 @@
|
|||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
struct HttpResponse* handleRequest(struct HttpRequest* request)
|
http_response_t* handle_request(http_request_t* request)
|
||||||
{
|
{
|
||||||
struct HttpResponse* http_response = {};
|
http_response_t* http_response = {};
|
||||||
return http_response;
|
return http_response;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
struct HttpServer http_server;
|
http_server_t http_server;
|
||||||
|
http_server.handle_request = &handle_request;
|
||||||
printf("Starting the server...\n");
|
printf("Starting the server...\n");
|
||||||
start_http_server(&http_server, ADDRESS, PORT);
|
start_http_server(&http_server, ADDRESS, PORT);
|
||||||
printf("Bye!\n");
|
printf("Bye!\n");
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
#define ADDRESS "127.0.0.1"
|
#define ADDRESS "127.0.0.1"
|
||||||
#define PORT 8080
|
#define PORT 8080
|
||||||
|
|
||||||
struct HttpResponse* handleRequest(struct HttpRequest* request);
|
http_response_t* handle_request(http_request_t* request);
|
||||||
|
|
||||||
int main();
|
int main();
|
||||||
|
|
||||||
#endif //SIMPLEHTTPSERVER_MAIN_H
|
#endif //SIMPLEHTTPSERVER_MAIN_H
|
||||||
207
src/httpserver.c
207
src/httpserver.c
@@ -3,17 +3,36 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "httpserver.h"
|
#include "httpserver.h"
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
void start_http_server(struct HttpServer* http_server, const char *addr, const short port)
|
struct {
|
||||||
|
const char* name;
|
||||||
|
enum http_method method;
|
||||||
|
} method_map[] = {
|
||||||
|
{ "GET", GET },
|
||||||
|
{ "HEAD", HEAD },
|
||||||
|
{ "POST", POST },
|
||||||
|
{ "PUT", PUT },
|
||||||
|
{ "DELETE", DELETE },
|
||||||
|
{ "CONNECT", CONNECT },
|
||||||
|
{ "OPTIONS", OPTIONS },
|
||||||
|
{ "TRACE", TRACE },
|
||||||
|
{ "PATCH", PATCH }
|
||||||
|
};
|
||||||
|
|
||||||
|
void start_http_server(http_server_t* http_server, const char *addr, const short port)
|
||||||
{
|
{
|
||||||
const int server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
const int server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
if (server_fd == -1)
|
if (server_fd == -1)
|
||||||
{
|
{
|
||||||
printf("Failed to create new socket.\n");
|
perror("Failed to create new socket.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int opt = 1;
|
||||||
|
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
|
||||||
|
perror("setsockopt(SO_REUSEADDR) failed");
|
||||||
|
}
|
||||||
|
|
||||||
struct sockaddr_in address;
|
struct sockaddr_in address;
|
||||||
address.sin_family = AF_INET;
|
address.sin_family = AF_INET;
|
||||||
address.sin_addr.s_addr = inet_addr(addr);
|
address.sin_addr.s_addr = inet_addr(addr);
|
||||||
@@ -22,14 +41,14 @@ void start_http_server(struct HttpServer* http_server, const char *addr, const s
|
|||||||
const int bind_c = bind(server_fd, (struct sockaddr*)&address, sizeof(address));
|
const int bind_c = bind(server_fd, (struct sockaddr*)&address, sizeof(address));
|
||||||
if (bind_c == -1)
|
if (bind_c == -1)
|
||||||
{
|
{
|
||||||
printf("Failed to bind address.\n");
|
perror("Failed to bind address.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int listen_c = listen(server_fd, 3);
|
const int listen_c = listen(server_fd, 3);
|
||||||
if (listen_c == -1)
|
if (listen_c == -1)
|
||||||
{
|
{
|
||||||
printf("Failed to begin listening.\n");
|
perror("Failed to begin listening.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,26 +60,194 @@ void start_http_server(struct HttpServer* http_server, const char *addr, const s
|
|||||||
close(server_fd);
|
close(server_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_conn(struct HttpServer* http_server, const int server_fd, struct sockaddr* address, socklen_t* addr_len)
|
void process_conn(http_server_t* http_server, const int server_fd, struct sockaddr* address, socklen_t* addr_len)
|
||||||
{
|
{
|
||||||
const int socket_d = accept(server_fd, address, addr_len);
|
const int socket_d = accept(server_fd, address, addr_len);
|
||||||
if (socket_d == -1)
|
if (socket_d == -1)
|
||||||
{
|
{
|
||||||
printf("Failed to accept connection.\n");
|
perror("Failed to accept connection.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char buffer[1024] = {0};
|
char buffer[1024] = {0};
|
||||||
|
|
||||||
const ssize_t bytes_read = read(socket_d, buffer, 1024);
|
const ssize_t bytes_read = read(socket_d, buffer, 1024);
|
||||||
|
if (bytes_read == -1)
|
||||||
|
{
|
||||||
|
perror("Failed white reading socket.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug output
|
||||||
printf("Client message (%ld):\n", bytes_read);
|
printf("Client message (%ld):\n", bytes_read);
|
||||||
for (int i = 0; i < bytes_read; i++)
|
for (int i = 0; i < bytes_read; i++)
|
||||||
{
|
{
|
||||||
if (buffer[i] == '\r')
|
if (buffer[i] != '\r')
|
||||||
continue;
|
putchar(buffer[i]);
|
||||||
putchar(buffer[i]);
|
|
||||||
}
|
}
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
|
|
||||||
|
enum http_request_parsing_stage request_parsing_stage = START_LINE_METHOD;
|
||||||
|
http_request_t http_request = {};
|
||||||
|
|
||||||
|
char method_str[8];
|
||||||
|
size_t method_str_len = 0;
|
||||||
|
|
||||||
|
char http_path[2048];
|
||||||
|
size_t http_path_len = 0;
|
||||||
|
|
||||||
|
char http_name[16];
|
||||||
|
size_t http_name_len = 0;
|
||||||
|
|
||||||
|
char header_key[256];
|
||||||
|
size_t header_key_length = 0;
|
||||||
|
char header_val[1024];
|
||||||
|
size_t header_val_length = 0;
|
||||||
|
|
||||||
|
http_request.http_content = nullptr;
|
||||||
|
http_request.http_content_len = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < bytes_read; i++)
|
||||||
|
{
|
||||||
|
bool drop = false;
|
||||||
|
switch (request_parsing_stage)
|
||||||
|
{
|
||||||
|
case START_LINE_METHOD:
|
||||||
|
if (buffer[i] == ' ')
|
||||||
|
{
|
||||||
|
method_str[method_str_len] = '\0';
|
||||||
|
http_request.http_method = parse_method_str(method_str, method_str_len);
|
||||||
|
|
||||||
|
if (http_request.http_method == UNKNOWN)
|
||||||
|
{
|
||||||
|
perror("Unknown HTTP method!\n");
|
||||||
|
drop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
request_parsing_stage = START_LINE_PATH;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
method_str[method_str_len++] = buffer[i];
|
||||||
|
break;
|
||||||
|
case START_LINE_PATH:
|
||||||
|
if (buffer[i] == ' ')
|
||||||
|
{
|
||||||
|
http_path[http_path_len] = '\0';
|
||||||
|
http_request.http_path = http_path;
|
||||||
|
http_request.http_path_len = http_path_len;
|
||||||
|
request_parsing_stage = START_LINE_HTTP_VERSION_NAME;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_path[http_path_len++] = buffer[i];
|
||||||
|
break;
|
||||||
|
case START_LINE_HTTP_VERSION_NAME:
|
||||||
|
if (buffer[i] == '/')
|
||||||
|
{
|
||||||
|
http_request.http_version.http_name = http_name;
|
||||||
|
http_request.http_version.http_name_len = http_name_len;
|
||||||
|
|
||||||
|
request_parsing_stage = START_LINE_HTTP_VERSION_MAJOR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_name[http_name_len++] = buffer[i];
|
||||||
|
break;
|
||||||
|
case START_LINE_HTTP_VERSION_MAJOR:
|
||||||
|
if (buffer[i] == '.')
|
||||||
|
{
|
||||||
|
request_parsing_stage = START_LINE_HTTP_VERSION_MINOR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_request.http_version.major = buffer[i] - '0';
|
||||||
|
break;
|
||||||
|
case START_LINE_HTTP_VERSION_MINOR:
|
||||||
|
if (i + 1 < bytes_read && buffer[i] == '\r' && buffer[i + 1] == '\n')
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
http_request.http_headers = new_hash_map();
|
||||||
|
request_parsing_stage = HEADERS_KEY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_request.http_version.minor = buffer[i] - '0';
|
||||||
|
break;
|
||||||
|
case HEADERS_KEY:
|
||||||
|
if (i + 1 < bytes_read && buffer[i] == ':' && buffer[i + 1] == ' ')
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
header_key[header_key_length] = '\0';
|
||||||
|
request_parsing_stage = HEADERS_VALUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + 1 < bytes_read && buffer[i] == '\r' && buffer[i + 1] == '\n')
|
||||||
|
{
|
||||||
|
request_parsing_stage = CONTENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
header_key[header_key_length++] = buffer[i];
|
||||||
|
break;
|
||||||
|
case HEADERS_VALUE:
|
||||||
|
if (i + 1 < bytes_read && buffer[i] == '\r' && buffer[i + 1] == '\n')
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
header_val[header_val_length] = '\0';
|
||||||
|
char* key = calloc(header_key_length + 1, sizeof(char));
|
||||||
|
strncpy(key, header_key, header_key_length);
|
||||||
|
char* val = calloc(header_val_length + 1, sizeof(char));
|
||||||
|
strncpy(val, header_val, header_val_length);
|
||||||
|
|
||||||
|
printf(
|
||||||
|
"Setting header '%s' (hash %d per cap %d): '%s'\n",
|
||||||
|
key, hashcode(http_request.http_headers, key),
|
||||||
|
http_request.http_headers->cap, val);
|
||||||
|
|
||||||
|
set(http_request.http_headers, key, val);
|
||||||
|
header_key[0] = '\0';
|
||||||
|
header_key_length = 0;
|
||||||
|
header_val[0] = '\0';
|
||||||
|
header_val_length = 0;
|
||||||
|
request_parsing_stage = HEADERS_KEY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
header_val[header_val_length++] = buffer[i];
|
||||||
|
break;
|
||||||
|
case CONTENT:
|
||||||
|
default:
|
||||||
|
drop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drop)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_t* http_response = http_server->handle_request(&http_request);
|
||||||
|
|
||||||
|
// TODO: Send response
|
||||||
|
|
||||||
|
free_hash_map(http_request.http_headers);
|
||||||
|
free(http_response);
|
||||||
|
|
||||||
close(socket_d);
|
close(socket_d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum http_method parse_method_str(const char* str, const size_t len)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < sizeof(method_map) / sizeof(method_map[0]); i++) {
|
||||||
|
if (strlen(method_map[i].name) == len &&
|
||||||
|
strncmp(str, method_map[i].name, len) == 0)
|
||||||
|
{
|
||||||
|
return method_map[i].method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
@@ -7,30 +7,79 @@
|
|||||||
#define SIMPLEHTTPSERVER_H
|
#define SIMPLEHTTPSERVER_H
|
||||||
|
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <netinet/in.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include "utils/hashmap.h"
|
||||||
|
|
||||||
struct HttpRequest
|
typedef struct http_version
|
||||||
{
|
{
|
||||||
|
char* http_name; // has to end with "HTTP" btw
|
||||||
|
size_t http_name_len;
|
||||||
|
unsigned short major;
|
||||||
|
unsigned short minor;
|
||||||
|
} http_version_t;
|
||||||
|
|
||||||
|
enum http_method
|
||||||
|
{
|
||||||
|
UNKNOWN,
|
||||||
|
GET,
|
||||||
|
HEAD,
|
||||||
|
POST,
|
||||||
|
PUT,
|
||||||
|
DELETE,
|
||||||
|
CONNECT,
|
||||||
|
OPTIONS,
|
||||||
|
TRACE,
|
||||||
|
PATCH
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HttpResponse
|
enum http_request_parsing_stage
|
||||||
{
|
{
|
||||||
|
START_LINE_METHOD,
|
||||||
|
START_LINE_PATH,
|
||||||
|
START_LINE_HTTP_VERSION_NAME,
|
||||||
|
START_LINE_HTTP_VERSION_MAJOR,
|
||||||
|
START_LINE_HTTP_VERSION_MINOR,
|
||||||
|
HEADERS_KEY,
|
||||||
|
HEADERS_VALUE,
|
||||||
|
CONTENT
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HttpServer
|
typedef struct http_request
|
||||||
{
|
{
|
||||||
struct HttpResponse* (*handleRequest)(struct HttpRequest* request);
|
enum http_method http_method;
|
||||||
};
|
char* http_path;
|
||||||
|
size_t http_path_len;
|
||||||
|
http_version_t http_version;
|
||||||
|
hashmap_t* http_headers;
|
||||||
|
char* http_content;
|
||||||
|
size_t http_content_len;
|
||||||
|
} http_request_t;
|
||||||
|
|
||||||
void start_http_server(struct HttpServer* http_server, const char *addr, short port);
|
typedef struct http_response
|
||||||
void process_conn(struct HttpServer* http_server, int server_fd, struct sockaddr* address, socklen_t* addr_len);
|
{
|
||||||
|
http_version_t http_version;
|
||||||
|
u_short http_status_code;
|
||||||
|
char* http_status_message;
|
||||||
|
size_t http_status_message_len;
|
||||||
|
hashmap_t* http_headers;
|
||||||
|
char* http_content;
|
||||||
|
size_t http_content_len;
|
||||||
|
} http_response_t;
|
||||||
|
|
||||||
|
typedef struct http_server
|
||||||
|
{
|
||||||
|
http_response_t* (*handle_request)(http_request_t* request);
|
||||||
|
} http_server_t;
|
||||||
|
|
||||||
|
void start_http_server(http_server_t* http_server, const char *addr, short port);
|
||||||
|
void process_conn(http_server_t* http_server, int server_fd, struct sockaddr* address, socklen_t* addr_len);
|
||||||
|
enum http_method parse_method_str(const char* str, size_t len);
|
||||||
|
|
||||||
#endif //SIMPLEHTTPSERVER_H
|
#endif //SIMPLEHTTPSERVER_H
|
||||||
125
src/utils/hashmap.c
Normal file
125
src/utils/hashmap.c
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
//
|
||||||
|
// Created by nazar on 13.09.2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "hashmap.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
hashmap_t* new_hash_map() {
|
||||||
|
hashmap_t* this = malloc(sizeof *this);
|
||||||
|
this->cap = DEFAULT_CAPACITY;
|
||||||
|
this->len = 0;
|
||||||
|
this->list = calloc(this->cap, sizeof(pair_t*));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int hashcode(const hashmap_t* this, const char* key)
|
||||||
|
{
|
||||||
|
unsigned int code;
|
||||||
|
for (code = 0; *key != '\0'; key++) {
|
||||||
|
code = *key + 31 * code;
|
||||||
|
}
|
||||||
|
return code % this->cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get(const hashmap_t* this, const char* key)
|
||||||
|
{
|
||||||
|
const unsigned hash = hashcode(this, key);
|
||||||
|
|
||||||
|
const pair_t* existing_pair = this->list[hash];
|
||||||
|
if (existing_pair == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
while (strcmp(existing_pair->key, key) != 0)
|
||||||
|
{
|
||||||
|
if (existing_pair->next == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
existing_pair = existing_pair->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return existing_pair->val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(hashmap_t* this, char* key, char* val)
|
||||||
|
{
|
||||||
|
const unsigned hash = hashcode(this, key);
|
||||||
|
|
||||||
|
pair_t* new_pair = malloc(sizeof *new_pair);
|
||||||
|
new_pair->key = key;
|
||||||
|
new_pair->val = val;
|
||||||
|
new_pair->next = nullptr;
|
||||||
|
|
||||||
|
pair_t* existing_pair = this->list[hash];
|
||||||
|
|
||||||
|
if (existing_pair == nullptr)
|
||||||
|
{
|
||||||
|
this->list[hash] = new_pair;
|
||||||
|
this->len++;
|
||||||
|
|
||||||
|
if (this->len >= this->cap)
|
||||||
|
expand_hash_map(this);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (existing_pair->next != nullptr)
|
||||||
|
{
|
||||||
|
if (strcmp(existing_pair->key, key) == 0)
|
||||||
|
{
|
||||||
|
existing_pair->val = val;
|
||||||
|
free(new_pair);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
existing_pair = existing_pair->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
existing_pair->next = new_pair;
|
||||||
|
this->len++;
|
||||||
|
|
||||||
|
if (this->len >= this->cap)
|
||||||
|
expand_hash_map(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void expand_hash_map(hashmap_t* this)
|
||||||
|
{
|
||||||
|
const unsigned int old_cap = this->cap;
|
||||||
|
pair_t** old_list = this->list;
|
||||||
|
|
||||||
|
this->cap *= 2;
|
||||||
|
this->len = 0;
|
||||||
|
this->list = calloc(this->cap, sizeof(pair_t*));
|
||||||
|
|
||||||
|
for (int i = 0; i < old_cap; i++)
|
||||||
|
{
|
||||||
|
pair_t* iter = old_list[i];
|
||||||
|
while (iter != nullptr)
|
||||||
|
{
|
||||||
|
set(this, iter->key, iter->val);
|
||||||
|
pair_t* prev_iter = iter;
|
||||||
|
iter = iter->next;
|
||||||
|
free(prev_iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(old_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_hash_map(const hashmap_t* this)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < this->cap; i++)
|
||||||
|
{
|
||||||
|
pair_t* iter = this->list[i];
|
||||||
|
while (iter != nullptr)
|
||||||
|
{
|
||||||
|
pair_t* prev_iter = iter;
|
||||||
|
iter = iter->next;
|
||||||
|
free(prev_iter->key);
|
||||||
|
free(prev_iter->val);
|
||||||
|
free(prev_iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(this->list);
|
||||||
|
}
|
||||||
30
src/utils/hashmap.h
Normal file
30
src/utils/hashmap.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// Created by nazar on 13.09.2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SIMPLEHTTPSERVER_HASHMAP_H
|
||||||
|
#define SIMPLEHTTPSERVER_HASHMAP_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define DEFAULT_CAPACITY 8
|
||||||
|
|
||||||
|
typedef struct pair {
|
||||||
|
char* key;
|
||||||
|
char* val;
|
||||||
|
struct pair* next;
|
||||||
|
} pair_t;
|
||||||
|
|
||||||
|
typedef struct hashmap {
|
||||||
|
pair_t** list;
|
||||||
|
unsigned int cap;
|
||||||
|
unsigned int len;
|
||||||
|
} hashmap_t;
|
||||||
|
|
||||||
|
hashmap_t* new_hash_map();
|
||||||
|
unsigned int hashcode(const hashmap_t* this, const char* key);
|
||||||
|
char* get(const hashmap_t* this, const char* key);
|
||||||
|
void set(hashmap_t* this, char* key, char* val);
|
||||||
|
void expand_hash_map(hashmap_t* this);
|
||||||
|
void free_hash_map(const hashmap_t* this);
|
||||||
|
|
||||||
|
#endif //SIMPLEHTTPSERVER_HASHMAP_H
|
||||||
Reference in New Issue
Block a user