X-Git-Url: https://git.cworth.org/git?p=ttt;a=blobdiff_plain;f=src%2Fttt-client.c;h=9d44945e506664678854fab51c41fa3fd04e870d;hp=80fca846e13ee46e4fae37b87ee966901a91de9a;hb=907321c065fb1383e800f0794981df91d4327f57;hpb=5d56d3cd8b4685e905ed604277ac9cb32c876127 diff --git a/src/ttt-client.c b/src/ttt-client.c index 80fca84..9d44945 100644 --- a/src/ttt-client.c +++ b/src/ttt-client.c @@ -21,46 +21,214 @@ #include "ttt-client.h" +#include "ttt-server.h" +#include "ttt-socket.h" +#include "ttt-error.h" + struct _ttt_client { + pthread_mutex_t mutex; + pthread_t thread; + ttt_server_t *server; int socket; + + int id; + + char buf[TTT_CLIENT_BUF_SIZE]; + char *buf_head; + char *buf_tail; + + char *request; + int request_size; + int request_len; }; -ttt_client_t * -ttt_client_create (ttt_server_t *server, int socket) +static void +_ttt_client_init (ttt_client_t *client, + ttt_server_t *server, + int socket) { - ttt_client_t *client; - - client = xmalloc (sizeof (ttt_client_t)); + pthread_mutex_init (&client->mutex, NULL); client->server = server; client->socket = socket; - return client; + client->buf_head = client->buf; + client->buf_tail = client->buf; + + client->request = NULL; + client->request_size = 0; + client->request_len = 0; + + /* XXX: Probably want to register only as the result of the HELO + command. Not only will that match the protocol correctly, but + it will also eliminate a race condition here. */ + client->id = ttt_server_register_client (server, client); } -void -ttt_client_destroy (ttt_client_t *client) +static void +_ttt_client_fini (ttt_client_t *client) { - close (client->socket); + pthread_mutex_lock (&client->mutex); - free (client); + ttt_server_unregister_client (client->server, client); + + shutdown (client->socket, SHUT_RDWR); + + free (client->request); + + pthread_mutex_unlock (&client->mutex); + + pthread_mutex_destroy (&client->mutex); } -void -ttt_client_handle_requests (ttt_client_t *client) +static void +_append_to_request (ttt_client_t *client, + const char *buf, + int size) { -#define BUF_SIZE 1024 + int size_needed = client->request_len + size; + + if (size_needed > client->request_size) { + if (client->request_size == 0) { + client->request_size = size_needed; + } else { + while (size_needed > client->request_size) + client->request_size *= 2; + } + + client->request = xrealloc (client->request, client->request_size); + } + + memcpy (client->request + client->request_len, + buf, size); + + client->request_len += size; +} + +static ttt_status_t +_read_into_request_until (ttt_client_t *client, char delimeter) +{ + ttt_bool_t found_delimeter = FALSE; + int bytes_read; + char *s; + + client->request_len = 0; while (1) { - char buf[BUF_SIZE]; - int cnt; - cnt = read (client->socket, buf, BUF_SIZE); - if (cnt == 0) + + if (client->buf_tail >= client->buf_head) { + bytes_read = xread (client->socket, + client->buf, + TTT_CLIENT_BUF_SIZE); + if (bytes_read == 0) + return TTT_STATUS_EOF; + client->buf_head = client->buf; + client->buf_tail = client->buf_head + bytes_read; + } + + for (s = client->buf_head; s < client->buf_tail; s++) { + if (*s == delimeter) { + found_delimeter = TRUE; + s++; + break; + } + } + + _append_to_request (client, + client->buf_head, + s - client->buf_head); + client->buf_head = s; + + if (found_delimeter) + return TTT_STATUS_SUCCESS; + } +} + +static ttt_status_t +_read_request (ttt_client_t *client) +{ + ttt_status_t status; + static const char null_terminator = '\0'; + + status = _read_into_request_until (client, '\n'); + if (status) + return status; + + _append_to_request (client, &null_terminator, 1); + + return TTT_STATUS_SUCCESS; +} + +static ttt_error_t +_execute_request (ttt_client_t *client) +{ + ttt_server_broadcast (client->server, client->request); + + return TTT_ERROR_NONE; +} + +static void * +_handle_requests_thread (void *closure) +{ + ttt_status_t status; + ttt_error_t error; + ttt_client_t *client = closure; + + while (1) { + + status = _read_request (client); + if (status == TTT_STATUS_EOF) break; - write (0, buf, cnt); - write (client->socket, buf, cnt); + if (status) + ASSERT_NOT_REACHED; + + error = _execute_request (client); + if (error) + ttt_client_send (client, ttt_error_string (error)); } + + _ttt_client_fini (client); + free (client); + + return (void *) 0; } +/* Exported: See ttt-client.h for documentation. */ +void +ttt_client_new (void *closure, int client_socket) +{ + ttt_server_t *server = closure; + ttt_client_t *client; + int err; + + client = xmalloc (sizeof (ttt_client_t)); + + _ttt_client_init (client, server, client_socket); + + err = pthread_create (&client->thread, NULL, + _handle_requests_thread, client); + if (err != 0) { + fprintf (stderr, "Error: pthread_create failed: %s. Aborting.\n", + strerror (err)); + exit (1); + } +} +/* Exported: See ttt-client.h for documentation. */ +void +ttt_client_send (ttt_client_t *client, const char *message) +{ + pthread_mutex_lock (&client->mutex); + + ttt_socket_write (client->socket, message, strlen (message)); + + pthread_mutex_unlock (&client->mutex); +} + +/* Exported: See ttt-client.h for documentation. */ +int +ttt_client_get_id (ttt_client_t *client) +{ + return client->id; +}