basic http implementation

development
Thomas Lindner 2022-10-19 23:34:21 +02:00
parent 05987d58a9
commit 5861deecd3
6 changed files with 169 additions and 14 deletions

View File

@ -5,6 +5,8 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
find_package(libev REQUIRED)
find_package(libtls REQUIRED)
set(CMAKE_BUILD_TYPE RelWithDebInfo)
include(AddAutoformatTarget)
include(Makeheaders)

View File

@ -1,7 +1,8 @@
add_executable(${PROJECT_NAME}
main.c
connection.c
buffer.c
connection.c
http.c
main.c
)
target_link_libraries(${PROJECT_NAME}
libev::ev

View File

@ -1,4 +1,6 @@
#include <buffer.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#if INTERFACE
@ -11,7 +13,7 @@ struct Buffer {
};
#define buffer_size(buffer) ((buffer)->size)
#define buffer_capacity(buffer) (sizeof(buffer)->data)
#define buffer_capacity(buffer) (sizeof((buffer)->data))
#define buffer_data(buffer) ((buffer)->data)
#endif
@ -25,7 +27,25 @@ void buffer_provide(Buffer *buffer, size_t n) {
buffer->size += n;
}
size_t buffer_size_remaining(Buffer *buffer) {
int buffer_printf(Buffer *buffer, const char *format, ...) {
va_list ap;
int ret;
va_start(ap, format);
ret = vsnprintf(buffer_data_remaining(buffer), buffer_size_remaining(buffer),
format, ap);
if (ret > 0) {
if ((size_t)ret < buffer_size_remaining(buffer)) {
buffer_provide(buffer, ret);
} else {
buffer_provide(buffer, buffer_size_remaining(buffer));
}
}
va_end(ap);
return ret;
}
size_t buffer_size_remaining(const Buffer *buffer) {
return buffer_capacity(buffer) - buffer_size(buffer);
}

View File

@ -1,5 +1,12 @@
#include <sys/socket.h>
#include <sys/types.h>
#include <assert.h>
#include <connection.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#if INTERFACE
@ -33,11 +40,69 @@ struct Connection {
#endif
void connection_init(EV_P_ Connection *connection, struct tls_config *config) {
int connection_open(EV_P_ Connection *connection, struct tls_config *config,
const char *servername, const char *serverport) {
struct addrinfo hints, *res, *res0;
int error, fd, tmp;
const char *cause;
(void)(EV_A_ 0);
ev_io_init(&connection->watcher, NULL, STDIN_FILENO, 0);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo(servername, serverport, &hints, &res0);
if (error) {
warnx("getaddrinfo: %s", gai_strerror(error));
goto err_getaddrinfo;
}
for (res = res0; res; res = res->ai_next) {
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (fd == -1) {
cause = "socket";
continue;
}
if (connect(fd, res->ai_addr, res->ai_addrlen) == -1) {
tmp = errno;
close(fd);
errno = tmp;
cause = "connect";
continue;
}
break;
}
if (!fd) {
warn("%s", cause);
goto err_connect;
}
ev_io_init(&connection->watcher, NULL, fd, 0);
connection->ctx = tls_client();
tls_configure(connection->ctx, config);
if (!connection->ctx) {
warnx("tls_client failed");
goto err_tls_client;
}
if (tls_configure(connection->ctx, config)) {
warnx("tls_configure: %s", tls_error(connection->ctx));
goto err_tls_configure;
}
if (tls_connect_socket(connection->ctx, fd, servername)) {
warnx("tls_connect: %s", tls_error(connection->ctx));
goto err_tls_connect_socket;
}
freeaddrinfo(res0);
return 0;
err_tls_connect_socket:
err_tls_configure:
tls_free(connection->ctx);
err_tls_client:
close(fd);
err_connect:
freeaddrinfo(res0);
err_getaddrinfo:
return -1;
}
void connection_read_some(EV_P_ Connection *connection) {
@ -61,6 +126,7 @@ static void connection_read_some_cb(EV_P_ ev_io *watcher, int revents) {
ev_io_modify(watcher, EV_WRITE);
ev_io_start(EV_A_ watcher);
} else if (n == -1) {
warnx("tls_read: %s", tls_error(connection->ctx));
connection->failure_cb(EV_A_ connection);
} else {
buffer_provide(&connection->read_buf, n);
@ -89,6 +155,7 @@ static void connection_write_some_cb(EV_P_ ev_io *watcher, int revents) {
ev_io_modify(watcher, EV_WRITE);
ev_io_start(EV_A_ watcher);
} else if (n == -1) {
warnx("tls_write: %s", tls_error(connection->ctx));
connection->failure_cb(EV_A_ connection);
} else {
buffer_consume(&connection->write_buf, n);
@ -96,8 +163,11 @@ static void connection_write_some_cb(EV_P_ ev_io *watcher, int revents) {
}
}
void connection_stop(EV_P_ Connection *connection) {
int connection_close(EV_P_ Connection *connection) {
int ret;
ev_io_stop(EV_A_ & connection->watcher);
close(connection->watcher.fd);
ret = close(connection->watcher.fd);
tls_free(connection->ctx);
return ret;
}

62
src/http.c Normal file
View File

@ -0,0 +1,62 @@
#include <http.h>
#include <stdio.h>
#if INTERFACE
struct Http {
Connection connection;
};
#endif
int http_get(EV_P_ Http *http, const char *servername, const char *path) {
struct tls_config *config;
config = tls_config_new();
if (connection_open(EV_DEFAULT_ & http->connection, config, servername,
"https")) {
goto err_connection_open;
}
buffer_printf(connection_write_buf(&http->connection),
"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",
path, servername);
connection_set_failure_cb(&http->connection, error_cb);
connection_set_cb(&http->connection, http_get_cb);
connection_write_some(EV_A_ & http->connection);
tls_config_free(config);
return 0;
err_connection_open:
tls_config_free(config);
return -1;
}
static void error_cb(EV_P_ Connection *connection) {
connection_close(EV_A_ connection);
ev_break(EV_A_ EVBREAK_ALL);
}
static void http_get_cb(EV_P_ Connection *connection) {
if (buffer_size(connection_write_buf(connection))) {
connection_write_some(EV_A_ connection);
return;
}
connection_set_cb(connection, http_response_cb);
connection_read_some(EV_A_ connection);
}
static void http_response_cb(EV_P_ Connection *connection) {
Buffer *read_buf;
size_t n;
read_buf = connection_read_buf(connection);
n = buffer_size(read_buf);
if (n) {
fwrite(buffer_data(read_buf), n, 1, stdout);
buffer_consume(read_buf, n);
connection_read_some(EV_A_ connection);
return;
}
connection_close(EV_A_ connection);
ev_break(EV_A_ EVBREAK_ALL);
}

View File

@ -3,14 +3,14 @@
#include <tls.h>
int main(int argc, char **argv) {
struct tls_config *config;
Connection connection;
Http http;
(void)argc, (void)argv;
tls_init();
config = tls_config_new();
connection_init(EV_DEFAULT_ & connection, config);
tls_config_free(config);
if (http_get(EV_DEFAULT_ & http, "chat.heizhaus.org",
"/.well-known/matrix/client")) {
return 1;
}
ev_run(EV_DEFAULT_UC_ 0);
return 0;
}