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 }