diff --git a/Makefile b/Makefile index cdda91f..aa1bc75 100644 --- a/Makefile +++ b/Makefile @@ -9,11 +9,12 @@ compile: @rebar compile ct: compile test_deps + mkdir -p ./logs @echo "Running common tests..." -@ct_run -noshell -pa ebin \ -pa deps/*/ebin \ -pa test/deps/*/ebin \ - -name test \ + -sname test \ -logdir ./logs \ -env TEST_DIR ./test \ -spec ./test/test_specs.spec \ diff --git a/README.md b/README.md index d9b5336..acf719b 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,7 @@ provided. The figure below provides a high level overview of the application and the main integration points. -![SOAP application](doc/soap architecture.png "Overview of the SOAP -application") +![Overview of the SOAP application](doc/soap%20architecture.png?raw=true) The starting point can be a WSDL for which you need to implement an Erlang SOAP client or server, or an existing Erlang function @@ -33,7 +32,7 @@ The figure below shows how the application can generate the relevant modules, either starting from a WSDL ("contract first") or starting from a set of Erlang type specifications ("contract last"). -![Process and artifacts](doc/soap process.png "Process and artifacts") +![Process and artifacts](doc/soap%20process.png?raw=true) ## Documentation overview diff --git a/doc/soap.md b/doc/soap.md index b53b5ff..c7cf70e 100644 --- a/doc/soap.md +++ b/doc/soap.md @@ -152,14 +152,17 @@ The following options are available: - `{erlsom_options, proplists:proplist()}` - These options are passed on to erlsom. This can for example be used to specify the options for dealing - with imported namespaces in the `types` (xsd) section of the WSDL. See - the documentation of erlsom for more details. + with imported namespaces in the `types` (xsd) section of the WSDL. The + default value for this option is `[{strict, true}]`. - `{strict, boolean()}` - If `false`, the functions that decode and encode XML will convert only integer (xsd:integer) and boolean values from the XML to and from Erlang integer/boolean values (this is the standard Erlsom behaviour). All other types will be represented as strings (without testing the - validity). + validity). (Note that this option can also be set as one of the + `erlsom_options`, see above. If the two settings conflict, the version + that is set explicitly prevails over the value that is part of the + `erlsom_options`). If `true` (the default for the `soap` application) a number of additional data-types will be converted (and checked for validity, hence diff --git a/doc/soap_client_tutorial.md b/doc/soap_client_tutorial.md index 6a3fad2..b8b8f7d 100644 --- a/doc/soap_client_tutorial.md +++ b/doc/soap_client_tutorial.md @@ -5,7 +5,7 @@ tutorial](soap_server_tutorial.md). The [server tutorial](soap_server_tutorial.md) shows how the client module that is generated by `soap:wsdl2soap()` can be used. It -explains how you can call a service, with or without SOAP headers.It does +explains how you can call a service, with or without SOAP headers. It does _not_ explain how the generated client can be customized. This is necessary in particular if you want to access information in the SOAP header of the response message. @@ -47,30 +47,33 @@ generating the client). must be `[]`). 2. The second argument is a list of SOAP header blocks. If any are - specified, these will be included in the SOAP header. + specified, these will be included in the SOAP header. - There are 2 ways to specify a SOAP header block: - 1. As an `iolist().` - This must be a valid XML snippet. It will simply be copied literally to the SOAP Header. - 2. As a record that corresponds to a type from the WSDL. - This is comparable with the way the content of the SOAP body is passed. - The record will be translated to XML by the `soap` application. + There are 2 ways to specify a SOAP header block: + 1. As an `iolist().` + This must be a valid XML snippet. It will simply be copied literally + to the SOAP Header. + + 2. As a record that corresponds to a type from the WSDL. + This is comparable with the way the content of the SOAP body is passed. + The record will be translated to XML by the `soap` application. 3. The third argument is a list of options. The following options are available: - 1. `{http_client, module()}` - use an HTTP - client that is different from what was specified when generating the - client module. The `Module` must be an Erlang module that implements the - interface that is described in [Integrating a HTTP client](integrating_a_http_client.md). - 2. `{url, URL::string()}` - use a URL that is different from the URL - specified in the WSDL. - 3. `{timeout, Value}`. This allows to set the timeout for the request, in milliseconds. - It defaults to the default of the used client (ibrowse: 3000 milliseconds, inets/httpc: infinity). - - All other options used here are passed on to the application that is used - to implement the HTTP client (so the options that are available and their - effect depends on the selected HTTP server). + 1. `{http_client, module()}` - use an HTTP + client that is different from what was specified when generating the + client module. The `Module` must be an Erlang module that implements the + interface that is described in [Integrating a HTTP client](integrating_a_http_client.md). + 2. `{url, URL::string()}` - use a URL that is different from the URL + specified in the WSDL. + 3. `{timeout, Value}`. This allows to set the timeout for the request, + in milliseconds. It defaults to the default of the used client + (ibrowse: 3000 milliseconds, inets/httpc: infinity). + + All other options used here are passed on to the application that is used + to implement the HTTP client (so the options that are available and their + effect depends on the selected HTTP server). 4. (Only if the option `{attachments, true}` was used to generate the client) - Attachments to be added to the request. See [SOAP diff --git a/doc/supported_standards_and_limitations.md b/doc/supported_standards_and_limitations.md index 17769fc..d5326cf 100644 --- a/doc/supported_standards_and_limitations.md +++ b/doc/supported_standards_and_limitations.md @@ -19,7 +19,7 @@ The following standards have been used as the starting points: - [SOAP 1.1](https://www.w3.org/TR/2000/NOTE-SOAP-20000508/) - [SOAP 1.2](https://www.w3.org/TR/soap/) - [SOAP Messages with Attachments](https://www.w3.org/TR/SOAP-attachments) -- [WSDL 1.1](https://www.w3.org/TR/wsdl) +- [WSDL 1.1](https://www.w3.org/TR/2001/NOTE-wsdl-20010315) - [WSDL 1.1 Binding extension for SOAP 1.2](https://www.w3.org/Submission/wsdl11soap12/) - [WSDL 2.0](https://www.w3.org/TR/2007/REC-wsdl20-20070626/) - but only to a limited extent. diff --git a/src/soap.erl b/src/soap.erl index f9ddecd..a6dbfa6 100644 --- a/src/soap.erl +++ b/src/soap.erl @@ -312,15 +312,28 @@ wsdl2erlang(File, Options) -> Port_names), Namespaces = soap_compile_wsdl:get_namespaces(File, Options), Prefixes = get_prefixes(Namespaces, Options), - Strict = proplists:get_value(strict, Options, true), + %% The default for 'strict' differs between soap and erlsom. + %% For reasons of backwards compatibility the Erlsom `strict` option + %% can be set in 2 ways: as an `erlsom_option` and as a separate + %% explicit option (the explicit option "wins" if both variants are + %% provided). + ErlsomOpts = proplists:get_value(erlsom_options, Options, []), + Strict1 = proplists:get_value(strict, ErlsomOpts, true), + Strict2 = proplists:get_value(strict, Options, Strict1), + ErlsomOpts2 = [Option + || {Key, _} = Option <- ErlsomOpts, Key /= strict], Remove = [generate, http_server, http_client, port, service, namespaces, - client_name, server_name, hrl_name, strict, generate_tests], + client_name, server_name, hrl_name, erlsom_options, strict, + generate_tests], Other_options = [Option || {Key, _} = Option <- Options, not(lists:member(Key, Remove))], Options2 = lists:flatten([{generate, Generate}, {namespaces, Prefixes}, {generate_tests, Generate_tests}, Http_server_options, Http_client_options, - {strict, Strict} | Other_options]), + {erlsom_options, [{namespaces, Prefixes}, + {strict, Strict2} + | ErlsomOpts2]} + | Other_options]), soap_compile_wsdl:file(File, Service, Port, Options2, Hrl_name). %% Generate a module with test functions (using default values) for every diff --git a/src/soap.hrl b/src/soap.hrl index 45dfe08..deb9b55 100644 --- a/src/soap.hrl +++ b/src/soap.hrl @@ -25,7 +25,7 @@ name :: string(), operation :: atom(), %% name of function as used in handler module soap_action = [] :: string(), - wrapper_ns = [] :: string(), %% namespace for the wrapper element (in + wrapper_ns = [] :: string() | undefined, %% namespace for the wrapper element (in %% case of rpc style) op_type :: notification | request_response, in_type :: [{string(), atom()}] | atom(), %% the list type is only used @@ -47,7 +47,7 @@ target_ns :: string(), soap_ns :: string(), style :: string() | undefined, %% "rpc" | "document" - decoders :: [{string(), module}], + decoders :: [{string(), module}] | undefined, url :: string(), port :: string(), binding :: string(), diff --git a/src/soap_client_util.erl b/src/soap_client_util.erl index 2f92a32..44b45bb 100644 --- a/src/soap_client_util.erl +++ b/src/soap_client_util.erl @@ -55,7 +55,7 @@ fault_parser :: fun(), parser_state :: any(), soap_headers = [] :: [string() | tuple()], - soap_body :: string(), + soap_body :: string() | tuple(), is_fault = false :: boolean(), namespaces = [] :: [{prefix(), uri()}] }). @@ -170,7 +170,8 @@ call_http(Http_body, version = Version, url = Url}, Http_headers, Content_type) -> %%io:format("request: ~nheaders: ~p~nbody: ~s~n", [Http_headers, Http_body]), - Http_res = Client:http_request(Url, Http_body, Http_client_options, + Http_options = lists:keydelete(http_headers, 1, Http_client_options), + Http_res = Client:http_request(Url, Http_body, Http_options, Http_headers, Content_type), case Http_res of {ok, Code, Response_headers, Response_body} diff --git a/src/soap_cowboy_1_protocol.erl b/src/soap_cowboy_1_protocol.erl index e108229..2e56299 100644 --- a/src/soap_cowboy_1_protocol.erl +++ b/src/soap_cowboy_1_protocol.erl @@ -27,7 +27,7 @@ -module(soap_cowboy_1_protocol). %% -behaviour(cowboy_sub_protocol). --export([upgrade/4]). +-export([upgrade/4, upgrade/5]). -export([enrich_req/2]). -export([respond/4]). @@ -48,9 +48,19 @@ {Implementation_handler::module(), Options::any()}) -> {ok, cowboy_req(), cowboy_env()}. upgrade(Cowboy_req, Env, Soap_handler, {Handler, Options}) -> - soap_cowboy_protocol:upgrade(Cowboy_req, Env, Soap_handler, - {Handler, Options}, cowboy_1, ?MODULE). + {ok, Message, Cowboy_req2} = cowboy_req:body(Cowboy_req), + upgrade(Cowboy_req2, Env, Soap_handler, {Handler, Options}, Message). +%% There might exist middleware that reads body from the cowboy_req, in which +%% case it will be no longer available while calling upgrade/4. In this case +%% you are responsible for propogating Body directly to upgrade/5 +-spec upgrade(Cowboy_req::cowboy_req(), Env::cowboy_env(), + Soap_handler::module(), + {Implementation_handler::module(), Options::any()}, Body::binary()) -> + {ok, cowboy_req(), cowboy_env()}. +upgrade(Cowboy_req, Env, Soap_handler, {Handler, Options}, Message) -> + soap_cowboy_protocol:upgrade(Cowboy_req, Env, Soap_handler, + {Handler, Options}, cowboy_1, ?MODULE, Message). enrich_req(Cowboy_req, Soap_req) -> {Method, Req2} = cowboy_req:method(Cowboy_req), diff --git a/src/soap_cowboy_2_protocol.erl b/src/soap_cowboy_2_protocol.erl index ef7237d..09453d7 100644 --- a/src/soap_cowboy_2_protocol.erl +++ b/src/soap_cowboy_2_protocol.erl @@ -29,7 +29,7 @@ -module(soap_cowboy_2_protocol). %%-behaviour(cowboy_sub_protocol). --export([upgrade/6]). +-export([upgrade/4, upgrade/5]). -export([enrich_req/2]). -export([respond/4]). @@ -46,11 +46,18 @@ %% This callback is expected to behave like a middleware and to return an %% updated req object and environment. -spec upgrade(Cowboy_req::cowboy_req(), Env::cowboy_env(), - Soap_handler::module(), {Implementation_handler::module(), Options::any()}, - Timeout::any(), Hibernate::any()) -> {ok, cowboy_req(), cowboy_env()}. -upgrade(Cowboy_req, Env, Soap_handler, {Handler, Options}, _, _) -> - soap_cowboy_protocol:upgrade(Cowboy_req, Env, Soap_handler, - {Handler, Options}, cowboy_2, ?MODULE). + Soap_handler::module(), {Implementation_handler::module(), Options::any()}) + -> {ok, cowboy_req(), cowboy_env()}. +upgrade(Cowboy_req, Env, Soap_handler, {Handler, Options}) -> + {ok, Message, Cowboy_req2} = cowboy_req:read_body(Cowboy_req), + upgrade(Cowboy_req2, Env, Soap_handler, {Handler, Options}, Message). + +%% There might exist middleware that reads body from the cowboy_req, in which +%% case it will be no longer available while calling upgrade/4. In this case +%% you are responsible for propogating Body directly to upgrade/5 +upgrade(Cowboy_req, Env, Soap_handler, {Handler, Options}, Message) -> + soap_cowboy_protocol:upgrade(Cowboy_req, Env, Soap_handler, + {Handler, Options}, cowboy_2, ?MODULE, Message). enrich_req(Cowboy_req, Soap_req) -> Method = cowboy_req:method(Cowboy_req), diff --git a/src/soap_cowboy_protocol.erl b/src/soap_cowboy_protocol.erl index a2d632e..e1fc672 100644 --- a/src/soap_cowboy_protocol.erl +++ b/src/soap_cowboy_protocol.erl @@ -28,7 +28,7 @@ -module(soap_cowboy_protocol). --export([upgrade/6]). +-export([upgrade/7]). -record(state, { env :: cowboy_middleware:env(), @@ -51,12 +51,12 @@ -spec upgrade(Cowboy_req::cowboy_req(), Env::cowboy_env(), Soap_handler::module(), {Implementation_handler::module(), Options::any()}, Version::atom(), - Version_module::module()) -> {ok, cowboy_req(), cowboy_env()}. -upgrade(Cowboy_req, Env, _, {Handler, Options}, Version, Version_module) -> + Version_module::module(), Body::binary()) -> {ok, cowboy_req(), cowboy_env()}. +upgrade(Cowboy_req, Env, _, {Handler, Options}, Version, Version_module, Body) -> Cowboy_state = #state{env = Env, handler = Handler}, case soap_server_handler:new_req(Handler, Version, Options, Cowboy_req) of {continue, Soap_req} -> - check_conformance(Soap_req, Cowboy_req, Cowboy_state, Version_module); + check_conformance(Soap_req, Cowboy_req, Cowboy_state, Version_module, Body); {ok, _StatusCode, _Headers, _Body, _Server_req} = Error -> make_response(Error, Cowboy_state, Version_module) end. @@ -65,26 +65,23 @@ upgrade(Cowboy_req, Env, _, {Handler, Options}, Version, Version_module) -> %%% Internal functions %%% ============================================================================ -check_conformance(Soap_req, Cowboy_req, Cowboy_state, Version_module) -> +check_conformance(Soap_req, Cowboy_req, Cowboy_state, Version_module, Body) -> %% collect some information about the protocol, so that %% conformance can be checked. Soap_req2 = Version_module:enrich_req(Cowboy_req, Soap_req), case soap_server_handler:check_http_conformance(Soap_req2) of {continue, Soap_req3} -> - handle_xml(Soap_req3, Cowboy_state, Version_module); + handle_xml(Soap_req3, Cowboy_state, Version_module, Body); {ok, _StatusCode, _Headers, _Body, _Server_req} = Error -> make_response(Error, Cowboy_state, Version_module) end. -handle_xml(Soap_req, Cowboy_state, Version_module) -> - Cowboy_req = soap_req:server_req(Soap_req), - {ok, Message, Cowboy_req2} = cowboy_req:body(Cowboy_req), - Soap_req2 = soap_req:set_server_req(Soap_req, Cowboy_req2), - Soap_req3 = soap_req:set_http_body(Soap_req2, Message), - Content_type = soap_req:content_type(Soap_req3), +handle_xml(Soap_req, Cowboy_state, Version_module, Message) -> + Soap_req2 = soap_req:set_http_body(Soap_req, Message), + Content_type = soap_req:content_type(Soap_req2), %% get the soap message (Xml) from the request body - {Xml, Soap_req4} = - case string:to_lower(lists:sublist(Content_type, 17)) of + {Xml, Soap_req3} = + case maybe_content_type(Content_type) of "multipart/related" -> %% soap with attachments, the message is in the first part try @@ -92,18 +89,23 @@ handle_xml(Soap_req, Cowboy_state, Version_module) -> mime_decode(Message, Content_type), {Body, soap_req:set_mime_headers( - soap_req:set_req_attachments(Soap_req3, Attachments), + soap_req:set_req_attachments(Soap_req2, Attachments), Mime_headers)} catch _Class:_Type -> - {Message, Soap_req3} + {Message, Soap_req2} end; _ -> - {Message, Soap_req3} + {Message, Soap_req2} end, - Handler_resp = soap_server_handler:handle_message(Xml, Soap_req4), + Handler_resp = soap_server_handler:handle_message(Xml, Soap_req3), make_response(Handler_resp, Cowboy_state, Version_module). +maybe_content_type(undefined) -> + undefined; +maybe_content_type(Content_type) -> + string:to_lower(lists:sublist(Content_type, 17)). + mime_decode(Message, Content_type_header) -> Mime_parameters = lists:nthtail(17, Content_type_header), Parsed_parameters = soap_mime:parse_mime_parameters(Mime_parameters), diff --git a/src/soap_parse_wsdl.erl b/src/soap_parse_wsdl.erl index 4598a58..f70258b 100644 --- a/src/soap_parse_wsdl.erl +++ b/src/soap_parse_wsdl.erl @@ -549,8 +549,8 @@ add_model({Xsd, Ns}, {CombinedModel, Namespaces, ErlsomOptions}) -> {ok, Model} = erlsom:compile_xsd(Xsd, [{include_any_attribs, false} ,{already_imported, Namespaces} - ,{prefix, Prefix} - ,{strict, true} | ErlsomOptions]), + ,{namespaces, [{Ns, Prefix}]} + | ErlsomOptions]), NewModel = case CombinedModel of undefined -> Model; diff --git a/src/soap_server_cowboy_1.erl b/src/soap_server_cowboy_1.erl index 1fcb003..b1a286d 100644 --- a/src/soap_server_cowboy_1.erl +++ b/src/soap_server_cowboy_1.erl @@ -63,17 +63,14 @@ start(Module) -> start(Module, []). start(Module, Options) -> - Port = proplists:get_value(port, Options, 8080), - Acceptors = proplists:get_value(nr_acceptors, Options, 100), - ok = application:ensure_started(crypto), - ok = application:ensure_started(ranch), - ok = application:ensure_started(cowlib), - ok = application:ensure_started(cowboy), - Dispatch = cowboy_router:compile([ - {'_', [{'_', ?MODULE, {Module, Options}}]}]), - {ok, _} = cowboy:start_http(http, Acceptors, [{port, Port}], [ - {env, [{dispatch, Dispatch}]}]). - + Port = proplists:get_value(port, Options, 8080), + Acceptors = proplists:get_value(nr_acceptors, Options, 100), + {ok, _} = application:ensure_all_started(cowboy), + Dispatch = cowboy_router:compile([ + {'_', [{'_', ?MODULE, {Module, Options}}]}]), + {ok, _} = cowboy:start_http(http, Acceptors, [{port, Port}], + [{env, [{dispatch, Dispatch}]}]). + stop() -> cowboy:stop_listener(http), application:stop(cowboy), diff --git a/src/soap_server_cowboy_2.erl b/src/soap_server_cowboy_2.erl index 2f8bd95..0efc148 100644 --- a/src/soap_server_cowboy_2.erl +++ b/src/soap_server_cowboy_2.erl @@ -65,15 +65,15 @@ start(Module) -> start(Module, Options) -> Port = proplists:get_value(port, Options, 8080), Acceptors = proplists:get_value(nr_acceptors, Options, 100), - ok = application:ensure_started(crypto), - ok = application:ensure_started(ranch), - ok = application:ensure_started(cowlib), - ok = application:ensure_started(cowboy), - Dispatch = cowboy_router:compile([ - {'_', [{'_', ?MODULE, {Module, Options}}]}]), - {ok, _} = cowboy:start_http(http, Acceptors, [{port, Port}], [ - {env, [{dispatch, Dispatch}]}]). - + {ok, _} = application:ensure_all_started(cowboy), + Dispatch = cowboy_router:compile( + [ + {'_', [{'_', ?MODULE, {Module, Options}}]}]), + + {ok, _} = cowboy:start_clear(http, [{port, Port}], + #{env => #{dispatch => Dispatch}} + ). + stop() -> cowboy:stop_listener(http), application:stop(cowboy), diff --git a/test/rebar.config b/test/rebar.config index 2971d4b..55256e4 100644 --- a/test/rebar.config +++ b/test/rebar.config @@ -1,8 +1,9 @@ {erl_opts, [debug_info]}. {deps, [ - {ibrowse, ".*", {git, "https://github.com/cmullaparthi/ibrowse.git" }}, - {cowboy, ".*", {git, "https://github.com/ninenines/cowboy.git", {branch, "1.1.x"}}}, - {mochiweb, ".*", {git, "https://github.com/mochi/mochiweb.git" }}, + {ibrowse, ".*", {git, "https://github.com/cmullaparthi/ibrowse.git", {tag, "v4.4.0"} }}, +%% {cowboy, ".*", {git, "https://github.com/ninenines/cowboy.git", {tag, "1.1.2"}}}, + {cowboy, ".*", {git, "https://github.com/ninenines/cowboy.git", {tag, "2.2.2"}}}, + {mochiweb, ".*", {git, "https://github.com/mochi/mochiweb.git" }}, {erlsom, ".*", {git, "https://github.com/willemdj/erlsom.git", {branch, "v1_4"}}} ]}. diff --git a/test/soap_SUITE.erl b/test/soap_SUITE.erl index 42fcd61..ff878bf 100644 --- a/test/soap_SUITE.erl +++ b/test/soap_SUITE.erl @@ -112,6 +112,12 @@ groups() -> ,{soap_11_client, [sequence], [client_11_ok ,client_11_fault]} ,{soap_12_client, [sequence], [client_12_ok ,client_12_fault]} ]} + ,{cowboy_middle_protocol, [sequence], + [correct_sms + ,sms_fault + ,sms_invalid_xml + ,sms_wrong_method + ]} ,{mochi_server, [sequence], [correct_sms ,sms_fault @@ -129,6 +135,9 @@ groups() -> ,{tempconvert_local, [], [tempconvert_local_ok]} ,test_client ]} + ,{strict, [], + [strict + ]} ,{tempconvert_12, [], [tempconvert_w3schools %% same as for v. 1.1 ,tempconvert_fault %% the server only returns a fault with v 1.2 @@ -197,6 +206,15 @@ init_per_group(cowboy_server, Config) -> {"[0-9]{13,}", throw}, {http_server, cowboy_version()}]), Config; +init_per_group(cowboy_middle_protocol, Config) -> + {ok, _} = soap:start_server(sendService_test_server, + [{"[a-zA-Z]+", invalid}, + {"^\\+?[0-9]{4,12}$", valid}, + {"[0-9]{13,}", throw}, + {http_server, test_cowboy_middle_protocol}, + {cowboy_version, cowboy_version()} + ]), + Config; init_per_group(inets_server, Config) -> {ok, _} = soap:start_server(sendService_test_server, [{http_server, soap_server_inets}, @@ -237,6 +255,20 @@ init_per_group(tempconvert, Config) -> ], compile_wsdl("tempconvert.wsdl", Options, Config), Config; +init_per_group(strict, Config) -> + Options = [{http_client, ibrowse}, {http_server, cowboy_version()}, + {namespaces, [{"http://www.w3schools.com/xml/", "t"}]}, + {generate, both}, {generate_tests, none}, + {client_name, "tempconvert_client_strict"}, + {server_name, "tempconvert_server_strict"}, + {erlsom_options, [{strict, true}]}, + {test_values,true}, + {service, "TempConvert"}, {port, "TempConvertSoap"} + ], + compile_wsdl("tempconvert_float.wsdl", Options, Config), + {ok, _} = soap:start_server(tempconvert_server_strict, [{port, 8080}, + {http_server, cowboy_version()}]), + Config; init_per_group(tempconvert_12, Config) -> inets:start(), Options = [{http_client, inets}, %% use inets for a change @@ -298,9 +330,15 @@ end_per_group(inets_server, _Config) -> end_per_group(cowboy_server, _Config) -> soap:stop_server(sendService_test_server), ok; +end_per_group(cowboy_middle_protocol, _Config) -> + soap:stop_server(sendService_test_server), + ok; end_per_group(tempconvert_local, _Config) -> soap:stop_server(tempconvert_server), ok; +end_per_group(strict, _Config) -> + soap:stop_server(tempconvert_server_strict), + ok; end_per_group(wsdl_2_0, _Config) -> soap:stop_server(wsdl_2_0_server), ok; @@ -325,9 +363,11 @@ end_per_group(_, _Config) -> %%-------------------------------------------------------------------- all() -> [{group, cowboy_server}, + {group, cowboy_middle_protocol}, {group, inets_server}, {group, tempconvert}, {group, tempconvert_12}, + {group, strict}, {group, client}, {group, soap_req}, {group, wsdls}, %% takes a long time @@ -447,6 +487,12 @@ tempconvert_local_ok(_Config) -> {'t:CelsiusToFahrenheit', "37.8"}, [], [{url, "http://localhost:8080"}]). +strict(_Config) -> + {ok,200, _, [], {'t:CelsiusToFahrenheitResponse',3.14159}, [], _} = + tempconvert_client_strict:'CelsiusToFahrenheit'( + {'t:CelsiusToFahrenheit', 37.8}, [], + [{url, "http://localhost:8080"}]). + wsdl_2_0_example(_Config) -> {ok,200, _, [], _, [], _} = wsdl_2_0_client:getBookList( @@ -884,7 +930,7 @@ cowboy_version() -> Info = cowboy:module_info(), Exports = proplists:get_value(exports, Info), case proplists:get_value(start_tls, Exports) of - 4 -> + 3 -> soap_server_cowboy_2; undefined -> soap_server_cowboy_1 diff --git a/test/soap_SUITE_data/tempconvert_float.wsdl b/test/soap_SUITE_data/tempconvert_float.wsdl new file mode 100644 index 0000000..48de5a4 --- /dev/null +++ b/test/soap_SUITE_data/tempconvert_float.wsdl @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/test_cowboy_middle_protocol.erl b/test/test_cowboy_middle_protocol.erl new file mode 100644 index 0000000..dba812e --- /dev/null +++ b/test/test_cowboy_middle_protocol.erl @@ -0,0 +1,70 @@ +-module(test_cowboy_middle_protocol). + +-export([start/2]). +-export([stop/0]). +-export([init/3, init/2]). +-export([on_request/1]). +-export([execute/2]). +-export([upgrade/4]). + + +start(Module, Options) -> + Port = proplists:get_value(port, Options, 8080), + {ok, _} = application:ensure_all_started(cowboy), + Dispatch = cowboy_router:compile([ + {'_', [{'_', ?MODULE, {Module, Options}}]}]), + + case proplists:get_value(cowboy_version, Options) of + soap_server_cowboy_1 -> + cowboy:start_http(http, 10, [{port, Port}], + [ {env, [{dispatch, Dispatch}]}, + {onrequest, fun ?MODULE:on_request/1} + ]); + soap_server_cowboy_2 -> + cowboy:start_clear(http, [{port, Port}], + #{env => #{dispatch => Dispatch}, + middlewares => [ cowboy_router + , ?MODULE + , cowboy_handler + ] + } + ) + end. + +stop() -> + cowboy:stop_listener(http), + application:stop(cowboy), + application:stop(ranch). + +on_request(Req0) -> + {ok, Body, Req1} = cowboy_req:body(Req0), + cowboy_req:set_meta(body, Body, Req1). + +execute(Req, Env) -> + {ok, Body, Req1} = cowboy_req:read_body(Req), + {ok, Req1, maps:put(req_body, Body, Env)}. + +%% cowboy 1 callback +init(_, _Req, {_Module, Options}) -> + %% The module 'soap_cowboy_protocol' will be called + %% for each request, with Module (= the handler module) and + %% the options as parameter. + soap_server_cowboy_1 = proplists:get_value(cowboy_version, Options), + {upgrade, protocol, ?MODULE}. + +%% cowboy 2 callback +init(Req, {Module, Options}) -> + soap_server_cowboy_2 = proplists:get_value(cowboy_version, Options), + {?MODULE, Req, {Module, Options}}. + +upgrade(Cowboy_req, Env, Soap_handler, {Handler, Options}) -> + case proplists:get_value(cowboy_version, Options) of + soap_server_cowboy_1 -> + Options2 = proplists:delete(cowboy_version, Options), + {Message, Cowboy_req2} = cowboy_req:meta(body, Cowboy_req), + soap_cowboy_1_protocol:upgrade(Cowboy_req2, Env, Soap_handler, {Handler, Options2}, Message); + soap_server_cowboy_2 -> + Options2 = proplists:delete(cowboy_version, Options), + Message = maps:get(req_body, Env), + soap_cowboy_2_protocol:upgrade(Cowboy_req, Env, Soap_handler, {Handler, Options2}, Message) + end.