./ct_report/coverage/mod_bosh_socket.COVER.html

1 -module(mod_bosh_socket).
2
3 -behaviour(gen_fsm_compat).
4 -behaviour(mongoose_c2s_socket).
5
6 %% API
7 -export([start/5,
8 start_link/5,
9 start_supervisor/0,
10 is_supervisor_started/0,
11 handle_request/2,
12 pause/2]).
13
14 %% Private API
15 -export([get_handlers/1,
16 get_pending/1,
17 get_client_acks/1,
18 set_client_acks/2,
19 get_cached_responses/1]).
20
21
22 %% mongoose_c2s_socket callbacks
23 -export([socket_new/2,
24 socket_peername/1,
25 tcp_to_tls/2,
26 socket_handle_data/2,
27 socket_activate/1,
28 socket_send_xml/2,
29 socket_close/1,
30 get_peer_certificate/2,
31 has_peer_cert/2,
32 is_channel_binding_supported/1,
33 get_tls_last_message/1,
34 is_ssl/1]).
35
36 %% gen_fsm callbacks
37 -export([init/1,
38 accumulate/2, accumulate/3,
39 normal/2, normal/3,
40 closing/2, closing/3,
41 handle_event/3,
42 handle_sync_event/4,
43 handle_info/3,
44 terminate/3,
45 code_change/4]).
46
47 -ignore_xref([{mod_bosh_backend, delete_session, 1},
48 accumulate/2, accumulate/3, closing/2,
49 closing/3, get_cached_responses/1,
50 get_client_acks/1, get_handlers/1, get_peer_certificate/1,
51 get_pending/1, normal/2, normal/3,
52 pause/2, set_client_acks/2, start_link/5]).
53
54 -include("mongoose.hrl").
55 -include("jlib.hrl").
56 -include_lib("exml/include/exml_stream.hrl").
57 -include("mod_bosh.hrl").
58 -define(ACCUMULATE_PERIOD, 10).
59 -define(DEFAULT_HOLD, 1).
60 -define(CONCURRENT_REQUESTS, 2).
61 -define(DEFAULT_WAIT, 60).
62 -define(DEFAULT_CLIENT_ACKS, false).
63
64 -type cached_response() :: {rid(), TStamp :: integer(), exml:element()}.
65 -type rid() :: pos_integer().
66
67 -record(state, {from :: binary() | undefined,
68 to :: binary() | undefined,
69 c2s_pid :: pid(),
70 handlers = [] :: [{rid(), reference(), pid()}],
71 %% Elements buffered for sending to the client.
72 pending = [] :: [jlib:xmlstreamel()],
73 sid :: mod_bosh:sid(),
74 wait = ?DEFAULT_WAIT :: integer(),
75 hold = ?DEFAULT_HOLD :: integer(),
76 rid :: rid() | undefined,
77 %% Requests deferred for later processing because
78 %% of having Rid greater than expected.
79 deferred = [] :: [{rid(), {mod_bosh:event_type(), exml:element()}}],
80 client_acks = ?DEFAULT_CLIENT_ACKS :: boolean(),
81 sent = [] :: [cached_response()],
82 %% Allowed inactivity period in seconds.
83 inactivity :: pos_integer() | infinity,
84 inactivity_tref :: reference() | 'undefined',
85 %% Max pause period in seconds.
86 max_pause :: pos_integer(),
87 max_wait :: pos_integer() | infinity,
88 %% Are acknowledgements used?
89 server_acks :: boolean(),
90 last_processed :: rid() | 'undefined',
91 %% Report scheduled for sending at the earliest
92 %% possible occasion.
93 report = false :: {rid(), Time :: non_neg_integer()} | 'false'}).
94 -type state() :: #state{}.
95
96 -type statename() :: 'accumulate' | 'normal' | 'closing'.
97 -type fsm_return() :: {'next_state', statename(), state()}.
98
99 %%--------------------------------------------------------------------
100 %% API
101 %%--------------------------------------------------------------------
102
103 -spec start(mongooseim:host_type(), mod_bosh:sid(), mongoose_transport:peer(),
104 binary() | undefined, map()) ->
105 {'error', _} | {'ok', 'undefined' | pid()} | {'ok', 'undefined' | pid(), _}.
106 start(HostType, Sid, Peer, PeerCert, Opts) ->
107 124 supervisor:start_child(?BOSH_SOCKET_SUP, [HostType, Sid, Peer, PeerCert, Opts]).
108
109 -spec start_link(mongooseim:host_type(), mod_bosh:sid(), mongoose_transport:peer(),
110 binary() | undefined, map()) ->
111 'ignore' | {'error', _} | {'ok', pid()}.
112 start_link(HostType, Sid, Peer, PeerCert, Opts) ->
113 124 gen_fsm_compat:start_link(?MODULE, [{HostType, Sid, Peer, PeerCert, Opts}], []).
114
115 -spec start_supervisor() -> {ok, pid()} | {error, any()}.
116 start_supervisor() ->
117 104 ChildId = ?BOSH_SOCKET_SUP,
118 104 ChildSpec =
119 {ChildId,
120 {ejabberd_tmp_sup, start_link,
121 [ChildId, ?MODULE]},
122 permanent,
123 infinity,
124 supervisor,
125 [ejabberd_tmp_sup]},
126 104 case supervisor:start_child(ejabberd_sup, ChildSpec) of
127 {ok, undefined} ->
128
:-(
{error, undefined};
129 {ok, Child} ->
130 104 {ok, Child};
131 {ok, Child, _Info} ->
132
:-(
{ok, Child};
133 {error, {already_started, Child}} ->
134
:-(
{ok, Child};
135 {error, Reason} ->
136
:-(
{error, Reason}
137 end.
138
139 -spec is_supervisor_started() -> boolean().
140 is_supervisor_started() ->
141 416 is_pid(whereis(?BOSH_SOCKET_SUP)).
142
143 -spec handle_request(Pid :: pid(),
144 {EventTag :: mod_bosh:event_type(),
145 Handler :: pid(),
146 Body :: exml:element()}) -> ok.
147 handle_request(Pid, Request) ->
148 2078 gen_fsm_compat:send_all_state_event(Pid, Request).
149
150
151 %% @doc TODO: no handler for this call is present!
152 %% No check for violating maxpause is made when calling this!
153 -spec pause(Pid :: pid(), Seconds :: pos_integer()) -> ok.
154 pause(Pid, Seconds) ->
155
:-(
gen_fsm_compat:send_all_state_event(Pid, {pause, Seconds}).
156
157 %%--------------------------------------------------------------------
158 %% Private API
159 %%--------------------------------------------------------------------
160
161 get_handlers(Pid) ->
162 19 gen_fsm_compat:sync_send_all_state_event(Pid, get_handlers).
163
164
165 get_pending(Pid) ->
166
:-(
gen_fsm_compat:sync_send_all_state_event(Pid, get_pending).
167
168
169 -spec get_client_acks(pid()) -> boolean().
170 get_client_acks(Pid) ->
171
:-(
gen_fsm_compat:sync_send_all_state_event(Pid, get_client_acks).
172
173
174 -spec set_client_acks(pid(), boolean()) -> any().
175 set_client_acks(Pid, Enabled) ->
176 4 gen_fsm_compat:sync_send_all_state_event(Pid, {set_client_acks, Enabled}).
177
178
179 -spec get_cached_responses(pid()) -> [cached_response()].
180 get_cached_responses(Pid) ->
181 1 gen_fsm_compat:sync_send_all_state_event(Pid, get_cached_responses).
182
183 %%--------------------------------------------------------------------
184 %% gen_fsm callbacks
185 %%--------------------------------------------------------------------
186
187 %%--------------------------------------------------------------------
188 %% @private
189 %% init(Args) -> {ok, StateName, State} |
190 %% {ok, StateName, State, Timeout} |
191 %% ignore |
192 %% {stop, StopReason}
193 %% @end
194 %%--------------------------------------------------------------------
195 -spec init([{mongooseim:host_type(), mod_bosh:sid(), mongoose_transport:peer(), undefined |
196 binary(), map()}]) ->
197 {ok, accumulate, state()}.
198 init([{HostType, Sid, Peer, PeerCert, ListenerOpts}]) ->
199 124 BoshSocket = #bosh_socket{sid = Sid, pid = self(), peer = Peer, peercert = PeerCert},
200 124 C2SOpts = ListenerOpts#{access => all,
201 shaper => none,
202 xml_socket => true,
203 max_stanza_size => 0,
204 hibernate_after => 0,
205 c2s_state_timeout => 5000,
206 backwards_compatible_session => true,
207 proto => tcp},
208 124 {ok, C2SPid} = mongoose_c2s:start({?MODULE, BoshSocket, C2SOpts}, []),
209 124 Opts = gen_mod:get_loaded_module_opts(HostType, mod_bosh),
210 124 State = new_state(Sid, C2SPid, Opts),
211 124 ?LOG_DEBUG(ls(#{what => bosh_socket_init, peer => Peer}, State)),
212 124 {ok, accumulate, State}.
213
214 new_state(Sid, C2SPid, #{inactivity := Inactivity, max_wait := MaxWait,
215 server_acks := ServerAcks, max_pause := MaxPause}) ->
216 124 #state{sid = Sid,
217 c2s_pid = C2SPid,
218 inactivity = Inactivity,
219 max_pause = MaxPause,
220 max_wait = MaxWait,
221 server_acks = ServerAcks}.
222
223 %%--------------------------------------------------------------------
224 %% @private
225 %% @doc
226 %% There should be one instance of this function for each possible
227 %% state name. Whenever a gen_fsm receives an event sent using
228 %% gen_fsm_compat:send_event/2, the instance of this function with the same
229 %% name as the current state name StateName is called to handle
230 %% the event. It is also called if a timeout occurs.
231 %%
232 %% state_name(Event, State) ->
233 %% {next_state, NextStateName, NextState} |
234 %% {next_state, NextStateName, NextState, Timeout} |
235 %% {stop, Reason, NewState}
236 %% @end
237 %%--------------------------------------------------------------------
238
239 -spec accumulate(_, state()) -> fsm_return().
240 accumulate(acc_off, #state{pending = Pending} = S) ->
241 244 NS = S#state{pending = []},
242 244 {next_state, normal, send_or_store(Pending, NS)};
243 accumulate(Event, State) ->
244
:-(
?LOG_DEBUG(ls(#{what => bosh_socket_unhandled_event, state_name => accumulate,
245
:-(
event => Event}, State)),
246
:-(
{next_state, accumulate, State}.
247
248
249 -spec normal(_, state()) -> fsm_return().
250 normal(acc_off, #state{} = S) ->
251
:-(
{next_state, normal, S};
252 normal(Event, State) ->
253
:-(
?LOG_DEBUG(ls(#{what => bosh_socket_unhandled_event, state_name => normal,
254
:-(
event => Event}, State)),
255
:-(
{next_state, normal, State}.
256
257 closing(Event, State) ->
258
:-(
?LOG_DEBUG(ls(#{what => bosh_socket_unhandled_event, state_name => closing,
259
:-(
event => Event}, State)),
260
:-(
{next_state, closing, State}.
261
262 %%--------------------------------------------------------------------
263 %% @private
264 %% @doc
265 %% There should be one instance of this function for each possible
266 %% state name. Whenever a gen_fsm receives an event sent using
267 %% gen_fsm_compat:sync_send_event/[2, 3], the instance of this function with
268 %% the same name as the current state name StateName is called to
269 %% handle the event.
270 %%
271 %% state_name(Event, From, State) ->
272 %% {next_state, NextStateName, NextState} |
273 %% {next_state, NextStateName, NextState, Timeout} |
274 %% {reply, Reply, NextStateName, NextState} |
275 %% {reply, Reply, NextStateName, NextState, Timeout} |
276 %% {stop, Reason, NewState} |
277 %% {stop, Reason, Reply, NewState}
278 %% @end
279 %%--------------------------------------------------------------------
280 accumulate(Event, _From, State) ->
281
:-(
?LOG_DEBUG(ls(#{what => bosh_socket_unhandled_sync_event, state_name => accumulate,
282
:-(
event => Event}, State)),
283
:-(
{reply, ok, accumulate, State}.
284
285 normal(Event, _From, State) ->
286
:-(
?LOG_DEBUG(ls(#{what => bosh_socket_unhandled_sync_event, state_name => normal,
287
:-(
event => Event}, State)),
288
:-(
{reply, ok, normal, State}.
289
290 closing(Event, _From, State) ->
291
:-(
?LOG_DEBUG(ls(#{what => bosh_socket_unhandled_sync_event, state_name => closing,
292
:-(
event => Event}, State)),
293
:-(
{reply, ok, closing, State}.
294
295 %%--------------------------------------------------------------------
296 %% @private
297 %% @doc
298 %% Whenever a gen_fsm receives an event sent using
299 %% gen_fsm_compat:send_all_state_event/2, this function is called to handle
300 %% the event.
301 %%
302 %% handle_event(Event, StateName, State) ->
303 %% {next_state, NextStateName, NextState} |
304 %% {next_state, NextStateName, NextState, Timeout} |
305 %% {stop, Reason, NewState}
306 %% @end
307 %%--------------------------------------------------------------------
308
309 handle_event({EventTag, Handler, #xmlel{} = Body}, SName, S) ->
310 2078 NS = cancel_inactivity_timer(S),
311 2078 Rid = binary_to_integer(exml_query:attr(Body, <<"rid">>)),
312 2078 try
313 2078 NNS = handle_stream_event({EventTag, Body, Rid}, Handler, SName, NS),
314 %% TODO: it's the event which determines the next state,
315 %% this ought to be returned from handle_stream_event
316 2075 determine_next_state(EventTag, SName, NNS)
317 catch
318 throw:{invalid_rid, TState} ->
319 2 {stop, {shutdown, invalid_rid}, TState};
320 throw:{invalid_pause, TState} ->
321 1 {stop, {shutdown, policy_violation}, TState}
322 end;
323
324 handle_event(Event, StateName, State) ->
325
:-(
?LOG_DEBUG(ls(#{what => bosh_socket_unhandled_all_state,
326
:-(
state_name => StateName, event => Event}, State)),
327
:-(
{next_state, StateName, State}.
328
329
330 determine_next_state(_EventTag, closing, NNS) ->
331 16 {stop, normal, NNS};
332 determine_next_state(EventTag, SName, NNS) ->
333 2059 case EventTag of
334 _ when EventTag == streamstart; EventTag == restart ->
335 244 timer:apply_after(?ACCUMULATE_PERIOD,
336 gen_fsm_compat, send_event, [self(), acc_off]),
337 244 {next_state, accumulate, NNS};
338 _ ->
339 1815 {next_state, SName, NNS}
340 end.
341
342 %%--------------------------------------------------------------------
343 %% @private
344 %% @doc
345 %% Whenever a gen_fsm receives an event sent using
346 %% gen_fsm_compat:sync_send_all_state_event/[2, 3], this function is called
347 %% to handle the event.
348 %%
349 %% handle_sync_event(Event, From, StateName, State) ->
350 %% {next_state, NextStateName, NextState} |
351 %% {next_state, NextStateName, NextState, Timeout} |
352 %% {reply, Reply, NextStateName, NextState} |
353 %% {reply, Reply, NextStateName, NextState, Timeout} |
354 %% {stop, Reason, NewState} |
355 %% {stop, Reason, Reply, NewState}
356 %% @end
357 %%--------------------------------------------------------------------
358 handle_sync_event(get_handlers, _From, StateName,
359 #state{handlers = Handlers} = S) ->
360 19 {reply, Handlers, StateName, S};
361 handle_sync_event(get_pending, _From, StateName,
362 #state{pending = Pending} = S) ->
363
:-(
{reply, Pending, StateName, S};
364 handle_sync_event(get_client_acks, _From, StateName,
365 #state{client_acks = ClientAcks} = S) ->
366
:-(
{reply, ClientAcks, StateName, S};
367 handle_sync_event({set_client_acks, ClientAcks}, _From, StateName,
368 #state{} = S) ->
369 4 NS = S#state{client_acks = ClientAcks},
370 4 {reply, ok, StateName, NS};
371 handle_sync_event(get_cached_responses, _From, StateName,
372 #state{sent = CachedResponses} = S) ->
373 1 {reply, CachedResponses, StateName, S};
374 handle_sync_event(Event, _From, StateName, State) ->
375
:-(
?LOG_DEBUG(ls(#{what => bosh_socket_unhandled_sync_all_state,
376
:-(
state_name => StateName, event => Event}, State)),
377
:-(
Reply = ok,
378
:-(
{reply, Reply, StateName, State}.
379
380 %%--------------------------------------------------------------------
381 %% @private
382 %% @doc
383 %% This function is called by a gen_fsm when it receives any
384 %% message other than a synchronous or asynchronous event
385 %% (or a system message).
386 %%
387 %% handle_info(Info, StateName, State)->
388 %% {next_state, NextStateName, NextState} |
389 %% {next_state, NextStateName, NextState, Timeout} |
390 %% {stop, Reason, NewState}
391 %% @end
392 %%--------------------------------------------------------------------
393
394 handle_info({send, #xmlstreamend{} = StreamEnd}, normal = SName,
395 #state{pending = Pending} = S) ->
396 119 NS = send_or_store(Pending ++ [StreamEnd], S#state{pending = []}),
397 119 {next_state, SName, NS};
398 handle_info({send, Data}, accumulate = SName, #state{} = S) ->
399 488 {next_state, SName, store([Data], S)};
400 handle_info({send, Data}, normal = SName, #state{} = S) ->
401 821 NS = send_or_store(Data, S),
402 821 {next_state, SName, NS};
403 handle_info(close, _SName, #state{pending = []} = State) ->
404 103 {stop, normal, State};
405 handle_info(close, _SName, State) ->
406 16 {next_state, closing, State};
407 handle_info(inactivity_timeout, _SName, State) ->
408 2 ?LOG_INFO(ls(#{what => bosh_socket_terminating, reason => inactivity_timeout}, State)),
409 2 {stop, {shutdown, inactivity_timeout}, State};
410 handle_info({wait_timeout, {Rid, Pid}}, SName,
411 #state{handlers = Handlers} = S) ->
412 2 ?LOG_INFO(ls(#{what => bosh_socket_wait_timeout,
413 2 handler_rid => Rid, handler_pid => Pid}, S)),
414 %% In case some message was being handled when the timer fired
415 %% it may turn out that Pid is no longer available in Handlers.
416 2 case lists:keytake(Rid, 1, Handlers) of
417 false ->
418
:-(
{next_state, SName, S};
419 {value, {Rid, _, Pid}, NewHandlers} ->
420 2 NS = send_to_handler({Rid, Pid}, [],
421 S#state{handlers = NewHandlers}),
422 2 {next_state, SName, NS}
423 end;
424 handle_info(Info, SName, State) ->
425
:-(
?UNEXPECTED_INFO(Info),
426
:-(
{next_state, SName, State}.
427
428 terminate(Reason, StateName, #state{sid = Sid, handlers = Handlers} = S) ->
429 124 [Pid ! {close, Sid} || {_, _, Pid} <- lists:sort(Handlers)],
430 124 mod_bosh_backend:delete_session(Sid),
431 124 catch mongoose_c2s:stop(S#state.c2s_pid, normal),
432 124 ?LOG_DEBUG(ls(#{what => bosh_socket_closing_session, reason => Reason,
433 state_name => StateName, handlers => Handlers,
434 124 pending => S#state.pending}, S)).
435
436 code_change(_OldVsn, StateName, State, _Extra) ->
437
:-(
{ok, StateName, State}.
438
439 %%--------------------------------------------------------------------
440 %% callback implementations
441 %%--------------------------------------------------------------------
442
443 handle_stream_event({EventTag, Body, Rid} = Event, Handler,
444 SName, #state{rid = OldRid} = S) ->
445 2083 ExpectedRid = maybe_add(1, OldRid),
446 2083 NS = maybe_add_handler(Handler, Rid, S),
447 2083 NNS = case {EventTag,
448 maybe_is_retransmission(Rid, OldRid, S#state.sent),
449 is_expected_rid(Rid, ExpectedRid),
450 is_acceptable_rid(Rid, ExpectedRid)}
451 of
452 {_, {true, CachedResponse}, _, _} when Handler /= none ->
453 1 case CachedResponse of
454 none ->
455
:-(
NS;
456 _ ->
457 1 resend_cached(CachedResponse, NS)
458 end;
459 {streamstart, _, _, _} ->
460 124 process_acked_stream_event(Event, SName, NS);
461 {_, _, true, _} ->
462 1951 process_acked_stream_event(Event, SName, NS);
463 {_, _, false, true} ->
464 5 ?LOG_INFO(ls(#{what => bosh_socket_deferring,
465 event_rid => Rid, body => Body,
466 5 expected_rid => ExpectedRid, event_tag => EventTag}, S)),
467 5 NS#state{deferred = [Event | NS#state.deferred]};
468 {_, _, false, false} ->
469 2 ?LOG_ERROR(#{what => bosh_socket_invalid_rid,
470 event_rid => Rid, body => Body,
471 expected_rid => ExpectedRid, event_tag => EventTag,
472
:-(
difference => maybe_diff(Rid, ExpectedRid)}),
473 2 [Pid ! item_not_found
474 2 || {_, _, Pid} <- lists:sort(NS#state.handlers)],
475 2 throw({invalid_rid, NS#state{handlers = []}})
476 end,
477 2080 return_surplus_handlers(SName, NNS).
478
479 -spec maybe_is_retransmission(rid(), rid(), [cached_response()])
480 -> false | {true, none} | {true, cached_response()}.
481 maybe_is_retransmission(Rid, OldRid, Sent) ->
482 2083 case {lists:keyfind(Rid, 1, Sent), Rid =:= OldRid} of
483 {false, false} ->
484 2082 false;
485 {false, true} ->
486
:-(
?LOG_INFO(#{what => bosh_socket_request_repeated,
487 text => <<"Request repeated but no response found in cache">>,
488
:-(
event_rid => Rid, old_rid => OldRid, sent => Sent}),
489
:-(
{true, none};
490 {CachedResponse, _} ->
491 1 {true, CachedResponse}
492 end.
493
494 -spec maybe_add(rid(), rid() | undefined)
495 -> rid() | undefined.
496 124 maybe_add(_, undefined) -> undefined;
497 maybe_add(Rid1, Rid2) when is_integer(Rid1),
498 1959 is_integer(Rid2) -> Rid1 + Rid2.
499
500 -spec maybe_diff(rid(), rid() | undefined)
501 -> non_neg_integer() | undefined.
502
:-(
maybe_diff(_, undefined) -> undefined;
503 2 maybe_diff(Rid, Expected) -> abs(Rid-Expected).
504
505 -spec resend_cached(cached_response(), state()) -> state().
506 resend_cached({_Rid, _, CachedBody}, S) ->
507 1 send_to_handler(CachedBody, S).
508
509
510 -spec process_acked_stream_event({EventTag :: mod_bosh:event_type(),
511 Body :: exml:element(),
512 Rid :: 'undefined' | rid()},
513 SName :: any(),
514 S :: state() ) -> state().
515 process_acked_stream_event({EventTag, Body, Rid}, SName,
516 #state{} = S) ->
517 2075 MaybeBAck = exml_query:attr(Body, <<"ack">>),
518 2075 {Action, Ack} = determine_report_action(MaybeBAck, S#state.client_acks,
519 Rid, S#state.last_processed),
520 2075 NS = maybe_trim_cache(Ack, S),
521 2075 case Action of
522 noreport ->
523 2074 process_stream_event(EventTag, Body, SName, rid(NS, Rid));
524 report ->
525 1 NS2 = schedule_report(Ack, NS),
526 1 NS3 = process_stream_event(EventTag, Body, SName, rid(NS2, Rid)),
527 1 maybe_send_report(NS3)
528 end.
529
530 rid(#state{} = S, Rid) when is_integer(Rid), Rid > 0 ->
531 2075 S#state{rid = Rid}.
532
533
534 -spec determine_report_action(BinAck :: 'undefined' | binary(),
535 boolean(),
536 Rid :: rid(),
537 LastProcessed :: 'undefined' | pos_integer()
538 ) -> {'noreport', _} | {'report', _}.
539 determine_report_action(undefined, false, _, _) ->
540 2070 {noreport, undefined};
541 determine_report_action(undefined, true, Rid, LastProcessed) ->
542 2 ?WARNING_MSG_IF(Rid+1 /= LastProcessed, "expected 'ack' attribute on ~p~n", [Rid]),
543 2 {noreport, undefined};
544 determine_report_action(BinAck, _, _, LastProcessed) ->
545 3 Ack = binary_to_integer(BinAck),
546 3 case {LastProcessed, is_valid_ack(Ack, LastProcessed)} of
547 {undefined, _} ->
548
:-(
{noreport, Ack};
549 {_, true} ->
550 2 {noreport, Ack};
551 {_, false} ->
552 1 {report, Ack}
553 end.
554
555
556 -spec is_valid_ack(Ack :: rid(), 'undefined' | pos_integer()) -> boolean().
557 is_valid_ack(Ack, LastProcessed)
558 when Ack < LastProcessed ->
559 1 false;
560 is_valid_ack(_, _) ->
561 2 true.
562
563
564 -spec maybe_trim_cache(undefined | any(), state()) -> state().
565 maybe_trim_cache(undefined, S) ->
566 2072 S;
567 maybe_trim_cache(Ack, S) ->
568 3 UpToAck = fun({R, _, _}) when R =< Ack ->
569 5 true;
570 (_) ->
571 1 false
572 end,
573 3 NewSent = lists:dropwhile(UpToAck, S#state.sent),
574 3 S#state{sent = NewSent}.
575
576
577 -spec schedule_report(rid(), state()) -> state().
578 schedule_report(Ack, #state{sent = Sent} = S) ->
579 1 ReportRid = Ack + 1,
580 1 try
581 1 {ReportRid, TimeSent, _} = lists:keyfind(ReportRid, 1, Sent),
582 1 ElapsedTimeMillis = erlang:monotonic_time(millisecond) - TimeSent,
583 1 Report = {ReportRid, ElapsedTimeMillis},
584 1 case S#state.report of
585 false ->
586 1 S#state{report = Report};
587 OldReport when OldReport < Report ->
588
:-(
S#state{report = OldReport};
589 _ ->
590
:-(
S#state{report = Report}
591 end
592 catch
593 error:{badmatch, {resp, false}} ->
594
:-(
?LOG_ERROR(ls(#{what => bosh_socket_no_cached_response,
595
:-(
responses => Sent, rid_offender => ReportRid}, S)),
596
:-(
S
597 end.
598
599
600 -spec maybe_send_report(state()) -> state().
601 maybe_send_report(#state{report = false} = S) ->
602
:-(
S;
603 maybe_send_report(#state{} = S) ->
604 1 send_or_store([], S).
605
606
607 -spec process_stream_event(mod_bosh:event_type(), exml:element(), _SName,
608 state()) -> state().
609 process_stream_event(pause, Body, SName, State) ->
610 3 Seconds = binary_to_integer(exml_query:attr(Body, <<"pause">>)),
611 3 NewState = process_pause_event(Seconds, State),
612 2 process_deferred_events(SName, NewState);
613 process_stream_event(EventTag, Body, SName, #state{c2s_pid = C2SPid} = State) ->
614 2072 {Els, NewState} = bosh_unwrap(EventTag, Body, State),
615 2072 [forward_to_c2s(C2SPid, El) || El <- Els],
616 2072 process_deferred_events(SName, NewState).
617
618
619 -spec process_pause_event('infinity' | 'undefined' | pos_integer(),
620 state()) -> state().
621 process_pause_event(Seconds, #state{max_pause = MaxPause} = S)
622 when MaxPause == undefined;
623 Seconds > MaxPause ->
624 1 [Pid ! policy_violation || {_, _, Pid} <- S#state.handlers],
625 1 throw({invalid_pause, S#state{handlers = []}});
626 process_pause_event(Seconds, State) ->
627 2 NS = State#state{inactivity = Seconds},
628 2 F = fun(_, S) ->
629 4 send_to_handler([], S)
630 end,
631 2 lists:foldl(F, NS, lists:seq(1, length(State#state.handlers))).
632
633
634 -spec process_deferred_events(_SName, state()) -> state().
635 process_deferred_events(SName, #state{deferred = Deferred} = S) ->
636 2074 lists:foldl(fun(Event, State) ->
637 5 ?LOG_DEBUG(ls(#{what => bosh_socket_processing_deferred_event,
638 5 event => Event}, S)),
639 5 handle_stream_event(Event, none, SName, State)
640 end,
641 S#state{deferred = []},
642 lists:sort(Deferred)).
643
644
645 -spec is_expected_rid(rid(), rid() | undefined) -> boolean().
646 is_expected_rid(Rid, ExpectedRid) when Rid == ExpectedRid ->
647 1951 true;
648 is_expected_rid(_, _) ->
649 132 false.
650
651 -spec is_acceptable_rid(rid(), rid() | undefined) -> boolean().
652 is_acceptable_rid(Rid, ExpectedRid)
653 when Rid > ExpectedRid,
654 Rid < ExpectedRid + ?CONCURRENT_REQUESTS ->
655 5 true;
656 is_acceptable_rid(_, _) ->
657 2078 false.
658
659 %% @doc Send data to the client if a request handler is available, that matches next RID.
660 %% Otherwise, store for sending later.
661 -spec send_or_store(_Data, state()) -> state().
662 send_or_store(Data, State) when not is_list(Data) ->
663 821 send_or_store([Data], State);
664 send_or_store(Data, State) ->
665 2167 case send_to_handler(Data, State) of
666 no_valid_handler ->
667 103 store(Data, State);
668 NewState ->
669 2064 NewState
670 end.
671
672
673 %% @doc send_to_handler() assumes that Handlers is not empty!
674 %% Be sure that's the case if calling it.
675 -spec send_to_handler([any()] | exml:element(), state()) -> state() | no_valid_handler.
676 send_to_handler(Data, State) ->
677 2173 case pick_handler(State) of
678 {Handler, NS} ->
679 2070 send_to_handler(Handler, Data, NS);
680 false ->
681 103 no_valid_handler
682 end.
683
684
685 %% Return handler and new state if a handler is available
686 %% or `false` otherwise.
687 -spec pick_handler(state()) -> {{rid(), pid()}, state()} | false.
688 pick_handler(#state{ handlers = [] }) ->
689 103 false;
690 pick_handler(#state{ handlers = Handlers, rid = Rid } = S) ->
691 2070 case lists:sort(Handlers) of
692 [{HandlerRid, TRef, Pid} | HRest] when HandlerRid =< Rid->
693 %% The cancellation might fail if the timer already fired.
694 %% Don't worry, it's handled on receiving the timeout message.
695 2070 erlang:cancel_timer(TRef),
696 2070 {{HandlerRid, Pid}, S#state{handlers = HRest}};
697 _ ->
698
:-(
false
699 end.
700
701
702 -spec send_to_handler({_, atom() | pid() | port() | {atom(), atom()}},
703 Wrapped :: [any()] | exml:element(),
704 State :: state() ) -> state().
705 send_to_handler({_, Pid}, #xmlel{name = <<"body">>} = Wrapped, State) ->
706 1 send_wrapped_to_handler(Pid, Wrapped, State);
707 send_to_handler({Rid, Pid}, Data, State) ->
708 2071 {Wrapped, NS} = bosh_wrap(Data, Rid, State),
709 2071 NS2 = cache_response({Rid, erlang:monotonic_time(millisecond), Wrapped}, NS),
710 2071 send_wrapped_to_handler(Pid, Wrapped, NS2).
711
712
713 %% @doc This is the most specific variant of send_to_handler()
714 %% and the *only one* actually performing a send
715 %% to the cowboy_loop_handler serving a HTTP request.
716 -spec send_wrapped_to_handler(atom() | pid() | port() | {atom(), atom()},
717 Wrapped :: exml:element(),
718 State :: state()) -> state().
719 send_wrapped_to_handler(Pid, Wrapped, #state{handlers = []} = State) ->
720 1173 Pid ! {bosh_reply, Wrapped},
721 1173 setup_inactivity_timer(State);
722 send_wrapped_to_handler(Pid, Wrapped, State) ->
723 899 Pid ! {bosh_reply, Wrapped},
724 899 State.
725
726
727 -spec maybe_ack(rid(), state()) -> [{binary(), _}].
728 maybe_ack(HandlerRid, #state{rid = Rid} = S) ->
729 2071 case Rid > HandlerRid of
730 true ->
731 898 server_ack(S#state.server_acks, Rid);
732 false ->
733 1173 []
734 end.
735
736
737 -spec maybe_report(state()) -> {[{binary(), _}], state()}.
738 maybe_report(#state{report = false} = S) ->
739 2070 {[], S};
740 maybe_report(#state{report = Report} = S) ->
741 1 {ReportRid, ElapsedTime} = Report,
742 1 NewAttrs = [{<<"report">>, integer_to_binary(ReportRid)},
743 {<<"time">>, integer_to_binary(ElapsedTime)}],
744 1 {NewAttrs, S#state{report = false}}.
745
746
747 -spec cache_response(cached_response(), state()) -> state().
748 cache_response({Rid, _, _} = Response, #state{sent = Sent} = S) ->
749 2071 NewSent = lists:keymerge(1, [Response], Sent),
750 2071 CacheUpTo = case S#state.client_acks of
751 true ->
752 %% Acknowledgements are on - there's no limit on the number
753 %% of cached responses.
754 4 infinity;
755 false ->
756 %% Leave up to ?CONCURRENT_REQUESTS responses in cache.
757 2067 ?CONCURRENT_REQUESTS
758 end,
759 2071 S#state{sent = cache_up_to(CacheUpTo, NewSent),
760 last_processed = last_processed(Rid, S#state.last_processed)}.
761
762
763 -spec cache_up_to('infinity' | 2, Responses :: [cached_response()])
764 -> [cached_response()].
765 cache_up_to(infinity, Responses) ->
766 4 Responses;
767 cache_up_to(N, Responses) ->
768 2067 lists:nthtail(max(0, length(Responses) - N), Responses).
769
770
771 -spec last_processed(rid(), 'undefined' | pos_integer()) -> rid().
772 last_processed(Rid, undefined) ->
773 124 Rid;
774 last_processed(Rid1, Rid2) ->
775 1947 max(Rid1, Rid2).
776
777
778 -spec setup_inactivity_timer(state()) -> state().
779 setup_inactivity_timer(#state{inactivity = infinity} = S) ->
780
:-(
S;
781 setup_inactivity_timer(S) ->
782 1173 cancel_inactivity_timer(S),
783 1173 TRef = erlang:send_after(timer:seconds(S#state.inactivity), self(),
784 inactivity_timeout),
785 1173 S#state{inactivity_tref = TRef}.
786
787
788 -spec cancel_inactivity_timer(state()) -> state().
789 cancel_inactivity_timer(#state{inactivity_tref = undefined} = S) ->
790 2199 S;
791 cancel_inactivity_timer(S) ->
792 1052 erlang:cancel_timer(S#state.inactivity_tref),
793 1052 S#state{inactivity_tref = undefined}.
794
795
796 %% @doc Store data for sending later.
797 -spec store([jlib:xmlstreamel()], state()) -> state().
798 store(Data, #state{pending = Pending} = S) ->
799 591 S#state{pending = Pending ++ Data}.
800
801
802 -spec forward_to_c2s(pid() | undefined, jlib:xmlstreamel()) -> ok.
803 forward_to_c2s(C2SPid, StreamElement) ->
804 1278 C2SPid ! {tcp, undefined, StreamElement},
805 1278 ok.
806
807
808 -spec maybe_add_handler(_, rid(), state()) -> state().
809 maybe_add_handler(Handler, Rid, S) when is_pid(Handler) ->
810 2078 add_handler({Rid, Handler}, S);
811 maybe_add_handler(_, _, S) ->
812 5 S.
813
814
815 -spec add_handler({rid(), pid()}, state()) -> state().
816 add_handler({Rid, Pid}, #state{handlers = Handlers} = S) ->
817 2078 TRef = erlang:send_after(timer:seconds(S#state.wait), self(),
818 {wait_timeout, {Rid, Pid}}),
819 2078 S#state{handlers = [{Rid, TRef, Pid} | Handlers]}.
820
821
822 %% @doc Keep in mind the hardcoding for hold == 1.
823 -spec return_surplus_handlers('accumulate' | 'normal' | 'closing', state()) -> state().
824 return_surplus_handlers(SName, #state{handlers = []} = State)
825 when SName == accumulate; SName == normal; SName == closing ->
826 2 State;
827 return_surplus_handlers(SName, #state{handlers = []} = State)
828 when SName == normal; SName == closing ->
829
:-(
State;
830 return_surplus_handlers(accumulate, #state{handlers = [_]} = State) ->
831 126 State;
832 return_surplus_handlers(SName, #state{handlers = [_], pending = []} = State)
833 when SName == normal; SName == closing ->
834 970 State;
835 return_surplus_handlers(accumulate, #state{handlers = _} = S) ->
836 1 case send_to_handler([], S) of
837
:-(
no_valid_handler -> S;
838 1 NS -> return_surplus_handlers(accumulate, NS)
839 end;
840 return_surplus_handlers(SName, #state{pending = Pending} = S)
841 when SName == normal; SName == closing ->
842 982 send_or_store(Pending, S#state{pending = []}).
843
844
845 -spec bosh_unwrap(EventTag :: mod_bosh:event_type(), exml:element(), state())
846 -> {[jlib:xmlstreamel()], state()}.
847 bosh_unwrap(StreamEvent, Body, #state{} = S)
848 when StreamEvent =:= streamstart ->
849 124 Wait = min(get_attr(<<"wait">>, Body, S#state.wait), S#state.max_wait),
850 124 Hold = get_attr(<<"hold">>, Body, S#state.hold),
851 124 ClientAcks = get_client_acks(StreamEvent, Body, S#state.client_acks, S),
852 124 From = exml_query:attr(Body, <<"from">>),
853 124 To = exml_query:attr(Body, <<"to">>),
854 124 E = stream_start(From, To),
855 124 S2 = S#state{wait = Wait, hold = Hold, client_acks = ClientAcks,
856 from = From, to = To},
857 124 {[E], S2};
858
859 bosh_unwrap(StreamEvent, _Body, #state{} = S)
860 when StreamEvent =:= restart ->
861 120 {[stream_start(S#state.from, S#state.to)], S};
862
863 bosh_unwrap(streamend, Body, State) ->
864 117 {Els, NewState} = bosh_unwrap(normal, Body, State),
865 117 {Els ++ [#xmlstreamend{name = <<>>}], NewState};
866
867 bosh_unwrap(normal, Body, #state{sid = Sid} = State) ->
868 1828 Sid = exml_query:attr(Body, <<"sid">>),
869 1828 ?NS_HTTPBIND = exml_query:attr(Body, <<"xmlns">>),
870
871 1828 {[El || El <- Body#xmlel.children,
872 %% Ignore whitespace keepalives.
873 917 El /= #xmlcdata{content = <<" ">>}],
874 State}.
875
876
877 -spec get_client_acks(streamstart, exml:element(), boolean(), #state{}) -> boolean().
878 get_client_acks(streamstart, Element, Default, State) ->
879 124 case exml_query:attr(Element, <<"ack">>) of
880 undefined ->
881 124 Default;
882 <<"1">> ->
883
:-(
true;
884 _ ->
885
:-(
?LOG_INFO(ls(#{what => bosh_socket_ignore_ack,
886
:-(
text => <<"Ignoring invalid client ack on stream start">>}, State)),
887
:-(
false
888 end.
889
890
891 -spec get_attr(Attr :: binary(), exml:element(), integer()) -> any().
892 get_attr(Attr, Element, Default) ->
893 248 case exml_query:attr(Element, Attr) of
894 undefined ->
895
:-(
Default;
896 Value ->
897 248 binary_to_integer(Value)
898 end.
899
900
901 -spec stream_start(binary(), binary()) -> jlib:xmlstreamstart().
902 stream_start(From, To) ->
903 244 #xmlstreamstart{name = <<"stream:stream">>,
904 attrs = [{<<"from">>, From},
905 {<<"to">>, To},
906 {<<"version">>, <<"1.0">>},
907 {<<"xml:lang">>, <<"en">>},
908 {<<"xmlns">>, ?NS_CLIENT},
909 {<<"xmlns:stream">>, ?NS_STREAM}]}.
910
911
912 -spec bosh_wrap([any()], rid(), state()) -> {exml:element(), state()}.
913 bosh_wrap(Elements, Rid, #state{} = S) ->
914 2071 EventsStanzas = lists:partition(fun is_stream_event/1, Elements),
915 2071 {{Body, Children}, NS} = case EventsStanzas of
916 {[], Stanzas} ->
917 1708 {{bosh_body(S), Stanzas}, S};
918 {[#xmlstreamstart{} = StreamStart], Stanzas} ->
919 244 {{bosh_stream_start_body(StreamStart, S), Stanzas}, S};
920 {[#xmlstreamend{}], []} ->
921 %% No stanzas except stream end - OK.
922 119 {{bosh_stream_end_body(), []}, S};
923 {[#xmlstreamend{} = StreamEnd], Stanzas} ->
924 %% Can't wrap remaining stanzas in a stream end body.
925 %% Send Stanzas and forfeit sending stream end.
926
:-(
?LOG_DEBUG(#{what => bosh_socket_cannot_send_stream_end,
927 text => <<"Can't send stream end yet. Still have pending stanzas">>,
928
:-(
stanzas => Stanzas}),
929
:-(
Pending = S#state.pending,
930
:-(
{{bosh_body(S), Stanzas},
931 S#state{pending = Pending ++ [StreamEnd]}}
932 end,
933 2071 MaybeAck = maybe_ack(Rid, NS),
934 2071 {MaybeReport, NNS} = maybe_report(NS),
935 2071 HasStreamPrefix = (exml_query:attr(Body, <<"xmlns:stream">>) /= undefined),
936 2071 MaybeStreamPrefix = maybe_stream_prefix(HasStreamPrefix, Children),
937 2071 ExtraAttrs = MaybeAck ++ MaybeReport ++ MaybeStreamPrefix,
938 2071 {Body#xmlel{attrs = Body#xmlel.attrs ++ ExtraAttrs,
939 children = maybe_add_default_ns_to_children(Children)}, NNS}.
940
941
942 -spec is_stream_event(jlib:xmlstreamel()) -> boolean().
943 is_stream_event(#xmlstreamstart{}) ->
944 244 true;
945 is_stream_event(#xmlstreamend{}) ->
946 119 true;
947 is_stream_event(_) ->
948 1065 false.
949
950
951 %% @doc Bosh body for a session creation response.
952 -spec bosh_stream_start_body(jlib:xmlstreamstart(), state()) -> exml:element().
953 bosh_stream_start_body(#xmlstreamstart{attrs = Attrs}, #state{} = S) ->
954 244 #xmlel{name = <<"body">>,
955 attrs = [{<<"wait">>, integer_to_binary(S#state.wait)},
956 {<<"requests">>,
957 integer_to_binary(?CONCURRENT_REQUESTS)},
958 {<<"hold">>, integer_to_binary(S#state.hold)},
959 {<<"from">>, proplists:get_value(<<"from">>, Attrs)},
960 %% TODO: how to support these with cowboy?
961 {<<"accept">>, <<"deflate, gzip">>},
962 {<<"sid">>, S#state.sid},
963 {<<"xmpp:restartlogic">>, <<"true">>},
964 {<<"xmpp:version">>, <<"1.0">>},
965 {<<"xmlns">>, ?NS_HTTPBIND},
966 {<<"xmlns:xmpp">>, <<"urn:xmpp:xbosh">>},
967 {<<"xmlns:stream">>, ?NS_STREAM}] ++
968 inactivity(S#state.inactivity) ++
969 maxpause(S#state.max_pause) ++
970 %% TODO: shouldn't an ack be sent on restart?
971 server_ack(S#state.server_acks, S#state.rid),
972 children = []}.
973
974
975 -spec inactivity('infinity' | 'undefined' | pos_integer()) -> [{binary(), _}].
976 inactivity(I) ->
977 244 [{<<"inactivity">>, integer_to_binary(I)} || is_integer(I)].
978
979
980 -spec maxpause('undefined' | pos_integer()) -> [{binary(), _}].
981 maxpause(MP) ->
982 244 [{<<"maxpause">>, integer_to_binary(MP)} || is_integer(MP)].
983
984
985 -spec server_ack('false' | 'true' | 'undefined', 'undefined' | rid())
986 -> [{binary(), _}].
987 server_ack(ServerAcks, Rid) ->
988 1142 [{<<"ack">>, integer_to_binary(Rid)} || ServerAcks =:= true].
989
990
991 %% @doc Bosh body for an ordinary stream element(s).
992 -spec bosh_body(state()) -> exml:element().
993 bosh_body(#state{} = S) ->
994 1708 #xmlel{name = <<"body">>,
995 attrs = [{<<"sid">>, S#state.sid},
996 {<<"xmlns">>, ?NS_HTTPBIND}],
997 children = []}.
998
999
1000 -spec bosh_stream_end_body() -> exml:element().
1001 bosh_stream_end_body() ->
1002 119 #xmlel{name = <<"body">>,
1003 attrs = [{<<"type">>, <<"terminate">>},
1004 {<<"xmlns">>, ?NS_HTTPBIND}],
1005 children = []}.
1006
1007 maybe_stream_prefix(true, _) ->
1008 244 [];
1009 maybe_stream_prefix(_, Stanzas) ->
1010 1827 case lists:any(fun is_stream_prefix/1, Stanzas) of
1011 false ->
1012 1825 [];
1013 true ->
1014 2 [{<<"xmlns:stream">>, ?NS_STREAM}]
1015 end.
1016
1017 2 is_stream_prefix(#xmlel{name = <<"stream:error">>}) -> true;
1018
:-(
is_stream_prefix(#xmlel{name = <<"stream:features">>}) -> true;
1019 819 is_stream_prefix(_) -> false.
1020
1021
1022 %%--------------------------------------------------------------------
1023 %% Helpers
1024 %%--------------------------------------------------------------------
1025
1026 maybe_add_default_ns_to_children(Children) ->
1027 2071 lists:map(fun maybe_add_default_ns/1, Children).
1028
1029 maybe_add_default_ns(#xmlel{name = Name, attrs = Attrs} = El)
1030 when Name =:= <<"message">>; Name =:= <<"presence">>; Name =:= <<"iq">> ->
1031 699 case xml:get_attr(<<"xmlns">>, Attrs) of
1032 false ->
1033 677 El#xmlel{attrs = [{<<"xmlns">>, ?NS_CLIENT} | Attrs]};
1034 _ ->
1035 22 El
1036 end;
1037 maybe_add_default_ns(El) ->
1038 366 El.
1039
1040 ls(LogMap, State) ->
1041
:-(
S = #{sid => State#state.sid,
1042 c2s_pid => State#state.c2s_pid,
1043 from_jid => State#state.from,
1044 to_jid => State#state.to,
1045 rid => State#state.rid},
1046
:-(
maps:merge(LogMap, ignore_undefined(S)).
1047
1048 ignore_undefined(Map) ->
1049
:-(
maps:filter(fun(_, V) -> V =/= undefined end, Map).
1050
1051 %% mongoose_c2s_socket callbacks
1052
1053 -spec socket_new(mod_bosh:socket(), mongoose_listener:options()) -> mod_bosh:socket().
1054 socket_new(Socket, _LOpts) ->
1055 124 Socket.
1056
1057 -spec socket_peername(mod_bosh:socket()) -> {inet:ip_address(), inet:port_number()}.
1058 socket_peername(#bosh_socket{peer = Peer}) ->
1059 244 Peer.
1060
1061 -spec tcp_to_tls(mod_bosh:socket(), mongoose_listener:options()) ->
1062 {ok, mod_bosh:socket()} | {error, term()}.
1063 tcp_to_tls(_Socket, _LOpts) ->
1064
:-(
{error, tls_not_allowed_on_bosh}.
1065
1066 -spec socket_handle_data(mod_bosh:socket(), {tcp | ssl, term(), iodata()}) ->
1067 iodata() | {raw, [exml:element()]} | {error, term()}.
1068 socket_handle_data(_Socket, {_Kind, _Term, Packet}) ->
1069 1278 {raw, [Packet]}.
1070
1071 -spec socket_activate(mod_bosh:socket()) -> ok.
1072 socket_activate(_Socket) ->
1073 1402 ok.
1074
1075 -spec socket_send_xml(mod_bosh:socket(),
1076 iodata() | exml_stream:element() | [exml_stream:element()]) ->
1077 ok | {error, term()}.
1078 socket_send_xml(#bosh_socket{pid = Pid}, XMLs) when is_list(XMLs) ->
1079 1428 [Pid ! {send, XML} || XML <- XMLs],
1080 1428 ok;
1081 socket_send_xml(#bosh_socket{pid = Pid}, XML) ->
1082
:-(
Pid ! {send, XML},
1083
:-(
ok.
1084
1085 -spec socket_close(mod_bosh:socket()) -> ok.
1086 socket_close(#bosh_socket{pid = Pid}) ->
1087 124 Pid ! close,
1088 124 ok.
1089
1090 -spec get_peer_certificate(mod_bosh:socket(), mongoose_listener:options()) ->
1091 mongoose_transport:peercert_return().
1092 get_peer_certificate(#bosh_socket{peercert = undefined}, _) ->
1093
:-(
no_peer_cert;
1094 get_peer_certificate(#bosh_socket{peercert = PeerCert}, _) ->
1095 9 Decoded = public_key:pkix_decode_cert(PeerCert, plain),
1096 9 {ok, Decoded}.
1097
1098 -spec has_peer_cert(mod_bosh:socket(), mongoose_listener:options()) -> boolean().
1099 has_peer_cert(Socket, LOpts) ->
1100 6 get_peer_certificate(Socket, LOpts) /= no_peer_cert.
1101
1102 -spec is_channel_binding_supported(mod_bosh:socket()) -> boolean().
1103 is_channel_binding_supported(_Socket) ->
1104 238 false.
1105
1106 -spec get_tls_last_message(mod_bosh:socket()) -> {ok, binary()} | {error, term()}.
1107 get_tls_last_message(_Socket) ->
1108
:-(
{error, tls_not_allowed_on_bosh}.
1109
1110 -spec is_ssl(mod_bosh:socket()) -> boolean().
1111 is_ssl(_Socket) ->
1112 364 false.
1113
1114 %%--------------------------------------------------------------------
1115 %% Tests
1116 %%--------------------------------------------------------------------
1117
1118 -ifdef(TEST).
1119
1120 -include_lib("eunit/include/eunit.hrl").
1121
1122 cache_up_to_test_() ->
1123 [?_test(?assertEqual( [4, 5], cache_up_to(2, [1, 2, 3, 4, 5]) ))].
1124
1125 -endif.
Line Hits Source