diff options
Diffstat (limited to 'lib/orber/src/orber_iiop.erl')
-rw-r--r-- | lib/orber/src/orber_iiop.erl | 550 |
1 files changed, 550 insertions, 0 deletions
diff --git a/lib/orber/src/orber_iiop.erl b/lib/orber/src/orber_iiop.erl new file mode 100644 index 0000000000..0e11f7d244 --- /dev/null +++ b/lib/orber/src/orber_iiop.erl @@ -0,0 +1,550 @@ +%%-------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% 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. +%% +%% %CopyrightEnd% +%% +%% +%%----------------------------------------------------------------- +%% File: orber_iiop.erl +%% Description: +%% This file contains the interface to the iiop operations +%% +%%----------------------------------------------------------------- +-module(orber_iiop). + +-include_lib("orber/include/corba.hrl"). +-include_lib("orber/src/orber_iiop.hrl"). + +-behaviour(supervisor). +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([start_sup/1, request/8, locate/4]). + +%%----------------------------------------------------------------- +%% Internal exports +%%----------------------------------------------------------------- +-export([init/1, terminate/2, handle_call/3]). + +%%----------------------------------------------------------------- +%% Internal defines +%%----------------------------------------------------------------- +-define(DEBUG_LEVEL, 7). + + +%%----------------------------------------------------------------- +%% External interface functions +%%----------------------------------------------------------------- +%%----------------------------------------------------------------- +%% Func: start_sup/1 +%%----------------------------------------------------------------- +start_sup(Opts) -> + supervisor:start_link({local, orber_iiop_sup}, ?MODULE, + {orber_iiop_sup, Opts}). + +%%%----------------------------------------------------------------- +%%% Func: connect/1 +%%%----------------------------------------------------------------- +%connect(OrbName) -> +% orber_iiop_net:connect(OrbName). + +%%%----------------------------------------------------------------- +%%% Func: request/5 +%%%----------------------------------------------------------------- +request({Host, Port, InitObjkey, Index, TaggedProfile, HostData}, + Op, Parameters, TypeCodes, ResponseExpected, Timeout, IOR, UserCtx) -> + {{Proxy, SysCtx, Interceptors, LocalInterface}, ObjKey, Version} = + connect(Host, Port, InitObjkey, Timeout, [Index], HostData, + TaggedProfile, IOR, UserCtx), + Ctx = add_user_context(SysCtx, UserCtx), + RequestId = orber_request_number:get(), + Env = #giop_env{interceptors = Interceptors, type = out, + flags = orber_env:get_flags(), host = LocalInterface, + version = Version, ctx = Ctx, request_id = RequestId, op = Op, + parameters = Parameters, tc = TypeCodes, objkey = ObjKey, + response_expected = ResponseExpected}, + Message = encode_request(Env), + case catch orber_iiop_outproxy:request(Proxy, ResponseExpected, Timeout, + Message, RequestId) of + {'EXCEPTION', MsgExc} -> + corba:raise(MsgExc); + _ when ResponseExpected == false -> + ok; + {reply, ReplyHeader, Rest, Len, ByteOrder, Bytes} -> + case catch decode_reply_body(Interceptors, ObjKey, Op, ReplyHeader, + Version, TypeCodes, Rest, Len, ByteOrder, + Bytes) of + {'EXCEPTION', DecodeException} -> + %% We cannot log this exception since it may be a correct exception. + corba:raise(DecodeException); + {'EXIT', message_error} -> + orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n" + "Got exit(message_error)", + [?LINE, Rest, Version, TypeCodes], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}); + {'EXIT', Why} -> + orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n" + "Got exit(~p)", + [?LINE, Rest, Version, TypeCodes, Why], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}); + 'message_error' -> + orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p);~n" + "Got message_error", + [?LINE, Rest, Version, TypeCodes], ?DEBUG_LEVEL), + %% Perhaps a resend should be done when a message error occurs + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}); + {Result, Par} -> + %% Check request id + case ReplyHeader#reply_header.reply_status of + 'no_exception' -> + case Par of + [] -> + Result; + _ -> + list_to_tuple([Result | Par]) + end; + 'system_exception' -> + corba:raise(Result); + 'user_exception' -> + corba:raise(Result); + 'location_forward' -> + case get(orber_forward_notify) of + true -> + {location_forward, Result}; + _ -> + case catch corba:call(Result, Op, Parameters, + TypeCodes, + [{timeout, Timeout}, + {context, UserCtx}]) of + {'EXCEPTION', E} -> + corba:raise(E); + {'EXIT', Reason} -> + orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n" + "location_forward resulted in exit(~p)", + [?LINE, Rest, Version, TypeCodes, Reason], ?DEBUG_LEVEL), + corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO}); + NewResult -> + NewResult + end + end; + 'location_forward_perm' -> + %% We should notify the client in this case. + case get(orber_forward_notify) of + true -> + {location_forward, Result}; + _ -> + case catch corba:call(Result, Op, Parameters, + TypeCodes, + [{timeout, Timeout}, + {context, UserCtx}]) of + {'EXCEPTION', E} -> + corba:raise(E); + {'EXIT', Reason} -> + orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n" + "location_forward_perm resulted in exit(~p)", + [?LINE, Rest, Version, TypeCodes, Reason], ?DEBUG_LEVEL), + corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO}); + NewResult -> + NewResult + end + end; + 'needs_addressing_mode' -> + orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n" + "needs_addressing_mode not supported.", + [?LINE, Rest, Version, TypeCodes], ?DEBUG_LEVEL), + corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO}) + end + end; + What -> + orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n" + "outproxy-request: ~p", [?LINE, Message, Version, TypeCodes, What], ?DEBUG_LEVEL), + corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO}) + end. + + +encode_request(#giop_env{interceptors = false} = Env) -> + case catch cdr_encode:enc_request(Env) of + {'EXCEPTION', Exc} -> + orber:dbg("[~p] orber_iiop:request(~p)~n" + "Got exception(~p)", + [?LINE, Env, Exc], ?DEBUG_LEVEL), + corba:raise(Exc); + {'EXIT', R} -> + orber:dbg("[~p] orber_iiop:request:( ~p )~n" + "Got exit(~p)", + [?LINE, Env, R], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO}); + Msg -> + Msg + end; +encode_request(#giop_env{interceptors = {native, Ref, PIs}, + objkey = ObjKey, ctx = Ctx, op = Op, + parameters = Params} = Env) -> + Parameters = orber_pi:out_request(PIs, ObjKey, Ctx, Op, Ref, Params), + case catch cdr_encode:enc_request_split(Env) of + {'EXCEPTION', Exc} -> + orber:dbg("[~p] orber_iiop:request( ~p, ~p); exception(~p)", + [?LINE, Env, Parameters, Exc], ?DEBUG_LEVEL), + corba:raise(Exc); + {'EXIT', R} -> + orber:dbg("[~p] orber_iiop:request:( ~p, ~p); got exit(~p)", + [?LINE, Env, Parameters, R], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO}); + {Hdr, Body, HdrLen, _, Flags} -> + NewBody = orber_pi:out_request_enc(PIs, ObjKey, Ctx, Op, Ref, Body), + cdr_encode:enc_giop_message_header(Env, 'request', Flags, + HdrLen+size(NewBody), + [Hdr|NewBody]) + end; +encode_request(Env) -> + case catch cdr_encode:enc_request(Env) of + {'EXCEPTION', Exc} -> + orber:dbg("[~p] orber_iiop:request( ~p ); exception(~p)", + [?LINE, Env, Exc], ?DEBUG_LEVEL), + corba:raise(Exc); + {'EXIT', R} -> + orber:dbg("[~p] orber_iiop:request:( ~p ); got exit(~p)", + [?LINE, Env, R], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO}); + Msg -> + Msg + end. + +%%----------------------------------------------------------------- +%% Func: locate/1 +%%----------------------------------------------------------------- +locate({Host, Port, InitObjkey, Index, TaggedProfile, HostData}, + Timeout, IOR, UserCtx) -> + {{Proxy, _Ctx, _Interceptors, LocalInterface}, ObjKey, Version} = + connect(Host, Port, InitObjkey, Timeout, [Index], HostData, + TaggedProfile, IOR, UserCtx), + RequestId = orber_request_number:get(), + Env = #giop_env{version = Version, objkey = ObjKey, request_id = RequestId, + flags = orber_env:get_flags(), host = LocalInterface}, + Result = + case catch cdr_encode:enc_locate_request(Env) of + {'EXCEPTION', EncE} -> + orber:dbg("[~p] orber_iiop:locate(~p); exception(~p)", + [?LINE, ObjKey, EncE], ?DEBUG_LEVEL), + corba:raise(EncE); + {'EXIT', EncR} -> + orber:dbg("[~p] orber_iiop:locate(~p); exit(~p)", + [?LINE, ObjKey, EncR], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO}); + Request -> + (catch orber_iiop_outproxy:request(Proxy, true, Timeout, + Request, RequestId)) + end, + case Result of + {'EXCEPTION', MsgExc} -> + corba:raise(MsgExc); + {locate_reply, ReplyHeader, Rest, Len, ByteOrder} -> + case catch cdr_decode:dec_locate_reply_body(Version, + ReplyHeader#locate_reply_header.locate_status, + Rest, Len, ByteOrder) of + {'EXCEPTION', DecodeException} -> + orber:dbg("[~p] orber_iiop:locate(locate_reply, ~p, ~p); exception(~p)", + [?LINE, Rest, Version, DecodeException], ?DEBUG_LEVEL), + corba:raise(DecodeException); + {'EXIT', message_error} -> + orber:dbg("[~p] orber_iiop:locate(locate_reply, ~p, ~p); exit(message_error)", + [?LINE, Rest, Version], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}); + {'EXIT', R} -> + orber:dbg("[~p] orber_iiop:locate(locate_reply, ~p, ~p); exit(~p)", + [?LINE, Rest, Version, R], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}); + [] -> + ReplyHeader#locate_reply_header.locate_status; + ObjRef -> + {ReplyHeader#locate_reply_header.locate_status, ObjRef} + end; + Other -> + orber:dbg("[~p] orber_iiop:locate(~p); exit(~p)", + [?LINE, ObjKey, Other], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO}) + end. + +%%%----------------------------------------------------------------- +%%% Func: cancel/1 +%%%----------------------------------------------------------------- +%cancel(X) -> +% ok. + +%%%----------------------------------------------------------------- +%%% Func: message_error/1 +%%%----------------------------------------------------------------- +%message_error(X) -> +% ok. + +%%----------------------------------------------------------------- +%% Server functions +%%----------------------------------------------------------------- +%%----------------------------------------------------------------- +%% Func: init/1 +%%----------------------------------------------------------------- +init({orber_iiop_sup, Opts}) -> + IIOP_port = orber:iiop_port(), + SSL_port = orber:iiop_ssl_port(), + SupFlags = {one_for_one, 5, 1000}, %Max 5 restarts in 1 second + PortList = if + SSL_port > -1 -> + [{port, ssl, SSL_port}]; + true -> + [] + end, + ChildSpec = + case orber:is_lightweight() of + true -> + [ + {orber_iiop_outsup, {orber_iiop_outsup, start, + [sup, Opts]}, + permanent, 10000, supervisor, [orber_iiop_outsup]}, + {orber_iiop_pm, {orber_iiop_pm, start, + [Opts]}, + permanent, 10000, worker, [orber_iiop_pm]} + ]; + false -> + [{orber_iiop_outsup, {orber_iiop_outsup, start, + [sup, Opts]}, + permanent, 10000, supervisor, [orber_iiop_outsup]}, + {orber_iiop_pm, {orber_iiop_pm, start, + [Opts]}, + permanent, 10000, worker, [orber_iiop_pm]}, + {orber_iiop_insup, {orber_iiop_insup, start, + [sup, Opts]}, + permanent, 10000, supervisor, [orber_iiop_insup]}, + {orber_iiop_socketsup, {orber_iiop_socketsup, start, + [sup, Opts]}, + permanent, 10000, supervisor, [orber_iiop_socketsup]}, + {orber_iiop_net, {orber_iiop_net, start, + [[{port, normal, IIOP_port} | PortList]]}, + permanent, 10000, worker, [orber_iiop_net]}] + end, + {ok, {SupFlags, ChildSpec}}. + + + + + +%%----------------------------------------------------------------- +%% Func: terminate/2 +%%----------------------------------------------------------------- +terminate(_Reason, _State) -> + ok. + +%%----------------------------------------------------------------- +%% Func: handle_call/3 +%%----------------------------------------------------------------- +handle_call(_Req, _From, State) -> + {reply, ok, State}. + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- +add_user_context([], UserCtx) -> UserCtx; +add_user_context(SysCtx, []) -> SysCtx; +add_user_context(SysCtx, UserCtx) -> SysCtx ++ UserCtx. + +decode_reply_body(false, _ObjKey, _Op, ReplyHeader, Version, TypeCodes, + Rest, Len, ByteOrder, Bytes) -> + case ReplyHeader#reply_header.reply_status of + 'no_exception' -> + {R, P, _} = cdr_decode:dec_reply_body(Version, TypeCodes, Rest, Len, ByteOrder, Bytes), + {R, P}; + 'system_exception' -> + {R, _} = cdr_decode:dec_system_exception(Version, Rest, Len, ByteOrder), + {R, []}; + 'user_exception' -> + {R, _} = cdr_decode:dec_user_exception(Version, Rest, Len, ByteOrder), + {R, []}; + 'location_forward' -> + {R, _, _} = cdr_decode:dec_reply_body(Version, {{'tk_objref', "", ""}, [],[]}, + Rest, Len, ByteOrder, Bytes), + {R, []}; + 'location_forward_perm' -> + {R, _, _} = cdr_decode:dec_reply_body(Version, {{'tk_objref', "", ""}, [],[]}, + Rest, Len, ByteOrder, Bytes), + {R, []}; + 'needs_addressing_mode' -> + {R, _, _} = cdr_decode:dec_reply_body(Version, {'tk_short', [],[]}, + Rest, Len, ByteOrder, Bytes), + {R, []} + end; +decode_reply_body(Interceptors, ObjKey, Op, ReplyHeader, Version, TypeCodes, + RestIn, Len, ByteOrder, Bytes) -> + Rest = + case Interceptors of + {portable, _PIs} -> + RestIn; + {native, Ref, PIs} -> + orber_pi:in_reply_enc(PIs, ObjKey, + ReplyHeader#reply_header.service_context, + Op, Ref, RestIn) + end, + Reply = + case ReplyHeader#reply_header.reply_status of + 'no_exception' -> + {R, P, _} = cdr_decode:dec_reply_body(Version, TypeCodes, Rest, Len, ByteOrder, Bytes), + {R, P}; + 'system_exception' -> + {R, _} = cdr_decode:dec_system_exception(Version, Rest, Len, ByteOrder), + {R, []}; + 'user_exception' -> + {R, _} = cdr_decode:dec_user_exception(Version, Rest, Len, ByteOrder), + {R, []}; + 'location_forward' -> + {R, _, _} = cdr_decode:dec_reply_body(Version, {{'tk_objref', "", ""}, [],[]}, + Rest, Len, ByteOrder, Bytes), + {R, []}; + 'location_forward_perm' -> + {R, _, _} = cdr_decode:dec_reply_body(Version, {{'tk_objref', "", ""}, [],[]}, + Rest, Len, ByteOrder, Bytes), + {R, []}; + 'needs_addressing_mode' -> + {R, _, _} = cdr_decode:dec_reply_body(Version, {'tk_short', [],[]}, + Rest, Len, ByteOrder, Bytes), + {R, []} + end, + case Interceptors of + {portable, _PI} -> + Reply; + {native, Refs, PI} -> + orber_pi:in_reply(PI, ObjKey, + ReplyHeader#reply_header.service_context, + Op, Refs, Reply) + end. + +%% "Plain" TCP/IP. +connect(Host, Port, Objkey, Timeout, Index, + #host_data{protocol = normal, csiv2_mech = undefined} = HostData, + TaggedProfile, IOR, Ctx) -> + connect2([{Host, Port}], Objkey, Timeout, Index, HostData, + TaggedProfile, IOR, Ctx); +%% "Plain" SSL +connect(Host, _, Objkey, Timeout, Index, + #host_data{protocol = ssl, + ssl_data = #'SSLIOP_SSL'{port = Port}, + csiv2_mech = undefined} = HostData, + TaggedProfile, IOR, Ctx) -> + connect2([{Host, Port}], Objkey, Timeout, Index, HostData, + TaggedProfile, IOR, Ctx); +%% TEMPORARY FIX TO AVOID RUNNING CSIv2. +connect(Host, _, Objkey, Timeout, Index, + #host_data{protocol = ssl, + ssl_data = #'SSLIOP_SSL'{port = Port}} = HostData, + TaggedProfile, IOR, Ctx) -> + connect2([{Host, Port}], Objkey, Timeout, Index, HostData, + TaggedProfile, IOR, Ctx); +%% CSIv2 over SSL (TAG_TLS_SEC_TRANS) using the SAS protocol. Note port must equal 0. +connect(_Host, 0, Objkey, Timeout, Index, + #host_data{protocol = ssl, + csiv2_mech = + #'CSIIOP_CompoundSecMech'{target_requires = _TR} = _Mech, + csiv2_addresses = Addresses} = HostData, + TaggedProfile, IOR, Ctx) -> + NewCtx = [#'IOP_ServiceContext' + {context_id=?IOP_SecurityAttributeService, + context_data = #'CSI_SASContextBody' + {label = ?CSI_MsgType_MTEstablishContext, + value = #'CSI_EstablishContext' + {client_context_id = 0, %% Always 0 when stateless. + authorization_token = + [#'CSI_AuthorizationElement'{the_element = []}], + identity_token = + #'CSI_IdentityToken'{label = ?CSI_IdentityTokenType_ITTAbsent, + value = true}, + client_authentication_token = []}}}|Ctx], + connect2(Addresses, Objkey, Timeout, Index, HostData, + TaggedProfile, IOR, NewCtx); +%% CSIv2 over SSL (TAG_NULL_TAG) using the SAS protocol. +connect(Host, _, Objkey, Timeout, Index, + #host_data{protocol = ssl, + ssl_data = #'SSLIOP_SSL'{port = Port}, + csiv2_mech = Mech} = HostData, + TaggedProfile, IOR, Ctx) when is_record(Mech, 'CSIIOP_CompoundSecMech') -> + connect2([{Host, Port}], Objkey, Timeout, Index, HostData, + TaggedProfile, IOR, Ctx); +%% CSIv2 over TCP (TAG_NULL_TAG) using the SAS protocol. +connect(Host, Port, Objkey, Timeout, Index, + #host_data{protocol = normal, + csiv2_mech = Mech} = HostData, + TaggedProfile, IOR, Ctx) when is_record(Mech, 'CSIIOP_CompoundSecMech') -> + connect2([{Host, Port}], Objkey, Timeout, Index, HostData, + TaggedProfile, IOR, Ctx); +connect(_Host, _Port, _Objkey, _Timeout, _Index, HostData, _TaggedProfile, + IOR, _Ctx) -> + orber:dbg("[~p] orber_iiop:connect(~p)~n" + "Unable to use the supplied IOR.~n" + "Connection Data: ~p", [?LINE, IOR, HostData], ?DEBUG_LEVEL), + corba:raise(#'INV_OBJREF'{completion_status=?COMPLETED_NO}). + + + +connect2(HostPort, Objkey, Timeout, Index, HostData, TaggedProfile, IOR, Ctx) -> + case try_connect(HostPort, HostData#host_data.protocol, Timeout, HostData, Ctx) of + error -> + Alts = iop_ior:get_alt_addr(TaggedProfile), + case try_connect(Alts, HostData#host_data.protocol, Timeout, HostData, Ctx) of + error -> + case iop_ior:get_key(IOR, Index) of + undefined -> + corba:raise(#'COMM_FAILURE'{completion_status = ?COMPLETED_NO}); + {'external', {NewHost, NewPort, NewObjkey, NewIndex, + NewTaggedProfile, NewHostData}} -> + connect(NewHost, NewPort, NewObjkey, Timeout, [NewIndex|Index], + NewHostData, NewTaggedProfile, IOR, Ctx); + _What -> + orber:dbg("[~p] orber_iiop:connect2(~p)~n" + "Illegal IOR; contains a mixture of local and external profiles.", + [?LINE, IOR], ?DEBUG_LEVEL), + corba:raise(#'INV_OBJREF'{completion_status=?COMPLETED_NO}) + end; + X -> + {X, Objkey, HostData#host_data.version} + end; + X -> + {X, Objkey, HostData#host_data.version} + end. + +try_connect([], _, _, _, _) -> + error; +try_connect([{Host, Port}|T], SocketType, Timeout, HostData, Ctx) -> + case catch orber_iiop_pm:connect(Host, Port, SocketType, Timeout, + HostData#host_data.charset, + HostData#host_data.wcharset, Ctx) of + {ok, P, Ctx2, Int, Interface} -> + {P, Ctx2, Int, Interface}; + {'EXCEPTION', #'BAD_CONTEXT'{} = CtxExc} -> + orber:dbg("[~p] orber_iiop:try_connect(~p, ~p) failed~n", + [?LINE, Host, Port], ?DEBUG_LEVEL), + corba:raise(CtxExc); + {'EXCEPTION', _PMExc} -> + try_connect(T, SocketType, Timeout, HostData, Ctx); + {'EXIT',{timeout,_}} -> + orber:dbg("[~p] orber_iiop:try_connect(~p, ~p, ~p)~n" + "Connect attempt timed out", + [?LINE, Host, Port, Timeout], ?DEBUG_LEVEL), + try_connect(T, SocketType, Timeout, HostData, Ctx); + {'EXIT', What} -> + orber:dbg("[~p] orber_iiop:try_connect(~p, ~p, ~p)~n" + "Connect attempt resulted in: ~p", + [?LINE, Host, Port, Timeout, What], ?DEBUG_LEVEL), + try_connect(T, SocketType, Timeout, HostData, Ctx) + end. + |