Commit a10d3f9e authored by Martin Vítek's avatar Martin Vítek

Working WebSocket handshake

parent 35dc2f9f
......@@ -19,6 +19,9 @@ include_directories(libs)
add_definitions("-DASIO_STANDALONE")
include_directories(libs/asio)
# libwshandshake
include_directories(libs/libwshandshake)
set(SOURCE_FILES
main.cpp
global.h
......
......@@ -7,6 +7,7 @@
#include <fstream>
#include "asio/asio.hpp"
#include "libwshandshake.hpp"
#include "global.h"
......@@ -72,27 +73,21 @@ namespace MagWebServer
std::string filename;
if (uri.compare("/") == 0) filename = "index.html";
else if (uri.compare("/communication") == 0)
{
console->info("WebSocket request");
else if (uri.compare("/communication") == 0) filename = "communication";
else filename = uri.substr(1);
auto websocket_key_pos = request.find("Sec-WebSocket-Key: ") + std::string("Sec-WebSocket-Key: ").length();
//console->info("File: {:s}", filename);
std::string_view key = request.substr(websocket_key_pos);
auto end = key.find("\r\n");
key = key.substr(0, end);
std::string_view filename_extension;
console->info("WebSocket Key: {:s}", key);
if (filename != "communication")
{
filename_extension = filename.substr(filename.find('.')+1);
//console->info("Filename extension: {:s}", filename_extension);
}
else filename = uri.substr(1);
//console->info("File: {:s}", filename);
std::string response;
std::string_view filename_extension = filename.substr(filename.find('.')+1);
console->info("Filename extension: {:s}", filename_extension);
// Handle text files
if (filename_extension == "html" || \
filename_extension == "xhtml" || \
......@@ -109,9 +104,16 @@ namespace MagWebServer
file.close();
std::string mime_type;
if (filename_extension == "html") mime_type = "text/html";
else if (filename_extension == "xhtml") mime_type = "application/xhtml+xml";
else if (filename_extension == "css") mime_type = "text/css";
else if (filename_extension == "js") mime_type = "application/javascript";
response = std::string("HTTP/1.1 200 OK\r\n") + \
std::string("Content-Length: ") + std::to_string(file_content.length()) + std::string("\r\n") +\
std::string("Content-Type: text/html\r\n") + \
std::string("Content-Type: ") + mime_type + std::string("\r\n") + \
std::string("Connection: Closed\r\n\r\n") + \
file_content;
}
......@@ -136,27 +138,6 @@ namespace MagWebServer
{
std::vector<char> file_content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
/*
char temp1 = 0;
char temp2 = 0;
for (size_t i=0; i<=(file_size/2); i++)
{
file.read(&temp1, 1);
file.read(&temp2, 1);
file_content.push_back(temp2);
file_content.push_back(temp1);
}*/
/*
if (file_size%2)
{
file.read(&temp1, 1);
file_content.push_back(temp1);
}
*/
file.close();
std::string content_type;
......@@ -167,7 +148,7 @@ namespace MagWebServer
else if (filename_extension == "ico") content_type = "image/x-icon";
response = std::string("HTTP/1.1 200 OK\r\n") + \
std::string("Content-Length: ") + std::to_string(file_content.size()) + std::string("\r\n") +\
std::string("Content-Length: ") + std::to_string(file_content.size()) + std::string("\r\n") + \
std::string("Content-Type: ") + content_type + std::string("\r\n") + \
std::string("Connection: Closed\r\n\r\n") + \
std::string(file_content.begin(), file_content.end());
......@@ -181,6 +162,32 @@ namespace MagWebServer
}
}
else if (filename == "communication")
{
console->info("WebSocket request");
auto websocket_key_pos = request.find("Sec-WebSocket-Key: ") + std::string("Sec-WebSocket-Key: ").length();
std::string_view key = request.substr(websocket_key_pos);
auto end = key.find("\r\n");
key = key.substr(0, end);
console->info("WebSocket Key: {:s}", key);
char key_response[29] = {};
WebSocketHandshake::generate(std::string(key).c_str(), key_response);
key_response[28] = '\0';
console->info("WebSocket Key Response: {:s}", (const char *)&key_response);
response = std::string("HTTP/1.1 101 Switching Protocols\r\n") + \
std::string("Upgrade: websocket\r\n") + \
std::string("Connection: Upgrade\r\n") + \
std::string("Sec-WebSocket-Accept: ") + key_response + std::string("\r\n\r\n");
}
else
{
console->warn("Not supported filename extension: {:s}", filename_extension);
......
......@@ -3,7 +3,7 @@
<title>MagWebServer</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="main.css">
<link rel="icon" type="image/jpg" href="favicon.jpg">
<!-- <link rel="icon" type="image/jpg" href="favicon.jpg"> -->
<script src="websocket_client.js"></script>
</head>
<body>
......@@ -13,6 +13,10 @@
<br>
<a href="/second.html">Druhá stranka</a>
<br>
<img src="test.png">
<!-- <img src="test.png"> -->
<button onclick="button_connect()">Connect</button>
<button onclick="button_click()">Send hello</button>
<button onclick="button_status()">Get status</button>
</body>
</html>
"use strict"
console.info("Starting WebSocket client");
let socket;
function button_status()
{
switch (socket.readyState)
{
case 0: console.info("Status: CONNECTING");
break;
case 1: console.info("Status: OPEN");
break;
case 2: console.info("Status: CLOSING");
break;
case 3: console.info("Status: CLOSED");
break;
}
}
function button_click()
{
console.info("Buttond clicked");
socket.send('Hello 2');
}
function button_connect()
{
console.log("Connecting");
socket = new WebSocket('ws://localhost:8080/communication');
socket.onopen = function (event) {console.info("Connected")};
socket.onclose = function (event) {console.info("Disconnected")};
socket.onerror = function (event) {console.error("WebSocket error!")};
socket.onmessage = function (event) {console.info("Data: " + event.data)};
}
const socket = new WebSocket('ws://localhost:8080/communication');
// Copyright (c) 2016 Alex Hultman and contributors
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef LIBWSHANDSHAKE_H
#define LIBWSHANDSHAKE_H
#include <cstdint>
#include <cstddef>
class WebSocketHandshake {
template <int N, typename T>
struct static_for {
void operator()(uint32_t *a, uint32_t *b) {
static_for<N - 1, T>()(a, b);
T::template f<N - 1>(a, b);
}
};
template <typename T>
struct static_for<0, T> {
void operator()(uint32_t *a, uint32_t *hash) {}
};
template <int state>
struct Sha1Loop {
static inline uint32_t rol(uint32_t value, size_t bits) {return (value << bits) | (value >> (32 - bits));}
static inline uint32_t blk(uint32_t b[16], size_t i) {
return rol(b[(i + 13) & 15] ^ b[(i + 8) & 15] ^ b[(i + 2) & 15] ^ b[i], 1);
}
template <int i>
static inline void f(uint32_t *a, uint32_t *b) {
switch (state) {
case 1:
a[i % 5] += ((a[(3 + i) % 5] & (a[(2 + i) % 5] ^ a[(1 + i) % 5])) ^ a[(1 + i) % 5]) + b[i] + 0x5a827999 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 2:
b[i] = blk(b, i);
a[(1 + i) % 5] += ((a[(4 + i) % 5] & (a[(3 + i) % 5] ^ a[(2 + i) % 5])) ^ a[(2 + i) % 5]) + b[i] + 0x5a827999 + rol(a[(5 + i) % 5], 5);
a[(4 + i) % 5] = rol(a[(4 + i) % 5], 30);
break;
case 3:
b[(i + 4) % 16] = blk(b, (i + 4) % 16);
a[i % 5] += (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) + b[(i + 4) % 16] + 0x6ed9eba1 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 4:
b[(i + 8) % 16] = blk(b, (i + 8) % 16);
a[i % 5] += (((a[(3 + i) % 5] | a[(2 + i) % 5]) & a[(1 + i) % 5]) | (a[(3 + i) % 5] & a[(2 + i) % 5])) + b[(i + 8) % 16] + 0x8f1bbcdc + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 5:
b[(i + 12) % 16] = blk(b, (i + 12) % 16);
a[i % 5] += (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) + b[(i + 12) % 16] + 0xca62c1d6 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 6:
b[i] += a[4 - i];
}
}
};
static inline void sha1(uint32_t hash[5], uint32_t b[16]) {
uint32_t a[5] = {hash[4], hash[3], hash[2], hash[1], hash[0]};
static_for<16, Sha1Loop<1>>()(a, b);
static_for<4, Sha1Loop<2>>()(a, b);
static_for<20, Sha1Loop<3>>()(a, b);
static_for<20, Sha1Loop<4>>()(a, b);
static_for<20, Sha1Loop<5>>()(a, b);
static_for<5, Sha1Loop<6>>()(a, hash);
}
static inline void base64(unsigned char *src, char *dst) {
const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (int i = 0; i < 18; i += 3) {
*dst++ = b64[(src[i] >> 2) & 63];
*dst++ = b64[((src[i] & 3) << 4) | ((src[i + 1] & 240) >> 4)];
*dst++ = b64[((src[i + 1] & 15) << 2) | ((src[i + 2] & 192) >> 6)];
*dst++ = b64[src[i + 2] & 63];
}
*dst++ = b64[(src[18] >> 2) & 63];
*dst++ = b64[((src[18] & 3) << 4) | ((src[19] & 240) >> 4)];
*dst++ = b64[((src[19] & 15) << 2)];
*dst++ = '=';
}
public:
static inline void generate(const char input[24], char output[28]) {
uint32_t b_output[5] = {
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0
};
uint32_t b_input[16] = {
0, 0, 0, 0, 0, 0, 0x32353845, 0x41464135, 0x2d453931, 0x342d3437, 0x44412d39,
0x3543412d, 0x43354142, 0x30444338, 0x35423131, 0x80000000
};
for (int i = 0; i < 6; i++) {
b_input[i] = (input[4 * i + 3] & 0xff) | (input[4 * i + 2] & 0xff) << 8 | (input[4 * i + 1] & 0xff) << 16 | (input[4 * i + 0] & 0xff) << 24;
}
sha1(b_output, b_input);
uint32_t last_b[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 480};
sha1(b_output, last_b);
for (int i = 0; i < 5; i++) {
uint32_t tmp = b_output[i];
char *bytes = (char *) &b_output[i];
bytes[3] = tmp & 0xff;
bytes[2] = (tmp >> 8) & 0xff;
bytes[1] = (tmp >> 16) & 0xff;
bytes[0] = (tmp >> 24) & 0xff;
}
base64((unsigned char *) b_output, output);
}
};
#endif // LIBWSHANDSHAKE_H
#include <iostream>
#include <libwshandshake/libwshandshake.hpp>
#include "fmt/format.h"
#include "spdlog/spdlog.h"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment