Subversion Repositories SmartDukaan

Rev

Rev 30 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
30 ashish 1
%%
2
%% Licensed to the Apache Software Foundation (ASF) under one
3
%% or more contributor license agreements. See the NOTICE file
4
%% distributed with this work for additional information
5
%% regarding copyright ownership. The ASF licenses this file
6
%% to you under the Apache License, Version 2.0 (the
7
%% "License"); you may not use this file except in compliance
8
%% with the License. You may obtain a copy of the License at
9
%%
10
%%   http://www.apache.org/licenses/LICENSE-2.0
11
%%
12
%% Unless required by applicable law or agreed to in writing,
13
%% software distributed under the License is distributed on an
14
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
%% KIND, either express or implied. See the License for the
16
%% specific language governing permissions and limitations
17
%% under the License.
18
%%
19
 
20
-module(thrift_framed_transport).
21
 
22
-behaviour(gen_server).
23
-behaviour(thrift_transport).
24
 
25
%% API
26
-export([new/1]).
27
 
28
%% gen_server callbacks
29
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
30
         terminate/2, code_change/3]).
31
 
32
%% thrift_transport callbacks
33
-export([write/2, read/2, flush/1, close/1]).
34
 
35
-record(framed_transport, {wrapped, % a thrift_transport
36
                           read_buffer, % iolist()
37
                           write_buffer % iolist()
38
                          }).
39
 
40
%%====================================================================
41
%% API
42
%%====================================================================
43
%%--------------------------------------------------------------------
44
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
45
%% Description: Starts the server
46
%%--------------------------------------------------------------------
47
new(WrappedTransport) ->
48
    case gen_server:start_link(?MODULE, [WrappedTransport], []) of
49
        {ok, Pid} ->
50
            thrift_transport:new(?MODULE, Pid);
51
        Else ->
52
            Else
53
    end.
54
 
55
%%--------------------------------------------------------------------
56
%% Function: write(Transport, Data) -> ok
57
%%
58
%% Data = iolist()
59
%%
60
%% Description: Writes data into the buffer
61
%%--------------------------------------------------------------------
62
write(Transport, Data) ->
63
    gen_server:call(Transport, {write, Data}).
64
 
65
%%--------------------------------------------------------------------
66
%% Function: flush(Transport) -> ok
67
%%
68
%% Description: Flushes the buffer through to the wrapped transport
69
%%--------------------------------------------------------------------
70
flush(Transport) ->
71
    gen_server:call(Transport, flush).
72
 
73
%%--------------------------------------------------------------------
74
%% Function: close(Transport) -> ok
75
%%
76
%% Description: Closes the transport and the wrapped transport
77
%%--------------------------------------------------------------------
78
close(Transport) ->
79
    gen_server:cast(Transport, close).
80
 
81
%%--------------------------------------------------------------------
82
%% Function: Read(Transport, Len) -> {ok, Data}
83
%%
84
%% Data = binary()
85
%%
86
%% Description: Reads data through from the wrapped transoprt
87
%%--------------------------------------------------------------------
88
read(Transport, Len) when is_integer(Len) ->
89
    gen_server:call(Transport, {read, Len}).
90
 
91
%%====================================================================
92
%% gen_server callbacks
93
%%====================================================================
94
 
95
%%--------------------------------------------------------------------
96
%% Function: init(Args) -> {ok, State} |
97
%%                         {ok, State, Timeout} |
98
%%                         ignore               |
99
%%                         {stop, Reason}
100
%% Description: Initiates the server
101
%%--------------------------------------------------------------------
102
init([Wrapped]) ->
103
    {ok, #framed_transport{wrapped = Wrapped,
104
                           read_buffer = [],
105
                           write_buffer = []}}.
106
 
