%%-------------------------------------------------------------------- %% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1999-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% %% %%-------------------------------------------------------------------- %% File: orber_iiop_inrequest.erl %% %% Description: %% This file contains the handling of incomming requests %% %%----------------------------------------------------------------- -module(orber_iiop_inrequest). -include_lib("orber/src/orber_iiop.hrl"). -include_lib("orber/include/corba.hrl"). -include_lib("orber/include/orber_pi.hrl"). %%----------------------------------------------------------------- %% External exports %%----------------------------------------------------------------- -export([start/5, start_fragment_collector/8]). %%----------------------------------------------------------------- %% Internal exports %%----------------------------------------------------------------- -export([handle_message/5, fragment_collector/8]). %%----------------------------------------------------------------- %% Macros %%----------------------------------------------------------------- -define(DEBUG_LEVEL, 8). %%----------------------------------------------------------------- %% External interface functions %%----------------------------------------------------------------- start(GIOPHdr, Message, Type, Socket, Env) -> spawn_link(orber_iiop_inrequest, handle_message, [GIOPHdr, Message, Type, Socket, Env]). start_fragment_collector(GIOPHdr, Message, Type, Socket, ReqId, Proxy, MaxFrags, Env) -> spawn_link(orber_iiop_inrequest, fragment_collector, [GIOPHdr, Message, Type, Socket, ReqId, Proxy, MaxFrags, Env]). %%----------------------------------------------------------------- %% Internal functions %%----------------------------------------------------------------- %%----------------------------------------------------------------- %% Func: fragment_collector/4 %%----------------------------------------------------------------- fragment_collector(GIOPHdr, Bytes, SocketType, Socket, ReqId, Proxy, MaxFrags, Env) -> case catch collect(Proxy, [], GIOPHdr#giop_message.byte_order, ReqId, MaxFrags, 0) of {ok, Buffer} -> NewGIOP = GIOPHdr#giop_message {message = list_to_binary([GIOPHdr#giop_message.message|Buffer])}, %% NOTE, the third argument to dec_message_header must be complete %% message (i.e. AllBytes), otherwise we cannot handle indirection. case handle_message(NewGIOP, list_to_binary([Bytes| Buffer]), SocketType, Socket, Env) of message_error -> Proxy ! {message_error, self(), ReqId}, ok; _ -> ok end; ok -> ok; {'EXCEPTION', E} -> Proxy ! {message_error, self(), ReqId}, Reply = marshal_exception(Env, ReqId, E, enc_reply), orber_socket:write(SocketType, Socket, Reply) end. collect(_Proxy, _Buffer, _ByteOrder, _ReqId, MaxFrags, MaxFrags) -> orber:dbg("[~p] ~p:collect(~p)~nMax fragments limit reached.", [?LINE, ?MODULE, MaxFrags], ?DEBUG_LEVEL), {'EXCEPTION', #'IMP_LIMIT'{completion_status=?COMPLETED_NO}}; collect(Proxy, Buffer, ByteOrder, ReqId, MaxFrags, FragCounter) -> receive {Proxy, #giop_message{byte_order = ByteOrder, message = Message, fragments = true} = GIOPHdr} -> {_, #fragment_header{request_id=ReqId}, FragBody, _, _} = cdr_decode:dec_message_header(null, GIOPHdr, Message), collect(Proxy, [FragBody | Buffer], ByteOrder, ReqId, MaxFrags, FragCounter+1); {Proxy, #giop_message{byte_order = ByteOrder, message = Message, fragments = false} = GIOPHdr} -> {_, #fragment_header{request_id=ReqId}, FragBody, _, _} = cdr_decode:dec_message_header(null, GIOPHdr, Message), {ok, lists:reverse([FragBody | Buffer])}; {Proxy, GIOPHdr, _Data, _} -> orber:dbg("[~p] orber_iiop_inrequest:collect(~p, ~p)~n" "Incorrect Fragment. Might be different byteorder.", [?LINE, ByteOrder, GIOPHdr], ?DEBUG_LEVEL), {'EXCEPTION', #'MARSHAL'{completion_status=?COMPLETED_NO}}; {Proxy, cancel_request_header} -> ok; Other -> orber:dbg("[~p] ~p:collect(~p)~n" "Unable to collect all fragments: ~p", [?LINE, ?MODULE, Buffer, Other], ?DEBUG_LEVEL), {'EXCEPTION', #'MARSHAL'{completion_status=?COMPLETED_NO}} end. %%----------------------------------------------------------------- %% Func: handle_message/4 %%----------------------------------------------------------------- handle_message(GIOPHdr, Message, SocketType, Socket, Env) -> %% Warning. We shouldn't set the flags like this here. But, for now, we'll %% do it due to performance reasons. put(oe_orber_flags, Env#giop_env.flags), case catch cdr_decode:dec_message_header(null, GIOPHdr, Message) of Hdr when is_record(Hdr, cancel_request_header) -> %% We just skips this message for the moment, the standard require that %% the client handles the reply anyway. message_error; {location_forward, Object, ReqId, Version, OldObj} -> Reply = call_interceptors_out(Env#giop_env{version = Version}, ReqId, [Object], OldObj, 'location_forward', "location_forward", {{'tk_objref', "", ""}, [],[]}), orber_socket:write(SocketType, Socket, Reply); {object_forward, Object, ReqId, Version, _OldObj} -> Reply = handle_locate_request(Env#giop_env{version = Version}, {object_forward, Object, ReqId}), orber_socket:write(SocketType, Socket, Reply); {Version, Hdr} when is_record(Hdr, locate_request_header) -> Reply = handle_locate_request(Env#giop_env{version = Version}, Hdr), orber_socket:write(SocketType, Socket, Reply); {Version, ReqHdr, Rest, Len, ByteOrder} when is_record(ReqHdr, request_header) -> handle_request(Env#giop_env{version = Version}, ReqHdr, Rest, Len, ByteOrder, SocketType, Socket, Message); Other -> %% This cluase takes care of all erranous messages. orber:dbg("[~p] orber_iiop_inrequest:handle_message(~p)~n" "Decoding Msg Header failed: ~p", [?LINE, Message, Other], ?DEBUG_LEVEL), Reply = cdr_encode:enc_message_error(Env), orber_socket:write(SocketType, Socket, Reply), message_error end. send_reply(oneway, _SocketType, _Socket) -> ok; send_reply(Reply, SocketType, Socket) -> orber_socket:write(SocketType, Socket, Reply). %%----------------------------------------------------------------- %% Func: handle_request %%----------------------------------------------------------------- handle_request(#giop_env{interceptors = false} = Env, ReqHdr, Rest, Len, ByteOrder, SocketType, Socket, Message) -> NewEnv = check_context(ReqHdr#request_header.service_context, [], Env), case decode_body(NewEnv, ReqHdr, Rest, Len, ByteOrder, Message, enc_reply) of {error, E} -> orber_socket:write(SocketType, Socket, E); {NewEnv2, Hdr, Par, TypeCodes} -> Result = invoke_request(Hdr, Par, SocketType, TypeCodes, Env), Reply = evaluate(NewEnv2, Hdr, Result, TypeCodes, enc_reply, 'no_exception'), send_reply(Reply, SocketType, Socket) end; handle_request(Env, ReqHdr, Rest, Len, ByteOrder, SocketType, Socket, Message) -> NewEnv = check_context(ReqHdr#request_header.service_context, [], Env), case catch call_interceptors(SocketType, NewEnv, ReqHdr, Rest, Len, ByteOrder, Message) of {error, E} -> %% Failed to decode body. orber_socket:write(SocketType, Socket, E); {'EXCEPTION', Exc} -> orber:dbg("[~p] orber_iiop_inrequest:handle_message(~p)~n" "Invoking the interceptors resulted in: ~p", [?LINE, Message, Exc], ?DEBUG_LEVEL), Reply = marshal_exception(NewEnv, ReqHdr#request_header.request_id, Exc, enc_reply), orber_socket:write(SocketType, Socket, Reply); {'EXIT', R} -> orber:dbg("[~p] orber_iiop_inrequest:handle_message(~p)~n" "Invoking the interceptors resulted in: ~p", [?LINE, ReqHdr, R], ?DEBUG_LEVEL), Reply = marshal_exception(NewEnv, ReqHdr#request_header.request_id, #'MARSHAL'{completion_status=?COMPLETED_MAYBE}, enc_reply), orber_socket:write(SocketType, Socket, Reply); Reply -> send_reply(Reply, SocketType, Socket) end. check_context([], [], Env) -> Env; check_context([], Acc, Env) -> Env#giop_env{ctx = Acc}; check_context([#'CSI_SASContextBody' {label = ?CSI_MsgType_MTEstablishContext, value = #'CSI_EstablishContext' {client_context_id = _Id, authorization_token = _AuthToken, identity_token = _IdToken, client_authentication_token = _CAuthToken}}|Rest], Acc, Env) -> check_context(Rest, [#'IOP_ServiceContext' {context_id=?IOP_SecurityAttributeService, context_data = #'CSI_SASContextBody' {label = ?CSI_MsgType_MTCompleteEstablishContext, value = #'CSI_CompleteEstablishContext' {client_context_id = 0, context_stateful = false, final_context_token = [0,255]}}}|Acc], Env); check_context([_|Rest], Acc, Env) -> check_context(Rest, Acc, Env). %%----------------------------------------------------------------- %% Func: call_interceptors %%----------------------------------------------------------------- -dialyzer({no_improper_lists, call_interceptors/7}). call_interceptors(SocketType, #giop_env{interceptors = {native, Ref, PIs}, ctx = Ctx} = Env, ReqHdr, Rest, Len, ByteOrder, Msg) -> NewRest = orber_pi:in_request_enc(PIs, ReqHdr, Ref, Rest), case decode_body(Env, ReqHdr, NewRest, Len, ByteOrder, Msg, enc_reply) of {NewEnv, Hdr, Par, TypeCodes} -> NewPar = orber_pi:in_request(PIs, ReqHdr, Ref, Par), ResultInv = invoke_request(Hdr, NewPar, SocketType, TypeCodes, NewEnv), Result = orber_pi:out_reply(PIs, ReqHdr, Ref, ResultInv, Ctx), case evaluate(NewEnv, ReqHdr, Result, TypeCodes, enc_reply_split, 'no_exception') of {ReplyHdr, Reply, HdrL, _BodyL, Flags} -> NewReply = orber_pi:out_reply_enc(PIs, ReqHdr, Ref, Reply, Ctx), MessSize = HdrL+size(NewReply), cdr_encode:enc_giop_message_header(NewEnv, 'reply', Flags, MessSize, [ReplyHdr|NewReply]); Other -> Other end; Other -> Other end; call_interceptors(SocketType, #giop_env{interceptors = {portable, _PIs}} = Env, ReqHdr, Rest, Len, ByteOrder, Msg) -> case decode_body(Env, ReqHdr, Rest, Len, ByteOrder, Msg, enc_reply) of {NewEnv, Hdr, Par, TypeCodes} -> Result = invoke_request(Hdr, Par, SocketType, TypeCodes, NewEnv), evaluate(NewEnv, ReqHdr, Result, TypeCodes, enc_reply, 'no_exception'); Other -> Other end. %%----------------------------------------------------------------- %% Func: call_interceptors_out %%----------------------------------------------------------------- -dialyzer({no_improper_lists, call_interceptors_out/7}). call_interceptors_out(#giop_env{interceptors = {native, Ref, PIs}, ctx = Ctx} = Env, ReqId, Result, Obj, Type, Operation, TypeCodes) -> ReqHdr = #request_header{object_key = Obj, service_context = Ctx, response_expected = true, request_id = ReqId, operation = Operation}, NewResult = (catch orber_pi:out_reply(PIs, ReqHdr, Ref, Result, Ctx)), {ReplyHdr, Reply, HdrL, _BodyL, Flags} = evaluate(Env, ReqHdr, NewResult, TypeCodes, enc_reply_split, Type), NewReply = case catch orber_pi:out_reply_enc(PIs, ReqHdr, Ref, Reply, Ctx) of {'EXCEPTION', Exception} -> %% Since evaluate don't need TypeCodes or Status no need to supply %% them. evaluate(Env, ReqHdr, {'EXCEPTION', Exception}, undefined, enc_reply_split, undefined); {'EXIT', E} -> orber:dbg("[~p] orber_iiop_inrequest:handle_location_forward(~p)~n" "Resulted in exit: ~p", [?LINE, PIs, E], ?DEBUG_LEVEL), marshal_exception(Env, ReqId, #'MARSHAL'{completion_status=?COMPLETED_NO}, enc_reply); R -> R end, MessSize = HdrL+size(NewReply), cdr_encode:enc_giop_message_header(Env, 'reply', Flags, MessSize, [ReplyHdr|NewReply]); call_interceptors_out(#giop_env{interceptors = {portable, _PIs}} = Env, ReqId, Result, _Obj, Type, _, TypeCodes) -> Hdr = #request_header{response_expected = true, request_id = ReqId}, evaluate(Env, Hdr, Result, TypeCodes, enc_reply, Type); call_interceptors_out(Env, ReqId, Result, _Obj, Type, _, TypeCodes) -> Hdr = #request_header{response_expected = true, request_id = ReqId}, evaluate(Env, Hdr, Result, TypeCodes, enc_reply, Type). %%----------------------------------------------------------------- %% Func: decode_body/2 %%----------------------------------------------------------------- decode_body(#giop_env{version = Version} = Env, ReqHdr, Rest, Len, ByteOrder, Message, Func) -> case catch cdr_decode:dec_request_body(Version, ReqHdr, Rest, Len, ByteOrder, Message) of {NewVersion, ReqHdr, Par, TypeCodes} -> {Env#giop_env{version = NewVersion}, ReqHdr, Par, TypeCodes}; {'EXCEPTION', E} -> orber:dbg("[~p] orber_iiop_inrequest:decode_body(~p, ~p)~n" "Failed decoding request body: ~p", [?LINE, ReqHdr, Message, E], ?DEBUG_LEVEL), {error, marshal_exception(Env, ReqHdr#request_header.request_id, E, Func)}; Other -> %% This cluase takes care of all erranous messages. orber:dbg("[~p] orber_iiop_inrequest:decode_body(~p, ~p)~n" "Failed decoding request body: ~p", [?LINE, ReqHdr, Message, Other], ?DEBUG_LEVEL), {error, marshal_exception(Env, ReqHdr#request_header.request_id, #'MARSHAL'{completion_status=?COMPLETED_NO}, Func)} end. %%----------------------------------------------------------------- %% Func: handle_locate_request/2 %%----------------------------------------------------------------- handle_locate_request(Env, {object_forward, Object, ReqId}) -> case catch cdr_encode:enc_locate_reply( Env#giop_env{request_id = ReqId, tc = {'tk_objref', "", ""}, result = Object, reply_status = 'object_forward'}) of {'EXCEPTION', Exception} -> orber:dbg("[~p] orber_iiop_inrequest:handle_locate_request(object_forward)~n" "Raised the exception: ~p", [?LINE, Exception], ?DEBUG_LEVEL), marshal_locate_exception(Env, ReqId, Exception); {'EXIT', E} -> orber:dbg("[~p] orber_iiop_inrequest:handle_locate_request(object_forward)~n" "Resulted in exit: ~p", [?LINE, E], ?DEBUG_LEVEL), marshal_locate_exception(Env, ReqId, #'MARSHAL'{completion_status=?COMPLETED_NO}); R -> R end; handle_locate_request(Env, Hdr) -> Location = orber_objectkeys:check(Hdr#locate_request_header.object_key), case catch cdr_encode:enc_locate_reply( Env#giop_env{request_id = Hdr#locate_request_header.request_id, reply_status = Location}) of {'EXCEPTION', Exception} -> orber:dbg("[~p] orber_iiop_inrequest:handle_locate_request(~p)~n" "Raised the exception: ~p", [?LINE, Location, Exception], ?DEBUG_LEVEL), marshal_locate_exception(Env, Hdr#locate_request_header.request_id, Exception); {'EXIT', E} -> orber:dbg("[~p] orber_iiop_inrequest:handle_locate_request(~p)~n" "Resulted in exit: ~p", [?LINE, Location, E], ?DEBUG_LEVEL), marshal_locate_exception(Env, Hdr#locate_request_header.request_id, #'MARSHAL'{completion_status=?COMPLETED_NO}); R -> R end. %%----------------------------------------------------------------- %% Func: invoke_request/2 %%----------------------------------------------------------------- invoke_request(Hdr, Par, normal, TypeCodes, #giop_env{iiop_ssl_port = SSLPort, partial_security = PartialSec}) -> Result = case SSLPort of -1 -> corba:request_from_iiop(Hdr#request_header.object_key, Hdr#request_header.operation, Par, [], Hdr#request_header.response_expected, Hdr#request_header.service_context); _ -> case Hdr#request_header.object_key of {_,registered,orber_init,_,_,_} -> corba:request_from_iiop(Hdr#request_header.object_key, Hdr#request_header.operation, Par, [], Hdr#request_header.response_expected, Hdr#request_header.service_context); {_,_,_,_,_,Flags} when PartialSec == true, ?ORB_FLAG_TEST(Flags, ?ORB_NO_SECURITY) == true -> corba:request_from_iiop(Hdr#request_header.object_key, Hdr#request_header.operation, Par, [], Hdr#request_header.response_expected, Hdr#request_header.service_context); _ -> orber:dbg("[~p] orber_iiop_inrequest:invoke_request(~p)~n" "SSL do not permit", [?LINE, Hdr#request_header.object_key], ?DEBUG_LEVEL), {'EXCEPTION', #'NO_PERMISSION'{completion_status=?COMPLETED_NO}} end end, result_to_list(Result, TypeCodes); invoke_request(Hdr, Par, ssl, TypeCodes, _) -> Result = corba:request_from_iiop(Hdr#request_header.object_key, Hdr#request_header.operation, Par, [], Hdr#request_header.response_expected, Hdr#request_header.service_context), result_to_list(Result, TypeCodes). %%----------------------------------------------------------------- %% Func: evaluate/4 %%----------------------------------------------------------------- evaluate(_, Hdr,_,_,_,_) when Hdr#request_header.response_expected == 'false' -> oneway; evaluate(Env, Hdr, _, _, Func, _) when Hdr#request_header.response_expected == 'true_oneway' -> %% Special case which only occurs when using IIOP-1.2 cdr_encode:Func(Env#giop_env{request_id = Hdr#request_header.request_id, reply_status = 'no_exception', tc = {tk_null,[],[]}, result = null}); evaluate(Env, Hdr, {'EXCEPTION', Exc}, _, Func, _) -> %% The exception can be user defined. Hence, we must check the result. case catch marshal_exception(Env, Hdr#request_header.request_id, Exc, Func) of {'EXCEPTION', Exception} -> orber:dbg("[~p] orber_iiop_inrequest:evaluate(~p)~n" "Encoding (reply) exception: ~p", [?LINE, Hdr, Exception], ?DEBUG_LEVEL), marshal_exception(Env, Hdr#request_header.request_id, Exception, Func); {'EXIT', E} -> orber:dbg("[~p] orber_iiop_inrequest:evaluate(~p)~n" "Encode (reply) resulted in: ~p", [?LINE, Hdr, E], ?DEBUG_LEVEL), marshal_exception(Env, Hdr#request_header.request_id, #'MARSHAL'{completion_status=?COMPLETED_YES}, Func); R -> R end; evaluate(#giop_env{version = {1,2}} = Env, Hdr, {'location_forward_perm', NewIOR}, _, Func, _)-> case catch cdr_encode:Func(#giop_env{version = {1,2}, request_id = Hdr#request_header.request_id, reply_status = 'location_forward_perm', tc = {{'tk_objref', "", ""}, [],[]}, result = NewIOR}) of {'EXCEPTION', Exception} -> orber:dbg("[~p] orber_iiop_inrequest:evaluate(~p) " ++ "Encoding (reply) exception: ~p", [?LINE, Hdr, Exception], ?DEBUG_LEVEL), marshal_exception(Env, Hdr#request_header.request_id, Exception, Func); {'EXIT', E} -> orber:dbg("[~p] orber_iiop_inrequest:evaluate(~p) " ++ "Encode (reply) resulted in: ~p", [?LINE, Hdr, E], ?DEBUG_LEVEL), marshal_exception(Env, Hdr#request_header.request_id, #'MARSHAL'{completion_status=?COMPLETED_YES}, Func); R -> R end; evaluate(Env, Hdr, [Res |OutPar], TypeCodes, Func, Type) -> case catch cdr_encode:Func(Env#giop_env{request_id = Hdr#request_header.request_id, reply_status = Type, tc = TypeCodes, result = Res, parameters = OutPar}) of {'EXCEPTION', Exception} -> orber:dbg("[~p] orber_iiop_inrequest:evaluate(~p, ~p, ~p)~n" "Encode exception: ~p", [?LINE, Hdr, Res, OutPar, Exception], ?DEBUG_LEVEL), marshal_exception(Env, Hdr#request_header.request_id, Exception, Func); {'EXIT', E} -> orber:dbg("[~p] orber_iiop_inrequest:evaluate(~p, ~p, ~p)~n" "Encode exit: ~p", [?LINE, Hdr, Res, OutPar, E], ?DEBUG_LEVEL), marshal_exception(Env, Hdr#request_header.request_id, #'MARSHAL'{completion_status=?COMPLETED_YES}, Func); R -> R end; evaluate(Env, Hdr, What, TypeCodes, Func, _) -> orber:dbg("[~p] orber_iiop_inrequest:evaluate(~p)~n" "Bad reply: ~p~n" "Should be: ~p~n" "GIOP Env : ~p", [?LINE, Hdr, What, TypeCodes, Env], ?DEBUG_LEVEL), marshal_exception(Env, Hdr#request_header.request_id, #'INTERNAL'{completion_status=?COMPLETED_MAYBE}, Func). %%----------------------------------------------------------------- %% Utility Functions %%----------------------------------------------------------------- result_to_list({'oe_location_forward_perm', NewIOR}, _) -> {'location_forward_perm', NewIOR}; result_to_list({'EXCEPTION', E}, _) -> {'EXCEPTION', E}; result_to_list(Result, {_TkRes, _, []}) -> [Result]; result_to_list(Result, {_TkRes, _, _TkOut}) -> tuple_to_list(Result). marshal_exception(Env, Id, Exception, Func) -> {TypeOfException, ExceptionTypeCode, NewExc} = orber_exceptions:get_def(Exception), cdr_encode:Func(Env#giop_env{request_id = Id, reply_status = TypeOfException, tc = {ExceptionTypeCode, [], []}, result = NewExc}). marshal_locate_exception(#giop_env{version = {1,2}} = Env, Id, Exception) -> case orber_exceptions:get_def(Exception) of {?SYSTEM_EXCEPTION, ExceptionTypeCode, NewExc} -> cdr_encode:enc_locate_reply( Env#giop_env{request_id = Id, reply_status = 'loc_system_exception', tc = ExceptionTypeCode, result = NewExc}); _ -> %% This case is impossible (i.e. Orber only throws system %% exceptions). But to be on the safe side... marshal_locate_exception(Env, Id, #'MARSHAL' {completion_status=?COMPLETED_YES}) end; marshal_locate_exception(Env, _Id, _Exception) -> %% There is no way to define an exception for IIOP-1.0/1.1 in a %% locate_reply. cdr_encode:enc_message_error(Env).