basic http implementation
This commit is contained in:
parent
05987d58a9
commit
5861deecd3
|
@ -5,6 +5,8 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
||||||
find_package(libev REQUIRED)
|
find_package(libev REQUIRED)
|
||||||
find_package(libtls REQUIRED)
|
find_package(libtls REQUIRED)
|
||||||
|
|
||||||
|
set(CMAKE_BUILD_TYPE RelWithDebInfo)
|
||||||
|
|
||||||
include(AddAutoformatTarget)
|
include(AddAutoformatTarget)
|
||||||
include(Makeheaders)
|
include(Makeheaders)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
add_executable(${PROJECT_NAME}
|
add_executable(${PROJECT_NAME}
|
||||||
main.c
|
|
||||||
connection.c
|
|
||||||
buffer.c
|
buffer.c
|
||||||
|
connection.c
|
||||||
|
http.c
|
||||||
|
main.c
|
||||||
)
|
)
|
||||||
target_link_libraries(${PROJECT_NAME}
|
target_link_libraries(${PROJECT_NAME}
|
||||||
libev::ev
|
libev::ev
|
||||||
|
|
24
src/buffer.c
24
src/buffer.c
|
@ -1,4 +1,6 @@
|
||||||
#include <buffer.h>
|
#include <buffer.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#if INTERFACE
|
#if INTERFACE
|
||||||
|
@ -11,7 +13,7 @@ struct Buffer {
|
||||||
};
|
};
|
||||||
|
|
||||||
#define buffer_size(buffer) ((buffer)->size)
|
#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)
|
#define buffer_data(buffer) ((buffer)->data)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -25,7 +27,25 @@ void buffer_provide(Buffer *buffer, size_t n) {
|
||||||
buffer->size += 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);
|
return buffer_capacity(buffer) - buffer_size(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <connection.h>
|
#include <connection.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <netdb.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#if INTERFACE
|
#if INTERFACE
|
||||||
|
@ -33,11 +40,69 @@ struct Connection {
|
||||||
|
|
||||||
#endif
|
#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);
|
(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();
|
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) {
|
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_modify(watcher, EV_WRITE);
|
||||||
ev_io_start(EV_A_ watcher);
|
ev_io_start(EV_A_ watcher);
|
||||||
} else if (n == -1) {
|
} else if (n == -1) {
|
||||||
|
warnx("tls_read: %s", tls_error(connection->ctx));
|
||||||
connection->failure_cb(EV_A_ connection);
|
connection->failure_cb(EV_A_ connection);
|
||||||
} else {
|
} else {
|
||||||
buffer_provide(&connection->read_buf, n);
|
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_modify(watcher, EV_WRITE);
|
||||||
ev_io_start(EV_A_ watcher);
|
ev_io_start(EV_A_ watcher);
|
||||||
} else if (n == -1) {
|
} else if (n == -1) {
|
||||||
|
warnx("tls_write: %s", tls_error(connection->ctx));
|
||||||
connection->failure_cb(EV_A_ connection);
|
connection->failure_cb(EV_A_ connection);
|
||||||
} else {
|
} else {
|
||||||
buffer_consume(&connection->write_buf, n);
|
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);
|
ev_io_stop(EV_A_ & connection->watcher);
|
||||||
close(connection->watcher.fd);
|
ret = close(connection->watcher.fd);
|
||||||
tls_free(connection->ctx);
|
tls_free(connection->ctx);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
62
src/http.c
Normal file
62
src/http.c
Normal 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);
|
||||||
|
}
|
10
src/main.c
10
src/main.c
|
@ -3,14 +3,14 @@
|
||||||
#include <tls.h>
|
#include <tls.h>
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
struct tls_config *config;
|
Http http;
|
||||||
Connection connection;
|
|
||||||
|
|
||||||
(void)argc, (void)argv;
|
(void)argc, (void)argv;
|
||||||
tls_init();
|
tls_init();
|
||||||
config = tls_config_new();
|
if (http_get(EV_DEFAULT_ & http, "chat.heizhaus.org",
|
||||||
connection_init(EV_DEFAULT_ & connection, config);
|
"/.well-known/matrix/client")) {
|
||||||
tls_config_free(config);
|
return 1;
|
||||||
|
}
|
||||||
ev_run(EV_DEFAULT_UC_ 0);
|
ev_run(EV_DEFAULT_UC_ 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue