Fixed punctuation
This commit is contained in:
		
							parent
							
								
									e92bf12c9c
								
							
						
					
					
						commit
						446251e4f8
					
				|  | @ -32,15 +32,16 @@ | |||
| 
 | ||||
| -behaviour(gen_server). | ||||
| 
 | ||||
| -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2]). | ||||
| -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, | ||||
|          handle_info/2]). | ||||
| -export([call/2, call/3, cast/2, cast/3]). | ||||
| -export([subscribe/3]). | ||||
| -export([register_direct_peer/2]). | ||||
| -export([register_return_handler/2]). | ||||
| -export([register_flow_handler/2]). | ||||
| 
 | ||||
| %% This diagram shows the interaction between the different component processes | ||||
| %% in an AMQP client scenario. | ||||
| %% This diagram shows the interaction between the different component | ||||
| %% processes in an AMQP client scenario. | ||||
| %% | ||||
| %%                             message* / reply*        +-------+ | ||||
| %%                            +----------------------   | queue | | ||||
|  | @ -62,11 +63,12 @@ | |||
| %%       | | ||||
| %% [consumer tag --> consumer pid] | ||||
| %% | ||||
| %% * These notifications are processed asynchronously via handle_info/2 callbacks | ||||
| %% These notifications are processed asynchronously via | ||||
| %% handle_info/2 callbacks | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % AMQP Channel API methods | ||||
| %--------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% AMQP Channel API methods | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| %% Generic AMQP RPC mechanism that expects a pseudo synchronous response | ||||
| call(Channel, Method) -> | ||||
|  | @ -84,18 +86,18 @@ cast(Channel, Method) -> | |||
| cast(Channel, Method, Content) -> | ||||
|     gen_server:cast(Channel, {cast, Method, Content}). | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % Consumer registration | ||||
| %--------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% Consumer registration | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| %% Registers a consumer pid with the channel | ||||
| subscribe(Channel, BasicConsume = #'basic.consume'{}, Consumer) -> | ||||
|     gen_server:call(Channel, {BasicConsume, Consumer}). | ||||
| 
 | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % Direct peer registration | ||||
| %--------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% Direct peer registration | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| %% Registers the direct channel peer with the state of this channel. | ||||
| %% This registration occurs after the amqp_channel gen_server instance | ||||
|  | @ -116,15 +118,15 @@ register_return_handler(Channel, ReturnHandler) -> | |||
| register_flow_handler(Channel, FlowHandler) -> | ||||
|     gen_server:cast(Channel, {register_flow_handler, FlowHandler} ). | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % Internal plumbing | ||||
| %--------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% Internal plumbing | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| rpc_top_half(Method, From, State = #channel_state{writer_pid = Writer, | ||||
|                                                   rpc_requests = RequestQueue, | ||||
|                                                   do2 = Do2}) -> | ||||
|     % Enqueue the incoming RPC request to serialize RPC dispatching | ||||
|     NewRequestQueue = queue:in({From,Method}, RequestQueue), | ||||
|     NewRequestQueue = queue:in({From, Method}, RequestQueue), | ||||
|     NewState = State#channel_state{rpc_requests = NewRequestQueue}, | ||||
|     case queue:len(NewRequestQueue) of | ||||
|         1 -> | ||||
|  | @ -135,8 +137,9 @@ rpc_top_half(Method, From, State = #channel_state{writer_pid = Writer, | |||
|     {noreply, NewState}. | ||||
| 
 | ||||
| rpc_bottom_half(#'channel.close'{reply_code = ReplyCode, | ||||
|                                  reply_text = ReplyText},State) -> | ||||
|     io:format("Channel received close from peer, code: ~p , message: ~p~n",[ReplyCode,ReplyText]), | ||||
|                                  reply_text = ReplyText}, State) -> | ||||
|     io:format("Channel received close from peer, code: ~p , message: ~p~n", | ||||
|               [ReplyCode,ReplyText]), | ||||
|     NewState = channel_cleanup(State), | ||||
|     {stop, normal, NewState}; | ||||
| 
 | ||||
