summaryrefslogtreecommitdiffstats
path: root/websocketpp/close.hpp
blob: ded7765757bf006820083df0d8f2b0900c8f12b0 (plain)
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
/*
 * Copyright (c) 2014, Peter Thorson. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the WebSocket++ Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef WEBSOCKETPP_CLOSE_HPP
#define WEBSOCKETPP_CLOSE_HPP

/** \file
 * A package of types and methods for manipulating WebSocket close codes.
 */

#include <websocketpp/error.hpp>
#include <websocketpp/common/network.hpp>
#include <websocketpp/common/stdint.hpp>
#include <websocketpp/utf8_validator.hpp>

#include <string>

namespace websocketpp {
/// A package of types and methods for manipulating WebSocket close codes.
namespace close {
/// A package of types and methods for manipulating WebSocket close status'
namespace status {
    /// The type of a close code value.
    typedef uint16_t value;

    /// A blank value for internal use.
    static value const blank = 0;

    /// Close the connection without a WebSocket close handshake.
    /**
     * This special value requests that the WebSocket connection be closed
     * without performing the WebSocket closing handshake. This does not comply
     * with RFC6455, but should be safe to do if necessary. This could be useful
     * for clients that need to disconnect quickly and cannot afford the
     * complete handshake.
     */
    static value const omit_handshake = 1;

    /// Close the connection with a forced TCP drop.
    /**
     * This special value requests that the WebSocket connection be closed by
     * forcibly dropping the TCP connection. This will leave the other side of
     * the connection with a broken connection and some expensive timeouts. this
     * should not be done except in extreme cases or in cases of malicious
     * remote endpoints.
     */
    static value const force_tcp_drop = 2;

    /// Normal closure, meaning that the purpose for which the connection was
    /// established has been fulfilled.
    static value const normal = 1000;

    /// The endpoint was "going away", such as a server going down or a browser
    /// navigating away from a page.
    static value const going_away = 1001;

    /// A protocol error occurred.
    static value const protocol_error = 1002;

    /// The connection was terminated because an endpoint received a type of
    /// data it cannot accept.
    /**
     * (e.g., an endpoint that understands only text data MAY send this if it
     * receives a binary message).
     */
    static value const unsupported_data = 1003;

    /// A dummy value to indicate that no status code was received.
    /**
     * This value is illegal on the wire.
     */
    static value const no_status = 1005;

    /// A dummy value to indicate that the connection was closed abnormally.
    /**
     * In such a case there was no close frame to extract a value from. This
     * value is illegal on the wire.
     */
    static value const abnormal_close = 1006;

    /// An endpoint received message data inconsistent with its type.
    /**
     * For example: Invalid UTF8 bytes in a text message.
     */
    static value const invalid_payload = 1007;

    /// An endpoint received a message that violated its policy.
    /**
     * This is a generic status code that can be returned when there is no other
     * more suitable status code (e.g., 1003 or 1009) or if there is a need to
     * hide specific details about the policy.
     */
    static value const policy_violation = 1008;

    /// An endpoint received a message too large to process.
    static value const message_too_big = 1009;

    /// A client expected the server to accept a required extension request
    /**
     * The list of extensions that are needed SHOULD appear in the /reason/ part
     * of the Close frame. Note that this status code is not used by the server,
     * because it can fail the WebSocket handshake instead.
     */
    static value const extension_required = 1010;

    /// An endpoint encountered an unexpected condition that prevented it from
    /// fulfilling the request.
    static value const internal_endpoint_error = 1011;

    /// Indicates that the service is restarted. A client may reconnect and if
    /// if it chooses to do so, should reconnect using a randomized delay of
    /// 5-30s
    static value const service_restart = 1012;

    /// Indicates that the service is experiencing overload. A client should
    /// only connect to a different IP (when there are multiple for the target)
    /// or reconnect to the same IP upon user action.
    static value const try_again_later = 1013;

    /// An endpoint failed to perform a TLS handshake
    /**
     * Designated for use in applications expecting a status code to indicate
     * that the connection was closed due to a failure to perform a TLS
     * handshake (e.g., the server certificate can't be verified). This value is
     * illegal on the wire.
     */
    static value const tls_handshake = 1015;
    
    /// A generic subprotocol error
    /**
     * Indicates that a subprotocol error occurred. Typically this involves
     * receiving a message that is not formatted as a valid message for the
     * subprotocol in use.
     */
    static value const subprotocol_error = 3000;
    
    /// A invalid subprotocol data
    /**
     * Indicates that data was received that violated the specification of the
     * subprotocol in use.
     */
    static value const invalid_subprotocol_data = 3001;

    /// First value in range reserved for future protocol use
    static value const rsv_start = 1016;
    /// Last value in range reserved for future protocol use
    static value const rsv_end = 2999;

