Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 203 lines (178 sloc) 7.333 kB
47e5a7c @klacke JSON ajax code from Gaspar Chilingarov, I added docs describing an ex…
authored
1 %% Copyright (C) 2003 Joakim Grebenö <jocke@gleipnir.com>.
2 %% All rights reserved.
3 %%
4 %% Copyright (C) 2006 Gaspar Chilingarov <nm@web.am>
5 %% Gurgen Tumanyan <barbarian@armkb.com>
6 %% All rights reserved.
7 %%
8 %%
9 %% Redistribution and use in source and binary forms, with or without
10 %% modification, are permitted provided that the following conditions
11 %% are met:
12 %%
13 %% 1. Redistributions of source code must retain the above copyright
14 %% notice, this list of conditions and the following disclaimer.
15 %% 2. Redistributions in binary form must reproduce the above
16 %% copyright notice, this list of conditions and the following
17 %% disclaimer in the documentation and/or other materials provided
18 %% with the distribution.
19 %%
20 %% THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
21 %% OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 %% WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 %% ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
24 %% DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 %% DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
26 %% GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 %% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 %% WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 %% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 %% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 -module(yaws_xmlrpc).
33 %-author('jocke@gleipnir.com').
34 -author("Gaspar Chilingarov <nm@web.am>, Gurgen Tumanyan <barbarian@armkb.com>").
35 -export([handler/2]).
36 -export([handler_session/2, handler_session/3]).
37
38 %-define(debug, 1).
39 %-include("../../yaws/src/yaws_debug.hrl").
40
41 -include_lib("yaws/include/yaws_api.hrl").
42
43
44 %%% ######################################################################
45 %%% public interface
46 %%%
47
48 %%%
49 %%% use XMLRPC handler which can automagically start sessions if we need
50 %%%
51 handler_session(Args, Handler) ->
52 handler_session(Args, Handler, 'SID').
53
54 %%%
55 %%% allow overriding session Cookie name
56 %%%
57 handler_session(Args, Handler, SID_NAME) when is_atom(SID_NAME) ->
58 handler_session(Args, Handler, atom_to_list(SID_NAME));
59
60 handler_session(Args, Handler, SID_NAME) ->
61 handler(Args, Handler, {session, SID_NAME}).
62
63 %%%
64 %%% xmlrpc:handler compatible call
65 %%% no session support will be available
66 handler(Args, Handler) ->
67 handler(Args, Handler, simple).
68
69
70 %%% ######################################################################
71 %%% private functions
72 %%%
73
74 %%% we should be called from yaws page or module
75 handler(Args, Handler, Type) when is_record(Args, arg) -> % {{{
76 case parse_request(Args) of
77 ok ->
78 handle_payload(Args, Handler, Type);
79 {status, StatusCode} -> % cannot parse request
80 send(Args, StatusCode)
81 end. % }}}
82
83 -define(ERROR_LOG(Reason),
84 error_logger:error_report({?MODULE, ?LINE, Reason})).
85
86 %%%
87 %%% check that request come in reasonable protocol version and reasonable method
88 %%%
89 parse_request(Args) -> % {{{
90 case {(Args#arg.req)#http_request.method, (Args#arg.req)#http_request.version} of
91 {'POST', {1,0}} ->
92 % ?Debug("HTTP Version 1.0~n", []),
93 ok;
94 {'POST', {1,1}} ->
95 % ?Debug("HTTP Version 1.1~n", []),
96 ok;
97 {'POST', _HTTPVersion} -> {status, 505};
98 {_Method, {1,1}} -> {status, 501};
99 _ -> {status, 400}
100 end. % }}}
101
102 handle_payload(Args, Handler, Type) -> % {{{
103 Payload = binary_to_list(Args#arg.clidata),
104 % ?Debug("xmlrpc encoded call ~p ~n", [Payload]),
105 case xmlrpc_decode:payload(Payload) of
106 {ok, DecodedPayload} ->
107 % ?Debug("xmlrpc decoded call ~p ~n", [DecodedPayload]),
108 eval_payload(Args, Handler, DecodedPayload, Type);
109 {error, Reason} ->
110 ?ERROR_LOG({xmlrpc_decode, payload, Payload, Reason}),
111 send(Args, 400)
112 end. % }}}
113
114 %%%
115 %%% call handler/3 and provide session support
116 eval_payload(Args, {M, F}, Payload, {session, CookieName}) -> % {{{
117 {SessionValue, Cookie} = case yaws_api:find_cookie_val(CookieName, (Args#arg.headers)#headers.cookie) of
118 [] -> % have no session started, just call handler
119 {undefined, undefined};
120 Cookie2 -> % get old session data
121 case yaws_api:cookieval_to_opaque(Cookie2) of
122 {ok, OP} ->
123 yaws_api:cookieval_to_opaque(Cookie2),
124 {OP, Cookie2};
125 {error, _ErrMsg} -> % cannot get corresponding session
126 {undefined, undefined}
127 end
128 end,
129
130 case catch M:F(Args#arg.state, Payload, SessionValue) of
131 {'EXIT', Reason} ->
132 ?ERROR_LOG({M, F, {'EXIT', Reason}}),
133 send(Args, 500);
134 {error, Reason} ->
135 ?ERROR_LOG({M, F, Reason}),
136 send(Args, 500);
137 {false, ResponsePayload} ->
138 % do not have updates in session data
139 encode_send(Args, 200, ResponsePayload, []);
140 {true, _NewTimeout, NewSessionValue, ResponsePayload} -> % be compatible with xmlrpc module
141 CO = case NewSessionValue of
142 undefined when Cookie == undefined -> []; % nothing to do
143 undefined -> % rpc handler requested session delete
144 yaws_api:delete_cookie_session(Cookie), []; % XXX: may be return set-cookie with empty val?
145 _ -> % any other value will stored in session
146 case SessionValue of
147 undefined -> % got session data and should start new session now
148 Cookie1 = yaws_api:new_cookie_session(NewSessionValue),
149 yaws_api:setcookie(CookieName, Cookie1, "/"); % return set_cookie header
150 _ ->
151 yaws_api:replace_cookie_session(Cookie, NewSessionValue),
152 [] % nothing to add to yaws data
153 end
154 end,
155 encode_send(Args, 200, ResponsePayload, CO)
156 end; % }}}
157
158 %%%
159 %%% call handler/2 without session support
160 %%%
161 eval_payload(Args, {M, F}, Payload, simple) -> % {{{
162 case catch M:F(Args#arg.state, Payload) of
163 {'EXIT', Reason} ->
164 ?ERROR_LOG({M, F, {'EXIT', Reason}}),
165 send(Args, 500);
166 {error, Reason} ->
167 ?ERROR_LOG({M, F, Reason}),
168 send(Args, 500);
169 {false, ResponsePayload} ->
170 encode_send(Args, 200, ResponsePayload, []);
171 {true, _NewTimeout, _NewState, ResponsePayload} ->
172 encode_send(Args, 200, ResponsePayload, [])
173 end. % }}}
174
175
176 encode_send(Args, StatusCode, Payload, AddOn) -> % {{{
177 % ?Debug("xmlrpc decoded response ~p ~n", [Payload]),
178 case xmlrpc_encode:payload(Payload) of
179 {ok, EncodedPayload} ->
180 % ?Debug("xmlrpc encoded response ~p ~n", [EncodedPayload]),
181 send(Args, StatusCode, EncodedPayload, AddOn);
182 {error, Reason} ->
183 ?ERROR_LOG({xmlrpc_encode, payload, Payload, Reason}),
184 send(Args, 500)
185 end. % }}}
186
187 send(Args, StatusCode) -> send(Args, StatusCode, "", []). % {{{
188
189 send(Args, StatusCode, Payload, AddOnData) when not is_list(AddOnData) ->
190 send(Args, StatusCode, Payload, [AddOnData]);
191
192 %%%
193 %%% generate valid yaws response
194 send(_Args, StatusCode, Payload, AddOnData) ->
195 A = [
196 {status, StatusCode},
197 {content, "text/xml", Payload},
198 {header, {content_length, lists:flatlength(Payload) }}
199 ] ++ AddOnData,
200 A. % }}}
201
202 % vim: tabstop=4 ft=erlang
Something went wrong with that request. Please try again.