|  | @ -145,7 +148,7 @@ rpc_bottom_half(Reply, State = #channel_state{writer_pid = Writer, | |||
|                                               do2 = Do2}) -> | ||||
|     NewRequestQueue = | ||||
|         case queue:out(RequestQueue) of | ||||
|             {empty, {[],[]}}        -> exit(empty_rpc_bottom_half); | ||||
|             {empty, {[], []}}        -> exit(empty_rpc_bottom_half); | ||||
|             {{value, {From, _}}, Q}  -> gen_server:reply(From, Reply), | ||||
|                                         Q | ||||
|         end, | ||||
|  | @ -163,15 +166,18 @@ resolve_consumer(_ConsumerTag, #channel_state{consumers = []}) -> | |||
| resolve_consumer(ConsumerTag, #channel_state{consumers = Consumers}) -> | ||||
|     dict:fetch(ConsumerTag, Consumers). | ||||
| 
 | ||||
| register_consumer(ConsumerTag, Consumer, State = #channel_state{consumers = Consumers0}) -> | ||||
| register_consumer(ConsumerTag, Consumer, | ||||
|                   State = #channel_state{consumers = Consumers0}) -> | ||||
|     Consumers1 = dict:store(ConsumerTag, Consumer, Consumers0), | ||||
|     State#channel_state{consumers = Consumers1}. | ||||
| 
 | ||||
| unregister_consumer(ConsumerTag, State = #channel_state{consumers = Consumers0}) -> | ||||
| unregister_consumer(ConsumerTag, | ||||
|                     State = #channel_state{consumers = Consumers0}) -> | ||||
|     Consumers1 = dict:erase(ConsumerTag, Consumers0), | ||||
|     State#channel_state{consumers = Consumers1}. | ||||
| 
 | ||||
| shutdown_writer(State = #channel_state{close_fun = CloseFun, writer_pid = WriterPid}) -> | ||||
| shutdown_writer(State = #channel_state{close_fun = CloseFun, | ||||
|                                        writer_pid = WriterPid}) -> | ||||
|     CloseFun(WriterPid), | ||||
|     State. | ||||
| 
 | ||||
|  | @ -193,41 +199,44 @@ return_handler(State = #channel_state{return_handler_pid = undefined}) -> | |||
| return_handler(State = #channel_state{return_handler_pid = ReturnHandler}) -> | ||||
|     {ReturnHandler, State}. | ||||
| 
 | ||||
| handle_method(BasicConsumeOk = #'basic.consume_ok'{consumer_tag = ConsumerTag}, | ||||
| handle_method(ConsumeOk = #'basic.consume_ok'{consumer_tag = ConsumerTag}, | ||||
|               State = #channel_state{anon_sub_requests = Anon, | ||||
|                                      tagged_sub_requests = Tagged}) -> | ||||
|     {_From, Consumer, State0} = | ||||
|         case dict:find(ConsumerTag,Tagged) of | ||||
|         case dict:find(ConsumerTag, Tagged) of | ||||
|             {ok, {F,C}} -> | ||||
|                 NewTagged = dict:erase(ConsumerTag,Tagged), | ||||
|                 {F,C,State#channel_state{tagged_sub_requests = NewTagged}}; | ||||
|             error -> | ||||
|                 case queue:out(Anon) of | ||||
|                     {empty,_} -> | ||||
|                     {empty, _} -> | ||||
|                         exit(anonymous_queue_empty, ConsumerTag); | ||||
|                     {{value, {F,C}}, NewAnon} -> | ||||
|                         {F,C,State#channel_state{anon_sub_requests = NewAnon}} | ||||
|                     {{value, {F, C}}, NewAnon} -> | ||||
|                         {F, C, | ||||
|                          State#channel_state{anon_sub_requests = NewAnon}} | ||||
|                 end | ||||
|         end, | ||||
|     Consumer ! BasicConsumeOk, | ||||
|     Consumer ! ConsumeOk, | ||||
|     State1 = register_consumer(ConsumerTag, Consumer, State0), | ||||
|     rpc_bottom_half(BasicConsumeOk,State1); | ||||
|     rpc_bottom_half(ConsumeOk, State1); | ||||
| 
 | ||||
| handle_method(BasicCancelOk = #'basic.cancel_ok'{consumer_tag = ConsumerTag}, State) -> | ||||
| handle_method(CancelOk = #'basic.cancel_ok'{consumer_tag = ConsumerTag}, | ||||
|               State) -> | ||||
|     Consumer = resolve_consumer(ConsumerTag, State), | ||||
|     Consumer ! BasicCancelOk, | ||||
|     Consumer ! CancelOk, | ||||
|     NewState = unregister_consumer(ConsumerTag, State), | ||||
|     rpc_bottom_half(BasicCancelOk, NewState); | ||||
|     rpc_bottom_half(CancelOk, NewState); | ||||
| 
 | ||||
| handle_method(ChannelCloseOk = #'channel.close_ok'{}, State) -> | ||||
|     {noreply, NewState} = rpc_bottom_half(ChannelCloseOk, State), | ||||
| handle_method(CloseOk = #'channel.close_ok'{}, State) -> | ||||
|     {noreply, NewState} = rpc_bottom_half(CloseOk, State), | ||||
|     {stop, normal, NewState}; | ||||
| 
 | ||||
| %% This handles the flow control flag that the broker initiates. | ||||
| %% If defined, it informs the flow control handler to suspend submitting | ||||
| %% any content bearing methods | ||||
| handle_method(Flow = #'channel.flow'{active = Active}, | ||||
|               State = #channel_state{writer_pid = Writer, do2 = Do2, | ||||
|               State = #channel_state{writer_pid = Writer, | ||||
|                                      do2 = Do2, | ||||
|                                      flow_handler_pid = FlowHandler}) -> | ||||
|     case FlowHandler of | ||||
|         undefined -> ok; | ||||
|  | @ -239,9 +248,10 @@ handle_method(Flow = #'channel.flow'{active = Active}, | |||
| handle_method(Method, State) -> | ||||
|     rpc_bottom_half(Method, State). | ||||
| 
 | ||||
| handle_method(BasicDeliver = #'basic.deliver'{consumer_tag = ConsumerTag}, Content, State) -> | ||||
| handle_method(Deliver = #'basic.deliver'{consumer_tag = ConsumerTag}, | ||||
|               Content, State) -> | ||||
|     Consumer = resolve_consumer(ConsumerTag, State), | ||||
|     Consumer ! {BasicDeliver, Content}, | ||||
|     Consumer ! {Deliver, Content}, | ||||
|     {noreply, State}; | ||||
| 
 | ||||
| %% Why is the consumer a handle_method/3 call with the network driver, | ||||
|  | @ -257,9 +267,9 @@ handle_method(BasicReturn = #'basic.return'{}, Content, State) -> | |||
| handle_method(Method, Content, State) -> | ||||
|     rpc_bottom_half( {Method, Content} , State). | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % gen_server callbacks | ||||
| %--------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% gen_server callbacks | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| init([InitialState]) -> | ||||
|     {ok, InitialState}. | ||||
|  | @ -298,7 +308,8 @@ handle_call({Method = #'basic.consume'{consumer_tag = Tag}, Consumer}, | |||
|     rpc_top_half(Method, From, NewState). | ||||
| 
 | ||||
| %% Standard implementation of the cast/2 command | ||||
| handle_cast({cast, Method}, State = #channel_state{writer_pid = Writer, do2 = Do2}) -> | ||||
| handle_cast({cast, Method}, State = #channel_state{writer_pid = Writer, | ||||
|                                                    do2 = Do2}) -> | ||||
|     Do2(Writer, Method), | ||||
|     {noreply, State}; | ||||
| 
 | ||||
|  | @ -319,7 +330,7 @@ handle_cast({cast, Method, Content}, | |||
| %% Registers the direct channel peer when using the direct client | ||||
| handle_cast({register_direct_peer, Peer}, State) -> | ||||
|     link(Peer), | ||||
|     process_flag(trap_exit,true), | ||||
|     process_flag(trap_exit, true), | ||||
|     NewState = State#channel_state{writer_pid = Peer}, | ||||
|     {noreply, NewState}; | ||||
| 
 | ||||
|  | @ -336,23 +347,29 @@ handle_cast({register_flow_handler, FlowHandler}, State) -> | |||
| handle_cast({notify_sent, _Peer}, State) -> | ||||
|     {noreply, State}. | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % Rabbit Writer API methods (gen_server callbacks). | ||||
| % These callbacks are invoked when a direct channel sends messages | ||||
| % to this gen_server instance. | ||||
| %---------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% Rabbit Writer API methods (gen_server callbacks). | ||||
| %% These callbacks are invoked when a direct channel sends messages | ||||
| %% to this gen_server instance. | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| handle_info( {send_command, Method}, State) -> handle_method(Method, State); | ||||
| handle_info( {send_command, Method, Content}, State) -> handle_method(Method, Content, State); | ||||
| handle_info( {send_command, Method}, State) -> | ||||
|     handle_method(Method, State); | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % Network Writer methods (gen_server callbacks). | ||||
| % These callbacks are invoked when a network channel sends messages | ||||
| % to this gen_server instance. | ||||
| %--------------------------------------------------------------------------- | ||||
| handle_info( {send_command, Method, Content}, State) -> | ||||
|     handle_method(Method, Content, State); | ||||
| 
 | ||||
| handle_info( {method, Method, none}, State) -> handle_method(Method, State); | ||||
| handle_info( {method, Method, Content}, State) -> handle_method(Method, Content, State); | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% Network Writer methods (gen_server callbacks). | ||||
| %% These callbacks are invoked when a network channel sends messages | ||||
| %% to this gen_server instance. | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| handle_info( {method, Method, none}, State) -> | ||||
|     handle_method(Method, State); | ||||
| 
 | ||||
| handle_info( {method, Method, Content}, State) -> | ||||
|     handle_method(Method, Content, State); | ||||
| 
 | ||||
| 
 | ||||
| %% Handles the delivery of a message from a direct channel | ||||
|  | @ -375,28 +392,24 @@ handle_info({'EXIT', _Pid, Reason}, | |||
|     NewState = channel_cleanup(State), | ||||
|     {stop, normal, NewState}; | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % This is for a race condition between a close.close_ok and a subsequent channel.open | ||||
| %--------------------------------------------------------------------------- | ||||
| 
 | ||||
| %% This is for a race condition between a close.close_ok and a subsequent | ||||
| %% channel.open | ||||
| handle_info( {channel_close, Peer}, State ) -> | ||||
|     NewState = channel_cleanup(State), | ||||
|     %% TODO Do we still need this?? | ||||
|     Peer ! handshake, | ||||
|     {noreply, NewState}; | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % This is for a channel exception that can't be otherwise handled | ||||
| %--------------------------------------------------------------------------- | ||||
| 
 | ||||
| %% This is for a channel exception that can't be otherwise handled | ||||
| handle_info( {channel_exception, Channel, Reason}, State) -> | ||||
|     io:format("Channel ~p is shutting down due to: ~p~n",[Channel, Reason]), | ||||
|     NewState = channel_cleanup(State), | ||||
|     {stop, shutdown, NewState}. | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % Rest of the gen_server callbacks | ||||
| %--------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% Rest of the gen_server callbacks | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| terminate(normal, _State) -> | ||||
|     ok; | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,19 +30,20 @@ | |||
| 
 | ||||
| -behaviour(gen_server). | ||||
| 
 | ||||
| -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2]). | ||||
| -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, | ||||
|          handle_info/2]). | ||||
| -export([open_channel/1, open_channel/3]). | ||||
| -export([start/2, start/3, start/4, close/2]). | ||||
| -export([start_link/2, start_link/3, start_link/4]). | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % AMQP Connection API Methods | ||||
| %--------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% AMQP Connection API Methods | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| %% Starts a direct connection to the Rabbit AMQP server, assuming that | ||||
| %% the server is running in the same process space. | ||||
| start(User,Password) -> start(User,Password,false). | ||||
| start(User,Password,ProcLink) when is_boolean(ProcLink) -> | ||||
| start(User, Password) -> start(User, Password, false). | ||||
| start(User, Password, ProcLink) when is_boolean(ProcLink) -> | ||||
|     InitialState = #connection_state{username = User, | ||||
|                                      password = Password, | ||||
|                                      vhostpath = <<"/">>}, | ||||
|  | @ -50,9 +51,13 @@ start(User,Password,ProcLink) when is_boolean(ProcLink) -> | |||
|     Pid; | ||||
| 
 | ||||
| %% Starts a networked conection to a remote AMQP server. | ||||
| start(User,Password,Host) -> start(User,Password,Host,<<"/">>,false). | ||||
| start(User,Password,Host,VHost) -> start(User,Password,Host,VHost,false). | ||||
| start(User,Password,Host,VHost,ProcLink) -> | ||||
| start(User, Password, Host) -> | ||||
|     start(User, Password, Host, <<"/">>, false). | ||||
| 
 | ||||
| start(User, Password, Host, VHost) -> | ||||
|     start(User, Password, Host, VHost, false). | ||||
| 
 | ||||
| start(User, Password, Host, VHost, ProcLink) -> | ||||
|     InitialState = #connection_state{username = User, | ||||
|                                      password = Password, | ||||
|                                      serverhost = Host, | ||||
|  | @ -60,9 +65,14 @@ start(User,Password,Host,VHost,ProcLink) -> | |||
|     {ok, Pid} = start_internal(InitialState, amqp_network_driver, ProcLink), | ||||
|     Pid. | ||||
| 
 | ||||
| start_link(User,Password) -> start(User,Password,true). | ||||
| start_link(User,Password,Host) -> start(User,Password,Host,<<"/">>,true). | ||||
| start_link(User,Password,Host,VHost) -> start(User,Password,Host,VHost,true). | ||||
| start_link(User, Password) -> | ||||
|     start(User, Password, true). | ||||
| 
 | ||||
| start_link(User, Password, Host) -> | ||||
|     start(User, Password, Host, <<"/">>, true). | ||||
| 
 | ||||
| start_link(User, Password, Host, VHost) -> | ||||
|     start(User, Password, Host, VHost, true). | ||||
| 
 | ||||
| start_internal(InitialState, Driver, _Link = true) when is_atom(Driver) -> | ||||
|     gen_server:start_link(?MODULE, [InitialState, Driver], []); | ||||
|  | @ -73,7 +83,8 @@ start_internal(InitialState, Driver, _Link = false) when is_atom(Driver) -> | |||
| %% Opens a channel without having to specify a channel number. | ||||
| %% This function assumes that an AMQP connection (networked or direct) | ||||
| %% has already been successfully established. | ||||
| open_channel(ConnectionPid) -> open_channel(ConnectionPid, none, ""). | ||||
| open_channel(ConnectionPid) -> | ||||
|     open_channel(ConnectionPid, none, ""). | ||||
| 
 | ||||
| %% Opens a channel with a specific channel number. | ||||
| %% This function assumes that an AMQP connection (networked or direct) | ||||
|  | @ -86,9 +97,9 @@ open_channel(ConnectionPid, ChannelNumber, OutOfBand) -> | |||
| %% Closes the AMQP connection | ||||
| close(ConnectionPid, Close) -> gen_server:call(ConnectionPid, Close). | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % Internal plumbing | ||||
| %--------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% Internal plumbing | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| %% Starts a new channel process, invokes the correct driver | ||||
| %% (network or direct) to perform any environment specific channel setup and | ||||
|  | @ -175,9 +186,9 @@ allocate_channel_number(Channels, _Max) -> | |||
| close_connection(Close, From, State = #connection_state{driver = Driver}) -> | ||||
|     Driver:close_connection(Close, From, State). | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % gen_server callbacks | ||||
| %--------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% gen_server callbacks | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| init([InitialState, Driver]) when is_atom(Driver) -> | ||||
|     State = Driver:handshake(InitialState), | ||||
|  | @ -190,14 +201,14 @@ handle_call({open_channel, ChannelNumber, OutOfBand}, _From, State) -> | |||
| %% Shuts the AMQP connection down | ||||
| handle_call(Close = #'connection.close'{}, From, State) -> | ||||
|     close_connection(Close, From, State), | ||||
|     {stop,normal,State}. | ||||
|     {stop, normal, State}. | ||||
| 
 | ||||
| handle_cast(_Message, State) -> | ||||
|     {noreply, State}. | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % Handle forced close from the broker | ||||
| %--------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% Handle forced close from the broker | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| handle_info({method, #'connection.close'{reply_code = Code, | ||||
|                                          reply_text = Text}, _Content}, | ||||
|  | @ -206,16 +217,18 @@ handle_info({method, #'connection.close'{reply_code = Code, | |||
|     Driver:handle_broker_close(State), | ||||
|     {stop, normal, State}; | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % Trap exits | ||||
| %--------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% Trap exits | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| handle_info( {'EXIT', Pid, {amqp,Reason,Msg,Context}}, State) -> | ||||
|     io:format("Channel Peer ~p sent this message: ~p -> ~p~n",[Pid,Msg,Context]), | ||||
| handle_info( {'EXIT', Pid, {amqp, Reason, Msg, Context}}, State) -> | ||||
|     io:format("Channel Peer ~p sent this message: ~p -> ~p~n", | ||||
|               [Pid, Msg, Context]), | ||||
|     {HardError, Code, Text} = rabbit_framing:lookup_amqp_exception(Reason), | ||||
|     case HardError of | ||||
|         false -> | ||||
|             io:format("Just trapping this exit and proceding to trap an exit from the client channel process~n"), | ||||
|             io:format("Just trapping this exit and proceding to trap an | ||||
|                       exit from the client channel process~n"), | ||||
|             {noreply, State}; | ||||
|         true -> | ||||
|             io:format("Hard error: (Code = ~p, Text = ~p)~n", [Code, Text]), | ||||
|  | @ -226,17 +239,17 @@ handle_info( {'EXIT', Pid, {amqp,Reason,Msg,Context}}, State) -> | |||
| handle_info( {'EXIT', Pid, normal}, State) -> | ||||
|     {noreply, unregister_channel(Pid, State) }; | ||||
| 
 | ||||
| % This is a special case for abruptly closed socket connections | ||||
| %% This is a special case for abruptly closed socket connections | ||||
| handle_info( {'EXIT', _Pid, {socket_error, Reason}}, State) -> | ||||
|     {stop, {socket_error, Reason}, State}; | ||||
| 
 | ||||
| handle_info( {'EXIT', Pid, Reason}, State) -> | ||||
|     io:format("Connection: Handling exit from ~p --> ~p~n",[Pid,Reason]), | ||||
|     io:format("Connection: Handling exit from ~p --> ~p~n", [Pid, Reason]), | ||||
|     {noreply, unregister_channel(Pid, State) }. | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % Rest of the gen_server callbacks | ||||
| %--------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% Rest of the gen_server callbacks | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| terminate(_Reason, _State) -> | ||||
|     ok. | ||||
|  |  | |||
|  | @ -30,7 +30,7 @@ | |||
| -include("amqp_client.hrl"). | ||||
| 
 | ||||
| -export([handshake/1, open_channel/3, close_channel/1, close_connection/3]). | ||||
| -export([do/2,do/3]). | ||||
| -export([do/2, do/3]). | ||||
| -export([handle_broker_close/1]). | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
|  | @ -43,23 +43,31 @@ handshake(ConnectionState = #connection_state{username = User, | |||
|     UserBin = amqp_util:binary(User), | ||||
|     PassBin = amqp_util:binary(Pass), | ||||
|     rabbit_access_control:user_pass_login(UserBin, PassBin), | ||||
|     rabbit_access_control:check_vhost_access(#user{username = UserBin}, VHostPath), | ||||
|     rabbit_access_control:check_vhost_access(#user{username = UserBin}, | ||||
|                                              VHostPath), | ||||
|     ConnectionState. | ||||
| 
 | ||||
| open_channel({_Channel, _OutOfBand}, ChannelPid, | ||||
|              State = #connection_state{username = User, vhostpath = VHost}) -> | ||||
|              State = #connection_state{username = User, | ||||
|                                        vhostpath = VHost}) -> | ||||
|     UserBin = amqp_util:binary(User), | ||||
|     ReaderPid = WriterPid = ChannelPid, | ||||
|     Peer = rabbit_channel:start_link(ReaderPid, WriterPid, UserBin, VHost), | ||||
|     amqp_channel:register_direct_peer(ChannelPid, Peer), | ||||
|     State. | ||||
| 
 | ||||
| close_channel(_WriterPid) -> ok. | ||||
| close_channel(_WriterPid) -> | ||||
|     ok. | ||||
| 
 | ||||
| close_connection(_Close, From, _State) -> | ||||
|     gen_server:reply(From, #'connection.close_ok'{}). | ||||
| 
 | ||||
| do(Writer, Method) -> rabbit_channel:do(Writer, Method). | ||||
| do(Writer, Method, Content) -> rabbit_channel:do(Writer, Method, Content). | ||||
| do(Writer, Method) -> | ||||
|     rabbit_channel:do(Writer, Method). | ||||
| 
 | ||||
| do(Writer, Method, Content) -> | ||||
|     rabbit_channel:do(Writer, Method, Content). | ||||
| 
 | ||||
| handle_broker_close(_State) -> | ||||
|     ok. | ||||
| 
 | ||||
| handle_broker_close(_State) -> ok. | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ | |||
| 
 | ||||
| -export([handshake/1, open_channel/3, close_channel/1, close_connection/3]). | ||||
| -export([start_reader/2, start_writer/2]). | ||||
| -export([do/2,do/3]). | ||||
| -export([do/2, do/3]). | ||||
| -export([handle_broker_close/1]). | ||||
| 
 | ||||
| -define(SOCKET_CLOSING_TIMEOUT, 1000). | ||||
|  | @ -40,23 +40,26 @@ | |||
| % Driver API Methods | ||||
| %--------------------------------------------------------------------------- | ||||
| 
 | ||||
| handshake(ConnectionState = #connection_state{serverhost = Host}) -> | ||||
|     case gen_tcp:connect(Host, 5672, [binary, {packet, 0},{active,false}]) of | ||||
| handshake(State = #connection_state{serverhost = Host}) -> | ||||
|     case gen_tcp:connect(Host, 5672, | ||||
|                          [binary, {packet, 0}, {active, false}]) of | ||||
|         {ok, Sock} -> | ||||
|             ok = gen_tcp:send(Sock, amqp_util:protocol_header()), | ||||
|             Parent = self(), | ||||
|             FramingPid = rabbit_framing_channel:start_link(fun(X) -> X end, [Parent]), | ||||
|             ReaderPid = spawn_link(?MODULE, start_reader, [Sock, FramingPid]), | ||||
|             FramingPid = rabbit_framing_channel:start_link(fun(X) -> X end, | ||||
|                                                            [Parent]), | ||||
|             ReaderPid = spawn_link(?MODULE, start_reader, | ||||
|                                    [Sock, FramingPid]), | ||||
|             WriterPid = start_writer(Sock, 0), | ||||
|             ConnectionState1 = ConnectionState#connection_state{channel0_writer_pid = WriterPid, | ||||
|             State1 = State#connection_state{channel0_writer_pid = WriterPid, | ||||
|                                             reader_pid = ReaderPid, | ||||
|                                             sock = Sock}, | ||||
|             ConnectionState2 = network_handshake(WriterPid, ConnectionState1), | ||||
|             #connection_state{heartbeat = Heartbeat} = ConnectionState2, | ||||
|             State2 = network_handshake(WriterPid, State1), | ||||
|             #connection_state{heartbeat = Heartbeat} = State2, | ||||
|             ReaderPid ! {heartbeat, Heartbeat}, | ||||
|             ConnectionState2; | ||||
|             State2; | ||||
|         {error, Reason} -> | ||||
|             io:format("Could not start the network driver: ~p~n",[Reason]), | ||||
|             io:format("Could not start the network driver: ~p~n", [Reason]), | ||||
|             exit(Reason) | ||||
|     end. | ||||
| 
 | ||||
|  | @ -72,7 +75,7 @@ open_channel({ChannelNumber, _OutOfBand}, ChannelPid, | |||
|     amqp_channel:register_direct_peer(ChannelPid, WriterPid ). | ||||
| 
 | ||||
| close_channel(WriterPid) -> | ||||
|     %io:format("Shutting the channel writer ~p down~n",[WriterPid]), | ||||
|     %io:format("Shutting the channel writer ~p down~n", [WriterPid]), | ||||
|     rabbit_writer:shutdown(WriterPid). | ||||
| 
 | ||||
| %% This closes the writer down, waits for the confirmation from the | ||||
|  | @ -89,8 +92,11 @@ close_connection(Close = #'connection.close'{}, From, | |||
|             exit(timeout_on_exit) | ||||
|     end. | ||||
| 
 | ||||
| do(Writer, Method) -> rabbit_writer:send_command(Writer, Method). | ||||
| do(Writer, Method, Content) -> rabbit_writer:send_command(Writer, Method, Content). | ||||
| do(Writer, Method) -> | ||||
|     rabbit_writer:send_command(Writer, Method). | ||||
| 
 | ||||
| do(Writer, Method, Content) -> | ||||
|     rabbit_writer:send_command(Writer, Method, Content). | ||||
| 
 | ||||
| handle_broker_close(#connection_state{channel0_writer_pid = Writer, | ||||
|                                       reader_pid = Reader}) -> | ||||
|  | @ -117,7 +123,8 @@ recv() -> | |||
| % Internal plumbing | ||||
| %--------------------------------------------------------------------------- | ||||
| 
 | ||||
| network_handshake(Writer, State = #connection_state{ vhostpath = VHostPath }) -> | ||||
| network_handshake(Writer, | ||||
|                   State = #connection_state{ vhostpath = VHostPath }) -> | ||||
|     #'connection.start'{} = recv(), | ||||
|     do(Writer, start_ok(State)), | ||||
|     #'connection.tune'{channel_max = ChannelMax, | ||||
|  | @ -129,7 +136,8 @@ network_handshake(Writer, State = #connection_state{ vhostpath = VHostPath }) -> | |||
|     do(Writer, TuneOk), | ||||
| 
 | ||||
|     %% This is something where I don't understand the protocol, | ||||
|     %% What happens if the following command reaches the server before the tune ok? | ||||
|     %% What happens if the following command reaches the server | ||||
|     %% before the tune ok? | ||||
|     %% Or doesn't get sent at all? | ||||
|     ConnectionOpen = #'connection.open'{virtual_host = VHostPath, | ||||
|                                         capabilities = <<"">>, | ||||
|  | @ -146,7 +154,7 @@ start_ok(#connection_state{username = Username, password = Password}) -> | |||
|            client_properties = [ | ||||
|                             {<<"product">>, longstr, <<"Erlang-AMQC">>}, | ||||
|                             {<<"version">>, longstr, <<"0.1">>}, | ||||
|                             {<<"platform">>,longstr, <<"Erlang">>} | ||||
|                             {<<"platform">>, longstr, <<"Erlang">>} | ||||
|                            ], | ||||
|            mechanism = <<"AMQPLAIN">>, | ||||
|            response = rabbit_binary_generator:generate_table(LoginTable), | ||||
|  | @ -154,7 +162,7 @@ start_ok(#connection_state{username = Username, password = Password}) -> | |||
| 
 | ||||
| start_reader(Sock, FramingPid) -> | ||||
|     process_flag(trap_exit, true), | ||||
|     put({channel, 0},{chpid, FramingPid}), | ||||
|     put({channel, 0}, {chpid, FramingPid}), | ||||
|     {ok, _Ref} = prim_inet:async_recv(Sock, 7, -1), | ||||
|     reader_loop(Sock, undefined, undefined, undefined), | ||||
|     gen_tcp:close(Sock). | ||||
|  | @ -164,7 +172,7 @@ start_writer(Sock, Channel) -> | |||
| 
 | ||||
| reader_loop(Sock, Type, Channel, Length) -> | ||||
|     receive | ||||
|         {inet_async, Sock, _, {ok, <<Payload:Length/binary,?FRAME_END>>} } -> | ||||
|         {inet_async, Sock, _, {ok, <<Payload:Length/binary, ?FRAME_END>>} } -> | ||||
|             case handle_frame(Type, Channel, Payload) of | ||||
|                 closed_ok -> | ||||
|                     ok; | ||||
|  | @ -172,9 +180,9 @@ reader_loop(Sock, Type, Channel, Length) -> | |||
|                     {ok, _Ref} = prim_inet:async_recv(Sock, 7, -1), | ||||
|                     reader_loop(Sock, undefined, undefined, undefined) | ||||
|             end; | ||||
|         {inet_async, Sock, _, {ok, <<_Type:8,_Channel:16,PayloadSize:32>>}} -> | ||||
|         {inet_async, Sock, _, {ok, <<_Type:8, _Chan:16, PayloadSize:32>>}} -> | ||||
|             {ok, _Ref} = prim_inet:async_recv(Sock, PayloadSize + 1, -1), | ||||
|             reader_loop(Sock, _Type, _Channel, PayloadSize); | ||||
|             reader_loop(Sock, _Type, _Chan, PayloadSize); | ||||
|         {inet_async, Sock, _Ref, {error, closed}} -> | ||||
|             ok; | ||||
|         {inet_async, Sock, _Ref, {error, Reason}} -> | ||||
|  | @ -187,11 +195,13 @@ reader_loop(Sock, Type, Channel, Length) -> | |||
|             start_framing_channel(ChannelPid, ChannelNumber), | ||||
|             reader_loop(Sock, Type, Channel, Length); | ||||
|         timeout -> | ||||
|             io:format("Reader (~p) received timeout from heartbeat, exiting ~n",[self()]); | ||||
|             io:format("Reader (~p) received timeout from heartbeat, | ||||
|                        exiting ~n", [self()]); | ||||
|         close -> | ||||
|             io:format("Reader (~p) received close command, exiting ~n",[self()]); | ||||
|             io:format("Reader (~p) received close command, | ||||
|                        exiting ~n", [self()]); | ||||
|         {'EXIT', Pid, _Reason} -> | ||||
|             [H|_] = get_keys({chpid,Pid}), | ||||
|             [H|_] = get_keys({chpid, Pid}), | ||||
|             erase(H), | ||||
|             reader_loop(Sock, Type, Channel, Length); | ||||
|         Other -> | ||||
|  | @ -200,8 +210,9 @@ reader_loop(Sock, Type, Channel, Length) -> | |||
|     end. | ||||
| 
 | ||||
| start_framing_channel(ChannelPid, ChannelNumber) -> | ||||
|     FramingPid = rabbit_framing_channel:start_link(fun(X) -> link(X), X end, [ChannelPid]), | ||||
|     put({channel, ChannelNumber},{chpid, FramingPid}). | ||||
|     FramingPid = rabbit_framing_channel:start_link(fun(X) -> link(X), X end, | ||||
|                                                    [ChannelPid]), | ||||
|     put({channel, ChannelNumber}, {chpid, FramingPid}). | ||||
| 
 | ||||
| handle_frame(Type, Channel, Payload) -> | ||||
|     case rabbit_reader:analyze_frame(Type, Payload) of | ||||
|  | @ -214,7 +225,7 @@ handle_frame(Type, Channel, Payload) -> | |||
|             heartbeat; | ||||
|         trace -> | ||||
|             trace; | ||||
|         {method,'connection.close_ok',Content} -> | ||||
|         {method, 'connection.close_ok', Content} -> | ||||
|             send_frame(Channel, {method, 'connection.close_ok', Content}), | ||||
|             closed_ok; | ||||
|         AnalyzedFrame -> | ||||
|  | @ -228,3 +239,4 @@ resolve_receiver(Channel) -> | |||
|        undefined -> | ||||
|             exit(unknown_channel) | ||||
|    end. | ||||
| 
 | ||||
|  |  | |||
|  | @ -33,7 +33,9 @@ | |||
| -export([basic_properties/0, protocol_header/0]). | ||||
| 
 | ||||
| basic_properties() -> | ||||
|     #'P_basic'{content_type = <<"application/octet-stream">>, delivery_mode = 1, priority = 0}. | ||||
|     #'P_basic'{content_type = <<"application/octet-stream">>, | ||||
|                delivery_mode = 1, | ||||
|                priority = 0}. | ||||
| 
 | ||||
| protocol_header() -> | ||||
|     <<"AMQP", 1, 1, ?PROTOCOL_VERSION_MAJOR, ?PROTOCOL_VERSION_MINOR>>. | ||||
|  |  | |||
|  | @ -33,30 +33,39 @@ | |||
| 
 | ||||
| -include_lib("eunit/include/eunit.hrl"). | ||||
| 
 | ||||
| basic_get_test() -> test_util:basic_get_test(new_connection()). | ||||
| basic_get_test() ->  | ||||
|     test_util:basic_get_test(new_connection()). | ||||
| 
 | ||||
| basic_return_test() -> test_util:basic_return_test(new_connection()). | ||||
| basic_return_test() -> | ||||
|     test_util:basic_return_test(new_connection()). | ||||
| 
 | ||||
| basic_qos_test() -> test_util:basic_qos_test(new_connection()). | ||||
| basic_qos_test() -> | ||||
|     test_util:basic_qos_test(new_connection()). | ||||
| 
 | ||||
| basic_recover_test() -> test_util:basic_recover_test(new_connection()). | ||||
| basic_recover_test() -> | ||||
|     test_util:basic_recover_test(new_connection()). | ||||
| 
 | ||||
| basic_consume_test() -> test_util:basic_consume_test(new_connection()). | ||||
| basic_consume_test() -> | ||||
|     test_util:basic_consume_test(new_connection()). | ||||
| 
 | ||||
| lifecycle_test() -> test_util:lifecycle_test(new_connection()). | ||||
| lifecycle_test() -> | ||||
|     test_util:lifecycle_test(new_connection()). | ||||
| 
 | ||||
| basic_ack_test() ->test_util:basic_ack_test(new_connection()). | ||||
| basic_ack_test() -> | ||||
|     test_util:basic_ack_test(new_connection()). | ||||
| 
 | ||||
| command_serialization_test() -> test_util:command_serialization_test(new_connection()). | ||||
| command_serialization_test() -> | ||||
|     test_util:command_serialization_test(new_connection()). | ||||
| 
 | ||||
| %---------------------------------------------------------------------------- | ||||
| % This must be kicked off manually because it can only be run after Rabbit | ||||
| % has been running for 1 minute | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% This must be kicked off manually because it can only be run after Rabbit | ||||
| %% has been running for 1 minute | ||||
| test_channel_flow() -> | ||||
|     test_util:channel_flow_test(new_connection()). | ||||
| 
 | ||||
| %---------------------------------------------------------------------------- | ||||
| % Negative Tests | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% Negative Tests | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| non_existent_exchange_test() ->  | ||||
|     negative_test_util:non_existent_exchange_test(new_connection()). | ||||
|  | @ -64,10 +73,12 @@ non_existent_exchange_test() -> | |||
| queue_unbind_test() -> | ||||
|     test_util:queue_unbind_test(new_connection()). | ||||
| 
 | ||||
| %---------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% Common Functions | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| new_connection() -> amqp_connection:start("guest", "guest"). | ||||
| new_connection() -> | ||||
|     amqp_connection:start("guest", "guest"). | ||||
| 
 | ||||
| test_coverage() -> | ||||
|     rabbit_misc:enable_cover(), | ||||
|  |  | |||
|  | @ -1,3 +1,28 @@ | |||
| %%   The contents of this file are subject to the Mozilla Public License | ||||
| %%   Version 1.1 (the "License"); you may not use this file except in | ||||
| %%   compliance with the License. You may obtain a copy of the License at | ||||
| %%   http://www.mozilla.org/MPL/ | ||||
| %% | ||||
| %%   Software distributed under the License is distributed on an "AS IS" | ||||
| %%   basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the | ||||
| %%   License for the specific language governing rights and limitations | ||||
| %%   under the License. | ||||
| %% | ||||
| %%   The Original Code is the RabbitMQ Erlang Client. | ||||
| %% | ||||
| %%   The Initial Developers of the Original Code are LShift Ltd., | ||||
| %%   Cohesive Financial Technologies LLC., and Rabbit Technologies Ltd. | ||||
| %% | ||||
| %%   Portions created by LShift Ltd., Cohesive Financial | ||||
| %%   Technologies LLC., and Rabbit Technologies Ltd. are Copyright (C) | ||||
| %%   2007 LShift Ltd., Cohesive Financial Technologies LLC., and Rabbit | ||||
| %%   Technologies Ltd.; | ||||
| %% | ||||
| %%   All Rights Reserved. | ||||
| %% | ||||
| %%   Contributor(s): Ben Hood <0x6e6562@gmail.com>. | ||||
| %% | ||||
| 
 | ||||
| -module(lib_amqp). | ||||
| 
 | ||||
| -include_lib("rabbitmq_server/include/rabbit.hrl"). | ||||
|  | @ -21,9 +46,12 @@ declare_exchange(Channel, X) -> | |||
| declare_exchange(Channel, X, Type) -> | ||||
|     ExchangeDeclare = #'exchange.declare'{exchange = X, | ||||
|                                           type = Type, | ||||
|                                           passive = false, durable = false, | ||||
|                                           auto_delete = false, internal = false, | ||||
|                                           nowait = false, arguments = []}, | ||||
|                                           passive = false, | ||||
|                                           durable = false, | ||||
|                                           auto_delete = false, | ||||
|                                           internal = false, | ||||
|                                           nowait = false, | ||||
|                                           arguments = []}, | ||||
|     amqp_channel:call(Channel, ExchangeDeclare). | ||||
| 
 | ||||
| delete_exchange(Channel, X) -> | ||||
|  | @ -31,9 +59,9 @@ delete_exchange(Channel, X) -> | |||
|                                         if_unused = false, nowait = false}, | ||||
|     #'exchange.delete_ok'{} = amqp_channel:call(Channel, ExchangeDelete). | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % TODO This whole section of optional properties and mandatory flags | ||||
| % may have to be re-thought | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% TODO This whole section of optional properties and mandatory flags | ||||
| %% may have to be re-thought | ||||
| publish(Channel, X, RoutingKey, Payload) -> | ||||
|     publish(Channel, X, RoutingKey, Payload, false). | ||||
| 
 | ||||
|  | @ -69,18 +97,23 @@ publish_internal(Fun, Channel, X, RoutingKey, | |||
|                        payload_fragments_rev = [Payload]}, | ||||
|     Fun(Channel, BasicPublish, Content). | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| close_channel(Channel) -> | ||||
|     ChannelClose = #'channel.close'{reply_code = 200, reply_text = <<"Goodbye">>, | ||||
|                                     class_id = 0, method_id = 0}, | ||||
|     ChannelClose = #'channel.close'{reply_code = 200, | ||||
|                                     reply_text = <<"Goodbye">>, | ||||
|                                     class_id = 0, | ||||
|                                     method_id = 0}, | ||||
|     #'channel.close_ok'{} = amqp_channel:call(Channel, ChannelClose), | ||||
|     ok. | ||||
| 
 | ||||
| close_connection(Connection) -> | ||||
|     ConnectionClose = #'connection.close'{reply_code = 200, reply_text = <<"Goodbye">>, | ||||
|                                               class_id = 0, method_id = 0}, | ||||
|     #'connection.close_ok'{} = amqp_connection:close(Connection, ConnectionClose), | ||||
|     ConnectionClose = #'connection.close'{reply_code = 200, | ||||
|                                           reply_text = <<"Goodbye">>, | ||||
|                                           class_id = 0, | ||||
|                                           method_id = 0}, | ||||
|     #'connection.close_ok'{} = amqp_connection:close(Connection, | ||||
|                                                      ConnectionClose), | ||||
|     ok. | ||||
| 
 | ||||
| teardown(Connection, Channel) -> | ||||
|  | @ -122,12 +155,12 @@ subscribe(Channel, Q, Consumer, Tag, NoAck) -> | |||
|                                     no_local = false, no_ack = NoAck, | ||||
|                                     exclusive = false, nowait = false}, | ||||
|     #'basic.consume_ok'{consumer_tag = ConsumerTag} = | ||||
|         amqp_channel:subscribe(Channel,BasicConsume, Consumer), | ||||
|         amqp_channel:subscribe(Channel, BasicConsume, Consumer), | ||||
|     ConsumerTag. | ||||
| 
 | ||||
| unsubscribe(Channel, Tag) -> | ||||
|     BasicCancel = #'basic.cancel'{consumer_tag = Tag, nowait = false}, | ||||
|     #'basic.cancel_ok'{} = amqp_channel:call(Channel,BasicCancel), | ||||
|     #'basic.cancel_ok'{} = amqp_channel:call(Channel, BasicCancel), | ||||
|     ok. | ||||
| 
 | ||||
| %%--------------------------------------------------------------------------- | ||||
|  | @ -174,10 +207,12 @@ delete_queue(Channel, Q) -> | |||
| 
 | ||||
| bind_queue(Channel, X, Q, Binding) -> | ||||
|     QueueBind = #'queue.bind'{queue = Q, exchange = X, | ||||
|                               routing_key = Binding, nowait = false, arguments = []}, | ||||
|                               routing_key = Binding, | ||||
|                               nowait = false, arguments = []}, | ||||
|     #'queue.bind_ok'{} = amqp_channel:call(Channel, QueueBind). | ||||
| 
 | ||||
| unbind_queue(Channel, X, Q, Binding) -> | ||||
|     Unbind = #'queue.unbind'{queue = Q, exchange = X, | ||||
|                              routing_key = Binding, arguments = []}, | ||||
|     #'queue.unbind_ok'{} = amqp_channel:call(Channel, Unbind). | ||||
| 
 | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ non_existent_exchange_test(Connection) -> | |||
|     Payload = <<"foobar">>, | ||||
|     Channel = lib_amqp:start_channel(Connection), | ||||
|     lib_amqp:declare_exchange(Channel, X), | ||||
|     % Deliberately mix up the routingkey and exchange arguments | ||||
|     %% Deliberately mix up the routingkey and exchange arguments | ||||
|     lib_amqp:publish(Channel, RoutingKey, X, Payload), | ||||
|     receive | ||||
|         X -> ok | ||||
|  |  | |||
|  | @ -65,13 +65,13 @@ teardown_test() -> | |||
| rpc_test() -> | ||||
|     test_util:rpc_test(new_connection()). | ||||
| 
 | ||||
| %---------------------------------------------------------------------------- | ||||
| % Negative Tests | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% Negative Tests | ||||
| 
 | ||||
| non_existent_exchange_test() ->  | ||||
|   negative_test_util:non_existent_exchange_test(new_connection()). | ||||
| 
 | ||||
| %---------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% Common Functions | ||||
| 
 | ||||
| new_connection() -> | ||||
|  |  | |||
|  | @ -32,37 +32,40 @@ | |||
| 
 | ||||
| -compile([export_all]). | ||||
| 
 | ||||
| -record(publish,{q, x, routing_key, bind_key, payload, | ||||
| -record(publish, {q, x, routing_key, bind_key, payload, | ||||
|                  mandatory = false, immediate = false}). | ||||
| 
 | ||||
| % The latch constant defines how many processes are spawned in order | ||||
| % to run certain functionality in parallel. It follows the standard | ||||
| % countdown latch pattern. | ||||
| %% The latch constant defines how many processes are spawned in order | ||||
| %% to run certain functionality in parallel. It follows the standard | ||||
| %% countdown latch pattern. | ||||
| -define(Latch, 100). | ||||
| 
 | ||||
| % The wait constant defines how long a consumer waits before it | ||||
| % unsubscribes | ||||
| %% The wait constant defines how long a consumer waits before it | ||||
| %% unsubscribes | ||||
| -define(Wait, 200). | ||||
| 
 | ||||
| %%%% | ||||
| % | ||||
| % This is an example of how the client interaction should work | ||||
| % | ||||
| %   Connection = amqp_connection:start(User, Password, Host), | ||||
| %   Channel = amqp_connection:open_channel(Connection), | ||||
| %   %%...do something useful | ||||
| %   ChannelClose = #'channel.close'{ %% set the appropriate fields }, | ||||
| %   amqp_channel:call(Channel, ChannelClose), | ||||
| %   ConnectionClose = #'connection.close'{ %% set the appropriate fields }, | ||||
| %   amqp_connection:close(Connection, ConnectionClose). | ||||
| % | ||||
| %% | ||||
| %% This is an example of how the client interaction should work | ||||
| %% | ||||
| %%   Connection = amqp_connection:start(User, Password, Host), | ||||
| %%   Channel = amqp_connection:open_channel(Connection), | ||||
| %%   %%...do something useful | ||||
| %%   ChannelClose = #'channel.close'{ %% set the appropriate fields }, | ||||
| %%   amqp_channel:call(Channel, ChannelClose), | ||||
| %%   ConnectionClose = #'connection.close'{ %% set the appropriate fields }, | ||||
| %%   amqp_connection:close(Connection, ConnectionClose). | ||||
| %% | ||||
| 
 | ||||
| lifecycle_test(Connection) -> | ||||
|     X = <<"x">>, | ||||
|     Channel = lib_amqp:start_channel(Connection), | ||||
|     lib_amqp:declare_exchange(Channel, X, <<"topic">>), | ||||
|     Parent = self(), | ||||
|     [spawn(fun() -> queue_exchange_binding(Channel, X, Parent, Tag) end) || Tag <- lists:seq(1,?Latch)], | ||||
|     [spawn( | ||||
|            fun() -> | ||||
|                 queue_exchange_binding(Channel, X, Parent, Tag) end) | ||||
|             || Tag <- lists:seq(1, ?Latch)], | ||||
|     latch_loop(?Latch), | ||||
|     lib_amqp:delete_exchange(Channel, X), | ||||
|     lib_amqp:teardown(Connection, Channel), | ||||
|  | @ -74,7 +77,7 @@ queue_exchange_binding(Channel, X, Parent, Tag) -> | |||
|     after (?Latch - Tag rem 7) * 10 -> | ||||
|         ok | ||||
|     end, | ||||
|     Q = <<"a.b.c",Tag:32>>, | ||||
|     Q = <<"a.b.c", Tag:32>>, | ||||
|     Binding = <<"a.b.c.*">>, | ||||
|     Q1 = lib_amqp:declare_queue(Channel, Q), | ||||
|     ?assertMatch(Q, Q1), | ||||
|  | @ -89,8 +92,8 @@ channel_lifecycle_test(Connection) -> | |||
|     lib_amqp:teardown(Connection, Channel2), | ||||
|     ok. | ||||
| 
 | ||||
| % This is designed to exercize the internal queuing mechanism | ||||
| % to ensure that commands are properly serialized | ||||
| %% This is designed to exercize the internal queuing mechanism | ||||
| %% to ensure that commands are properly serialized | ||||
| command_serialization_test(Connection) -> | ||||
|     Channel = lib_amqp:start_channel(Connection), | ||||
|     Parent = self(), | ||||
|  | @ -99,7 +102,7 @@ command_serialization_test(Connection) -> | |||
|                 Q1 = lib_amqp:declare_queue(Channel, Q), | ||||
|                 ?assertMatch(Q, Q1), | ||||
|                 Parent ! finished | ||||
|            end) || _ <- lists:seq(1,?Latch)], | ||||
|            end) || _ <- lists:seq(1, ?Latch)], | ||||
|     latch_loop(?Latch), | ||||
|     lib_amqp:teardown(Connection, Channel). | ||||
| 
 | ||||
|  | @ -129,8 +132,8 @@ get_and_assert_equals(Channel, Q, Payload) -> | |||
| basic_get_test(Connection) -> | ||||
|     Channel = lib_amqp:start_channel(Connection), | ||||
|     {ok, Q} = setup_publish(Channel), | ||||
|     % TODO: This could be refactored to use get_and_assert_equals, | ||||
|     % get_and_assert_empty .... would require another bug though :-) | ||||
|     %% TODO: This could be refactored to use get_and_assert_equals, | ||||
|     %% get_and_assert_empty .... would require another bug though :-) | ||||
|     Content = lib_amqp:get(Channel, Q), | ||||
|     #content{payload_fragments_rev = PayloadFragments} = Content, | ||||
|     ?assertMatch([<<"foobar">>], PayloadFragments), | ||||
|  | @ -158,7 +161,7 @@ basic_return_test(Connection) -> | |||
|             ?assertMatch([Payload], Payload2); | ||||
|         WhatsThis -> | ||||
|             %% TODO investigate where this comes from | ||||
|             io:format("Spurious message ~p~n",[WhatsThis]) | ||||
|             io:format("Spurious message ~p~n", [WhatsThis]) | ||||
|     after 2000 -> | ||||
|         exit(no_return_received) | ||||
|     end, | ||||
|  | @ -221,7 +224,7 @@ basic_recover_test(Connection) -> | |||
|         exit(did_not_receive_first_message) | ||||
|     end, | ||||
|     BasicRecover = #'basic.recover'{requeue = true}, | ||||
|     amqp_channel:cast(Channel,BasicRecover), | ||||
|     amqp_channel:cast(Channel, BasicRecover), | ||||
|     receive | ||||
|         {#'basic.deliver'{delivery_tag = DeliveryTag2}, _} -> | ||||
|             lib_amqp:ack(Channel, DeliveryTag2) | ||||
|  | @ -230,27 +233,28 @@ basic_recover_test(Connection) -> | |||
|     end, | ||||
|     lib_amqp:teardown(Connection, Channel). | ||||
| 
 | ||||
| % QOS is not yet implemented in RabbitMQ | ||||
| %% QOS is not yet implemented in RabbitMQ | ||||
| basic_qos_test(Connection) -> | ||||
|     lib_amqp:close_connection(Connection). | ||||
| 
 | ||||
| % Reject is not yet implemented in RabbitMQ | ||||
| %% Reject is not yet implemented in RabbitMQ | ||||
| basic_reject_test(Connection) -> | ||||
|     lib_amqp:close_connection(Connection). | ||||
| 
 | ||||
| %---------------------------------------------------------------------------- | ||||
| % Unit test for the direct client | ||||
| % This just relies on the fact that a fresh Rabbit VM must consume more than | ||||
| % 0.1 pc of the system memory: | ||||
| % 0. Wait 1 minute to let memsup do stuff | ||||
| % 1. Make sure that the high watermark is set high | ||||
| % 2. Start a process to receive the pause and resume commands from the broker | ||||
| % 3. Register this as flow control notification handler | ||||
| % 4. Let the system settle for a little bit | ||||
| % 5. Set the threshold to the lowest possible value | ||||
| % 6. When the flow handler receives the pause command, it sets the watermark | ||||
| %    to a high value in order to get the broker to send the resume command | ||||
| % 7. Allow 10 secs to receive the pause and resume, otherwise timeout and fail | ||||
| %%---------------------------------------------------------------------------- | ||||
| %% Unit test for the direct client | ||||
| %% This just relies on the fact that a fresh Rabbit VM must consume more than | ||||
| %% 0.1 pc of the system memory: | ||||
| %% 0. Wait 1 minute to let memsup do stuff | ||||
| %% 1. Make sure that the high watermark is set high | ||||
| %% 2. Start a process to receive the pause and resume commands from the broker | ||||
| %% 3. Register this as flow control notification handler | ||||
| %% 4. Let the system settle for a little bit | ||||
| %% 5. Set the threshold to the lowest possible value | ||||
| %% 6. When the flow handler receives the pause command, it sets the watermark | ||||
| %%    to a high value in order to get the broker to send the resume command | ||||
| %% 7. Allow 10 secs to receive the pause and resume, otherwise timeout and | ||||
| %%    fail | ||||
| channel_flow_test(Connection) -> | ||||
|     X = <<"amq.direct">>, | ||||
|     K = Payload = <<"x">>, | ||||
|  | @ -280,9 +284,9 @@ channel_flow_test(Connection) -> | |||
|         exit(did_not_receive_channel_flow) | ||||
|     end. | ||||
| 
 | ||||
| %---------------------------------------------------------------------------- | ||||
| % This is a test, albeit not a unit test, to see if the producer | ||||
| % handles the effect of being throttled. | ||||
| %%---------------------------------------------------------------------------- | ||||
| %% This is a test, albeit not a unit test, to see if the producer | ||||
| %% handles the effect of being throttled. | ||||
| 
 | ||||
| channel_flow_sync(Connection) -> | ||||
|     start_channel_flow(Connection, fun lib_amqp:publish/4). | ||||
|  | @ -358,9 +362,9 @@ cf_handler_loop(Producer) -> | |||
|         stop -> ok | ||||
|     end. | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| % This tests whether RPC over AMQP produces the same result as invoking the | ||||
| % same argument against the same underlying gen_server instance. | ||||
| %%--------------------------------------------------------------------------- | ||||
| %% This tests whether RPC over AMQP produces the same result as invoking the | ||||
| %% same argument against the same underlying gen_server instance. | ||||
| rpc_test(Connection) -> | ||||
|     Q = uuid(), | ||||
|     Fun = fun(X) -> X + 1 end, | ||||
|  | @ -376,7 +380,7 @@ rpc_test(Connection) -> | |||
|     amqp_rpc_server:stop(Server), | ||||
|     ok. | ||||
| 
 | ||||
| %--------------------------------------------------------------------------- | ||||
| %%--------------------------------------------------------------------------- | ||||
| 
 | ||||
| setup_publish(Channel) -> | ||||
|     Publish = #publish{routing_key = <<"a.b.c.d">>, | ||||
|  | @ -409,7 +413,9 @@ setup_exchange(Channel, Q, X, Binding) -> | |||
|     lib_amqp:bind_queue(Channel, X, Q, Binding), | ||||
|     ok. | ||||
| 
 | ||||
| latch_loop(0) -> ok; | ||||
| latch_loop(0) -> | ||||
|     ok; | ||||
| 
 | ||||
| latch_loop(Latch) -> | ||||
|     receive | ||||
|         finished -> | ||||
|  | @ -420,5 +426,5 @@ latch_loop(Latch) -> | |||
| 
 | ||||
| uuid() -> | ||||
|     {A, B, C} = now(), | ||||
|     <<A:32,B:32,C:32>>. | ||||
|     <<A:32, B:32, C:32>>. | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue