99-behavior (gen_server ).
1010
1111% % API exports
12- -export ([get /2 , get /3 ,
12+ -export ([get /2 , get /3 , put / 4 ,
1313 post /4 ,
1414 refresh_credentials /0 ,
1515 request /5 , request /6 , request /7 ,
@@ -75,6 +75,18 @@ post(Service, Path, Body, Headers) ->
7575 request (Service , post , Path , Body , Headers ).
7676
7777
78+ -spec put (Service :: string (),
79+ Path :: path (),
80+ Body :: body (),
81+ Headers :: headers ()) -> result ().
82+ % % @doc Perform a HTTP Post request to the AWS API for the specified service. The
83+ % % response will automatically be decoded if it is either in JSON or XML
84+ % % format.
85+ % % @end
86+ put (Service , Path , Body , Headers ) ->
87+ request (Service , put , Path , Body , Headers ).
88+
89+
7890-spec refresh_credentials () -> ok | error .
7991% % @doc Manually refresh the credentials from the environment, filesystem or EC2 Instance Metadata Service.
8092% % @end
@@ -287,6 +299,8 @@ endpoint_tld(_Other) ->
287299% % @end
288300format_response ({ok , {{_Version , 200 , _Message }, Headers , Body }}) ->
289301 {ok , {Headers , maybe_decode_body (get_content_type (Headers ), Body )}};
302+ format_response ({ok , {{_Version , 206 , _Message }, Headers , Body }}) ->
303+ {ok , {Headers , maybe_decode_body (get_content_type (Headers ), Body )}};
290304format_response ({ok , {{_Version , StatusCode , Message }, Headers , Body }}) when StatusCode >= 400 ->
291305 {error , Message , {Headers , maybe_decode_body (get_content_type (Headers ), Body )}};
292306format_response ({error , Reason }) ->
@@ -297,9 +311,9 @@ format_response({error, Reason}) ->
297311% % {Type, Subtype}.
298312% % @end
299313get_content_type (Headers ) ->
300- Value = case proplists :get_value (" content-type" , Headers , undefined ) of
314+ Value = case proplists :get_value (<< " content-type" >> , Headers , undefined ) of
301315 undefined ->
302- proplists :get_value (" Content-Type" , Headers , " text/xml" );
316+ proplists :get_value (<< " Content-Type" >> , Headers , " text/xml" );
303317 Other -> Other
304318 end ,
305319 parse_content_type (Value ).
@@ -368,6 +382,8 @@ local_time() ->
368382-spec maybe_decode_body (ContentType :: {nonempty_string (), nonempty_string ()}, Body :: body ()) -> list () | body ().
369383% % @doc Attempt to decode the response body by its MIME
370384% % @end
385+ maybe_decode_body (_ , <<>>) ->
386+ <<>>;
371387maybe_decode_body ({" application" , " x-amz-json-1.0" }, Body ) ->
372388 rabbitmq_aws_json :decode (Body );
373389maybe_decode_body ({" application" , " json" }, Body ) ->
@@ -381,6 +397,8 @@ maybe_decode_body(_ContentType, Body) ->
381397-spec parse_content_type (ContentType :: string ()) -> {Type :: string (), Subtype :: string ()}.
382398% % @doc parse a content type string returning a tuple of type/subtype
383399% % @end
400+ parse_content_type (ContentType ) when is_binary (ContentType )->
401+ parse_content_type (binary_to_list (ContentType ));
384402parse_content_type (ContentType ) ->
385403 Parts = string :tokens (ContentType , " ;" ),
386404 [Type , Subtype ] = string :tokens (lists :nth (1 , Parts ), " /" ),
@@ -437,25 +455,23 @@ perform_request_creds_expired(true, State, _, _, _, _, _, _, _) ->
437455% % expired, perform the request and return the response.
438456% % @end
439457perform_request_with_creds (State , Service , Method , Headers , Path , Body , Options , Host ) ->
440- URI = endpoint (State , Host , Service , Path ),
441- SignedHeaders = sign_headers (State , Service , Method , URI , Headers , Body ),
442- ContentType = proplists :get_value (" content-type" , SignedHeaders , undefined ),
443- perform_request_with_creds (State , Method , URI , SignedHeaders , ContentType , Body , Options ).
458+ URI = endpoint (State , Host , Service , Path ),
459+ SignedHeaders = sign_headers (State , Service , Method , URI , Headers , Body ),
460+ perform_request_with_creds (State , Method , URI , SignedHeaders , Body , Options ).
444461
445462
446463-spec perform_request_with_creds (State :: state (), Method :: method (), URI :: string (),
447- Headers :: headers (), ContentType :: string () | undefined ,
464+ Headers :: headers (),
448465 Body :: body (), Options :: http_options ())
449466 -> {Result :: result (), NewState :: state ()}.
450467% % @doc Once it is validated that there are credentials to try and that they have not
451468% % expired, perform the request and return the response.
452469% % @end
453- perform_request_with_creds (State , Method , URI , Headers , undefined , " " , Options0 ) ->
470+ perform_request_with_creds (State , Method , URI , Headers , " " , Options0 ) ->
454471 {Response , NewState } = gun_request (State , Method , URI , Headers , <<>>, Options0 ),
455472 {format_response (Response ), NewState };
456- perform_request_with_creds (State , Method , URI , Headers , ContentType , Body , Options0 ) ->
457- GunHeaders = [{" content-type" , ContentType } | Headers ],
458- {Response , NewState } = gun_request (State , Method , URI , GunHeaders , Body , Options0 ),
473+ perform_request_with_creds (State , Method , URI , Headers , Body , Options0 ) ->
474+ {Response , NewState } = gun_request (State , Method , URI , Headers , Body , Options0 ),
459475 {format_response (Response ), NewState }.
460476
461477
@@ -580,11 +596,15 @@ api_get_request_with_retries(Service, Path, Retries, WaitTimeBetweenRetries) ->
580596
581597% % Gun HTTP client functions
582598gun_request (State , Method , URI , Headers , Body , Options ) ->
599+ HeadersBin = lists :map (
600+ fun ({Key , Value }) ->
601+ {list_to_binary (Key ), list_to_binary (Value )}
602+ end , Headers ),
583603 {Host , Port , Path } = parse_uri (URI ),
584604 {ConnPid , NewState } = get_or_create_gun_connection (State , Host , Port , Path , Options ),
585605 Timeout = proplists :get_value (timeout , Options , ? DEFAULT_HTTP_TIMEOUT ),
586606 try
587- StreamRef = do_gun_request (ConnPid , Method , Path , Headers , Body ),
607+ StreamRef = do_gun_request (ConnPid , Method , Path , HeadersBin , Body ),
588608 case gun :await (ConnPid , StreamRef , Timeout ) of
589609 {response , fin , Status , RespHeaders } ->
590610 Response = {ok , {{http_version , Status , status_text (Status )}, RespHeaders , <<>>}},
@@ -637,7 +657,7 @@ get_or_create_gun_connection(State, Host, Port, Path, Options) ->
637657 end .
638658
639659get_connection_key (Host , Port , Path , Options ) ->
640- case proplists :get_value (connection_per_path , Options , false ) of
660+ case proplists :get_value (connection_per_path , Options , true ) of
641661 true -> Host ++ " :" ++ integer_to_list (Port ) ++ Path ; % Per-path
642662 false -> Host ++ " :" ++ integer_to_list (Port ) % Per-host (default)
643663 end .
@@ -700,9 +720,11 @@ parse_host_port(HostPort, Scheme) ->
700720 end .
701721
702722status_text (200 ) -> " OK" ;
723+ status_text (206 ) -> " Partial Content" ;
703724status_text (400 ) -> " Bad Request" ;
704725status_text (401 ) -> " Unauthorized" ;
705726status_text (403 ) -> " Forbidden" ;
706727status_text (404 ) -> " Not Found" ;
728+ status_text (416 ) -> " Range Not Satisfiable" ;
707729status_text (500 ) -> " Internal Server Error" ;
708730status_text (Code ) -> integer_to_list (Code ).
0 commit comments