-
Notifications
You must be signed in to change notification settings - Fork 53
/
websocket.C
110 lines (99 loc) · 3.96 KB
/
websocket.C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/*
* websocket.C
*
* Created on: Jun 1, 2013
* Author: xaxaxa
*/
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
* */
#include <cppsp/websocket.H>
#include <cppsp/page.H>
#include <cryptopp/cryptlib.h>
#include <cryptopp/sha.h>
#include <cryptopp/filters.h>
#include <cryptopp/base64.h>
using namespace CryptoPP;
using namespace CP;
using namespace std;
namespace cppsp
{
static uint64_t htonll(uint64_t value) {
// The answer is 42
static const int32_t num = 42;
// Check the endianness
if (*reinterpret_cast<const char*>(&num) == num) {
const uint32_t high_part = htonl(static_cast<uint32_t>(value >> 32));
const uint32_t low_part = htonl(static_cast<uint32_t>(value & 0xFFFFFFFFLL));
return (static_cast<uint64_t>(low_part) << 32) | high_part;
} else {
return value;
}
}
//len must be known in advance; you can not pass a subString of the returned buffer to ws_endWriteFrame()
String ws_beginWriteFrame(FrameWriter& fw, int len) {
int hdrlen = sizeof(WebSocketParser::ws_header1);
if (len > 125 && len <= 0xFFFF) hdrlen += sizeof(WebSocketParser::ws_header_extended16);
if (len > 0xFFFF) hdrlen += sizeof(WebSocketParser::ws_header_extended64);
String buf = fw.beginInsert(hdrlen + len);
return buf.subString(hdrlen, len);
}
void ws_endWriteFrame(FrameWriter& fw, String buf, int opcode) {
int hdrlen = sizeof(WebSocketParser::ws_header1);
if (buf.length() > 125 && buf.length() <= 0xFFFF) hdrlen +=
sizeof(WebSocketParser::ws_header_extended16);
if (buf.length() > 0xFFFF) hdrlen += sizeof(WebSocketParser::ws_header_extended64);
WebSocketParser::ws_header1* h1 = ((WebSocketParser::ws_header1*) (buf.data() - hdrlen));
memset(h1, 0, sizeof(*h1));
h1->fin = true;
h1->mask = false;
h1->opcode = opcode;
if (buf.length() > 125 && buf.length() <= 0xFFFF) {
h1->payload_len = 126;
WebSocketParser::ws_header_extended16* h2 = (WebSocketParser::ws_header_extended16*) (h1
+ 1);
h2->payload_len = htons((uint16_t) buf.length());
} else if (buf.length() > 0xFFFF) {
h1->payload_len = 127;
WebSocketParser::ws_header_extended64* h2 = (WebSocketParser::ws_header_extended64*) (h1
+ 1);
h2->payload_len = htonll((uint64_t) buf.length());
} else {
h1->payload_len = (char) buf.length();
}
fw.endInsert(hdrlen + buf.length());
}
void ws_init(Page& p, CP::Callback cb) {
p.response->headersExtraSpace = 0;
p.response->statusCode = 101;
p.response->statusName = "Switching Protocols";
//response->headers["Sec-WebSocket-Protocol"]="chat";
String s = concat(*p.sp, p.request->headers["Sec-WebSocket-Key"],
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
SHA1 sha1;
byte tmp[sha1.DigestSize()];
sha1.CalculateDigest(tmp, (const byte*) s.data(), s.length());
string encoded;
StringSource src(tmp, sizeof(tmp), true, new Base64Encoder(new StringSink(encoded), false));
//printf("Sec-WebSocket-Accept: %s\n",encoded.c_str());
p.response->headers = cppsp::serializeHeaders(*p.sp, 0, String("Connection"),
String("Upgrade"), String("Upgrade"), String("WebSocket"), String("Date"),
p.server->host->curRFCTime, String("Sec-WebSocket-Accept"), String(encoded),
String(nullptr));
p.response->serializeHeaders(p.response->output);
p.response->output.flush();
p.response->outputStream->write(p.response->buffer, cb);
}
bool ws_iswebsocket(const Request& req) {
return (ci_compare(req.headers["Upgrade"], "websocket") == 0);
}
}