h2o_websocket_conn_t *h2o_upgrade_to_websocket(h2o_req_t *req, const char *client_key, void *data, h2o_websocket_msg_callback cb) { h2o_websocket_conn_t *conn = h2o_mem_alloc(sizeof(*conn)); char accept_key[29]; /* only for http1 connection */ assert(req->version < 0x200); /* setup the context */ memset(conn, 0, sizeof(*conn)); // conn->sock = sock; set by on_complete conn->ws_callbacks.recv_callback = recv_callback; conn->ws_callbacks.send_callback = send_callback; conn->ws_callbacks.on_msg_recv_callback = on_msg_callback; conn->data = data; conn->cb = cb; wslay_event_context_server_init(&conn->ws_ctx, &conn->ws_callbacks, conn); /* build response */ create_accept_key(accept_key, client_key); req->res.status = 101; req->res.reason = "Switching Protocols"; h2o_add_header(&req->pool, &req->res.headers, H2O_TOKEN_UPGRADE, NULL, H2O_STRLIT("websocket")); h2o_add_header_by_str(&req->pool, &req->res.headers, H2O_STRLIT("sec-websocket-accept"), 0, NULL, accept_key, strlen(accept_key)); /* send */ h2o_http1_upgrade(req, NULL, 0, on_complete, conn); return conn; }
/* * Performs HTTP handshake. *fd* is the file descriptor of the * connection to the client. This function returns 0 if it succeeds, * or returns -1. */ int http_handshake(int fd) { char header[16384], *accept_key, *keyhdstart, *keyhdend, res_header[256]; size_t header_length = 0, res_header_sent = 0, res_header_length; ssize_t r; while(1) { while((r = read(fd, header+header_length, sizeof(header)-header_length)) == -1 && errno == EINTR); if(r == -1) { perror("read"); return -1; } else if(r == 0) { fprintf(stderr, "HTTP Handshake: Got EOF\n"); return -1; } else { header_length += r; if(header_length >= 4 && memcmp(header+header_length-4, "\r\n\r\n", 4) == 0) { break; } else if(header_length == sizeof(header)) { fprintf(stderr, "HTTP Handshake: Too large HTTP headers\n"); return -1; } } } if(http_header_find_field_value(header, "Upgrade", "websocket") == NULL || http_header_find_field_value(header, "Connection", "Upgrade") == NULL || (keyhdstart = http_header_find_field_value(header, "Sec-WebSocket-Key", NULL)) == NULL) { fprintf(stderr, "HTTP Handshake: Missing required header fields\n"); return -1; } for(; *keyhdstart == ' '; ++keyhdstart); keyhdend = keyhdstart; for(; *keyhdend != '\r' && *keyhdend != ' '; ++keyhdend); if(keyhdend-keyhdstart != 24) { printf("%s\n", keyhdstart); fprintf(stderr, "HTTP Handshake: Invalid value in Sec-WebSocket-Key\n"); return -1; } accept_key = create_accept_key(keyhdstart); snprintf(res_header, sizeof(res_header), "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Accept: %s\r\n" "\r\n", accept_key); free(accept_key); res_header_length = strlen(res_header); while(res_header_sent < res_header_length) { while((r = write(fd, res_header+res_header_sent, res_header_length-res_header_sent)) == -1 && errno == EINTR); if(r == -1) { perror("write"); return -1; } else { res_header_sent += r; } } return 0; }
/* * Performs HTTP handshake. *fd* is the file descriptor of the * connection to the client. This function returns 0 if it succeeds, * or returns -1. */ int http_handshake(int fd) { /* * Note: The implementation of HTTP handshake in this function is * written for just a example of how to use of wslay library and is * not meant to be used in production code. In practice, you need * to do more strict verification of the client's handshake. */ char header[16384], accept_key[29], *keyhdstart, *keyhdend, res_header[256]; size_t header_length = 0, res_header_sent = 0, res_header_length; ssize_t r; while(1) { while((r = read(fd, header+header_length, sizeof(header)-header_length)) == -1 && errno == EINTR); if(r == -1) { perror("read"); return -1; } else if(r == 0) { fprintf(stderr, "HTTP Handshake: Got EOF"); return -1; } else { header_length += r; if(header_length >= 4 && memcmp(header+header_length-4, "\r\n\r\n", 4) == 0) { break; } else if(header_length == sizeof(header)) { fprintf(stderr, "HTTP Handshake: Too large HTTP headers"); return -1; } } } if(http_header_find_field_value(header, "Upgrade", "websocket") == NULL || http_header_find_field_value(header, "Connection", "Upgrade") == NULL || (keyhdstart = http_header_find_field_value(header, "Sec-WebSocket-Key", NULL)) == NULL) { fprintf(stderr, "HTTP Handshake: Missing required header fields"); return -1; } for(; *keyhdstart == ' '; ++keyhdstart); keyhdend = keyhdstart; for(; *keyhdend != '\r' && *keyhdend != ' '; ++keyhdend); if(keyhdend-keyhdstart != 24) { printf("%s\n", keyhdstart); fprintf(stderr, "HTTP Handshake: Invalid value in Sec-WebSocket-Key"); return -1; } create_accept_key(accept_key, keyhdstart); snprintf(res_header, sizeof(res_header), "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Accept: %s\r\n" "\r\n", accept_key); res_header_length = strlen(res_header); while(res_header_sent < res_header_length) { while((r = write(fd, res_header+res_header_sent, res_header_length-res_header_sent)) == -1 && errno == EINTR); if(r == -1) { perror("write"); return -1; } else { res_header_sent += r; } } return 0; }
/* * \brief Performs simple HTTP handshake. *fd* is the file descriptor of the * connection to the client. This function returns 0 if it succeeds, * or returns -1. */ static int http_handshake(int sockfd) { char header[16384], accept_key[29], res_header[256]; char *keyhdstart, *keyhdend; size_t header_length = 0, res_header_sent = 0, res_header_length; ssize_t ret; fd_set set; struct timeval timeout_tv, start_tv, current_tv; /* Get current time */ gettimeofday(&start_tv, NULL); /* Set default timeout */ timeout_tv.tv_sec = VRS_TIMEOUT; timeout_tv.tv_usec = 0; /* Try to read whole header without blocking read, use select and * timeout */ while(1) { FD_ZERO(&set); FD_SET(sockfd, &set); if( (ret = select(sockfd + 1, &set, NULL, NULL, &timeout_tv)) == -1 ) { v_print_log(VRS_PRINT_ERROR, "%s:%s():%d select(): %s\n", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); return -1; /* Was event on the listen socket */ } else if(ret > 0) { if(FD_ISSET(sockfd, &set)) { ret = read(sockfd, header + header_length, sizeof(header) - header_length); if(ret == -1) { v_print_log(VRS_PRINT_ERROR, "read(): %s\n", strerror(errno)); return -1; } else if(ret == 0) { v_print_log(VRS_PRINT_ERROR, "HTTP Handshake: Got EOF"); return -1; } else { header_length += ret; /* Was end of HTTP header reached? */ if(header_length >= 4 && memcmp(header + header_length - 4, "\r\n\r\n", 4) == 0) { break; } else if(header_length == sizeof(header)) { v_print_log(VRS_PRINT_ERROR, "HTTP Handshake: Too large HTTP headers\n"); return -1; } } } } gettimeofday(¤t_tv, NULL); /* Update timeout */ timeout_tv.tv_sec = VRS_TIMEOUT - (current_tv.tv_sec - start_tv.tv_sec); timeout_tv.tv_usec = 0; /* Where there is no elapsing time, then exit handshake */ if(timeout_tv.tv_sec <= 0) { v_print_log(VRS_PRINT_DEBUG_MSG, "HTTP Handshake: Timed out\n"); return -1; } } header[header_length] = '\0'; v_print_log(VRS_PRINT_DEBUG_MSG, "HTTP Handshake: received request: %s\n", header); /* Check if required HTTP headers were received in the request */ /* Header has to contain field "Upgrade: websocket" */ if(http_header_find_field_value(header, "Upgrade", "websocket") == NULL) { v_print_log(VRS_PRINT_ERROR, "HTTP Handshake: Missing required header field 'Upgrade: websocket'\n"); return -1; } /* Header has to contain field "Connection: Upgrade" */ if(http_header_find_field_value(header, "Connection", "Upgrade") == NULL) { v_print_log(VRS_PRINT_ERROR, "HTTP Handshake: Missing required header field 'Connection: Upgrade'\n"); return -1; } /* Client has to send field Sec-WebSocket-Key in HTTP header */ if( (keyhdstart = http_header_find_field_value(header, "Sec-WebSocket-Key", NULL)) == NULL) { v_print_log(VRS_PRINT_ERROR, "HTTP Handshake: Missing required header field 'Sec-WebSocket-Key: SOME_SECRET_KEY'\n"); return -1; } /* Requested protocol name has to be equal to "v1.verse.tul.cz" */ if( http_header_find_field_value(header, "Sec-WebSocket-Protocol", WEB_SOCKET_PROTO_NAME) == NULL) { v_print_log(VRS_PRINT_ERROR, "HTTP Handshake: Missing required header field 'Sec-WebSocket-Protocol: %s'\n", WEB_SOCKET_PROTO_NAME); return -1; } /* Check the length of WebSocket key */ for(; *keyhdstart == ' '; ++keyhdstart); keyhdend = keyhdstart; for(; *keyhdend != '\r' && *keyhdend != ' '; ++keyhdend); if(keyhdend - keyhdstart != WS_CLIENT_KEY_LEN) { v_print_log(VRS_PRINT_ERROR, "HTTP Handshake: Invalid value in Sec-WebSocket-Key\n"); return -1; } /* Create accepted key */ create_accept_key(accept_key, keyhdstart); /* Create response for client */ snprintf(res_header, sizeof(res_header), "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Accept: %s\r\n" "Sec-WebSocket-Protocol: %s\r\n" "\r\n", accept_key, WEB_SOCKET_PROTO_NAME); /* Send response to the client */ res_header_length = strlen(res_header); while(res_header_sent < res_header_length) { while((ret = write(sockfd, res_header + res_header_sent, res_header_length - res_header_sent)) == -1 && errno == EINTR); if(ret == -1) { v_print_log(VRS_PRINT_ERROR, "write(): %s\n", strerror(errno)); return -1; } else { res_header_sent += ret; } } v_print_log(VRS_PRINT_DEBUG_MSG, "HTTP Handshake: sent response\n"); return 0; }