101
101
data => binary ()
102
102
}.
103
103
104
+ -type state () :: #{}.
105
+
104
106
-export_type ([event / 0 ]).
105
107
106
108
% % @doc Starts the application and all the ones it depends on.
@@ -237,6 +239,7 @@ put(Pid, Uri, Headers0, Body, Options) ->
237
239
-spec request (pid (), http_verb (), iodata (), headers (), iodata (), options ()) ->
238
240
result ().
239
241
request (Pid , get , Uri , Headers0 , Body , Options ) ->
242
+ Ref = make_ref (),
240
243
try
241
244
check_uri (Uri ),
242
245
#{handle_event := HandleEvent ,
@@ -249,22 +252,29 @@ request(Pid, get, Uri, Headers0, Body, Options) ->
249
252
true ->
250
253
{get_async ,
251
254
{HandleEvent , AsyncMode },
252
- {Uri , Headers , Body }};
255
+ {Ref , Uri , Headers , Body }};
253
256
false ->
254
- {get , {Uri , Headers , Body }}
257
+ {get , {Ref , Uri , Headers , Body }}
255
258
end ,
256
259
gen_fsm :sync_send_event (Pid , Event , Timeout )
257
260
catch
261
+ exit :{timeout , Rest } ->
262
+ gen_fsm :send_all_state_event (Pid , {cancel_req , Ref }),
263
+ {error , {timeout , Rest }};
258
264
_ :Reason -> {error , Reason }
259
265
end ;
260
266
request (Pid , Method , Uri , Headers0 , Body , Options ) ->
267
+ Ref = make_ref (),
261
268
try
262
269
check_uri (Uri ),
263
270
#{headers := Headers , timeout := Timeout } =
264
271
process_options (Options , Headers0 , Method ),
265
- Event = {Method , {Uri , Headers , Body }},
272
+ Event = {Method , {Ref , Uri , Headers , Body }},
266
273
gen_fsm :sync_send_event (Pid , Event , Timeout )
267
274
catch
275
+ exit :{timeout , Rest } ->
276
+ gen_fsm :send_all_state_event (Pid , {cancel_req , Ref }),
277
+ {error , {timeout , Rest }};
268
278
_ :Reason -> {error , Reason }
269
279
end .
270
280
@@ -299,8 +309,6 @@ parse_event(EventBin) ->
299
309
% % gen_fsm callbacks
300
310
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
301
311
302
- -type state () :: #{}.
303
-
304
312
% % @private
305
313
-spec init ([term ()]) ->
306
314
{ok , at_rest , state ()} | {stop , gun_open_timeout } | {stop , gun_open_failed }.
@@ -332,7 +340,26 @@ init([Host, Port, Type, Opts]) ->
332
340
end .
333
341
334
342
% % @private
335
- -spec handle_event (shutdown , atom (), state ()) -> {stop , normal , state ()}.
343
+ -spec handle_event (shutdown | {cancel_req , reference ()},
344
+ StateName :: atom (), state ()) ->
345
+ {stop , normal , state ()} | {next_state , at_rest , state ()} |
346
+ {next_state , StateName :: atom (), state ()}.
347
+ % Cancel our current unit of work...
348
+ handle_event ({cancel_req , ReqRef }, _ ,
349
+ #{req_ref := ReqRef , stream := StreamRef , pid := Pid } = State ) ->
350
+ % This isn't guaranteed to cancel the pending request, but it will
351
+ % prevent us from getting messages about it in the future.
352
+ ok = gun :cancel (Pid , StreamRef ),
353
+ {next_state , at_rest , State , 0 };
354
+ % Or unqueue a queued unit of work...
355
+ handle_event ({cancel_req , ReqRef }, StateName , StateData ) ->
356
+ NewStateData = remove_request (ReqRef , StateData ),
357
+ case StateName of
358
+ at_rest ->
359
+ {next_state , at_rest , NewStateData , 0 };
360
+ _ ->
361
+ {next_state , StateName , NewStateData }
362
+ end ;
336
363
handle_event (shutdown , _StateName , StateData ) ->
337
364
{stop , normal , StateData }.
338
365
@@ -393,26 +420,30 @@ at_rest(timeout, State) ->
393
420
ok = gen_fsm :send_event (self (), Work ),
394
421
{next_state , at_rest , NewState }
395
422
end ;
396
- at_rest ({get_async , {HandleEvent , AsyncMode }, Args , From },
423
+ at_rest ({get_async , {HandleEvent , AsyncMode },
424
+ {ReqRef , _ , _ , _ } = Args , From },
397
425
State = #{pid := Pid }) ->
398
426
StreamRef = do_http_verb (get , Pid , Args ),
399
427
CleanState = clean_state (State ),
400
428
NewState = CleanState #{
401
429
from => From ,
430
+ req_ref => ReqRef ,
402
431
pid => Pid ,
403
432
stream => StreamRef ,
404
433
handle_event => HandleEvent ,
405
434
async => true ,
406
435
async_mode => AsyncMode
407
436
},
408
437
{next_state , wait_response , NewState };
409
- at_rest ({HttpVerb , Args , From }, State = #{pid := Pid }) ->
438
+ at_rest ({HttpVerb , {ReqRef , _ , _ , _ } = Args , From },
439
+ State = #{pid := Pid }) ->
410
440
StreamRef = do_http_verb (HttpVerb , Pid , Args ),
411
441
CleanState = clean_state (State ),
412
442
NewState = CleanState #{
413
443
pid => Pid ,
414
444
stream => StreamRef ,
415
- from => From
445
+ from => From ,
446
+ req_ref => ReqRef
416
447
},
417
448
{next_state , wait_response , NewState }.
418
449
@@ -520,10 +551,12 @@ receive_chunk({gun_error, _Pid, _StreamRef, _Reason}, State) ->
520
551
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
521
552
522
553
% % @private
523
- clean_state () -> clean_state (queue :new ()).
554
+ clean_state () ->
555
+ clean_state (queue :new ()).
524
556
525
557
% % @private
526
- -spec clean_state (map ()) -> map (); (queue :queue ()) -> map ().
558
+ -spec clean_state (map ()) -> map ();
559
+ (queue :queue ()) -> map ().
527
560
clean_state (State ) when is_map (State ) ->
528
561
clean_state (get_pending_reqs (State ));
529
562
clean_state (Reqs ) ->
@@ -539,12 +572,13 @@ clean_state(Reqs) ->
539
572
async => false ,
540
573
async_mode => binary ,
541
574
buffer => <<" " >>,
575
+ req_ref => undefined ,
542
576
pending_requests => Reqs
543
577
}.
544
578
545
579
% % @private
546
580
-spec do_http_verb (http_verb (), pid (), tuple ()) -> reference ().
547
- do_http_verb (Method , Pid , {Uri , Headers , Body }) ->
581
+ do_http_verb (Method , Pid , {_ , Uri , Headers , Body }) ->
548
582
MethodStr = string :to_upper (atom_to_list (Method )),
549
583
MethodBin = list_to_binary (MethodStr ),
550
584
gun :request (Pid , MethodBin , Uri , Headers , Body ).
@@ -642,17 +676,14 @@ check_uri(U) ->
642
676
end .
643
677
644
678
% % @private
645
- enqueue_work_or_stop (FSM = at_rest , Event , From , State ) ->
646
- enqueue_work_or_stop (FSM , Event , From , State , 0 );
647
- enqueue_work_or_stop (FSM , Event , From , State ) ->
648
- enqueue_work_or_stop (FSM , Event , From , State , infinity ).
649
-
650
- % % @private
651
- enqueue_work_or_stop (FSM , Event , From , State , Timeout ) ->
679
+ enqueue_work_or_stop (StateName , Event , From , State ) ->
652
680
case create_work (Event , From ) of
653
681
{ok , Work } ->
654
682
NewState = append_work (Work , State ),
655
- {next_state , FSM , NewState , Timeout };
683
+ case StateName of
684
+ at_rest -> {next_state , at_rest , NewState , 0 };
685
+ _ -> {next_state , StateName , NewState }
686
+ end ;
656
687
not_work ->
657
688
{stop , {unexpected , Event }, State }
658
689
end .
@@ -690,3 +721,17 @@ append_work(Work, State) ->
690
721
% % @private
691
722
get_pending_reqs (State ) ->
692
723
maps :get (pending_requests , State ).
724
+
725
+ % % @private
726
+ -spec remove_request (reference (), state ()) -> state ().
727
+ remove_request (ReqRef , #{pending_requests := PendingReqs } = StateData ) ->
728
+ NewPendingReqs =
729
+ queue :filter (fun (E ) ->
730
+ case E of
731
+ % Handle get_async
732
+ {_ , _ , {Ref , _ , _ , _ }, _ } -> Ref /= ReqRef ;
733
+ % Handle all other work.
734
+ {_ , {Ref , _ , _ , _ }, _ } -> Ref /= ReqRef
735
+ end
736
+ end , PendingReqs ),
737
+ StateData #{pending_requests := NewPendingReqs }.
0 commit comments