107
%%--------------------------------------------------------------------
108
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
109
%%                                      {reply, Reply, State, Timeout} |
110
%%                                      {noreply, State} |
111
%%                                      {noreply, State, Timeout} |
112
%%                                      {stop, Reason, Reply, State} |
113
%%                                      {stop, Reason, State}
114
%% Description: Handling call messages
115
%%--------------------------------------------------------------------
116
handle_call({write, Data}, _From, State = #framed_transport{write_buffer = WBuf}) ->
117
    {reply, ok, State#framed_transport{write_buffer = [WBuf, Data]}};
118
 
119
handle_call({read, Len}, _From, State = #framed_transport{wrapped = Wrapped,
120
                                                          read_buffer = RBuf}) ->
121
    {RBuf1, RBuf1Size} =
122
        %% if the read buffer is empty, read another frame
123
        %% otherwise, just read from what's left in the buffer
124
        case iolist_size(RBuf) of
125
 
126
                %% read the frame length
127
                {ok, <<FrameLen:32/integer-signed-big, _/binary>>} =
128
                    thrift_transport:read(Wrapped, 4),
129
                %% then read the data
130
                {ok, Bin} =
131
                    thrift_transport:read(Wrapped, FrameLen),
132
                {Bin, erlang:byte_size(Bin)};
133
            Sz ->
134
                {RBuf, Sz}
135
        end,
136
 
137
    %% pull off Give bytes, return them to the user, leave the rest in the buffer
138
    Give = min(RBuf1Size, Len),
139
    <<Data:Give/binary, RBuf2/binary>> = iolist_to_binary(RBuf1),
140
 
141
    Response = {ok, Data},
142
    State1 = State#framed_transport{read_buffer=RBuf2},
143
 
144
    {reply, Response, State1};
145
 
146
handle_call(flush, _From, State) ->
147
    {Response, State1} = do_flush(State),
148
    {reply, Response, State1}.
149
 
150
%%--------------------------------------------------------------------
151
%% Function: handle_cast(Msg, State) -> {noreply, State} |
152
%%                                      {noreply, State, Timeout} |
153
%%                                      {stop, Reason, State}
154
%% Description: Handling cast messages
155
%%--------------------------------------------------------------------
156
handle_cast(close, State) ->
157
    {_, State1} = do_flush(State),
158
    %% Wrapped is closed by terminate/2
159
    %%  error_logger:info_msg("thrift_framed_transport ~p: closing", [self()]),
160
    {stop, normal, State};
161
handle_cast(Msg, State=#framed_transport{}) ->
162
    {noreply, State}.
163
 
164
%%--------------------------------------------------------------------
165
%% Function: handle_info(Info, State) -> {noreply, State} |
166
%%                                       {noreply, State, Timeout} |
167
%%                                       {stop, Reason, State}
168
%% Description: Handling all non call/cast messages
169
%%--------------------------------------------------------------------
170
handle_info(_Info, State) ->
171
    {noreply, State}.
172
 
173
%%--------------------------------------------------------------------
174
%% Function: terminate(Reason, State) -> void()
175
%% Description: This function is called by a gen_server when it is about to
176
%% terminate. It should be the opposite of Module:init/1 and do any necessary
177
%% cleaning up. When it returns, the gen_server terminates with Reason.
178
%% The return value is ignored.
179
%%--------------------------------------------------------------------
180
terminate(_Reason, State = #framed_transport{wrapped=Wrapped}) ->
181
    thrift_transport:close(Wrapped),
182
    ok.
183
 
184
%%--------------------------------------------------------------------
185
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
186
%% Description: Convert process state when code is changed
187
%%--------------------------------------------------------------------
188
code_change(_OldVsn, State, _Extra) ->
189
    {ok, State}.
190
 
191
%%--------------------------------------------------------------------
192
%%% Internal functions
193
%%--------------------------------------------------------------------
194
do_flush(State = #framed_transport{write_buffer = Buffer,
195
                                   wrapped = Wrapped}) ->
196
    FrameLen = iolist_size(Buffer),
197
    Data     = [<<FrameLen:32/integer-signed-big>>, Buffer],
198
 
199
    Response = thrift_transport:write(Wrapped, Data),
200
 
201
    thrift_transport:flush(Wrapped),
202
 
203
    State1 = State#framed_transport{write_buffer = []},
204
    {Response, State1}.
205
 
206
min(A,B) when A<B -> A;
207
min(_,B)          -> B.
208