Subversion Repositories SmartDukaan

Rev

Go to most recent revision | Details | 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_http_transport).
21
 
22
-behaviour(gen_server).
23
-behaviour(thrift_transport).
24
 
25
%% API
26
-export([new/2, new/3]).
27
 
28
%% gen_server callbacks
29
-export([init/1,
30
         handle_call/3,
31
         handle_cast/2,
32
         handle_info/2,
33
         terminate/2,
34
         code_change/3]).
35
 
36
%% thrift_transport callbacks
37
-export([write/2, read/2, flush/1, close/1]).
38
 
39
-record(http_transport, {host, % string()
40
                         path, % string()
41
                         read_buffer, % iolist()
42
                         write_buffer, % iolist()
43
                         http_options, % see http(3)
44
                         extra_headers % [{str(), str()}, ...]
45
                        }).
46
 
47
%%====================================================================
48
%% API
49
%%====================================================================
50
%%--------------------------------------------------------------------
51
%% Function: new() -> {ok, Transport} | ignore | {error,Error}
52
%% Description: Starts the server
53
%%--------------------------------------------------------------------
54
new(Host, Path) ->
55
    new(Host, Path, _Options = []).
56
 
57
%%--------------------------------------------------------------------
58
%% Options include:
59
%%   {http_options, HttpOptions}  = See http(3)
60
%%   {extra_headers, ExtraHeaders}  = List of extra HTTP headers
61
%%--------------------------------------------------------------------
62
new(Host, Path, Options) ->
63
    case gen_server:start_link(?MODULE, {Host, Path, Options}, []) of
64
        {ok, Pid} ->
65
            thrift_transport:new(?MODULE, Pid);
66
        Else ->
67
            Else
68
    end.
69
 
70
%%--------------------------------------------------------------------
71
%% Function: write(Transport, Data) -> ok
72
%%
73
%% Data = iolist()
74
%%
75
%% Description: Writes data into the buffer
76
%%--------------------------------------------------------------------
77
write(Transport, Data) ->
78
    gen_server:call(Transport, {write, Data}).
79
 
80
%%--------------------------------------------------------------------
81
%% Function: flush(Transport) -> ok
82
%%
83
%% Description: Flushes the buffer, making a request
84
%%--------------------------------------------------------------------
85
flush(Transport) ->
86
    gen_server:call(Transport, flush).
87
 
88
%%--------------------------------------------------------------------
89
%% Function: close(Transport) -> ok
90
%%
91
%% Description: Closes the transport
92
%%--------------------------------------------------------------------
93
close(Transport) ->
94
    gen_server:cast(Transport, close).
95
 
96
%%--------------------------------------------------------------------
97
%% Function: Read(Transport, Len) -> {ok, Data}
98
%%
99
%% Data = binary()
100
%%
101
%% Description: Reads data through from the wrapped transoprt
102
%%--------------------------------------------------------------------
103
read(Transport, Len) when is_integer(Len) ->
104
    gen_server:call(Transport, {read, Len}).
105
 
106
%%====================================================================
107
%% gen_server callbacks
108
%%====================================================================
109
 
110
init({Host, Path, Options}) ->
111
    State1 = #http_transport{host = Host,
112
                             path = Path,
113
                             read_buffer = [],
114
                             write_buffer = [],
115
                             http_options = [],
116
                             extra_headers = []},
117
    ApplyOption =
118
        fun
119
            ({http_options, HttpOpts}, State = #http_transport{}) ->
120
                State#http_transport{http_options = HttpOpts};
121
            ({extra_headers, ExtraHeaders}, State = #http_transport{}) ->
122
                State#http_transport{extra_headers = ExtraHeaders};
123
            (Other, #http_transport{}) ->
124
                {invalid_option, Other};
125
            (_, Error) ->
126
                Error
127
        end,
128
    case lists:foldl(ApplyOption, State1, Options) of
129
        State2 = #http_transport{} ->
130
            {ok, State2};
131
        Else ->
132
            {stop, Else}
133
    end.
134
 
135
handle_call({write, Data}, _From, State = #http_transport{write_buffer = WBuf}) ->
136
    {reply, ok, State#http_transport{write_buffer = [WBuf, Data]}};
137
 
138
handle_call({read, Len}, _From, State = #http_transport{read_buffer = RBuf}) ->
139
    %% Pull off Give bytes, return them to the user, leave the rest in the buffer.
140
    Give = min(iolist_size(RBuf), Len),
141
    case iolist_to_binary(RBuf) of
142
        <<Data:Give/binary, RBuf1/binary>> ->
143
            Response = {ok, Data},
144
            State1 = State#http_transport{read_buffer=RBuf1},
145
            {reply, Response, State1};
146
        _ ->
147
            {reply, {error, 'EOF'}, State}
148
    end;
149
 
150
handle_call(flush, _From, State) ->
151
    {Response, State1} = do_flush(State),
152
    {reply, Response, State1}.
153
 
154
handle_cast(close, State) ->
155
    {_, State1} = do_flush(State),
156
    {stop, normal, State1};
157
 
158
handle_cast(_Msg, State=#http_transport{}) ->
159
    {noreply, State}.
160
 
161
handle_info(_Info, State) ->
162
    {noreply, State}.
163
 
164
terminate(_Reason, _State) ->
165
    ok.
166
 
167
code_change(_OldVsn, State, _Extra) ->
168
    {ok, State}.
169
 
170
%%--------------------------------------------------------------------
171
%%% Internal functions
172
%%--------------------------------------------------------------------
173
do_flush(State = #http_transport{host = Host,
174
                                 path = Path,
175
                                 read_buffer = Rbuf,
176
                                 write_buffer = Wbuf,
177
                                 http_options = HttpOptions,
178
                                 extra_headers = ExtraHeaders}) ->
179
    case iolist_to_binary(Wbuf) of
180
        <<>> ->
181
            %% Don't bother flushing empty buffers.
182
            {ok, State};
183
        WBinary ->
184
            {ok, {{_Version, 200, _ReasonPhrase}, _Headers, Body}} =
185
              http:request(post,
186
                           {"http://" ++ Host ++ Path,
187
                            [{"User-Agent", "Erlang/thrift_http_transport"} | ExtraHeaders],
188
                            "application/x-thrift",
189
                            WBinary},
190
                           HttpOptions,
191
                           [{body_format, binary}]),
192
 
193
            State1 = State#http_transport{read_buffer = [Rbuf, Body],
194
                                          write_buffer = []},
195
            {ok, State1}
196
    end.
197
 
198
min(A,B) when A<B -> A;
199
min(_,B)          -> B.