]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) | |
3 | // | |
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | // | |
7 | ||
8 | #ifndef BEAST_WEBSOCKET_DETAIL_FRAME_HPP | |
9 | #define BEAST_WEBSOCKET_DETAIL_FRAME_HPP | |
10 | ||
11 | #include <beast/websocket/rfc6455.hpp> | |
12 | #include <beast/websocket/detail/endian.hpp> | |
13 | #include <beast/websocket/detail/utf8_checker.hpp> | |
14 | #include <beast/core/consuming_buffers.hpp> | |
15 | #include <beast/core/static_streambuf.hpp> | |
16 | #include <beast/core/static_string.hpp> | |
17 | #include <boost/asio/buffer.hpp> | |
18 | #include <boost/assert.hpp> | |
19 | #include <boost/endian/buffers.hpp> | |
20 | #include <cstdint> | |
21 | ||
22 | namespace beast { | |
23 | namespace websocket { | |
24 | namespace detail { | |
25 | ||
26 | // Contents of a WebSocket frame header | |
27 | struct frame_header | |
28 | { | |
29 | opcode op; | |
30 | bool fin; | |
31 | bool mask; | |
32 | bool rsv1; | |
33 | bool rsv2; | |
34 | bool rsv3; | |
35 | std::uint64_t len; | |
36 | std::uint32_t key; | |
37 | }; | |
38 | ||
39 | // holds the largest possible frame header | |
40 | using fh_streambuf = | |
41 | static_streambuf_n<14>; | |
42 | ||
43 | // holds the largest possible control frame | |
44 | using frame_streambuf = | |
45 | static_streambuf_n< 2 + 8 + 4 + 125 >; | |
46 | ||
47 | inline | |
48 | bool constexpr | |
49 | is_reserved(opcode op) | |
50 | { | |
51 | return | |
52 | (op >= opcode::rsv3 && op <= opcode::rsv7) || | |
53 | (op >= opcode::crsvb && op <= opcode::crsvf); | |
54 | } | |
55 | ||
56 | inline | |
57 | bool constexpr | |
58 | is_valid(opcode op) | |
59 | { | |
60 | return op <= opcode::crsvf; | |
61 | } | |
62 | ||
63 | inline | |
64 | bool constexpr | |
65 | is_control(opcode op) | |
66 | { | |
67 | return op >= opcode::close; | |
68 | } | |
69 | ||
70 | inline | |
71 | bool | |
72 | is_valid_close_code(std::uint16_t v) | |
73 | { | |
74 | switch(v) | |
75 | { | |
76 | case close_code::normal: // 1000 | |
77 | case close_code::going_away: // 1001 | |
78 | case close_code::protocol_error: // 1002 | |
79 | case close_code::unknown_data: // 1003 | |
80 | case close_code::bad_payload: // 1007 | |
81 | case close_code::policy_error: // 1008 | |
82 | case close_code::too_big: // 1009 | |
83 | case close_code::needs_extension: // 1010 | |
84 | case close_code::internal_error: // 1011 | |
85 | case close_code::service_restart: // 1012 | |
86 | case close_code::try_again_later: // 1013 | |
87 | return true; | |
88 | ||
89 | // explicitly reserved | |
90 | case close_code::reserved1: // 1004 | |
91 | case close_code::no_status: // 1005 | |
92 | case close_code::abnormal: // 1006 | |
93 | case close_code::reserved2: // 1014 | |
94 | case close_code::reserved3: // 1015 | |
95 | return false; | |
96 | } | |
97 | // reserved | |
98 | if(v >= 1016 && v <= 2999) | |
99 | return false; | |
100 | // not used | |
101 | if(v <= 999) | |
102 | return false; | |
103 | return true; | |
104 | } | |
105 | ||
106 | //------------------------------------------------------------------------------ | |
107 | ||
108 | // Write frame header to dynamic buffer | |
109 | // | |
110 | template<class DynamicBuffer> | |
111 | void | |
112 | write(DynamicBuffer& db, frame_header const& fh) | |
113 | { | |
114 | using boost::asio::buffer; | |
115 | using boost::asio::buffer_copy; | |
116 | using namespace boost::endian; | |
117 | std::size_t n; | |
118 | std::uint8_t b[14]; | |
119 | b[0] = (fh.fin ? 0x80 : 0x00) | static_cast<std::uint8_t>(fh.op); | |
120 | if(fh.rsv1) | |
121 | b[0] |= 0x40; | |
122 | if(fh.rsv2) | |
123 | b[0] |= 0x20; | |
124 | if(fh.rsv3) | |
125 | b[0] |= 0x10; | |
126 | b[1] = fh.mask ? 0x80 : 0x00; | |
127 | if(fh.len <= 125) | |
128 | { | |
129 | b[1] |= fh.len; | |
130 | n = 2; | |
131 | } | |
132 | else if(fh.len <= 65535) | |
133 | { | |
134 | b[1] |= 126; | |
135 | ::new(&b[2]) big_uint16_buf_t{ | |
136 | (std::uint16_t)fh.len}; | |
137 | n = 4; | |
138 | } | |
139 | else | |
140 | { | |
141 | b[1] |= 127; | |
142 | ::new(&b[2]) big_uint64_buf_t{fh.len}; | |
143 | n = 10; | |
144 | } | |
145 | if(fh.mask) | |
146 | { | |
147 | native_to_little_uint32(fh.key, &b[n]); | |
148 | n += 4; | |
149 | } | |
150 | db.commit(buffer_copy( | |
151 | db.prepare(n), buffer(b))); | |
152 | } | |
153 | ||
154 | // Read data from buffers | |
155 | // This is for ping and pong payloads | |
156 | // | |
157 | template<class Buffers> | |
158 | void | |
159 | read(ping_data& data, Buffers const& bs) | |
160 | { | |
161 | using boost::asio::buffer_copy; | |
162 | using boost::asio::buffer_size; | |
163 | using boost::asio::mutable_buffers_1; | |
164 | BOOST_ASSERT(buffer_size(bs) <= data.max_size()); | |
165 | data.resize(buffer_size(bs)); | |
166 | buffer_copy(mutable_buffers_1{ | |
167 | data.data(), data.size()}, bs); | |
168 | } | |
169 | ||
170 | // Read close_reason, return true on success | |
171 | // This is for the close payload | |
172 | // | |
173 | template<class Buffers> | |
174 | void | |
175 | read(close_reason& cr, | |
176 | Buffers const& bs, close_code& code) | |
177 | { | |
178 | using boost::asio::buffer; | |
179 | using boost::asio::buffer_copy; | |
180 | using boost::asio::buffer_size; | |
181 | using namespace boost::endian; | |
182 | auto n = buffer_size(bs); | |
183 | BOOST_ASSERT(n <= 125); | |
184 | if(n == 0) | |
185 | { | |
186 | cr = close_reason{}; | |
187 | code = close_code::none; | |
188 | return; | |
189 | } | |
190 | if(n == 1) | |
191 | { | |
192 | code = close_code::protocol_error; | |
193 | return; | |
194 | } | |
195 | consuming_buffers<Buffers> cb(bs); | |
196 | { | |
197 | std::uint8_t b[2]; | |
198 | buffer_copy(buffer(b), cb); | |
199 | cr.code = big_uint16_to_native(&b[0]); | |
200 | cb.consume(2); | |
201 | n -= 2; | |
202 | if(! is_valid_close_code(cr.code)) | |
203 | { | |
204 | code = close_code::protocol_error; | |
205 | return; | |
206 | } | |
207 | } | |
208 | if(n > 0) | |
209 | { | |
210 | cr.reason.resize(n); | |
211 | buffer_copy(buffer(&cr.reason[0], n), cb); | |
212 | if(! detail::check_utf8( | |
213 | cr.reason.data(), cr.reason.size())) | |
214 | { | |
215 | code = close_code::protocol_error; | |
216 | return; | |
217 | } | |
218 | } | |
219 | else | |
220 | { | |
221 | cr.reason = ""; | |
222 | } | |
223 | code = close_code::none; | |
224 | } | |
225 | ||
226 | } // detail | |
227 | } // websocket | |
228 | } // beast | |
229 | ||
230 | #endif |