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(libtls REQUIRED)
|
||||
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo)
|
||||
|
||||
include(AddAutoformatTarget)
|
||||
include(Makeheaders)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
24
src/buffer.c
24
src/buffer.c
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
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>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue