ws

ws.git
git clone git://git.lenczewski.org/ws.git
Log | Files | Refs | LICENSE

ws-client.c (3994B)


      1 #define _GNU_SOURCE 1
      2 #define _POSIX_C_SOURCE 1
      3 
      4 #include <signal.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #include <unistd.h>
      9 
     10 #include <arpa/inet.h>
     11 #include <sys/socket.h>
     12 
     13 #include <openssl/evp.h>
     14 #include <openssl/sha.h>
     15 
     16 #include "sockaddr.h"
     17 #include "http.h"
     18 #include "ws.h"
     19 
     20 static int
     21 do_http_handshake(int client, struct arena *arena, char *protocols, char *extensions)
     22 {
     23 	char buf[4096], *term = NULL;
     24 	size_t len = 0;
     25 
     26 	/* build request */
     27 	unsigned char key[16] = "0123456789abcdef", keyenc[WS_KEY_LENGTH];
     28 	int keyenc_len = ws_key_digest(key, sizeof key, keyenc);
     29 
     30 	size_t cap = snprintf(buf, sizeof buf,
     31 			"GET /home HTTP/1.1\r\n"
     32 			"Origin: localhost\r\n"
     33 			"Host: localhost\r\n"
     34 			"Connection: Upgrade\r\n"
     35 			"Upgrade: websocket\r\n"
     36 			"Sec-WebSocket-Version: 13\r\n"
     37 			"Sec-WebSocket-Key: %.*s\r\n", keyenc_len, keyenc);
     38 
     39 	if (protocols)
     40 		cap += snprintf(buf + cap, sizeof buf - cap,
     41 				"Sec-WebSocket-Protocol: %s\r\n", protocols);
     42 
     43 	if (extensions)
     44 		cap += snprintf(buf + cap, sizeof buf - cap,
     45 				"Sec-WebSocket-Extensions: %s\r\n", extensions);
     46 
     47 	cap += snprintf(buf + cap, sizeof buf - cap, "\r\n");
     48 
     49 	/* send request */
     50 	do {
     51 		ssize_t res = send(client, buf + len, cap - len, 0);
     52 		if (res < 0) return -1;
     53 		len += cap;
     54 	} while (len < cap);
     55 
     56 	/* recv response */
     57 	if (http_receive_msg(client, buf, sizeof buf, &term) < 0)
     58 		return -1;
     59 
     60 	/* parse response */
     61 	struct http_msg response;
     62 	if (http_parse_msg(arena, buf, &response) < 0)
     63 		return -1;
     64 
     65 	http_print_msg(&response);
     66 
     67 	/* handle response */
     68 	struct http_header *connection, *upgrade;
     69 	struct http_header *ws_accept, *ws_proto, *ws_ext;
     70 
     71 	connection = http_header_find(response.headers, "connection");
     72 	upgrade = http_header_find(response.headers, "upgrade");
     73 
     74 	if (!connection || !upgrade) return -1;
     75 	if (!http_header_has_value(connection, "Upgrade")) return -1;
     76 	if (!http_header_has_value(upgrade, "websocket")) return -1;
     77 
     78 	ws_accept = http_header_find(response.headers, "sec-websocket-accept");
     79 	ws_proto = http_header_find(response.headers, "sec-websocket-protocol");
     80 	ws_ext = http_header_find(response.headers, "sec-websocket-extensions");
     81 
     82 	if (!ws_accept) return -1;
     83 
     84 	unsigned char acceptenc[WS_KEY_LENGTH];
     85 	ws_key_digest(keyenc, keyenc_len, acceptenc);
     86 
     87 	if (!http_header_has_value(ws_accept, (char *) acceptenc)) return -1;
     88 	if (ws_proto && !http_header_has_value(ws_proto, protocols)) return -1;
     89 	if (ws_ext && !http_header_has_value(ws_ext, extensions)) return -1;
     90 
     91 	return 0;
     92 }
     93 
     94 static int
     95 handle(int client)
     96 {
     97 	char buf[8192];
     98 	struct arena arena = { .ptr = buf, .cap = sizeof buf, .len = 0, };
     99 
    100 	if (do_http_handshake(client, &arena, NULL, NULL) < 0)
    101 		return -1;
    102 
    103 	printf("client completed http handshake\n");
    104 
    105 	char msg[] = "Hello, World!\n";
    106 	uint32_t mask = rand();
    107 	int fragment = 0;
    108 
    109 	if (ws_msg_send(client, WS_DATA_UTF8, (unsigned char *) msg, sizeof msg, mask, fragment) < 0)
    110 		return -1;
    111 
    112 	printf("client sent websocket message\n");
    113 
    114 	struct ws_frame frame;
    115 	if (ws_frame_recv(client, &frame) < 0)
    116 		return -1;
    117 
    118 	printf("client received websocket frame: fin: %d, mask: %u, len: %lu\n",
    119 			frame.fin, frame.mask, frame.len);
    120 
    121 	if (ws_data_recv(client, &frame, (unsigned char *) buf, sizeof buf) < 0)
    122 		return -1;
    123 
    124 	printf("client reeived websocket payload:\n%.*s\n",
    125 			(int) frame.len, buf);
    126 
    127 	if (ws_close(client, WS_ERROR_OK, NULL, 0, rand()) < 0)
    128 		return -1;
    129 
    130 	printf("client closed websocket\n");
    131 
    132 	return 0;
    133 }
    134 
    135 int
    136 main(void)
    137 {
    138 	int client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    139 	if (client < 0) {
    140 		perror("socket");
    141 		exit(EXIT_FAILURE);
    142 	}
    143 
    144 	struct sockaddr_storage addr;
    145 	socklen_t addrlen;
    146 	get_server_addr(&addr, &addrlen);
    147 
    148 	if (connect(client, (struct sockaddr *) &addr, sizeof addr) < 0) {
    149 		perror("connect");
    150 		exit(EXIT_FAILURE);
    151 	}
    152 
    153 	printf("client connected to %s:%d\n", SERVER_ADDR, SERVER_PORT);
    154 
    155 	handle(client);
    156 
    157 	shutdown(client, SHUT_RDWR);
    158 	close(client);
    159 
    160 	printf("client disconnected\n");
    161 
    162 	exit(EXIT_SUCCESS);
    163 }