    /// Test whether a close code is in a reserved range
    /**
     * @param [in] code The code to test
     * @return Whether or not code is reserved
     */
    inline bool reserved(value code) {
        return ((code >= rsv_start && code <= rsv_end) ||
                code == 1004 || code == 1014);
    }

    /// First value in range that is always invalid on the wire
    static value const invalid_low = 999;
    /// Last value in range that is always invalid on the wire
    static value const invalid_high = 5000;

    /// Test whether a close code is invalid on the wire
    /**
     * @param [in] code The code to test
     * @return Whether or not code is invalid on the wire
     */
    inline bool invalid(value code) {
        return (code <= invalid_low || code >= invalid_high ||
                code == no_status || code == abnormal_close ||
                code == tls_handshake);
    }

    /// Determine if the code represents an unrecoverable error
    /**
     * There is a class of errors for which once they are discovered normal
     * WebSocket functionality can no longer occur. This function determines
     * if a given code is one of these values. This information is used to
     * determine if the system has the capability of waiting for a close
     * acknowledgement or if it should drop the TCP connection immediately
     * after sending its close frame.
     *
     * @param [in] code The value to test.
     * @return True if the code represents an unrecoverable error
     */
    inline bool terminal(value code) {
        return (code == protocol_error || code == invalid_payload ||
                code == policy_violation || code == message_too_big ||
                 code == internal_endpoint_error);
    }
    
    /// Return a human readable interpretation of a WebSocket close code
    /**
     * See https://tools.ietf.org/html/rfc6455#section-7.4 for more details.
     *
     * @since 0.3.0
     *
     * @param [in] code The code to look up.
     * @return A human readable interpretation of the code.
     */
    inline std::string get_string(value code) {
        switch (code) {
            case normal:
                return "Normal close";
            case going_away:
                return "Going away";
            case protocol_error:
                return "Protocol error";
            case unsupported_data:
                return "Unsupported data";
            case no_status:
                return "No status set";
            case abnormal_close:
                return "Abnormal close";
            case invalid_payload:
                return "Invalid payload";
            case policy_violation:
                return "Policy violoation";
            case message_too_big:
                return "Message too big";
            case extension_required:
                return "Extension required";
            case internal_endpoint_error:
                return "Internal endpoint error";
            case tls_handshake:
                return "TLS handshake failure";
            case subprotocol_error:
                return "Generic subprotocol error";
            case invalid_subprotocol_data:
                return "Invalid subprotocol data";
            default:
                return "Unknown";
        }
    }
} // namespace status

/// Type used to convert close statuses between integer and wire representations
union code_converter {
    uint16_t i;
    char c[2];
};

/// Extract a close code value from a close payload
/**
 * If there is no close value (ie string is empty) status::no_status is
 * returned. If a code couldn't be extracted (usually do to a short or
 * otherwise mangled payload) status::protocol_error is returned and the ec
 * value is flagged as an error. Note that this case is different than the case
 * where protocol error is received over the wire.
 *
 * If the value is in an invalid or reserved range ec is set accordingly.
 *
 * @param [in] payload Close frame payload value received over the wire.
 * @param [out] ec Set to indicate what error occurred, if any.
 * @return The extracted value
 */
inline status::value extract_code(std::string const & payload, lib::error_code
    & ec)
{
    ec = lib::error_code();

    if (payload.size() == 0) {
        return status::no_status;
    } else if (payload.size() == 1) {
        ec = make_error_code(error::bad_close_code);
        return status::protocol_error;
    }

    code_converter val;

    val.c[0] = payload[0];
    val.c[1] = payload[1];

    status::value code(ntohs(val.i));

    if (status::invalid(code)) {
        ec = make_error_code(error::invalid_close_code);
    }

    if (status::reserved(code)) {
        ec = make_error_code(error::reserved_close_code);
    }

    return code;
}

/// Extract the reason string from a close payload
/**
 * The string should be a valid UTF8 message. error::invalid_utf8 will be set if
 * the function extracts a reason that is not valid UTF8.
 *
 * @param [in] payload The payload string to extract a reason from.
 * @param [out] ec Set to indicate what error occurred, if any.
 * @return The reason string.
 */
inline std::string extract_reason(std::string const & payload, lib::error_code
    & ec)
{
    std::string reason;
    ec = lib::error_code();

    if (payload.size() > 2) {
        reason.append(payload.begin()+2,payload.end());
    }

    if (!websocketpp::utf8_validator::validate(reason)) {
        ec = make_error_code(error::invalid_utf8);
    }

    return reason;
}

} // namespace close
} // namespace websocketpp

#endif // WEBSOCKETPP_CLOSE_HPP