diff options
author | Simon Rettberg | 2014-03-15 01:49:50 +0100 |
---|---|---|
committer | Simon Rettberg | 2014-03-15 01:49:50 +0100 |
commit | bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad (patch) | |
tree | c7d1995a09f6ed0c4e6873252e957d72f5d07d07 /client.c | |
download | ldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.tar.gz ldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.tar.xz ldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.zip |
Lean and mean initial commit
Not much functionality yet
Diffstat (limited to 'client.c')
-rw-r--r-- | client.c | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/client.c b/client.c new file mode 100644 index 0000000..b71b69e --- /dev/null +++ b/client.c @@ -0,0 +1,136 @@ +#include "client.h" +#include "proxy.h" +#include "epoll.h" +#include "helper.h" +#include "asn1.h" +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> + +#define MAX_SEND_BUFFER 100000 + +static void client_flush(epoll_client_t *client); + +static void client_free(epoll_client_t *client) +{ + proxy_removeClient(client); + if (client->sendBuffer != NULL) free(client->sendBuffer); + if (client->fd != -1) { + //ePoll_remove(client->fd); // epoll would remove closed descriptors automatically, but just to be sure we don't cause use after free, we remove it explicitly + close(client->fd); + } + free(client); +} + +void client_callback(void *data, int haveIn, int haveOut, int doCleanup) +{ + epoll_client_t *client = (epoll_client_t*)data; + if (doCleanup) { + printf("Client gone.\n"); + client_free(client); + return; + } + // Anything to read? + if (haveIn) { + for (;;) { + if (client->rbPos >= REQLEN) { + printf("[C->Proxy] Read buffer overflow. Disconnecting.\n"); + client_free(client); + return; + } + const size_t buflen = REQLEN - client->rbPos; + const ssize_t ret = read(client->fd, client->readBuffer + client->rbPos, buflen); + if (ret < 0 && errno == EINTR) continue; + if (ret < 0 && errno == EAGAIN) break; + if (ret <= 0) { + printf("Client gone while reading.\n"); + client_free(client); + return; + } + client->rbPos += ret; + // Request complete? + for (;;) { + size_t consumed, len; + consumed = scan_asn1SEQUENCE(client->readBuffer, client->readBuffer + client->rbPos, &len); + if (consumed == 0) break; // Length-Header not complete + len += consumed; + if (len > client->rbPos) break; // Body not complete yet + printf("Received complete requrest...\n"); + if (proxy_fromClient(client, len) == -1) { + printf("Error parsing request from client.\n"); + client_free(client); + return; + } + // Shift remaining buffer contents + if (len == client->rbPos) { + client->rbPos = 0; + break; + } + memmove(client->readBuffer, client->readBuffer + len, client->rbPos - len); + client->rbPos -= len; + } + if ((ssize_t)buflen > ret) break; // Read less than buffer len, epoll will fire again + } + } + // Anything to send? + if (haveOut) client_flush(client); +} + +static void client_flush(epoll_client_t *client) +{ + while (client->sbPos < client->sbFill) { + const int tosend = client->sbFill - client->sbPos; + const int ret = write(client->fd, client->sendBuffer + client->sbPos, tosend); + if (ret < 0 && errno == EINTR) continue; + if (ret < 0 && errno == EAGAIN) return; + if (ret <= 0) { + printf("Cannot send to client (ret: %d, errno: %d)\n", ret, errno); + return; + } + client->sbPos += ret; + if (ret != tosend) return; // Sent less than requested -> buffer full, epoll will trigger us again + } + client->sbPos = client->sbFill = 0; +} + +int client_send(epoll_client_t *client, const char *buffer, size_t len, const BOOL cork) +{ + if (client->sbFill == 0 && !cork) { + // Nothing in send buffer, fire away + const int ret = write(client->fd, buffer, len); + if (ret == 0 || (ret < 0 && errno != EINTR && errno != EAGAIN)) { + printf("Client gone when trying to send.\n"); + return -1; + } + if (ret == (int)len) return 0; + // Couldn't send everything, continue with buffering logic below + if (ret > 0) { + buffer += ret; + len -= (size_t)ret; + } + } + // Sanity + if ((client->sbLen - client->sbPos) + len > MAX_SEND_BUFFER) { + printf("Dropping client as the send buffer would exceed %d bytes.\n", (int)MAX_SEND_BUFFER); + return -1; + } + // Buffer... + if (client->sbLen - client->sbFill < len) { // Buffer too small? + if (client->sbPos != 0) { // See if we can compact it + memmove(client->sendBuffer, client->sendBuffer + client->sbPos, client->sbFill - client->sbPos); + client->sbFill -= client->sbPos; + client->sbPos = 0; + } + if (client->sbLen - client->sbFill < len) { // Still too small after compacting? realloc + if (helper_realloc(&client->sendBuffer, &client->sbLen, client->sbLen + len + 1500, "client_send") == -1) return -1; + } + } + // Finally append to buffer + memcpy(client->sendBuffer + client->sbFill, buffer, len); + client->sbFill += len; + if (!cork) client_flush(client); + return 0; +} + |