WebSocket++
0.8.3-dev
C++ websocket client/server library
Main Page
Related Pages
Namespaces
Namespace List
Namespace Members
All
a
b
c
d
e
f
g
h
i
m
n
o
p
r
s
t
u
v
w
z
Functions
b
c
d
e
g
h
i
m
p
r
s
t
u
v
w
Variables
a
b
c
d
e
f
g
h
i
m
n
o
p
r
s
t
u
Typedefs
Enumerations
Enumerator
a
b
c
d
e
f
g
h
i
m
n
o
p
r
s
t
u
z
Classes
Class List
Class Index
Class Hierarchy
Class Members
All
a
b
c
d
e
f
g
h
i
l
m
n
o
p
r
s
t
v
w
~
Functions
a
b
c
d
e
f
g
h
i
l
m
n
o
p
r
s
t
v
w
~
Variables
a
c
d
e
f
h
i
l
m
n
r
s
t
w
Typedefs
a
c
e
i
m
p
r
s
t
v
w
Related Functions
Files
File List
•
All
Classes
Namespaces
Files
Functions
Variables
Typedefs
Enumerations
Enumerator
Friends
Pages
websocketpp
impl
connection_impl.hpp
1
/*
2
* Copyright (c) 2014, Peter Thorson. All rights reserved.
3
*
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions are met:
6
* * Redistributions of source code must retain the above copyright
7
* notice, this list of conditions and the following disclaimer.
8
* * Redistributions in binary form must reproduce the above copyright
9
* notice, this list of conditions and the following disclaimer in the
10
* documentation and/or other materials provided with the distribution.
11
* * Neither the name of the WebSocket++ Project nor the
12
* names of its contributors may be used to endorse or promote products
13
* derived from this software without specific prior written permission.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
19
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
*
26
*/
27
28
#
ifndef
WEBSOCKETPP_CONNECTION_IMPL_HPP
29
#
define
WEBSOCKETPP_CONNECTION_IMPL_HPP
30
31
#
include
<
websocketpp
/
processors
/
hybi00
.
hpp
>
32
#
include
<
websocketpp
/
processors
/
hybi07
.
hpp
>
33
#
include
<
websocketpp
/
processors
/
hybi08
.
hpp
>
34
#
include
<
websocketpp
/
processors
/
hybi13
.
hpp
>
35
36
#
include
<
websocketpp
/
processors
/
processor
.
hpp
>
37
38
#
include
<
websocketpp
/
common
/
platforms
.
hpp
>
39
#
include
<
websocketpp
/
common
/
system_error
.
hpp
>
40
41
#
include
<
algorithm
>
42
#
include
<
exception
>
43
#
include
<
sstream
>
44
#
include
<
string
>
45
#
include
<
utility
>
46
#
include
<
vector
>
47
48
namespace
websocketpp
{
49
50
namespace
istate
=
session
::
internal_state
;
51
52
template
<
typename
config
>
53
void
connection
<
config
>::
set_termination_handler
(
54
termination_handler
new_handler
)
55
{
56
m_alog
->
write
(
log
::
alevel
::
devel
,
57
"connection set_termination_handler"
);
58
59
//scoped_lock_type lock(m_connection_state_lock);
60
61
m_termination_handler
=
new_handler
;
62
}
63
64
template
<
typename
config
>
65
std
::
string
const
&
connection
<
config
>::
get_origin
()
const
{
66
//scoped_lock_type lock(m_connection_state_lock);
67
return
m_processor
->
get_origin
(
m_request
);
68
}
69
70
template
<
typename
config
>
71
size_t
connection
<
config
>::
get_buffered_amount
()
const
{
72
//scoped_lock_type lock(m_connection_state_lock);
73
return
m_send_buffer_size
;
74
}
75
76
template
<
typename
config
>
77
session
::
state
::
value
connection
<
config
>::
get_state
()
const
{
78
//scoped_lock_type lock(m_connection_state_lock);
79
return
m_state
;
80
}
81
82
template
<
typename
config
>
83
lib
::
error_code
connection
<
config
>::
send
(
std
::
string
const
&
payload
,
84
frame
::
opcode
::
value
op
)
85
{
86
message_ptr
msg
=
m_msg_manager
->
get_message
(
op
,
payload
.
size
());
87
msg
->
append_payload
(
payload
);
88
msg
->
set_compressed
(
true
);
89
90
return
send
(
msg
);
91
}
92
93
template
<
typename
config
>
94
lib
::
error_code
connection
<
config
>::
send
(
void
const
*
payload
,
size_t
len
,
95
frame
::
opcode
::
value
op
)
96
{
97
message_ptr
msg
=
m_msg_manager
->
get_message
(
op
,
len
);
98
msg
->
append_payload
(
payload
,
len
);
99
100
return
send
(
msg
);
101
}
102
103
template
<
typename
config
>
104
lib
::
error_code
connection
<
config
>::
send
(
typename
config
::
message_type
::
ptr
msg
)
105
{
106
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
107
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection send"
);
108
}
109
110
{
111
scoped_lock_type
lock
(
m_connection_state_lock
);
112
if
(
m_state
!=
session
::
state
::
open
) {
113
return
error
::
make_error_code
(
error
::
invalid_state
);
114
}
115
}
116
117
message_ptr
outgoing_msg
;
118
bool
needs_writing
=
false
;
119
120
if
(
msg
->
get_prepared
()) {
121
outgoing_msg
=
msg
;
122
123
scoped_lock_type
lock
(
m_write_lock
);
124
write_push
(
outgoing_msg
);
125
needs_writing
= !
m_write_flag
&& !
m_send_queue
.
empty
();
126
}
else
{
127
outgoing_msg
=
m_msg_manager
->
get_message
();
128
129
if
(!
outgoing_msg
) {
130
return
error
::
make_error_code
(
error
::
no_outgoing_buffers
);
131
}
132
133
scoped_lock_type
lock
(
m_write_lock
);
134
lib
::
error_code
ec
=
m_processor
->
prepare_data_frame
(
msg
,
outgoing_msg
);
135
136
if
(
ec
) {
137
return
ec
;
138
}
139
140
write_push
(
outgoing_msg
);
141
needs_writing
= !
m_write_flag
&& !
m_send_queue
.
empty
();
142
}
143
144
if
(
needs_writing
) {
145
transport_con_type
::
dispatch
(
lib
::
bind
(
146
&
type
::
write_frame
,
147
type
::
get_shared
()
148
));
149
}
150
151
return
lib
::
error_code
();
152
}
153
154
template
<
typename
config
>
155
void
connection
<
config
>::
ping
(
std
::
string
const
&
payload
,
lib
::
error_code
&
ec
) {
156
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
157
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection ping"
);
158
}
159
160
{
161
scoped_lock_type
lock
(
m_connection_state_lock
);
162
if
(
m_state
!=
session
::
state
::
open
) {
163
std
::
stringstream
ss
;
164
ss
<<
"connection::ping called from invalid state "
<<
m_state
;
165
m_alog
->
write
(
log
::
alevel
::
devel
,
ss
.
str
());
166
ec
=
error
::
make_error_code
(
error
::
invalid_state
);
167
return
;
168
}
169
}
170
171
message_ptr
msg
=
m_msg_manager
->
get_message
();
172
if
(!
msg
) {
173
ec
=
error
::
make_error_code
(
error
::
no_outgoing_buffers
);
174
return
;
175
}
176
177
ec
=
m_processor
->
prepare_ping
(
payload
,
msg
);
178
if
(
ec
) {
return
;}
179
180
// set ping timer if we are listening for one
181
if
(
m_pong_timeout_handler
) {
182
// Cancel any existing timers
183
if
(
m_ping_timer
) {
184
m_ping_timer
->
cancel
();
185
}
186
187
if
(
m_pong_timeout_dur
> 0) {
188
m_ping_timer
=
transport_con_type
::
set_timer
(
189
m_pong_timeout_dur
,
190
lib
::
bind
(
191
&
type
::
handle_pong_timeout
,
192
type
::
get_shared
(),
193
payload
,
194
lib
::
placeholders
::
_1
195
)
196
);
197
}
198
199
if
(!
m_ping_timer
) {
200
// Our transport doesn't support timers
201
m_elog
->
write
(
log
::
elevel
::
warn
,
"Warning: a pong_timeout_handler is \
202
set but the transport in use does not support timeouts."
);
203
}
204
}
205
206
bool
needs_writing
=
false
;
207
{
208
scoped_lock_type
lock
(
m_write_lock
);
209
write_push
(
msg
);
210
needs_writing
= !
m_write_flag
&& !
m_send_queue
.
empty
();
211
}
212
213
if
(
needs_writing
) {
214
transport_con_type
::
dispatch
(
lib
::
bind
(
215
&
type
::
write_frame
,
216
type
::
get_shared
()
217
));
218
}
219
220
ec
=
lib
::
error_code
();
221
}
222
223
template
<
typename
config
>
224
void
connection
<
config
>::
ping
(
std
::
string
const
&
payload
) {
225
lib
::
error_code
ec
;
226
ping
(
payload
,
ec
);
227
if
(
ec
) {
228
throw
exception
(
ec
);
229
}
230
}
231
232
template
<
typename
config
>
233
void
connection
<
config
>::
handle_pong_timeout
(
std
::
string
payload
,
234
lib
::
error_code
const
&
ec
)
235
{
236
if
(
ec
) {
237
if
(
ec
==
transport
::
error
::
operation_aborted
) {
238
// ignore, this is expected
239
return
;
240
}
241
242
m_elog
->
write
(
log
::
elevel
::
devel
,
"pong_timeout error: "
+
ec
.
message
());
243
return
;
244
}
245
246
if
(
m_pong_timeout_handler
) {
247
m_pong_timeout_handler
(
m_connection_hdl
,
payload
);
248
}
249
}
250
251
template
<
typename
config
>
252
void
connection
<
config
>::
pong
(
std
::
string
const
&
payload
,
lib
::
error_code
&
ec
) {
253
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
254
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection pong"
);
255
}
256
257
{
258
scoped_lock_type
lock
(
m_connection_state_lock
);
259
if
(
m_state
!=
session
::
state
::
open
) {
260
std
::
stringstream
ss
;
261
ss
<<
"connection::pong called from invalid state "
<<
m_state
;
262
m_alog
->
write
(
log
::
alevel
::
devel
,
ss
.
str
());
263
ec
=
error
::
make_error_code
(
error
::
invalid_state
);
264
return
;
265
}
266
}
267
268
message_ptr
msg
=
m_msg_manager
->
get_message
();
269
if
(!
msg
) {
270
ec
=
error
::
make_error_code
(
error
::
no_outgoing_buffers
);
271
return
;
272
}
273
274
ec
=
m_processor
->
prepare_pong
(
payload
,
msg
);
275
if
(
ec
) {
return
;}
276
277
bool
needs_writing
=
false
;
278
{
279
scoped_lock_type
lock
(
m_write_lock
);
280
write_push
(
msg
);
281
needs_writing
= !
m_write_flag
&& !
m_send_queue
.
empty
();
282
}
283
284
if
(
needs_writing
) {
285
transport_con_type
::
dispatch
(
lib
::
bind
(
286
&
type
::
write_frame
,
287
type
::
get_shared
()
288
));
289
}
290
291
ec
=
lib
::
error_code
();
292
}
293
294
template
<
typename
config
>
295
void
connection
<
config
>::
pong
(
std
::
string
const
&
payload
) {
296
lib
::
error_code
ec
;
297
pong
(
payload
,
ec
);
298
if
(
ec
) {
299
throw
exception
(
ec
);
300
}
301
}
302
303
template
<
typename
config
>
304
void
connection
<
config
>::
close
(
close
::
status
::
value
const
code
,
305
std
::
string
const
&
reason
,
lib
::
error_code
&
ec
)
306
{
307
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
308
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection close"
);
309
}
310
311
// Truncate reason to maximum size allowable in a close frame.
312
std
::
string
tr
(
reason
,0,
std
::
min
<
size_t
>(
reason
.
size
(),
313
frame
::
limits
::
close_reason_size
));
314
315
scoped_lock_type
lock
(
m_connection_state_lock
);
316
317
if
(
m_state
!=
session
::
state
::
open
) {
318
ec
=
error
::
make_error_code
(
error
::
invalid_state
);
319
return
;
320
}
321
322
ec
=
this
->
send_close_frame
(
code
,
tr
,
false
,
close
::
status
::
terminal
(
code
));
323
}
324
325
template
<
typename
config
>
326
void
connection
<
config
>::
close
(
close
::
status
::
value
const
code
,
327
std
::
string
const
&
reason
)
328
{
329
lib
::
error_code
ec
;
330
close
(
code
,
reason
,
ec
);
331
if
(
ec
) {
332
throw
exception
(
ec
);
333
}
334
}
335
336
/// Trigger the on_interrupt handler
337
/**
338
* This is thread safe if the transport is thread safe
339
*/
340
template
<
typename
config
>
341
lib
::
error_code
connection
<
config
>::
interrupt
() {
342
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection connection::interrupt"
);
343
return
transport_con_type
::
interrupt
(
344
lib
::
bind
(
345
&
type
::
handle_interrupt
,
346
type
::
get_shared
()
347
)
348
);
349
}
350
351
352
template
<
typename
config
>
353
void
connection
<
config
>::
handle_interrupt
() {
354
if
(
m_interrupt_handler
) {
355
m_interrupt_handler
(
m_connection_hdl
);
356
}
357
}
358
359
template
<
typename
config
>
360
lib
::
error_code
connection
<
config
>::
pause_reading
() {
361
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection connection::pause_reading"
);
362
return
transport_con_type
::
dispatch
(
363
lib
::
bind
(
364
&
type
::
handle_pause_reading
,
365
type
::
get_shared
()
366
)
367
);
368
}
369
370
/// Pause reading handler. Not safe to call directly
371
template
<
typename
config
>
372
void
connection
<
config
>::
handle_pause_reading
() {
373
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection connection::handle_pause_reading"
);
374
m_read_flag
=
false
;
375
}
376
377
template
<
typename
config
>
378
lib
::
error_code
connection
<
config
>::
resume_reading
() {
379
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection connection::resume_reading"
);
380
return
transport_con_type
::
dispatch
(
381
lib
::
bind
(
382
&
type
::
handle_resume_reading
,
383
type
::
get_shared
()
384
)
385
);
386
}
387
388
/// Resume reading helper method. Not safe to call directly
389
template
<
typename
config
>
390
void
connection
<
config
>::
handle_resume_reading
() {
391
m_read_flag
=
true
;
392
read_frame
();
393
}
394
395
396
397
398
399
400
401
402
403
404
405
template
<
typename
config
>
406
bool
connection
<
config
>::
get_secure
()
const
{
407
//scoped_lock_type lock(m_connection_state_lock);
408
return
m_uri
->
get_secure
();
409
}
410
411
template
<
typename
config
>
412
std
::
string
const
&
connection
<
config
>::
get_host
()
const
{
413
//scoped_lock_type lock(m_connection_state_lock);
414
return
m_uri
->
get_host
();
415
}
416
417
template
<
typename
config
>
418
std
::
string
const
&
connection
<
config
>::
get_resource
()
const
{
419
//scoped_lock_type lock(m_connection_state_lock);
420
return
m_uri
->
get_resource
();
421
}
422
423
template
<
typename
config
>
424
uint16_t
connection
<
config
>::
get_port
()
const
{
425
//scoped_lock_type lock(m_connection_state_lock);
426
return
m_uri
->
get_port
();
427
}
428
429
template
<
typename
config
>
430
uri_ptr
connection
<
config
>::
get_uri
()
const
{
431
//scoped_lock_type lock(m_connection_state_lock);
432
return
m_uri
;
433
}
434
435
template
<
typename
config
>
436
void
connection
<
config
>::
set_uri
(
uri_ptr
uri
) {
437
//scoped_lock_type lock(m_connection_state_lock);
438
m_uri
=
uri
;
439
}
440
441
442
443
444
445
446
template
<
typename
config
>
447
std
::
string
const
&
connection
<
config
>::
get_subprotocol
()
const
{
448
return
m_subprotocol
;
449
}
450
451
template
<
typename
config
>
452
std
::
vector
<
std
::
string
>
const
&
453
connection
<
config
>::
get_requested_subprotocols
()
const
{
454
return
m_requested_subprotocols
;
455
}
456
457
template
<
typename
config
>
458
void
connection
<
config
>::
add_subprotocol
(
std
::
string
const
&
value
,
459
lib
::
error_code
&
ec
)
460
{
461
if
(
m_is_server
) {
462
ec
=
error
::
make_error_code
(
error
::
client_only
);
463
return
;
464
}
465
466
// If the value is empty or has a non-RFC2616 token character it is invalid.
467
if
(
value
.
empty
() ||
std
::
find_if
(
value
.
begin
(),
value
.
end
(),
468
http
::
is_not_token_char
) !=
value
.
end
())
469
{
470
ec
=
error
::
make_error_code
(
error
::
invalid_subprotocol
);
471
return
;
472
}
473
474
m_requested_subprotocols
.
push_back
(
value
);
475
}
476
477
template
<
typename
config
>
478
void
connection
<
config
>::
add_subprotocol
(
std
::
string
const
&
value
) {
479
lib
::
error_code
ec
;
480
this
->
add_subprotocol
(
value
,
ec
);
481
if
(
ec
) {
482
throw
exception
(
ec
);
483
}
484
}
485
486
487
template
<
typename
config
>
488
void
connection
<
config
>::
select_subprotocol
(
std
::
string
const
&
value
,
489
lib
::
error_code
&
ec
)
490
{
491
if
(!
m_is_server
) {
492
ec
=
error
::
make_error_code
(
error
::
server_only
);
493
return
;
494
}
495
496
if
(
value
.
empty
()) {
497
ec
=
lib
::
error_code
();
498
return
;
499
}
500
501
std
::
vector
<
std
::
string
>::
iterator
it
;
502
503
it
=
std
::
find
(
m_requested_subprotocols
.
begin
(),
504
m_requested_subprotocols
.
end
(),
505
value
);
506
507
if
(
it
==
m_requested_subprotocols
.
end
()) {
508
ec
=
error
::
make_error_code
(
error
::
unrequested_subprotocol
);
509
return
;
510
}
511
512
m_subprotocol
=
value
;
513
ec
=
lib
::
error_code
();
514
}
515
516
template
<
typename
config
>
517
void
connection
<
config
>::
select_subprotocol
(
std
::
string
const
&
value
) {
518
lib
::
error_code
ec
;
519
this
->
select_subprotocol
(
value
,
ec
);
520
if
(
ec
) {
521
throw
exception
(
ec
);
522
}
523
}
524
525
526
template
<
typename
config
>
527
std
::
string
const
&
528
connection
<
config
>::
get_request_header
(
std
::
string
const
&
key
)
const
{
529
return
m_request
.
get_header
(
key
);
530
}
531
532
template
<
typename
config
>
533
std
::
string
const
&
534
connection
<
config
>::
get_request_body
()
const
{
535
return
m_request
.
get_body
();
536
}
537
538
template
<
typename
config
>
539
std
::
string
const
&
540
connection
<
config
>::
get_response_header
(
std
::
string
const
&
key
)
const
{
541
return
m_response
.
get_header
(
key
);
542
}
543
544
template
<
typename
config
>
545
void
connection
<
config
>::
set_status
(
http
::
status_code
::
value
code
,
546
lib
::
error_code
&
ec
)
547
{
548
if
(
m_internal_state
!=
istate
::
PROCESS_HTTP_REQUEST
) {
549
ec
=
error
::
make_error_code
(
error
::
invalid_state
);
550
return
;
551
}
552
m_response
.
set_status
(
code
);
553
ec
=
lib
::
error_code
();
554
}
555
556
template
<
typename
config
>
557
void
connection
<
config
>::
set_status
(
http
::
status_code
::
value
code
)
558
{
559
lib
::
error_code
ec
;
560
this
->
set_status
(
code
,
ec
);
561
if
(
ec
) {
562
throw
exception
(
"Call to set_status from invalid state"
,
ec
);
563
}
564
}
565
566
template
<
typename
config
>
567
void
connection
<
config
>::
set_status
(
http
::
status_code
::
value
code
,
568
std
::
string
const
&
msg
,
lib
::
error_code
&
ec
)
569
{
570
if
(
m_internal_state
!=
istate
::
PROCESS_HTTP_REQUEST
) {
571
ec
=
error
::
make_error_code
(
error
::
invalid_state
);
572
return
;
573
}
574
575
m_response
.
set_status
(
code
,
msg
);
576
ec
=
lib
::
error_code
();
577
}
578
579
template
<
typename
config
>
580
void
connection
<
config
>::
set_status
(
http
::
status_code
::
value
code
,
581
std
::
string
const
&
msg
)
582
{
583
lib
::
error_code
ec
;
584
this
->
set_status
(
code
,
msg
);
585
if
(
ec
) {
586
throw
exception
(
"Call to set_status from invalid state"
,
ec
);
587
}
588
}
589
590
template
<
typename
config
>
591
void
connection
<
config
>::
set_body
(
std
::
string
const
&
value
,
592
lib
::
error_code
&
ec
)
593
{
594
if
(
m_internal_state
!=
istate
::
PROCESS_HTTP_REQUEST
) {
595
ec
=
error
::
make_error_code
(
error
::
invalid_state
);
596
return
;
597
}
598
599
m_response
.
set_body
(
value
,
ec
);
600
}
601
602
template
<
typename
config
>
603
void
connection
<
config
>::
set_body
(
std
::
string
const
&
value
) {
604
lib
::
error_code
ec
;
605
this
->
set_body
(
value
,
ec
);
606
if
(
ec
) {
607
throw
exception
(
"Call to set_status from invalid state"
,
ec
);
608
}
609
}
610
611
template
<
typename
config
>
612
void
connection
<
config
>::
append_header
(
std
::
string
const
&
key
,
613
std
::
string
const
&
val
,
lib
::
error_code
&
ec
)
614
{
615
if
(
m_is_server
) {
616
if
(
m_internal_state
==
istate
::
PROCESS_HTTP_REQUEST
) {
617
// we are setting response headers for an incoming server connection
618
m_response
.
append_header
(
key
,
val
,
ec
);
619
}
else
{
620
ec
=
error
::
make_error_code
(
error
::
invalid_state
);
621
}
622
}
else
{
623
if
(
m_internal_state
==
istate
::
USER_INIT
) {
624
// we are setting initial headers for an outgoing client connection
625
m_request
.
append_header
(
key
,
val
,
ec
);
626
}
else
{
627
ec
=
error
::
make_error_code
(
error
::
invalid_state
);
628
}
629
}
630
}
631
632
template
<
typename
config
>
633
void
connection
<
config
>::
append_header
(
std
::
string
const
&
key
,
634
std
::
string
const
&
val
)
635
{
636
lib
::
error_code
ec
;
637
this
->
append_header
(
key
,
val
,
ec
);
638
if
(
ec
) {
639
throw
exception
(
"Call to append_header from invalid state"
,
ec
);
640
}
641
}
642
643
template
<
typename
config
>
644
void
connection
<
config
>::
replace_header
(
std
::
string
const
&
key
,
645
std
::
string
const
&
val
)
646
{
647
if
(
m_is_server
) {
648
if
(
m_internal_state
==
istate
::
PROCESS_HTTP_REQUEST
) {
649
// we are setting response headers for an incoming server connection
650
m_response
.
replace_header
(
key
,
val
,
ec
);
651
}
else
{
652
ec
=
error
::
make_error_code
(
error
::
invalid_state
);
653
}
654
}
else
{
655
if
(
m_internal_state
==
istate
::
USER_INIT
) {
656
// we are setting initial headers for an outgoing client connection
657
m_request
.
replace_header
(
key
,
val
,
ec
);
658
}
else
{
659
ec
=
error
::
make_error_code
(
error
::
invalid_state
);
660
}
661
}
662
}
663
664
template
<
typename
config
>
665
void
connection
<
config
>::
replace_header
(
std
::
string
const
&
key
,
666
std
::
string
const
&
val
)
667
{
668
lib
::
error_code
ec
;
669
this
->
replace_header
(
key
,
val
,
ec
);
670
if
(
ec
) {
671
throw
exception
(
"Call to replace_header from invalid state"
,
ec
);
672
}
673
}
674
675
template
<
typename
config
>
676
void
connection
<
config
>::
remove_header
(
std
::
string
const
&
key
)
677
{
678
if
(
m_is_server
) {
679
if
(
m_internal_state
==
istate
::
PROCESS_HTTP_REQUEST
) {
680
// we are setting response headers for an incoming server connection
681
m_response
.
remove_header
(
key
,
ec
);
682
}
else
{
683
ec
=
error
::
make_error_code
(
error
::
invalid_state
);
684
}
685
}
else
{
686
if
(
m_internal_state
==
istate
::
USER_INIT
) {
687
// we are setting initial headers for an outgoing client connection
688
m_request
.
remove_header
(
key
,
ec
);
689
}
else
{
690
ec
=
error
::
make_error_code
(
error
::
invalid_state
);
691
}
692
}
693
}
694
695
template
<
typename
config
>
696
void
connection
<
config
>::
remove_header
(
std
::
string
const
&
key
)
697
{
698
lib
::
error_code
ec
;
699
this
->
remove_header
(
key
,
ec
);
700
if
(
ec
) {
701
throw
exception
(
"Call to remove_header from invalid state"
,
ec
);
702
}
703
}
704
705
/// Defer HTTP Response until later
706
/**
707
* Used in the http handler to defer the HTTP response for this connection
708
* until later. Handshake timers will be canceled and the connection will be
709
* left open until `send_http_response` or an equivalent is called.
710
*
711
* Warning: deferred connections won't time out and as a result can tie up
712
* resources.
713
*
714
* @return A status code, zero on success, non-zero otherwise
715
*/
716
template
<
typename
config
>
717
lib
::
error_code
connection
<
config
>::
defer_http_response
() {
718
// Cancel handshake timer, otherwise the connection will time out and we'll
719
// close the connection before the app has a chance to send a response.
720
if
(
m_handshake_timer
) {
721
m_handshake_timer
->
cancel
();
722
m_handshake_timer
.
reset
();
723
}
724
725
// Do something to signal deferral
726
m_http_state
=
session
::
http_state
::
deferred
;
727
728
return
lib
::
error_code
();
729
}
730
731
/// Send deferred HTTP Response (exception free)
732
/**
733
* Sends an http response to an HTTP connection that was deferred. This will
734
* send a complete response including all headers, status line, and body
735
* text. The connection will be closed afterwards.
736
*
737
* @since 0.6.0
738
*
739
* @param ec A status code, zero on success, non-zero otherwise
740
*/
741
template
<
typename
config
>
742
void
connection
<
config
>::
send_http_response
(
lib
::
error_code
&
ec
) {
743
{
744
scoped_lock_type
lock
(
m_connection_state_lock
);
745
if
(
m_http_state
!=
session
::
http_state
::
deferred
) {
746
ec
=
error
::
make_error_code
(
error
::
invalid_state
);
747
return
;
748
}
749
750
m_http_state
=
session
::
http_state
::
body_written
;
751
}
752
753
this
->
write_http_response
(
lib
::
error_code
());
754
ec
=
lib
::
error_code
();
755
}
756
757
template
<
typename
config
>
758
void
connection
<
config
>::
send_http_response
() {
759
lib
::
error_code
ec
;
760
this
->
send_http_response
(
ec
);
761
if
(
ec
) {
762
throw
exception
(
ec
);
763
}
764
}
765
766
767
768
769
/******** logic thread ********/
770
771
template
<
typename
config
>
772
void
connection
<
config
>::
start
() {
773
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection start"
);
774
775
if
(
m_internal_state
!=
istate
::
USER_INIT
) {
776
m_alog
->
write
(
log
::
alevel
::
devel
,
"Start called in invalid state"
);
777
this
->
terminate
(
error
::
make_error_code
(
error
::
invalid_state
));
778
return
;
779
}
780
781
m_internal_state
=
istate
::
TRANSPORT_INIT
;
782
783
// Depending on how the transport implements init this function may return
784
// immediately and call handle_transport_init later or call
785
// handle_transport_init from this function.
786
transport_con_type
::
init
(
787
lib
::
bind
(
788
&
type
::
handle_transport_init
,
789
type
::
get_shared
(),
790
lib
::
placeholders
::
_1
791
)
792
);
793
}
794
795
template
<
typename
config
>
796
void
connection
<
config
>::
handle_transport_init
(
lib
::
error_code
const
&
ec
) {
797
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection handle_transport_init"
);
798
799
lib
::
error_code
ecm
=
ec
;
800
801
if
(
m_internal_state
!=
istate
::
TRANSPORT_INIT
) {
802
m_alog
->
write
(
log
::
alevel
::
devel
,
803
"handle_transport_init must be called from transport init state"
);
804
ecm
=
error
::
make_error_code
(
error
::
invalid_state
);
805
}
806
807
if
(
ecm
) {
808
std
::
stringstream
s
;
809
s
<<
"handle_transport_init received error: "
<<
ecm
.
message
();
810
m_elog
->
write
(
log
::
elevel
::
rerror
,
s
.
str
());
811
812
this
->
terminate
(
ecm
);
813
return
;
814
}
815
816
// At this point the transport is ready to read and write bytes.
817
if
(
m_is_server
) {
818
m_internal_state
=
istate
::
READ_HTTP_REQUEST
;
819
this
->
read_handshake
(1);
820
}
else
{
821
// We are a client. Set the processor to the version specified in the
822
// config file and send a handshake request.
823
m_internal_state
=
istate
::
WRITE_HTTP_REQUEST
;
824
m_processor
=
get_processor
(
config
::
client_version
);
825
this
->
send_http_request
();
826
}
827
}
828
829
template
<
typename
config
>
830
void
connection
<
config
>::
read_handshake
(
size_t
num_bytes
) {
831
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection read_handshake"
);
832
833
if
(
m_open_handshake_timeout_dur
> 0) {
834
m_handshake_timer
=
transport_con_type
::
set_timer
(
835
m_open_handshake_timeout_dur
,
836
lib
::
bind
(
837
&
type
::
handle_open_handshake_timeout
,
838
type
::
get_shared
(),
839
lib
::
placeholders
::
_1
840
)
841
);
842
}
843
844
transport_con_type
::
async_read_at_least
(
845
num_bytes
,
846
m_buf
,
847
config
::
connection_read_buffer_size
,
848
lib
::
bind
(
849
&
type
::
handle_read_handshake
,
850
type
::
get_shared
(),
851
lib
::
placeholders
::
_1
,
852
lib
::
placeholders
::
_2
853
)
854
);
855
}
856
857
// All exit paths for this function need to call write_http_response() or submit
858
// a new read request with this function as the handler.
859
template
<
typename
config
>
860
void
connection
<
config
>::
handle_read_handshake
(
lib
::
error_code
const
&
ec
,
861
size_t
bytes_transferred
)
862
{
863
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection handle_read_handshake"
);
864
865
lib
::
error_code
ecm
=
ec
;
866
867
if
(!
ecm
) {
868
scoped_lock_type
lock
(
m_connection_state_lock
);
869
870
if
(
m_state
==
session
::
state
::
connecting
) {
871
if
(
m_internal_state
!=
istate
::
READ_HTTP_REQUEST
) {
872
ecm
=
error
::
make_error_code
(
error
::
invalid_state
);
873
}
874
}
else
if
(
m_state
==
session
::
state
::
closed
) {
875
// The connection was canceled while the response was being sent,
876
// usually by the handshake timer. This is basically expected
877
// (though hopefully rare) and there is nothing we can do so ignore.
878
m_alog
->
write
(
log
::
alevel
::
devel
,
879
"handle_read_handshake invoked after connection was closed"
);
880
return
;
881
}
else
{
882
ecm
=
error
::
make_error_code
(
error
::
invalid_state
);
883
}
884
}
885
886
if
(
ecm
) {
887
if
(
ecm
==
transport
::
error
::
eof
&&
m_state
==
session
::
state
::
closed
) {
888
// we expect to get eof if the connection is closed already
889
m_alog
->
write
(
log
::
alevel
::
devel
,
890
"got (expected) eof/state error from closed con"
);
891
return
;
892
}
893
894
log_err
(
log
::
elevel
::
rerror
,
"handle_read_handshake"
,
ecm
);
895
this
->
terminate
(
ecm
);
896
return
;
897
}
898
899
// Boundaries checking. TODO: How much of this should be done?
900
if
(
bytes_transferred
>
config
::
connection_read_buffer_size
) {
901
m_elog
->
write
(
log
::
elevel
::
fatal
,
"Fatal boundaries checking error."
);
902
this
->
terminate
(
make_error_code
(
error
::
general
));
903
return
;
904
}
905
906
size_t
bytes_processed
= 0;
907
try
{
908
bytes_processed
=
m_request
.
consume
(
m_buf
,
bytes_transferred
);
909
}
catch
(
http
::
exception
&
e
) {
910
// All HTTP exceptions will result in this request failing and an error
911
// response being returned. No more bytes will be read in this con.
912
m_response
.
set_status
(
e
.
m_error_code
,
e
.
m_error_msg
);
913
this
->
write_http_response_error
(
error
::
make_error_code
(
error
::
http_parse_error
));
914
return
;
915
}
916
917
// More paranoid boundaries checking.
918
// TODO: Is this overkill?
919
if
(
bytes_processed
>
bytes_transferred
) {
920
m_elog
->
write
(
log
::
elevel
::
fatal
,
"Fatal boundaries checking error."
);
921
this
->
terminate
(
make_error_code
(
error
::
general
));
922
return
;
923
}
924
925
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
926
std
::
stringstream
s
;
927
s
<<
"bytes_transferred: "
<<
bytes_transferred
928
<<
" bytes, bytes processed: "
<<
bytes_processed
<<
" bytes"
;
929
m_alog
->
write
(
log
::
alevel
::
devel
,
s
.
str
());
930
}
931
932
if
(
m_request
.
ready
()) {
933
lib
::
error_code
processor_ec
=
this
->
initialize_processor
();
934
if
(
processor_ec
) {
935
this
->
write_http_response_error
(
processor_ec
);
936
return
;
937
}
938
939
if
(
m_processor
&&
m_processor
->
get_version
() == 0) {
940
// Version 00 has an extra requirement to read some bytes after the
941
// handshake
942
if
(
bytes_transferred
-
bytes_processed
>= 8) {
943
m_request
.
replace_header
(
944
"Sec-WebSocket-Key3"
,
945
std
::
string
(
m_buf
+
bytes_processed
,
m_buf
+
bytes_processed
+8)
946
);
947
bytes_processed
+= 8;
948
}
else
{
949
// TODO: need more bytes
950
m_alog
->
write
(
log
::
alevel
::
devel
,
"short key3 read"
);
951
m_response
.
set_status
(
http
::
status_code
::
internal_server_error
);
952
this
->
write_http_response_error
(
processor
::
error
::
make_error_code
(
processor
::
error
::
short_key3
));
953
return
;
954
}
955
}
956
957
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
958
m_alog
->
write
(
log
::
alevel
::
devel
,
m_request
.
raw
());
959
if
(!
m_request
.
get_header
(
"Sec-WebSocket-Key3"
).
empty
()) {
960
m_alog
->
write
(
log
::
alevel
::
devel
,
961
utility
::
to_hex
(
m_request
.
get_header
(
"Sec-WebSocket-Key3"
)));
962
}
963
}
964
965
// The remaining bytes in m_buf are frame data. Copy them to the
966
// beginning of the buffer and note the length. They will be read after
967
// the handshake completes and before more bytes are read.
968
std
::
copy
(
m_buf
+
bytes_processed
,
m_buf
+
bytes_transferred
,
m_buf
);
969
m_buf_cursor
=
bytes_transferred
-
bytes_processed
;
970
971
972
m_internal_state
=
istate
::
PROCESS_HTTP_REQUEST
;
973
974
// We have the complete request. Process it.
975
lib
::
error_code
handshake_ec
=
this
->
process_handshake_request
();
976
977
// Write a response if this is a websocket connection or if it is an
978
// HTTP connection for which the response has not been deferred or
979
// started yet by a different system (i.e. still in init state).
980
if
(!
m_is_http
||
m_http_state
==
session
::
http_state
::
init
) {
981
this
->
write_http_response
(
handshake_ec
);
982
}
983
}
else
{
984
// The HTTP parser reported that it was not ready and wants more data.
985
// Assert that it actually consumed all the data present before overwriting
986
// the buffer. This should always be the case.
987
if
(
bytes_transferred
!=
bytes_processed
) {
988
m_elog
->
write
(
log
::
elevel
::
fatal
,
"Assertion Failed: HTTP request parser failed to consume all bytes from a read request."
);
989
this
->
terminate
(
make_error_code
(
error
::
general
));
990
return
;
991
}
992
993
// read at least 1 more byte
994
transport_con_type
::
async_read_at_least
(
995
1,
996
m_buf
,
997
config
::
connection_read_buffer_size
,
998
lib
::
bind
(
999
&
type
::
handle_read_handshake
,
1000
type
::
get_shared
(),
1001
lib
::
placeholders
::
_1
,
1002
lib
::
placeholders
::
_2
1003
)
1004
);
1005
}
1006
}
1007
1008
// write_http_response requires the request to be fully read and the connection
1009
// to be in the PROCESS_HTTP_REQUEST state. In some cases we can detect errors
1010
// before the request is fully read (specifically at a point where we aren't
1011
// sure if the hybi00 key3 bytes need to be read). This method sets the correct
1012
// state and calls write_http_response
1013
template
<
typename
config
>
1014
void
connection
<
config
>::
write_http_response_error
(
lib
::
error_code
const
&
ec
) {
1015
if
(
m_internal_state
!=
istate
::
READ_HTTP_REQUEST
) {
1016
m_alog
->
write
(
log
::
alevel
::
devel
,
1017
"write_http_response_error called in invalid state"
);
1018
this
->
terminate
(
error
::
make_error_code
(
error
::
invalid_state
));
1019
return
;
1020
}
1021
1022
m_internal_state
=
istate
::
PROCESS_HTTP_REQUEST
;
1023
1024
this
->
write_http_response
(
ec
);
1025
}
1026
1027
// All exit paths for this function need to call write_http_response() or submit
1028
// a new read request with this function as the handler.
1029
template
<
typename
config
>
1030
void
connection
<
config
>::
handle_read_frame
(
lib
::
error_code
const
&
ec
,
1031
size_t
bytes_transferred
)
1032
{
1033
//m_alog->write(log::alevel::devel,"connection handle_read_frame");
1034
1035
lib
::
error_code
ecm
=
ec
;
1036
1037
if
(!
ecm
&&
m_internal_state
!=
istate
::
PROCESS_CONNECTION
) {
1038
ecm
=
error
::
make_error_code
(
error
::
invalid_state
);
1039
}
1040
1041
if
(
ecm
) {
1042
log
::
level
echannel
=
log
::
elevel
::
rerror
;
1043
1044
if
(
ecm
==
transport
::
error
::
eof
) {
1045
if
(
m_state
==
session
::
state
::
closed
) {
1046
// we expect to get eof if the connection is closed already
1047
// just ignore it
1048
m_alog
->
write
(
log
::
alevel
::
devel
,
"got eof from closed con"
);
1049
return
;
1050
}
else
if
(
m_state
==
session
::
state
::
closing
&& !
m_is_server
) {
1051
// If we are a client we expect to get eof in the closing state,
1052
// this is a signal to terminate our end of the connection after
1053
// the closing handshake
1054
terminate
(
lib
::
error_code
());
1055
return
;
1056
}
1057
}
else
if
(
ecm
==
error
::
invalid_state
) {
1058
// In general, invalid state errors in the closed state are the
1059
// result of handlers that were in the system already when the state
1060
// changed and should be ignored as they pose no problems and there
1061
// is nothing useful that we can do about them.
1062
if
(
m_state
==
session
::
state
::
closed
) {
1063
m_alog
->
write
(
log
::
alevel
::
devel
,
1064
"handle_read_frame: got invalid istate in closed state"
);
1065
return
;
1066
}
1067
}
else
if
(
ecm
==
transport
::
error
::
action_after_shutdown
) {
1068
echannel
=
log
::
elevel
::
info
;
1069
}
else
{
1070
// TODO: more generally should we do something different here in the
1071
// case that m_state is cosed? Are errors after the connection is
1072
// already closed really an rerror?
1073
}
1074
1075
1076
1077
log_err
(
echannel
,
"handle_read_frame"
,
ecm
);
1078
this
->
terminate
(
ecm
);
1079
return
;
1080
}
1081
1082
// Boundaries checking. TODO: How much of this should be done?
1083
/*if (bytes_transferred > config::connection_read_buffer_size) {
1084
m_elog->write(log::elevel::fatal,"Fatal boundaries checking error");
1085
this->terminate(make_error_code(error::general));
1086
return;
1087
}*/
1088
1089
size_t
p
= 0;
1090
1091
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
1092
std
::
stringstream
s
;
1093
s
<<
"p = "
<<
p
<<
" bytes transferred = "
<<
bytes_transferred
;
1094
m_alog
->
write
(
log
::
alevel
::
devel
,
s
.
str
());
1095
}
1096
1097
while
(
p
<
bytes_transferred
) {
1098
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
1099
std
::
stringstream
s
;
1100
s
<<
"calling consume with "
<<
bytes_transferred
-
p
<<
" bytes"
;
1101
m_alog
->
write
(
log
::
alevel
::
devel
,
s
.
str
());
1102
}
1103
1104
lib
::
error_code
consume_ec
;
1105
1106
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
1107
std
::
stringstream
s
;
1108
s
<<
"Processing Bytes: "
<<
utility
::
to_hex
(
reinterpret_cast
<
uint8_t
*>(
m_buf
)+
p
,
bytes_transferred
-
p
);
1109
m_alog
->
write
(
log
::
alevel
::
devel
,
s
.
str
());
1110
}
1111
1112
p
+=
m_processor
->
consume
(
1113
reinterpret_cast
<
uint8_t
*>(
m_buf
)+
p
,
1114
bytes_transferred
-
p
,
1115
consume_ec
1116
);
1117
1118
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
1119
std
::
stringstream
s
;
1120
s
<<
"bytes left after consume: "
<<
bytes_transferred
-
p
;
1121
m_alog
->
write
(
log
::
alevel
::
devel
,
s
.
str
());
1122
}
1123
if
(
consume_ec
) {
1124
log_err
(
log
::
elevel
::
rerror
,
"consume"
,
consume_ec
);
1125
1126
if
(
config
::
drop_on_protocol_error
) {
1127
this
->
terminate
(
consume_ec
);
1128
return
;
1129
}
else
{
1130
lib
::
error_code
close_ec
;
1131
this
->
close
(
1132
processor
::
error
::
to_ws
(
consume_ec
),
1133
consume_ec
.
message
(),
1134
close_ec
1135
);
1136
1137
if
(
close_ec
) {
1138
log_err
(
log
::
elevel
::
fatal
,
"Protocol error close frame "
,
close_ec
);
1139
this
->
terminate
(
close_ec
);
1140
return
;
1141
}
1142
}
1143
return
;
1144
}
1145
1146
if
(
m_processor
->
ready
()) {
1147
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
1148
std
::
stringstream
s
;
1149
s
<<
"Complete message received. Dispatching"
;
1150
m_alog
->
write
(
log
::
alevel
::
devel
,
s
.
str
());
1151
}
1152
1153
message_ptr
msg
=
m_processor
->
get_message
();
1154
1155
if
(!
msg
) {
1156
m_alog
->
write
(
log
::
alevel
::
devel
,
"null message from m_processor"
);
1157
}
else
if
(!
is_control
(
msg
->
get_opcode
())) {
1158
// data message, dispatch to user
1159
if
(
m_state
!=
session
::
state
::
open
) {
1160
m_elog
->
write
(
log
::
elevel
::
warn
,
"got non-close frame while closing"
);
1161
}
else
if
(
m_message_handler
) {
1162
m_message_handler
(
m_connection_hdl
,
msg
);
1163
}
1164
}
else
{
1165
process_control_frame
(
msg
);
1166
}
1167
}
1168
}
1169
1170
read_frame
();
1171
}
1172
1173
/// Issue a new transport read unless reading is paused.
1174
template
<
typename
config
>
1175
void
connection
<
config
>::
read_frame
() {
1176
if
(!
m_read_flag
) {
1177
return
;
1178
}
1179
1180
transport_con_type
::
async_read_at_least
(
1181
// std::min wont work with undefined static const values.
1182
// TODO: is there a more elegant way to do this?
1183
// Need to determine if requesting 1 byte or the exact number of bytes
1184
// is better here. 1 byte lets us be a bit more responsive at a
1185
// potential expense of additional runs through handle_read_frame
1186
/*(m_processor->get_bytes_needed() > config::connection_read_buffer_size ?
1187
config::connection_read_buffer_size : m_processor->get_bytes_needed())*/
1188
1,
1189
m_buf
,
1190
config
::
connection_read_buffer_size
,
1191
m_handle_read_frame
1192
);
1193
}
1194
1195
template
<
typename
config
>
1196
lib
::
error_code
connection
<
config
>::
initialize_processor
() {
1197
m_alog
->
write
(
log
::
alevel
::
devel
,
"initialize_processor"
);
1198
1199
// if it isn't a websocket handshake nothing to do.
1200
if
(!
processor
::
is_websocket_handshake
(
m_request
)) {
1201
return
lib
::
error_code
();
1202
}
1203
1204
int
version
=
processor
::
get_websocket_version
(
m_request
);
1205
1206
if
(
version
< 0) {
1207
m_alog
->
write
(
log
::
alevel
::
devel
,
"BAD REQUEST: can't determine version"
);
1208
m_response
.
set_status
(
http
::
status_code
::
bad_request
);
1209
return
error
::
make_error_code
(
error
::
invalid_version
);
1210
}
1211
1212
m_processor
=
get_processor
(
version
);
1213
1214
// if the processor is not null we are done
1215
if
(
m_processor
) {
1216
return
lib
::
error_code
();
1217
}
1218
1219
// We don't have a processor for this version. Return bad request
1220
// with Sec-WebSocket-Version header filled with values we do accept
1221
m_alog
->
write
(
log
::
alevel
::
devel
,
"BAD REQUEST: no processor for version"
);
1222
m_response
.
set_status
(
http
::
status_code
::
bad_request
);
1223
1224
std
::
stringstream
ss
;
1225
std
::
string
sep
;
1226
std
::
vector
<
int
>::
const_iterator
it
;
1227
for
(
it
=
versions_supported
.
begin
();
it
!=
versions_supported
.
end
();
it
++)
1228
{
1229
ss
<<
sep
<< *
it
;
1230
sep
=
","
;
1231
}
1232
1233
m_response
.
replace_header
(
"Sec-WebSocket-Version"
,
ss
.
str
());
1234
return
error
::
make_error_code
(
error
::
unsupported_version
);
1235
}
1236
1237
template
<
typename
config
>
1238
lib
::
error_code
connection
<
config
>::
process_handshake_request
() {
1239
m_alog
->
write
(
log
::
alevel
::
devel
,
"process handshake request"
);
1240
1241
if
(!
processor
::
is_websocket_handshake
(
m_request
)) {
1242
// this is not a websocket handshake. Process as plain HTTP
1243
m_alog
->
write
(
log
::
alevel
::
devel
,
"HTTP REQUEST"
);
1244
1245
// extract URI from request
1246
m_uri
=
processor
::
get_uri_from_host
(
1247
m_request
,
1248
(
transport_con_type
::
is_secure
() ?
"https"
:
"http"
)
1249
);
1250
1251
if
(!
m_uri
->
get_valid
()) {
1252
m_alog
->
write
(
log
::
alevel
::
devel
,
"Bad request: failed to parse uri"
);
1253
m_response
.
set_status
(
http
::
status_code
::
bad_request
);
1254
return
error
::
make_error_code
(
error
::
invalid_uri
);
1255
}
1256
1257
if
(
m_http_handler
) {
1258
m_is_http
=
true
;
1259
m_http_handler
(
m_connection_hdl
);
1260
1261
if
(
m_state
==
session
::
state
::
closed
) {
1262
return
error
::
make_error_code
(
error
::
http_connection_ended
);
1263
}
1264
}
else
{
1265
set_status
(
http
::
status_code
::
upgrade_required
);
1266
return
error
::
make_error_code
(
error
::
upgrade_required
);
1267
}
1268
1269
return
lib
::
error_code
();
1270
}
1271
1272
lib
::
error_code
ec
=
m_processor
->
validate_handshake
(
m_request
);
1273
1274
// Validate: make sure all required elements are present.
1275
if
(
ec
){
1276
// Not a valid handshake request
1277
m_alog
->
write
(
log
::
alevel
::
devel
,
"Bad request "
+
ec
.
message
());
1278
m_response
.
set_status
(
http
::
status_code
::
bad_request
);
1279
return
ec
;
1280
}
1281
1282
// Read extension parameters and set up values necessary for the end user
1283
// to complete extension negotiation.
1284
std
::
pair
<
lib
::
error_code
,
std
::
string
>
neg_results
;
1285
neg_results
=
m_processor
->
negotiate_extensions
(
m_request
);
1286
1287
if
(
neg_results
.
first
==
processor
::
error
::
make_error_code
(
processor
::
error
::
extension_parse_error
)) {
1288
// There was a fatal error in extension parsing that should result in
1289
// a failed connection attempt.
1290
m_elog
->
write
(
log
::
elevel
::
info
,
"Bad request: "
+
neg_results
.
first
.
message
());
1291
m_response
.
set_status
(
http
::
status_code
::
bad_request
);
1292
return
neg_results
.
first
;
1293
}
else
if
(
neg_results
.
first
) {
1294
// There was a fatal error in extension processing that is probably our
1295
// fault. Consider extension negotiation to have failed and continue as
1296
// if extensions were not supported
1297
m_elog
->
write
(
log
::
elevel
::
info
,
1298
"Extension negotiation failed: "
+
neg_results
.
first
.
message
());
1299
}
else
{
1300
// extension negotiation succeeded, set response header accordingly
1301
// we don't send an empty extensions header because it breaks many
1302
// clients.
1303
if
(
neg_results
.
second
.
size
() > 0) {
1304
m_response
.
replace_header
(
"Sec-WebSocket-Extensions"
,
1305
neg_results
.
second
);
1306
}
1307
}
1308
1309
// extract URI from request
1310
m_uri
=
m_processor
->
get_uri
(
m_request
);
1311
1312
1313
if
(!
m_uri
->
get_valid
()) {
1314
m_alog
->
write
(
log
::
alevel
::
devel
,
"Bad request: failed to parse uri"
);
1315
m_response
.
set_status
(
http
::
status_code
::
bad_request
);
1316
return
error
::
make_error_code
(
error
::
invalid_uri
);
1317
}
1318
1319
// extract subprotocols
1320
lib
::
error_code
subp_ec
=
m_processor
->
extract_subprotocols
(
m_request
,
1321
m_requested_subprotocols
);
1322
1323
if
(
subp_ec
) {
1324
// should we do anything?
1325
}
1326
1327
// Ask application to validate the connection
1328
if
(!
m_validate_handler
||
m_validate_handler
(
m_connection_hdl
)) {
1329
m_response
.
set_status
(
http
::
status_code
::
switching_protocols
);
1330
1331
// Write the appropriate response headers based on request and
1332
// processor version
1333
ec
=
m_processor
->
process_handshake
(
m_request
,
m_subprotocol
,
m_response
);
1334
1335
if
(
ec
) {
1336
std
::
stringstream
s
;
1337
s
<<
"Processing error: "
<<
ec
<<
"("
<<
ec
.
message
() <<
")"
;
1338
m_alog
->
write
(
log
::
alevel
::
devel
,
s
.
str
());
1339
1340
m_response
.
set_status
(
http
::
status_code
::
internal_server_error
);
1341
return
ec
;
1342
}
1343
}
else
{
1344
// User application has rejected the handshake
1345
m_alog
->
write
(
log
::
alevel
::
devel
,
"USER REJECT"
);
1346
1347
// Use Bad Request if the user handler did not provide a more
1348
// specific http response error code.
1349
// TODO: is there a better default?
1350
if
(
m_response
.
get_status_code
() ==
http
::
status_code
::
uninitialized
) {
1351
m_response
.
set_status
(
http
::
status_code
::
bad_request
);
1352
}
1353
1354
return
error
::
make_error_code
(
error
::
rejected
);
1355
}
1356
1357
return
lib
::
error_code
();
1358
}
1359
1360
template
<
typename
config
>
1361
void
connection
<
config
>::
write_http_response
(
lib
::
error_code
const
&
ec
) {
1362
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection write_http_response"
);
1363
1364
if
(
ec
==
error
::
make_error_code
(
error
::
http_connection_ended
)) {
1365
m_alog
->
write
(
log
::
alevel
::
http
,
"An HTTP handler took over the connection."
);
1366
return
;
1367
}
1368
1369
if
(
m_response
.
get_status_code
() ==
http
::
status_code
::
uninitialized
) {
1370
m_response
.
set_status
(
http
::
status_code
::
internal_server_error
);
1371
m_ec
=
error
::
make_error_code
(
error
::
general
);
1372
}
else
{
1373
m_ec
=
ec
;
1374
}
1375
1376
m_response
.
set_version
(
"HTTP/1.1"
);
1377
1378
// Set server header based on the user agent settings
1379
if
(
m_response
.
get_header
(
"Server"
).
empty
()) {
1380
if
(!
m_user_agent
.
empty
()) {
1381
m_response
.
replace_header
(
"Server"
,
m_user_agent
);
1382
}
else
{
1383
m_response
.
remove_header
(
"Server"
);
1384
}
1385
}
1386
1387
// have the processor generate the raw bytes for the wire (if it exists)
1388
if
(
m_processor
) {
1389
m_handshake_buffer
=
m_processor
->
get_raw
(
m_response
);
1390
}
else
{
1391
// a processor wont exist for raw HTTP responses.
1392
m_handshake_buffer
=
m_response
.
raw
();
1393
}
1394
1395
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
1396
m_alog
->
write
(
log
::
alevel
::
devel
,
"Raw Handshake response:\n"
+
m_handshake_buffer
);
1397
if
(!
m_response
.
get_header
(
"Sec-WebSocket-Key3"
).
empty
()) {
1398
m_alog
->
write
(
log
::
alevel
::
devel
,
1399
utility
::
to_hex
(
m_response
.
get_header
(
"Sec-WebSocket-Key3"
)));
1400
}
1401
}
1402
1403
// write raw bytes
1404
transport_con_type
::
async_write
(
1405
m_handshake_buffer
.
data
(),
1406
m_handshake_buffer
.
size
(),
1407
lib
::
bind
(
1408
&
type
::
handle_write_http_response
,
1409
type
::
get_shared
(),
1410
lib
::
placeholders
::
_1
1411
)
1412
);
1413
}
1414
1415
template
<
typename
config
>
1416
void
connection
<
config
>::
handle_write_http_response
(
lib
::
error_code
const
&
ec
) {
1417
m_alog
->
write
(
log
::
alevel
::
devel
,
"handle_write_http_response"
);
1418
1419
lib
::
error_code
ecm
=
ec
;
1420
1421
if
(!
ecm
) {
1422
scoped_lock_type
lock
(
m_connection_state_lock
);
1423
1424
if
(
m_state
==
session
::
state
::
connecting
) {
1425
if
(
m_internal_state
!=
istate
::
PROCESS_HTTP_REQUEST
) {
1426
ecm
=
error
::
make_error_code
(
error
::
invalid_state
);
1427
}
1428
}
else
if
(
m_state
==
session
::
state
::
closed
) {
1429
// The connection was canceled while the response was being sent,
1430
// usually by the handshake timer. This is basically expected
1431
// (though hopefully rare) and there is nothing we can do so ignore.
1432
m_alog
->
write
(
log
::
alevel
::
devel
,
1433
"handle_write_http_response invoked after connection was closed"
);
1434
return
;
1435
}
else
{
1436
ecm
=
error
::
make_error_code
(
error
::
invalid_state
);
1437
}
1438
}
1439
1440
if
(
ecm
) {
1441
if
(
ecm
==
transport
::
error
::
eof
&&
m_state
==
session
::
state
::
closed
) {
1442
// we expect to get eof if the connection is closed already
1443
m_alog
->
write
(
log
::
alevel
::
devel
,
1444
"got (expected) eof/state error from closed con"
);
1445
return
;
1446
}
1447
1448
log_err
(
log
::
elevel
::
rerror
,
"handle_write_http_response"
,
ecm
);
1449
this
->
terminate
(
ecm
);
1450
return
;
1451
}
1452
1453
if
(
m_handshake_timer
) {
1454
m_handshake_timer
->
cancel
();
1455
m_handshake_timer
.
reset
();
1456
}
1457
1458
if
(
m_response
.
get_status_code
() !=
http
::
status_code
::
switching_protocols
)
1459
{
1460
/*if (m_processor || m_ec == error::http_parse_error ||
1461
m_ec == error::invalid_version || m_ec == error::unsupported_version
1462
|| m_ec == error::upgrade_required)
1463
{*/
1464
if
(!
m_is_http
) {
1465
std
::
stringstream
s
;
1466
s
<<
"Handshake ended with HTTP error: "
1467
<<
m_response
.
get_status_code
();
1468
m_elog
->
write
(
log
::
elevel
::
rerror
,
s
.
str
());
1469
}
else
{
1470
// if this was not a websocket connection, we have written
1471
// the expected response and the connection can be closed.
1472
1473
this
->
log_http_result
();
1474
1475
if
(
m_ec
) {
1476
m_alog
->
write
(
log
::
alevel
::
devel
,
1477
"got to writing HTTP results with m_ec set: "
+
m_ec
.
message
());
1478
}
1479
m_ec
=
make_error_code
(
error
::
http_connection_ended
);
1480
}
1481
1482
this
->
terminate
(
m_ec
);
1483
return
;
1484
}
1485
1486
this
->
log_open_result
();
1487
1488
m_internal_state
=
istate
::
PROCESS_CONNECTION
;
1489
m_state
=
session
::
state
::
open
;
1490
1491
if
(
m_open_handler
) {
1492
m_open_handler
(
m_connection_hdl
);
1493
}
1494
1495
this
->
handle_read_frame
(
lib
::
error_code
(),
m_buf_cursor
);
1496
}
1497
1498
template
<
typename
config
>
1499
void
connection
<
config
>::
send_http_request
() {
1500
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection send_http_request"
);
1501
1502
// TODO: origin header?
1503
1504
// Have the protocol processor fill in the appropriate fields based on the
1505
// selected client version
1506
if
(
m_processor
) {
1507
lib
::
error_code
ec
;
1508
ec
=
m_processor
->
client_handshake_request
(
m_request
,
m_uri
,
1509
m_requested_subprotocols
);
1510
1511
if
(
ec
) {
1512
log_err
(
log
::
elevel
::
fatal
,
"Internal library error: Processor"
,
ec
);
1513
return
;
1514
}
1515
}
else
{
1516
m_elog
->
write
(
log
::
elevel
::
fatal
,
"Internal library error: missing processor"
);
1517
return
;
1518
}
1519
1520
// Unless the user has overridden the user agent, send generic WS++ UA.
1521
if
(
m_request
.
get_header
(
"User-Agent"
).
empty
()) {
1522
if
(!
m_user_agent
.
empty
()) {
1523
m_request
.
replace_header
(
"User-Agent"
,
m_user_agent
);
1524
}
else
{
1525
m_request
.
remove_header
(
"User-Agent"
);
1526
}
1527
}
1528
1529
m_handshake_buffer
=
m_request
.
raw
();
1530
1531
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
1532
m_alog
->
write
(
log
::
alevel
::
devel
,
"Raw Handshake request:\n"
+
m_handshake_buffer
);
1533
}
1534
1535
if
(
m_open_handshake_timeout_dur
> 0) {
1536
m_handshake_timer
=
transport_con_type
::
set_timer
(
1537
m_open_handshake_timeout_dur
,
1538
lib
::
bind
(
1539
&
type
::
handle_open_handshake_timeout
,
1540
type
::
get_shared
(),
1541
lib
::
placeholders
::
_1
1542
)
1543
);
1544
}
1545
1546
transport_con_type
::
async_write
(
1547
m_handshake_buffer
.
data
(),
1548
m_handshake_buffer
.
size
(),
1549
lib
::
bind
(
1550
&
type
::
handle_send_http_request
,
1551
type
::
get_shared
(),
1552
lib
::
placeholders
::
_1
1553
)
1554
);
1555
}
1556
1557
template
<
typename
config
>
1558
void
connection
<
config
>::
handle_send_http_request
(
lib
::
error_code
const
&
ec
) {
1559
m_alog
->
write
(
log
::
alevel
::
devel
,
"handle_send_http_request"
);
1560
1561
lib
::
error_code
ecm
=
ec
;
1562
1563
if
(!
ecm
) {
1564
scoped_lock_type
lock
(
m_connection_state_lock
);
1565
1566
if
(
m_state
==
session
::
state
::
connecting
) {
1567
if
(
m_internal_state
!=
istate
::
WRITE_HTTP_REQUEST
) {
1568
ecm
=
error
::
make_error_code
(
error
::
invalid_state
);
1569
}
else
{
1570
m_internal_state
=
istate
::
READ_HTTP_RESPONSE
;
1571
}
1572
}
else
if
(
m_state
==
session
::
state
::
closed
) {
1573
// The connection was canceled while the response was being sent,
1574
// usually by the handshake timer. This is basically expected
1575
// (though hopefully rare) and there is nothing we can do so ignore.
1576
m_alog
->
write
(
log
::
alevel
::
devel
,
1577
"handle_send_http_request invoked after connection was closed"
);
1578
return
;
1579
}
else
{
1580
ecm
=
error
::
make_error_code
(
error
::
invalid_state
);
1581
}
1582
}
1583
1584
if
(
ecm
) {
1585
if
(
ecm
==
transport
::
error
::
eof
&&
m_state
==
session
::
state
::
closed
) {
1586
// we expect to get eof if the connection is closed already
1587
m_alog
->
write
(
log
::
alevel
::
devel
,
1588
"got (expected) eof/state error from closed con"
);
1589
return
;
1590
}
1591
1592
log_err
(
log
::
elevel
::
rerror
,
"handle_send_http_request"
,
ecm
);
1593
this
->
terminate
(
ecm
);
1594
return
;
1595
}
1596
1597
transport_con_type
::
async_read_at_least
(
1598
1,
1599
m_buf
,
1600
config
::
connection_read_buffer_size
,
1601
lib
::
bind
(
1602
&
type
::
handle_read_http_response
,
1603
type
::
get_shared
(),
1604
lib
::
placeholders
::
_1
,
1605
lib
::
placeholders
::
_2
1606
)
1607
);
1608
}
1609
1610
template
<
typename
config
>
1611
void
connection
<
config
>::
handle_read_http_response
(
lib
::
error_code
const
&
ec
,
1612
size_t
bytes_transferred
)
1613
{
1614
m_alog
->
write
(
log
::
alevel
::
devel
,
"handle_read_http_response"
);
1615
1616
lib
::
error_code
ecm
=
ec
;
1617
1618
if
(!
ecm
) {
1619
scoped_lock_type
lock
(
m_connection_state_lock
);
1620
1621
if
(
m_state
==
session
::
state
::
connecting
) {
1622
if
(
m_internal_state
!=
istate
::
READ_HTTP_RESPONSE
) {
1623
ecm
=
error
::
make_error_code
(
error
::
invalid_state
);
1624
}
1625
}
else
if
(
m_state
==
session
::
state
::
closed
) {
1626
// The connection was canceled while the response was being sent,
1627
// usually by the handshake timer. This is basically expected
1628
// (though hopefully rare) and there is nothing we can do so ignore.
1629
m_alog
->
write
(
log
::
alevel
::
devel
,
1630
"handle_read_http_response invoked after connection was closed"
);
1631
return
;
1632
}
else
{
1633
ecm
=
error
::
make_error_code
(
error
::
invalid_state
);
1634
}
1635
}
1636
1637
if
(
ecm
) {
1638
if
(
ecm
==
transport
::
error
::
eof
&&
m_state
==
session
::
state
::
closed
) {
1639
// we expect to get eof if the connection is closed already
1640
m_alog
->
write
(
log
::
alevel
::
devel
,
1641
"got (expected) eof/state error from closed con"
);
1642
return
;
1643
}
1644
1645
log_err
(
log
::
elevel
::
rerror
,
"handle_read_http_response"
,
ecm
);
1646
this
->
terminate
(
ecm
);
1647
return
;
1648
}
1649
1650
size_t
bytes_processed
= 0;
1651
// TODO: refactor this to use error codes rather than exceptions
1652
try
{
1653
bytes_processed
=
m_response
.
consume
(
m_buf
,
bytes_transferred
);
1654
}
catch
(
http
::
exception
&
e
) {
1655
m_elog
->
write
(
log
::
elevel
::
rerror
,
1656
std
::
string
(
"error in handle_read_http_response: "
)+
e
.
what
());
1657
this
->
terminate
(
make_error_code
(
error
::
general
));
1658
return
;
1659
}
1660
1661
m_alog
->
write
(
log
::
alevel
::
devel
,
std
::
string
(
"Raw response: "
)+
m_response
.
raw
());
1662
1663
if
(
m_response
.
headers_ready
()) {
1664
if
(
m_handshake_timer
) {
1665
m_handshake_timer
->
cancel
();
1666
m_handshake_timer
.
reset
();
1667
}
1668
1669
lib
::
error_code
validate_ec
=
m_processor
->
validate_server_handshake_response
(
1670
m_request
,
1671
m_response
1672
);
1673
if
(
validate_ec
) {
1674
log_err
(
log
::
elevel
::
rerror
,
"Server handshake response"
,
validate_ec
);
1675
this
->
terminate
(
validate_ec
);
1676
return
;
1677
}
1678
1679
// Read extension parameters and set up values necessary for the end
1680
// user to complete extension negotiation.
1681
std
::
pair
<
lib
::
error_code
,
std
::
string
>
neg_results
;
1682
neg_results
=
m_processor
->
negotiate_extensions
(
m_response
);
1683
1684
if
(
neg_results
.
first
) {
1685
// There was a fatal error in extension negotiation. For the moment
1686
// kill all connections that fail extension negotiation.
1687
1688
// TODO: deal with cases where the response is well formed but
1689
// doesn't match the options requested by the client. Its possible
1690
// that the best behavior in this cases is to log and continue with
1691
// an unextended connection.
1692
m_alog
->
write
(
log
::
alevel
::
devel
,
"Extension negotiation failed: "
1693
+
neg_results
.
first
.
message
());
1694
this
->
terminate
(
make_error_code
(
error
::
extension_neg_failed
));
1695
// TODO: close connection with reason 1010 (and list extensions)
1696
}
1697
1698
// response is valid, connection can now be assumed to be open
1699
m_internal_state
=
istate
::
PROCESS_CONNECTION
;
1700
m_state
=
session
::
state
::
open
;
1701
1702
this
->
log_open_result
();
1703
1704
if
(
m_open_handler
) {
1705
m_open_handler
(
m_connection_hdl
);
1706
}
1707
1708
// The remaining bytes in m_buf are frame data. Copy them to the
1709
// beginning of the buffer and note the length. They will be read after
1710
// the handshake completes and before more bytes are read.
1711
std
::
copy
(
m_buf
+
bytes_processed
,
m_buf
+
bytes_transferred
,
m_buf
);
1712
m_buf_cursor
=
bytes_transferred
-
bytes_processed
;
1713
1714
this
->
handle_read_frame
(
lib
::
error_code
(),
m_buf_cursor
);
1715
}
else
{
1716
// The HTTP parser reported that it was not ready and wants more data.
1717
// Assert that it actually consumed all the data present before overwriting
1718
// the buffer. This should always be the case.
1719
if
(
bytes_transferred
!=
bytes_processed
) {
1720
m_elog
->
write
(
log
::
elevel
::
fatal
,
"Assertion Failed: HTTP response parser failed to consume all bytes from a read request."
);
1721
this
->
terminate
(
make_error_code
(
error
::
general
));
1722
return
;
1723
}
1724
1725
transport_con_type
::
async_read_at_least
(
1726
1,
1727
m_buf
,
1728
config
::
connection_read_buffer_size
,
1729
lib
::
bind
(
1730
&
type
::
handle_read_http_response
,
1731
type
::
get_shared
(),
1732
lib
::
placeholders
::
_1
,
1733
lib
::
placeholders
::
_2
1734
)
1735
);
1736
}
1737
}
1738
1739
template
<
typename
config
>
1740
void
connection
<
config
>::
handle_open_handshake_timeout
(
1741
lib
::
error_code
const
&
ec
)
1742
{
1743
if
(
ec
==
transport
::
error
::
operation_aborted
) {
1744
m_alog
->
write
(
log
::
alevel
::
devel
,
"open handshake timer cancelled"
);
1745
}
else
if
(
ec
) {
1746
m_alog
->
write
(
log
::
alevel
::
devel
,
1747
"open handle_open_handshake_timeout error: "
+
ec
.
message
());
1748
// TODO: ignore or fail here?
1749
}
else
{
1750
m_alog
->
write
(
log
::
alevel
::
devel
,
"open handshake timer expired"
);
1751
terminate
(
make_error_code
(
error
::
open_handshake_timeout
));
1752
}
1753
}
1754
1755
template
<
typename
config
>
1756
void
connection
<
config
>::
handle_close_handshake_timeout
(
1757
lib
::
error_code
const
&
ec
)
1758
{
1759
if
(
ec
==
transport
::
error
::
operation_aborted
) {
1760
m_alog
->
write
(
log
::
alevel
::
devel
,
"asio close handshake timer cancelled"
);
1761
}
else
if
(
ec
) {
1762
m_alog
->
write
(
log
::
alevel
::
devel
,
1763
"asio open handle_close_handshake_timeout error: "
+
ec
.
message
());
1764
// TODO: ignore or fail here?
1765
}
else
{
1766
m_alog
->
write
(
log
::
alevel
::
devel
,
"asio close handshake timer expired"
);
1767
terminate
(
make_error_code
(
error
::
close_handshake_timeout
));
1768
}
1769
}
1770
1771
template
<
typename
config
>
1772
void
connection
<
config
>::
terminate
(
lib
::
error_code
const
&
ec
) {
1773
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
1774
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection terminate"
);
1775
}
1776
1777
// Cancel close handshake timer
1778
if
(
m_handshake_timer
) {
1779
m_handshake_timer
->
cancel
();
1780
m_handshake_timer
.
reset
();
1781
}
1782
1783
terminate_status
tstat
=
unknown
;
1784
if
(
ec
) {
1785
m_ec
=
ec
;
1786
m_local_close_code
=
close
::
status
::
abnormal_close
;
1787
m_local_close_reason
=
ec
.
message
();
1788
}
1789
1790
// TODO: does any of this need a mutex?
1791
if
(
m_is_http
) {
1792
m_http_state
=
session
::
http_state
::
closed
;
1793
}
1794
if
(
m_state
==
session
::
state
::
connecting
) {
1795
m_state
=
session
::
state
::
closed
;
1796
tstat
=
failed
;
1797
1798
// Log fail result here before socket is shut down and we can't get
1799
// the remote address, etc anymore
1800
if
(
m_ec
!=
error
::
http_connection_ended
) {
1801
log_fail_result
();
1802
}
1803
}
else
if
(
m_state
!=
session
::
state
::
closed
) {
1804
m_state
=
session
::
state
::
closed
;
1805
tstat
=
closed
;
1806
}
else
{
1807
m_alog
->
write
(
log
::
alevel
::
devel
,
1808
"terminate called on connection that was already terminated"
);
1809
return
;
1810
}
1811
1812
// TODO: choose between shutdown and close based on error code sent
1813
1814
transport_con_type
::
async_shutdown
(
1815
lib
::
bind
(
1816
&
type
::
handle_terminate
,
1817
type
::
get_shared
(),
1818
tstat
,
1819
lib
::
placeholders
::
_1
1820
)
1821
);
1822
}
1823
1824
template
<
typename
config
>
1825
void
connection
<
config
>::
handle_terminate
(
terminate_status
tstat
,
1826
lib
::
error_code
const
&
ec
)
1827
{
1828
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
1829
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection handle_terminate"
);
1830
}
1831
1832
if
(
ec
) {
1833
// there was an error actually shutting down the connection
1834
log_err
(
log
::
elevel
::
devel
,
"handle_terminate"
,
ec
);
1835
}
1836
1837
// clean shutdown
1838
if
(
tstat
==
failed
) {
1839
if
(
m_ec
!=
error
::
http_connection_ended
) {
1840
if
(
m_fail_handler
) {
1841
m_fail_handler
(
m_connection_hdl
);
1842
}
1843
}
1844
}
else
if
(
tstat
==
closed
) {
1845
if
(
m_close_handler
) {
1846
m_close_handler
(
m_connection_hdl
);
1847
}
1848
log_close_result
();
1849
}
else
{
1850
m_elog
->
write
(
log
::
elevel
::
rerror
,
"Unknown terminate_status"
);
1851
}
1852
1853
// call the termination handler if it exists
1854
// if it exists it might (but shouldn't) refer to a bad memory location.
1855
// If it does, we don't care and should catch and ignore it.
1856
if
(
m_termination_handler
) {
1857
try
{
1858
m_termination_handler
(
type
::
get_shared
());
1859
}
catch
(
std
::
exception
const
&
e
) {
1860
m_elog
->
write
(
log
::
elevel
::
warn
,
1861
std
::
string
(
"termination_handler call failed. Reason was: "
)+
e
.
what
());
1862
}
1863
}
1864
}
1865
1866
template
<
typename
config
>
1867
void
connection
<
config
>::
write_frame
() {
1868
//m_alog->write(log::alevel::devel,"connection write_frame");
1869
1870
{
1871
scoped_lock_type
lock
(
m_write_lock
);
1872
1873
// Check the write flag. If true, there is an outstanding transport
1874
// write already. In this case we just return. The write handler will
1875
// start a new write if the write queue isn't empty. If false, we set
1876
// the write flag and proceed to initiate a transport write.
1877
if
(
m_write_flag
) {
1878
return
;
1879
}
1880
1881
// pull off all the messages that are ready to write.
1882
// stop if we get a message marked terminal
1883
message_ptr
next_message
=
write_pop
();
1884
while
(
next_message
) {
1885
m_current_msgs
.
push_back
(
next_message
);
1886
if
(!
next_message
->
get_terminal
()) {
1887
next_message
=
write_pop
();
1888
}
else
{
1889
next_message
=
message_ptr
();
1890
}
1891
}
1892
1893
if
(
m_current_msgs
.
empty
()) {
1894
// there was nothing to send
1895
return
;
1896
}
else
{
1897
// At this point we own the next messages to be sent and are
1898
// responsible for holding the write flag until they are
1899
// successfully sent or there is some error
1900
m_write_flag
=
true
;
1901
}
1902
}
1903
1904
typename
std
::
vector
<
message_ptr
>::
iterator
it
;
1905
for
(
it
=
m_current_msgs
.
begin
();
it
!=
m_current_msgs
.
end
(); ++
it
) {
1906
std
::
string
const
&
header
= (*
it
)->
get_header
();
1907
std
::
string
const
&
payload
= (*
it
)->
get_payload
();
1908
1909
m_send_buffer
.
push_back
(
transport
::
buffer
(
header
.
c_str
(),
header
.
size
()));
1910
m_send_buffer
.
push_back
(
transport
::
buffer
(
payload
.
c_str
(),
payload
.
size
()));
1911
}
1912
1913
// Print detailed send stats if those log levels are enabled
1914
if
(
m_alog
->
static_test
(
log
::
alevel
::
frame_header
)) {
1915
if
(
m_alog
->
dynamic_test
(
log
::
alevel
::
frame_header
)) {
1916
std
::
stringstream
general
,
header
,
payload
;
1917
1918
general
<<
"Dispatching write containing "
<<
m_current_msgs
.
size
()
1919
<<
" message(s) containing "
;
1920
header
<<
"Header Bytes: \n"
;
1921
payload
<<
"Payload Bytes: \n"
;
1922
1923
size_t
hbytes
= 0;
1924
size_t
pbytes
= 0;
1925
1926
for
(
size_t
i
= 0;
i
<
m_current_msgs
.
size
();
i
++) {
1927
hbytes
+=
m_current_msgs
[
i
]->
get_header
().
size
();
1928
pbytes
+=
m_current_msgs
[
i
]->
get_payload
().
size
();
1929
1930
1931
header
<<
"["
<<
i
<<
"] ("
1932
<<
m_current_msgs
[
i
]->
get_header
().
size
() <<
") "
1933
<<
utility
::
to_hex
(
m_current_msgs
[
i
]->
get_header
()) <<
"\n"
;
1934
1935
if
(
m_alog
->
static_test
(
log
::
alevel
::
frame_payload
)) {
1936
if
(
m_alog
->
dynamic_test
(
log
::
alevel
::
frame_payload
)) {
1937
payload
<<
"["
<<
i
<<
"] ("
1938
<<
m_current_msgs
[
i
]->
get_payload
().
size
() <<
") ["
<<
m_current_msgs
[
i
]->
get_opcode
()<<
"] "
1939
<< (
m_current_msgs
[
i
]->
get_opcode
() ==
frame
::
opcode
::
text
?
1940
m_current_msgs
[
i
]->
get_payload
() :
1941
utility
::
to_hex
(
m_current_msgs
[
i
]->
get_payload
())
1942
)
1943
<<
"\n"
;
1944
}
1945
}
1946
}
1947
1948
general
<<
hbytes
<<
" header bytes and "
<<
pbytes
<<
" payload bytes"
;
1949
1950
m_alog
->
write
(
log
::
alevel
::
frame_header
,
general
.
str
());
1951
m_alog
->
write
(
log
::
alevel
::
frame_header
,
header
.
str
());
1952
m_alog
->
write
(
log
::
alevel
::
frame_payload
,
payload
.
str
());
1953
}
1954
}
1955
1956
transport_con_type
::
async_write
(
1957
m_send_buffer
,
1958
m_write_frame_handler
1959
);
1960
}
1961
1962
template
<
typename
config
>
1963
void
connection
<
config
>::
handle_write_frame
(
lib
::
error_code
const
&
ec
)
1964
{
1965
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
1966
m_alog
->
write
(
log
::
alevel
::
devel
,
"connection handle_write_frame"
);
1967
}
1968
1969
bool
terminal
=
m_current_msgs
.
back
()->
get_terminal
();
1970
1971
m_send_buffer
.
clear
();
1972
m_current_msgs
.
clear
();
1973
// TODO: recycle instead of deleting
1974
1975
if
(
ec
) {
1976
log_err
(
log
::
elevel
::
fatal
,
"handle_write_frame"
,
ec
);
1977
this
->
terminate
(
ec
);
1978
return
;
1979
}
1980
1981
if
(
terminal
) {
1982
this
->
terminate
(
lib
::
error_code
());
1983
return
;
1984
}
1985
1986
bool
needs_writing
=
false
;
1987
{
1988
scoped_lock_type
lock
(
m_write_lock
);
1989
1990
// release write flag
1991
m_write_flag
=
false
;
1992
1993
needs_writing
= !
m_send_queue
.
empty
();
1994
}
1995
1996
if
(
needs_writing
) {
1997
transport_con_type
::
dispatch
(
lib
::
bind
(
1998
&
type
::
write_frame
,
1999
type
::
get_shared
()
2000
));
2001
}
2002
}
2003
2004
template
<
typename
config
>
2005
std
::
vector
<
int
>
const
&
connection
<
config
>::
get_supported_versions
()
const
2006
{
2007
return
versions_supported
;
2008
}
2009
2010
template
<
typename
config
>
2011
void
connection
<
config
>::
process_control_frame
(
typename
config
::
message_type
::
ptr
msg
)
2012
{
2013
m_alog
->
write
(
log
::
alevel
::
devel
,
"process_control_frame"
);
2014
2015
frame
::
opcode
::
value
op
=
msg
->
get_opcode
();
2016
lib
::
error_code
ec
;
2017
2018
std
::
stringstream
s
;
2019
s
<<
"Control frame received with opcode "
<<
op
;
2020
m_alog
->
write
(
log
::
alevel
::
control
,
s
.
str
());
2021
2022
if
(
m_state
==
session
::
state
::
closed
) {
2023
m_elog
->
write
(
log
::
elevel
::
warn
,
"got frame in state closed"
);
2024
return
;
2025
}
2026
if
(
op
!=
frame
::
opcode
::
CLOSE
&&
m_state
!=
session
::
state
::
open
) {
2027
m_elog
->
write
(
log
::
elevel
::
warn
,
"got non-close frame in state closing"
);
2028
return
;
2029
}
2030
2031
if
(
op
==
frame
::
opcode
::
PING
) {
2032
bool
should_reply
=
true
;
2033
2034
if
(
m_ping_handler
) {
2035
should_reply
=
m_ping_handler
(
m_connection_hdl
,
msg
->
get_payload
());
2036
}
2037
2038
if
(
should_reply
) {
2039
this
->
pong
(
msg
->
get_payload
(),
ec
);
2040
if
(
ec
) {
2041
log_err
(
log
::
elevel
::
devel
,
"Failed to send response pong"
,
ec
);
2042
}
2043
}
2044
}
else
if
(
op
==
frame
::
opcode
::
PONG
) {
2045
if
(
m_ping_timer
) {
2046
m_ping_timer
->
cancel
();
2047
}
2048
if
(
m_pong_handler
) {
2049
m_pong_handler
(
m_connection_hdl
,
msg
->
get_payload
());
2050
}
2051
}
else
if
(
op
==
frame
::
opcode
::
CLOSE
) {
2052
m_alog
->
write
(
log
::
alevel
::
devel
,
"got close frame"
);
2053
// record close code and reason somewhere
2054
2055
m_remote_close_code
=
close
::
extract_code
(
msg
->
get_payload
(),
ec
);
2056
if
(
ec
) {
2057
s
.
str
(
""
);
2058
if
(
config
::
drop_on_protocol_error
) {
2059
s
<<
"Received invalid close code "
<<
m_remote_close_code
2060
<<
" dropping connection per config."
;
2061
m_elog
->
write
(
log
::
elevel
::
devel
,
s
.
str
());
2062
this
->
terminate
(
ec
);
2063
}
else
{
2064
s
<<
"Received invalid close code "
<<
m_remote_close_code
2065
<<
" sending acknowledgement and closing"
;
2066
m_elog
->
write
(
log
::
elevel
::
devel
,
s
.
str
());
2067
ec
=
send_close_ack
(
close
::
status
::
protocol_error
,
2068
"Invalid close code"
);
2069
if
(
ec
) {
2070
log_err
(
log
::
elevel
::
devel
,
"send_close_ack"
,
ec
);
2071
}
2072
}
2073
return
;
2074
}
2075
2076
m_remote_close_reason
=
close
::
extract_reason
(
msg
->
get_payload
(),
ec
);
2077
if
(
ec
) {
2078
if
(
config
::
drop_on_protocol_error
) {
2079
m_elog
->
write
(
log
::
elevel
::
devel
,
2080
"Received invalid close reason. Dropping connection per config"
);
2081
this
->
terminate
(
ec
);
2082
}
else
{
2083
m_elog
->
write
(
log
::
elevel
::
devel
,
2084
"Received invalid close reason. Sending acknowledgement and closing"
);
2085
ec
=
send_close_ack
(
close
::
status
::
protocol_error
,
2086
"Invalid close reason"
);
2087
if
(
ec
) {
2088
log_err
(
log
::
elevel
::
devel
,
"send_close_ack"
,
ec
);
2089
}
2090
}
2091
return
;
2092
}
2093
2094
if
(
m_state
==
session
::
state
::
open
) {
2095
s
.
str
(
""
);
2096
s
<<
"Received close frame with code "
<<
m_remote_close_code
2097
<<
" and reason "
<<
m_remote_close_reason
;
2098
m_alog
->
write
(
log
::
alevel
::
devel
,
s
.
str
());
2099
2100
ec
=
send_close_ack
();
2101
if
(
ec
) {
2102
log_err
(
log
::
elevel
::
devel
,
"send_close_ack"
,
ec
);
2103
}
2104
}
else
if
(
m_state
==
session
::
state
::
closing
&& !
m_was_clean
) {
2105
// ack of our close
2106
m_alog
->
write
(
log
::
alevel
::
devel
,
"Got acknowledgement of close"
);
2107
2108
m_was_clean
=
true
;
2109
2110
// If we are a server terminate the connection now. Clients should
2111
// leave the connection open to give the server an opportunity to
2112
// initiate the TCP close. The client's timer will handle closing
2113
// its side of the connection if the server misbehaves.
2114
//
2115
// TODO: different behavior if the underlying transport doesn't
2116
// support timers?
2117
if
(
m_is_server
) {
2118
terminate
(
lib
::
error_code
());
2119
}
2120
}
else
{
2121
// spurious, ignore
2122
m_elog
->
write
(
log
::
elevel
::
devel
,
"Got close frame in wrong state"
);
2123
}
2124
}
else
{
2125
// got an invalid control opcode
2126
m_elog
->
write
(
log
::
elevel
::
devel
,
"Got control frame with invalid opcode"
);
2127
// initiate protocol error shutdown
2128
}
2129
}
2130
2131
template
<
typename
config
>
2132
lib
::
error_code
connection
<
config
>::
send_close_ack
(
close
::
status
::
value
code
,
2133
std
::
string
const
&
reason
)
2134
{
2135
return
send_close_frame
(
code
,
reason
,
true
,
m_is_server
);
2136
}
2137
2138
template
<
typename
config
>
2139
lib
::
error_code
connection
<
config
>::
send_close_frame
(
close
::
status
::
value
code
,
2140
std
::
string
const
&
reason
,
bool
ack
,
bool
terminal
)
2141
{
2142
m_alog
->
write
(
log
::
alevel
::
devel
,
"send_close_frame"
);
2143
2144
// check for special codes
2145
2146
// If silent close is set, respect it and blank out close information
2147
// Otherwise use whatever has been specified in the parameters. If
2148
// parameters specifies close::status::blank then determine what to do
2149
// based on whether or not this is an ack. If it is not an ack just
2150
// send blank info. If it is an ack then echo the close information from
2151
// the remote endpoint.
2152
if
(
config
::
silent_close
) {
2153
m_alog
->
write
(
log
::
alevel
::
devel
,
"closing silently"
);
2154
m_local_close_code
=
close
::
status
::
no_status
;
2155
m_local_close_reason
.
clear
();
2156
}
else
if
(
code
!=
close
::
status
::
blank
) {
2157
m_alog
->
write
(
log
::
alevel
::
devel
,
"closing with specified codes"
);
2158
m_local_close_code
=
code
;
2159
m_local_close_reason
=
reason
;
2160
}
else
if
(!
ack
) {
2161
m_alog
->
write
(
log
::
alevel
::
devel
,
"closing with no status code"
);
2162
m_local_close_code
=
close
::
status
::
no_status
;
2163
m_local_close_reason
.
clear
();
2164
}
else
if
(
m_remote_close_code
==
close
::
status
::
no_status
) {
2165
m_alog
->
write
(
log
::
alevel
::
devel
,
2166
"acknowledging a no-status close with normal code"
);
2167
m_local_close_code
=
close
::
status
::
normal
;
2168
m_local_close_reason
.
clear
();
2169
}
else
{
2170
m_alog
->
write
(
log
::
alevel
::
devel
,
"acknowledging with remote codes"
);
2171
m_local_close_code
=
m_remote_close_code
;
2172
m_local_close_reason
=
m_remote_close_reason
;
2173
}
2174
2175
std
::
stringstream
s
;
2176
s
<<
"Closing with code: "
<<
m_local_close_code
<<
", and reason: "
2177
<<
m_local_close_reason
;
2178
m_alog
->
write
(
log
::
alevel
::
devel
,
s
.
str
());
2179
2180
message_ptr
msg
=
m_msg_manager
->
get_message
();
2181
if
(!
msg
) {
2182
return
error
::
make_error_code
(
error
::
no_outgoing_buffers
);
2183
}
2184
2185
lib
::
error_code
ec
=
m_processor
->
prepare_close
(
m_local_close_code
,
2186
m_local_close_reason
,
msg
);
2187
if
(
ec
) {
2188
return
ec
;
2189
}
2190
2191
// Messages flagged terminal will result in the TCP connection being dropped
2192
// after the message has been written. This is typically used when servers
2193
// send an ack and when any endpoint encounters a protocol error
2194
if
(
terminal
) {
2195
msg
->
set_terminal
(
true
);
2196
}
2197
2198
m_state
=
session
::
state
::
closing
;
2199
2200
if
(
ack
) {
2201
m_was_clean
=
true
;
2202
}
2203
2204
// Start a timer so we don't wait forever for the acknowledgement close
2205
// frame
2206
if
(
m_close_handshake_timeout_dur
> 0) {
2207
m_handshake_timer
=
transport_con_type
::
set_timer
(
2208
m_close_handshake_timeout_dur
,
2209
lib
::
bind
(
2210
&
type
::
handle_close_handshake_timeout
,
2211
type
::
get_shared
(),
2212
lib
::
placeholders
::
_1
2213
)
2214
);
2215
}
2216
2217
bool
needs_writing
=
false
;
2218
{
2219
scoped_lock_type
lock
(
m_write_lock
);
2220
write_push
(
msg
);
2221
needs_writing
= !
m_write_flag
&& !
m_send_queue
.
empty
();
2222
}
2223
2224
if
(
needs_writing
) {
2225
transport_con_type
::
dispatch
(
lib
::
bind
(
2226
&
type
::
write_frame
,
2227
type
::
get_shared
()
2228
));
2229
}
2230
2231
return
lib
::
error_code
();
2232
}
2233
2234
template
<
typename
config
>
2235
typename
connection
<
config
>::
processor_ptr
2236
connection
<
config
>::
get_processor
(
int
version
)
const
{
2237
// TODO: allow disabling certain versions
2238
2239
processor_ptr
p
;
2240
2241
switch
(
version
) {
2242
case
0:
2243
p
=
lib
::
make_shared
<
processor
::
hybi00
<
config
> >(
2244
transport_con_type
::
is_secure
(),
2245
m_is_server
,
2246
m_msg_manager
2247
);
2248
break
;
2249
case
7:
2250
p
=
lib
::
make_shared
<
processor
::
hybi07
<
config
> >(
2251
transport_con_type
::
is_secure
(),
2252
m_is_server
,
2253
m_msg_manager
,
2254
lib
::
ref
(
m_rng
)
2255
);
2256
break
;
2257
case
8:
2258
p
=
lib
::
make_shared
<
processor
::
hybi08
<
config
> >(
2259
transport_con_type
::
is_secure
(),
2260
m_is_server
,
2261
m_msg_manager
,
2262
lib
::
ref
(
m_rng
)
2263
);
2264
break
;
2265
case
13:
2266
p
=
lib
::
make_shared
<
processor
::
hybi13
<
config
> >(
2267
transport_con_type
::
is_secure
(),
2268
m_is_server
,
2269
m_msg_manager
,
2270
lib
::
ref
(
m_rng
)
2271
);
2272
break
;
2273
default
:
2274
return
p
;
2275
}
2276
2277
// Settings not configured by the constructor
2278
p
->
set_max_message_size
(
m_max_message_size
);
2279
2280
return
p
;
2281
}
2282
2283
template
<
typename
config
>
2284
void
connection
<
config
>::
write_push
(
typename
config
::
message_type
::
ptr
msg
)
2285
{
2286
if
(!
msg
) {
2287
return
;
2288
}
2289
2290
m_send_buffer_size
+=
msg
->
get_payload
().
size
();
2291
m_send_queue
.
push
(
msg
);
2292
2293
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
2294
std
::
stringstream
s
;
2295
s
<<
"write_push: message count: "
<<
m_send_queue
.
size
()
2296
<<
" buffer size: "
<<
m_send_buffer_size
;
2297
m_alog
->
write
(
log
::
alevel
::
devel
,
s
.
str
());
2298
}
2299
}
2300
2301
template
<
typename
config
>
2302
typename
config
::
message_type
::
ptr
connection
<
config
>::
write_pop
()
2303
{
2304
message_ptr
msg
;
2305
2306
if
(
m_send_queue
.
empty
()) {
2307
return
msg
;
2308
}
2309
2310
msg
=
m_send_queue
.
front
();
2311
2312
m_send_buffer_size
-=
msg
->
get_payload
().
size
();
2313
m_send_queue
.
pop
();
2314
2315
if
(
m_alog
->
static_test
(
log
::
alevel
::
devel
)) {
2316
std
::
stringstream
s
;
2317
s
<<
"write_pop: message count: "
<<
m_send_queue
.
size
()
2318
<<
" buffer size: "
<<
m_send_buffer_size
;
2319
m_alog
->
write
(
log
::
alevel
::
devel
,
s
.
str
());
2320
}
2321
return
msg
;
2322
}
2323
2324
template
<
typename
config
>
2325
void
connection
<
config
>::
log_open_result
()
2326
{
2327
std
::
stringstream
s
;
2328
2329
int
version
;
2330
if
(!
processor
::
is_websocket_handshake
(
m_request
)) {
2331
version
= -1;
2332
}
else
{
2333
version
=
processor
::
get_websocket_version
(
m_request
);
2334
}
2335
2336
// Connection Type
2337
s
<< (
version
== -1 ?
"HTTP"
:
"WebSocket"
) <<
" Connection "
;
2338
2339
// Remote endpoint address
2340
s
<<
transport_con_type
::
get_remote_endpoint
() <<
" "
;
2341
2342
// Version string if WebSocket
2343
if
(
version
!= -1) {
2344
s
<<
"v"
<<
version
<<
" "
;
2345
}
2346
2347
// User Agent
2348
std
::
string
ua
=
m_request
.
get_header
(
"User-Agent"
);
2349
if
(
ua
.
empty
()) {
2350
s
<<
"\"\" "
;
2351
}
else
{
2352
// check if there are any quotes in the user agent
2353
s
<<
"\""
<<
utility
::
string_replace_all
(
ua
,
"\""
,
"\\\""
) <<
"\" "
;
2354
}
2355
2356
// URI
2357
s
<< (
m_uri
?
m_uri
->
get_resource
() :
"NULL"
) <<
" "
;
2358
2359
// Status code
2360
s
<<
m_response
.
get_status_code
();
2361
2362
m_alog
->
write
(
log
::
alevel
::
connect
,
s
.
str
());
2363
}
2364
2365
template
<
typename
config
>
2366
void
connection
<
config
>::
log_close_result
()
2367
{
2368
std
::
stringstream
s
;
2369
2370
s
<<
"Disconnect "
2371
<<
"close local:["
<<
m_local_close_code
2372
<< (
m_local_close_reason
.
empty
() ?
""
:
","
+
m_local_close_reason
)
2373
<<
"] remote:["
<<
m_remote_close_code
2374
<< (
m_remote_close_reason
.
empty
() ?
""
:
","
+
m_remote_close_reason
) <<
"]"
;
2375
2376
m_alog
->
write
(
log
::
alevel
::
disconnect
,
s
.
str
());
2377
}
2378
2379
template
<
typename
config
>
2380
void
connection
<
config
>::
log_fail_result
()
2381
{
2382
std
::
stringstream
s
;
2383
2384
int
version
=
processor
::
get_websocket_version
(
m_request
);
2385
2386
// Connection Type
2387
s
<<
"WebSocket Connection "
;
2388
2389
// Remote endpoint address & WebSocket version
2390
s
<<
transport_con_type
::
get_remote_endpoint
();
2391
if
(
version
< 0) {
2392
s
<<
" -"
;
2393
}
else
{
2394
s
<<
" v"
<<
version
;
2395
}
2396
2397
// User Agent
2398
std
::
string
ua
=
m_request
.
get_header
(
"User-Agent"
);
2399
if
(
ua
.
empty
()) {
2400
s
<<
" \"\" "
;
2401
}
else
{
2402
// check if there are any quotes in the user agent
2403
s
<<
" \""
<<
utility
::
string_replace_all
(
ua
,
"\""
,
"\\\""
) <<
"\" "
;
2404
}
2405
2406
// URI
2407
s
<< (
m_uri
?
m_uri
->
get_resource
() :
"-"
);
2408
2409
// HTTP Status code
2410
s
<<
" "
<<
m_response
.
get_status_code
();
2411
2412
// WebSocket++ error code & reason
2413
s
<<
" "
<<
m_ec
<<
" "
<<
m_ec
.
message
();
2414
2415
m_alog
->
write
(
log
::
alevel
::
fail
,
s
.
str
());
2416
}
2417
2418
template
<
typename
config
>
2419
void
connection
<
config
>::
log_http_result
() {
2420
std
::
stringstream
s
;
2421
2422
if
(
processor
::
is_websocket_handshake
(
m_request
)) {
2423
m_alog
->
write
(
log
::
alevel
::
devel
,
"Call to log_http_result for WebSocket"
);
2424
return
;
2425
}
2426
2427
// Connection Type
2428
s
<< (
m_request
.
get_header
(
"host"
).
empty
() ?
"-"
:
m_request
.
get_header
(
"host"
))
2429
<<
" "
<<
transport_con_type
::
get_remote_endpoint
()
2430
<<
" \""
<<
m_request
.
get_method
()
2431
<<
" "
<< (
m_uri
?
m_uri
->
get_resource
() :
"-"
)
2432
<<
" "
<<
m_request
.
get_version
() <<
"\" "
<<
m_response
.
get_status_code
()
2433
<<
" "
<<
m_response
.
get_body
().
size
();
2434
2435
// User Agent
2436
std
::
string
ua
=
m_request
.
get_header
(
"User-Agent"
);
2437
if
(
ua
.
empty
()) {
2438
s
<<
" \"\" "
;
2439
}
else
{
2440
// check if there are any quotes in the user agent
2441
s
<<
" \""
<<
utility
::
string_replace_all
(
ua
,
"\""
,
"\\\""
) <<
"\" "
;
2442
}
2443
2444
m_alog
->
write
(
log
::
alevel
::
http
,
s
.
str
());
2445
}
2446
2447
}
// namespace websocketpp
2448
2449
#
endif
// WEBSOCKETPP_CONNECTION_IMPL_HPP
websocketpp::versions_supported
static std::vector< int > const versions_supported(helper, helper+4)
Container that stores the list of protocol versions supported.
Generated by
1.8.18