From: Ina C. <in...@st...> - 2001-02-22 06:57:12
|
Hi Fergus, I've made the changes that you've pointed out this morning. However, there is a bug (software error) inside `soap_interface.m' when I tried to abstract out the code. I've included the error message and marked as XXX inside the code. Can you please take a look at it? Sorry for the last minute rush. ps. Since there is a limit on the size of the mail, I've separated the diff into 2 parts. Thanks, Ina <in...@st...> ======================================================================== Estimated hours taken: 38 Add an interface between server and client such that the client can send messages in mercury types to the server. /server/client_demo.m new module showing how to call the soap_interface to send messages to the SOAP server. /server/options.m add new options to handle command line arguments. /server/soap_interface.m new module - provide an interface for programmers to send messages to the SOAP server. /server/soap_test_methods.m /server/web_methods.m add a function to demonstrate the soap interface. /server/soap.m /server/server.m modify some error handling code. [Part 1] Index: client_demo.m =================================================================== RCS file: client_demo.m diff -N client_demo.m --- /dev/null Mon Dec 11 17:26:27 2000 +++ client_demo.m Wed Feb 21 22:48:27 2001 @@ -0,0 +1,184 @@ +%---------------------------------------------------------------------------% +% Copyright (C) 2001 The University Of Melbourne +% This file may only be copied under the terms of the GNU General Public +% License - see the file COPYING +%---------------------------------------------------------------------------% +% +% File: client_demo.m +% Author: inch +% +% This module provide a sample of how to call the SOAP interface to send +% messages from the client to the SOAP server. +% +%---------------------------------------------------------------------------% + +:- module client_demo. +:- interface. +:- import_module io. + +:- pred main(io__state::di, io__state::uo) is cc_multi. + +%---------------------------------------------------------------------------% + +:- implementation. +:- import_module http, options. + +:- import_module bool, char, exception, getopt. +:- import_module int, list, require, std_util, string. + +:- import_module soap_interface. + +main --> + io__command_line_arguments(Args0), + { OptionOpts = option_ops(short_option, long_option, option_defaults)}, + { getopt__process_options(OptionOpts, Args0, _Args, OptionsResult) }, + ( + { OptionsResult = ok(OptTable) }, + { getopt__lookup_bool_option(OptTable, help, Help) }, + ( + { Help = yes }, + options_help + ; + { Help = no }, + { getopt__lookup_string_option(OptTable, host, Host) }, + { getopt__lookup_int_option(OptTable, port, Port) }, + { getopt__lookup_string_option(OptTable, method, Method) }, + { getopt__lookup_string_option(OptTable, uri, URI) }, + { getopt__lookup_string_option(OptTable, xml, XML) }, + ( + { XML = "" } + -> + ( + { Method_Handler = get_method_handler(Method) } + -> + call(Method_Handler, OptTable, Host, Port, Method, + URI, Responses, Mesg) + ; + { error("Method not supported.") } + ), + display_mercury_response(Mesg, Responses) + ; + soap_call_xml_type(Host, Port, Method, URI, XML, + Responses), + io__write_string(Responses) + ) + ) + ; + { OptionsResult = error(OptionErrorString) }, + io__write_string(OptionErrorString), + io__nl, + options_help + ). + +%---------------------------------------------------------------------------% + +:- type method_handler == pred(option_table(option), string, int, method, + uri, list(univ), string, io__state, io__state). +:- inst method_handler == (pred(in, in, in, in, in, out, out, di, uo) is det). + +:- func get_method_handler(string) = method_handler. +:- mode get_method_handler(in) = out(method_handler) is semidet. + + % Choose a corresponding predicate for a particular method. +get_method_handler("Hello") = handle_Hello. +get_method_handler("GetStockPrice") = handle_GetStockPrice. +get_method_handler("Add3Ints") = handle_Add3Ints. +get_method_handler("PurchaseBook") = handle_PurchaseBook. + +%---------------------------------------------------------------------------% + +:- pred handle_Hello(option_table(option), string, int, method, uri, + list(univ), string, io__state, io__state). +:- mode handle_Hello(in, in, in, in, in, out, out, di, uo) is det. + +handle_Hello(_OptTable, Host, Port, Method, URI, Responses, Mesg) --> + soap_call_mercury_type(Host, Port, Method, URI, [], Responses), + { Mesg = "" }. + +%---------------------------------------------------------------------------% + +:- pred handle_GetStockPrice(option_table(option), string, int, method, uri, + list(univ), string, io__state, io__state). +:- mode handle_GetStockPrice(in, in, in, in, in, out, out, di, uo) is det. + +handle_GetStockPrice(OptTable, Host, Port, Method, URI, Responses, Mesg) --> + { getopt__lookup_int_option(OptTable, int, Int) }, + ( + { Int = -1 } + -> + io__write_string("Parameter not supplied.\n"), + io__write_string("Program terminated.\n"), + { Mesg = "" }, + { Responses = [] } + ; + soap_call_mercury_type(Host, Port, Method, URI, + [univ(Int)], Responses), + { Mesg = "Stockprice = " } + ). + +%---------------------------------------------------------------------------% + +:- pred handle_Add3Ints(option_table(option), string, int, method, uri, + list(univ), string, io__state, io__state). +:- mode handle_Add3Ints(in, in, in, in, in, out, out, di, uo) is det. + +handle_Add3Ints(OptTable, Host, Port, Method, URI, Responses, Mesg) --> + { getopt__lookup_int_option(OptTable, add1, X) }, + { getopt__lookup_int_option(OptTable, add2, Y) }, + { getopt__lookup_int_option(OptTable, add3, Z) }, + ( + % If some of the arguments are not supplied, + % they are treated as 0. Only when all + % arguments are not supplied, the program + % terminates. + { X = 0 }, { Y = 0 }, { Z = 0 } + -> + io__write_string("Parameter not supplied.\n"), + io__write_string("Program terminated.\n"), + { Mesg = "" }, + { Responses = [] } + ; + soap_call_mercury_type(Host, Port, Method, URI, + [univ(X), univ(Y), univ(Z)], Responses), + { Mesg = "Add3Ints = " } + ). +%---------------------------------------------------------------------------% + +:- pred handle_PurchaseBook(option_table(option), string, int, method, uri, + list(univ), string, io__state, io__state). +:- mode handle_PurchaseBook(in, in, in, in, in, out, out, di, uo) is det. + +handle_PurchaseBook(OptTable, Host, Port, Method, URI, Responses, Mesg) --> + { getopt__lookup_string_option(OptTable, title, Title) }, + { getopt__lookup_string_option(OptTable, author, Author)}, + ( + { Title = "" } + -> + io__write_string("Title not supplied.\n"), + io__write_string("Program terminated.\n"), + { Mesg = "" }, + { Responses = [] } + ; + { Author = "" } + -> + io__write_string("Author not supplied.\n"), + io__write_string("Program terminated.\n"), + { Mesg = "" }, + { Responses = [] } + ; + soap_call_mercury_type(Host, Port, Method, URI, + [univ(Title), univ(Author)], Responses), + { Mesg = "" } + ). + +%---------------------------------------------------------------------------% +:- pred display_mercury_response(string::in, list(univ)::in, io__state::di, + io__state::uo) is det. + +display_mercury_response(Message, Responses) --> + io__write_string(Message), + list__foldl((pred(X::in, di, uo) is det --> + write(univ_value(X)), nl), + Responses). + + Index: options.m =================================================================== RCS file: /cvsroot/quicksilver/webserver/server/options.m,v retrieving revision 1.1 diff -u -r1.1 options.m --- options.m 2000/11/27 10:02:58 1.1 +++ options.m 2001/02/22 06:48:27 @@ -26,6 +26,17 @@ ; port ; root + % Client options + ; method + ; uri + ; xml + ; int % for GetStockPrice + ; add1 % for Add3Ints + ; add2 % for Add3Ints + ; add3 % for Add3Ints + ; title % for PurchaseBook + ; author % for PurchaseBook + % Miscellaneous Options ; help. @@ -63,25 +74,55 @@ option_default(port, int(8080)). option_default(root, string("/var/www")). + % General client options +option_default(method, string("")). +option_default(uri, string("no value")). + % for uri, empty string means that the intent of the + % SOAP message is provided by the HTTP Request-URI + % whereas no value means that there is no indication + % of the intent of the message. +option_default(xml, string("")). +option_default(int, int(-1)). +option_default(add1, int(0)). +option_default(add2, int(0)). +option_default(add3, int(0)). +option_default(title, string("")). +option_default(author, string("")). + % Miscellaneous Options option_default(help, bool(no)). % please keep this in alphabetic order +short_option('a', author). % short_option('f', config_file). short_option('h', help). short_option('H', host). +short_option('i', int). +short_option('m', method). short_option('P', port). short_option('R', root). +short_option('t', title). +short_option('u', uri). short_option('v', verbose). short_option('V', very_verbose). +short_option('x', xml). % long_option("config-file", config_file). +long_option("add1", add1). +long_option("add2", add2). +long_option("add3", add3). +long_option("author", author). long_option("help", help). long_option("host", host). +long_option("int", int). +long_option("method", method). long_option("port", port). long_option("root", root). +long_option("title", title). +long_option("uri", uri). long_option("verbose", verbose). long_option("very-verbose", very_verbose). +long_option("xml", xml). options_help --> io__write_strings([ @@ -99,6 +140,26 @@ "\t\tWhich port quicksilver listens on.\n", "\t-R, --root\n", "\t\tThe root directory for the html files to be served.\n", + + "\nClient Options:\n", + "\t-m, --method\n", + "\t\tMethod Name to be called.\n", + "\t-u, --uri\n", + "\t\tURI of SOAPAction header.\n", + "\t-x, --xml\n", + "\t\tAn XML message for the RPC.\n", + "\t-i, --int\n", + "\t\tStocknum for GetStockPrice.\n", + "\t--add1\n", + "\t\tFirst integer parameter for Add3Ints.\n", + "\t--add2\n", + "\t\tSecond integer parameter for Add3Ints.\n", + "\t--add3\n", + "\t\tThird integer parameter for Add3Ints.\n", + "\t-a, --author surname,firstname\n", + "\t\tAuthor of the book.\n", + "\t-t, --title\n", + "\t\tTitle of the book.\n", "\nVerbosity Options:\n", "\t-v, --verbose\n", Index: server.m =================================================================== RCS file: /cvsroot/quicksilver/webserver/server/server.m,v retrieving revision 1.6 diff -u -r1.6 server.m --- server.m 2001/02/09 05:15:42 1.6 +++ server.m 2001/02/22 06:48:27 @@ -420,26 +420,29 @@ { Request^cmd = post }, ( { Request^body = yes(Body) } , - parse_soapmessage(Body, NsBody), - % XXX parse_soapmessage calls error/1 when failed - % should change to response with http code = 415 - - { get_procedure_call(NsBody, Proc) }, - write(Proc), nl, nl, + parse_soapmessage(Body, NsBody, ErrMesg), + ( + { ErrMesg = yes(Mesg) }, + { HttpCode = 415 }, + { Result = Mesg }, + { ResBody = no_body } + ; + { ErrMesg = no }, + { get_procedure_call(NsBody, Proc) }, - { make_web_request(NsBody, Proc, WebRequest) }, - write(WebRequest), nl, nl, + { make_web_request(NsBody, Proc, WebRequest) }, - load_dynamic_library(uri_to_filename(Request^uri), + load_dynamic_library(uri_to_filename(Request^uri), WebRequest, Result, HttpCode, ErrorFlag), - ( + ( { ErrorFlag = no }, { generate_response_body(NsBody, Proc, Result, ResBody0) }, { ResBody = string_body(ResBody0) } - ; + ; { ErrorFlag = yes }, { ResBody = no_body } + ) ) ; % 200 - 299 is client request successful Index: soap.m =================================================================== RCS file: /cvsroot/quicksilver/webserver/server/soap.m,v retrieving revision 1.2 diff -u -r1.2 soap.m --- soap.m 2001/02/09 05:15:42 1.2 +++ soap.m 2001/02/22 06:48:27 @@ -14,13 +14,14 @@ :- module soap. :- interface. -:- import_module io, string. +:- import_module io, std_util, string. :- import_module xml, xml:ns. :- import_module web_methods. % Translates SOAP message to namespace aware SOAP message. -:- pred parse_soapmessage(string, ((xml:ns):nsDocument), io__state, io__state). -:- mode parse_soapmessage(in, out, di, uo) is det. +:- pred parse_soapmessage(string, ((xml:ns):nsDocument), maybe(string), + io__state, io__state). +:- mode parse_soapmessage(in, out, out, di, uo) is det. % Retrieves method name from the SOAP message. :- pred get_procedure_call(nsDocument, nsElement). @@ -50,7 +51,7 @@ % Parses soap message. % NsDoc is namespace aware soap message. -parse_soapmessage(SoapMessage, NsDoc) --> +parse_soapmessage(SoapMessage, NsDoc, ErrorMesg) --> pstate(mkEntity(SoapMessage), mkEncoding(utf8), init), io((pred(Dirs0::out, di, uo) is det --> get_environment_var("XML_DIRS", MStr), @@ -76,14 +77,16 @@ finish(Res), ( { Res = ok((_, Doc)) }, - { nsTranslate(Doc, NsDoc) } + { nsTranslate(Doc, NsDoc) }, + { ErrorMesg = no } ; % XXX instead of error, should change to response % with http code 415 Unsupported Media Type { Res = error(Err) }, { string__append("parse_soapmessage failed: ", Err, ErrMsg) }, - { error(ErrMsg) } + { ErrorMesg = yes(ErrMsg) }, + { NsDoc = nsDoc([], 0, [], array([comment("")])) } ). Index: soap_interface.m =================================================================== RCS file: soap_interface.m diff -N soap_interface.m --- /dev/null Mon Dec 11 17:26:27 2000 +++ soap_interface.m Wed Feb 21 22:48:27 2001 @@ -0,0 +1,458 @@ +%---------------------------------------------------------------------------% +% Copyright (C) 2001 The University of Melbourne +% This file may only be copied under the terms of the GNU General Public +% License - see the file COPYING +%---------------------------------------------------------------------------% +% +% File: soap_inteface.m +% Author: inch +% +% Reference: +% http://www.w3.org/TR/SOAP +% +% This module provide an interface for programmers to send messages +% to the SOAP server. It will provide as basic functionality the +% ability to do RPC over SOAP. The hard-coded XML encoding for +% Mercury datatypes (the same encoding as the SOAP server currently +% expects) will be used to encode the RPC calls. +% +%---------------------------------------------------------------------------% + +:- module soap_interface. +:- interface. +:- import_module io, list, std_util, string. +:- import_module tcp. + +:- type method == string. +:- type uri == string. +:- type xml == string. + + % Sends messages (in mercury types) to the SOAP server and returns + % the response (in mercury types). + % host and port specifies the host and port number that the client + % connects to; method specifies the method name of the RPC; + % uri indicates the intent of the SOAP HTTP request (see reference + % section 6.1); lists of univ are the input parameters of RPC and + % the output responses from the server. + +:- pred soap_call_mercury_type(host::in, port::in, method::in, + uri::in, list(univ)::in, list(univ)::out, + io__state::di, io__state::uo) is det. + + % Sends messages (in XML format) to the SOAP server and returns + % the response (in XML format). + % host and port specifies the host and port number that the client + % connects to; method specifies the method name of the RPC; + % uri indicates the intent of the SOAP HTTP request (see reference + % section 6.1); xmls are the message sends to the server and + % the response receives from it. + +:- pred soap_call_xml_type(host::in, port::in, method::in, uri::in, + xml::in, xml::out, io__state::di, io__state::uo) is det. + + +%---------------------------------------------------------------------------% + +:- implementation. +:- import_module int, require. +:- import_module stream. + + +soap_call_mercury_type(Host, Port, Method, SOAPuri, Parameters, + Responses) --> + { generate_request(Host, Method, SOAPuri, Parameters, Request) }, + tcp__connect(Host, Port, Result), + ( + { Result = ok(TCP_Connect) }, + service_connection(TCP_Connect, Request, XMLResponse) + ; + { Result = error(String) }, + { error(String) } + ), + tcp__shutdown(TCP_Connect), + ( + { Decode_Handler = decode_response(Method) }, + { call(Decode_Handler, XMLResponse, Responses) } + ; + { error("Soap_call_mercury_type: Method not supported") } + ). + + + % Choose a corresponding predicate to decode response for + % a particular method. + +:- type decode_handler == pred(string, list(univ)). +:- inst decode_handler == (pred(in, out) is det). + +:- func decode_response(string) = decode_handler. +:- mode decode_response(in) = out(decode_handler) is semidet. + + % XXX Add a handler for the new method here. + +decode_response("Hello") = decode_Hello_response. +decode_response("GetStockPrice") = decode_SP_response. +decode_response("Add3Ints") = decode_AddInt_response. +decode_response("PurchaseBook") = decode_PB_response. + + +%--------------------------------------------------------------------------% + +soap_call_xml_type(Host, Port, _Method, SOAPuri, XMLMesg, XMLResponse) --> + { generate_xml_request(Host, SOAPuri, XMLMesg, Request) }, + tcp__connect(Host, Port, Result), + ( + { Result = ok(TCP_Connect) }, + service_connection(TCP_Connect, Request, Response) + ; + { Result = error(String) }, + { error(String) } + ), + tcp__shutdown(TCP_Connect), + { retrieve_body(Response, XMLResponse) }. + + +:- pred generate_xml_request(host::in, uri::in, xml::in, string::out) + is det. + +generate_xml_request(Host, SOAPuri, XMLMesg, Request) :- + generate_xml_body(XMLMesg, Body), + string__length(Body, Length), + generate_header(Host, Length, SOAPuri, Header), + Request = insert_cr(Header) ++ Body. + +:- pred generate_xml_body(xml::in, string::out) is det. + +generate_xml_body(XMLMesg, SOAPBody) :- + SOAPBody = "<Envelope><Body>" ++ XMLMesg ++ "</Body></Envelope>". + +:- pred retrieve_body(string::in, xml::out) is det. + +retrieve_body(Response, Body) :- + ( + string__sub_string_search(Response, "\r\n<", Pos) + -> + string__length(Response, Length), + string__right(Response, Length - Pos, Body) + ; + error("error in retrieving body") + ). + +%-------------------------------------------------------------------------% + + % Generates HTTP header information and SOAP message in the body. +:- pred generate_request(host, method, uri, list(univ), string). +:- mode generate_request(in, in, in, in, out) is det. + +generate_request(Host, Method, SOAPuri, Parameters, Request) :- + /* + ( + Body_Handler = generate_body(Method), + call(Body_Handler, Method, Parameters, Body) + + */ + ( + Method = "Hello" + -> + generate_Hello_body(Method, Parameters, Body) + ; + Method = "GetStockPrice" + -> + generate_SP_body(Method, Parameters, Body) + ; + Method = "Add3Ints" + -> + generate_AddInt_body(Method, Parameters, Body) + ; + Method = "PurchaseBook" + -> + generate_PB_body(Method, Parameters, Body) + ; + error("Generate message body failed.\n") + ), + string__length(Body, Length), + generate_header(Host, Length, SOAPuri, Header), + Request = insert_cr(Header) ++ Body. + +% XXX if I switch to use higher order instead, the following error occur +% +% Uncaught exception: +% Software Error: Sorry, not implemented: determinism inference for +% higher-order predicate terms +% Stack dump not available in this grade. + + +:- type body_handler == pred(string, list(univ), string). +:- inst body_handler == (pred(in, in, out) is det). + +/* +:- func generate_body(string) = body_handler. +:- mode generate_body(in) = out(body_handler) is semidet. + + % XXX Add a body handler for the new method here. + +generate_body("Hello") = generate_Hello_body. +generate_body("GetStockPrice") = generate_SP_body. +generate_body("Add3Ints") = generate_AddInt_body. +generate_body("PurchaseBook") = generate_PB_body. + +*/ + +%-------------------------------------------------------------------------% +% Hello +%-------------------------------------------------------------------------% + + % Generates SOAP message. + % Hello pred has no input. +:- pred generate_Hello_body(string, list(univ), string). +:- mode generate_Hello_body(in, in, out). + +generate_Hello_body(MethodName, _Parameters, Body) :- + insert_envelope(MethodName, "", Body). + +:- pred decode_Hello_response(string::in, list(univ)::out) is det. + +decode_Hello_response(XMLResponse, Responses) :- + ( + string__sub_string_search(XMLResponse, "<output>", Start), + string__sub_string_search(XMLResponse, "</output>", End) + -> + string__left(XMLResponse, End, Response0), + TagLength = 8, % <output> + string__right(Response0, End-Start-TagLength, Response1), + Responses = [univ(Response1)] + ; + error("decode error") + ). + +%--------------------------------------------------------------------------% +% Add3Ints +%--------------------------------------------------------------------------% + + % Generate message body for Add3Ints. +:- pred generate_AddInt_body(method::in, list(univ)::in, string::out) + is det. + +generate_AddInt_body(Method, Parameters, Body) :- + list__map(create_xml_parameter, Parameters, XMLList), + string__append_list(XMLList, XMLString), + insert_envelope(Method, XMLString, Body). + +:- pred create_xml_parameter(univ::in, string::out) is det. + +create_xml_parameter(ParameterAsUniv, ParameterAsXML) :- + det_univ_to_type(ParameterAsUniv, ParameterAsInt), + string__int_to_string(ParameterAsInt, ParameterAsString), + ParameterAsXML = "<int>" ++ ParameterAsString ++ "</int>". + +:- pred decode_AddInt_response(string::in, list(univ)::out) is det. + +decode_AddInt_response(XMLResponse, Responses) :- + ( + string__sub_string_search(XMLResponse, "<result>", Start), + string__sub_string_search(XMLResponse, "</result>", End) + -> + string__left(XMLResponse, End, Response0), + TagLength = 8, % <result> + string__right(Response0, End-Start-TagLength, Response1), + ( + string__to_int(Response1, ResponseAsInt) + -> + Responses = [univ(ResponseAsInt)] + ; + error("decode error") + ) + ; + error("decode error") + ). + +%--------------------------------------------------------------------------% +% GetStockPrice +%--------------------------------------------------------------------------% + + % Generates SOAP message body for GetStockPrice. + % XXX assume no namespace and client has a copy of the schema + % used in the server side to encode mercury types. +:- pred generate_SP_body(method, list(univ), string). +:- mode generate_SP_body(in, in, out). + + % schema for GetStockPrice: + % <element name="stocknum" type="xsd:int"> + +generate_SP_body(MethodName, Parameters, Body) :- + % since client knows that this function takes in only + % one parameter, there is no need to call list_foldl to + % translate the parameters. + list__index1_det(Parameters, 1, Parameter), + generate_SP_param(Parameter, ParamAsString), + insert_envelope(MethodName, ParamAsString, Body). + + +:- pred generate_SP_param(univ, string). +:- mode generate_SP_param(in, out) is det. + +generate_SP_param(ParamAsUniv, Tag) :- + det_univ_to_type(ParamAsUniv, ParamAsValue), + string__int_to_string(ParamAsValue, ParamAsString), + Tag = "<stocknum>" ++ ParamAsString ++ "</stocknum>". + +:- pred decode_SP_response(string::in, list(univ)::out) is det. + +decode_SP_response(XMLResponse, Responses) :- + ( + string__sub_string_search(XMLResponse, "<price>", Start), + string__sub_string_search(XMLResponse, "</price>", End) + -> + string__left(XMLResponse, End, Response0), + TagLength = 7, % <price> + string__right(Response0, End - Start - TagLength, Response1), + ( + string__to_int(Response1, ResponseAsInt) + -> + Response = ResponseAsInt + ; + error("decode error") + ), + Responses = [univ(Response)] + ; + error("decode error") + ). + +%-------------------------------------------------------------------------% +% PurchaseBook +%-------------------------------------------------------------------------% + + % Generate message body for PurchaseBook. +:- pred generate_PB_body(method::in, list(univ)::in, string::out) is det. +generate_PB_body(Method, Parameters, Body) :- + generate_PB_parameters(Parameters, ParameterString0), + ParameterString = "<book>" ++ ParameterString0 ++ "</book>", + insert_envelope(Method, ParameterString, Body). + +:- pred generate_PB_parameters(list(univ)::in, string::out) is det. + +generate_PB_parameters(ParamList, Body):- + list__index1_det(ParamList, 1, TitleAsUniv), + det_univ_to_type(TitleAsUniv, TitleAsString), + Title = "<title>" ++ TitleAsString ++ "</title>", + + list__index1_det(ParamList, 2, AuthorAsUniv), + det_univ_to_type(AuthorAsUniv, AuthorAsString), + ( + string__sub_string_search(AuthorAsString, ",", Pos) + -> + string__left(AuthorAsString, Pos, Surname), + string__length(AuthorAsString, Length), + string__right(AuthorAsString, Length - (Pos + 1), Firstname) + ; + Surname = AuthorAsString, + Firstname = " " + ), + Author = "<author>" ++ + "<surname>" ++ Surname ++ "</surname>" ++ + "<firstname>" ++ Firstname ++ "</firstname>" ++ + "</author>", + Body = Title ++ Author. + +:- pred decode_PB_response(string::in, list(univ)::out) is det. + +decode_PB_response(XMLResponse, Responses) :- + ( + string__sub_string_search(XMLResponse, "<comment>", Start), + string__sub_string_search(XMLResponse, "</comment>", End) + -> + string__left(XMLResponse, End, Comment0), + TagLength = 9, % <comment> + string__right(Comment0, End - Start - TagLength, Comment1), + Comment = [univ(Comment1)] + ; + Comment = [] + ), + ( + string__sub_string_search(XMLResponse, "<price>", Start1), + string__sub_string_search(XMLResponse, "</price>", End1) + -> + string__left(XMLResponse, End1, Price0), + TagLength1 = 7, % <price> + string__right(Price0, End1 - Start1 - TagLength1, Price1), + Price = [univ("Price = " ++ Price1)] + ; + Price = [] + ), + list__append(Comment, Price, Responses). + + +%-------------------------------------------------------------------------% +% Shared functions +%-------------------------------------------------------------------------% + + % Generates HTTP header. +:- pred generate_header(host::in, int::in, uri::in, string::out) is det. + +generate_header(Host, Length, SOAPuri, Headers) :- + Header1 = "POST /libsoap_test_methods.so HTTP/1.1", + Header2 = "Host: " ++ Host, + Header3 = "Content-Type: text/xml", + string__int_to_string(Length, LengthAsString), + Header4 = "Content-Length: " ++ LengthAsString, + ( + SOAPuri = "no value" + -> + Header5 = "SOAPAction:" + ; + Header5 = "SOAPAction: " ++ SOAPuri + ), + Headers = insert_cr(Header1) ++ insert_cr(Header2) ++ + insert_cr(Header3) ++ insert_cr(Header4) ++ + insert_cr(Header5). + + % Generate SOAP Envelope + +:- pred insert_envelope(string::in, string::in, string::out). +insert_envelope(MethodName, Parameters, Body) :- + Body = "<Envelope><Body><" ++ MethodName ++ ">" ++ + Parameters ++ "</" ++ MethodName ++ ">" ++ + "</Body></Envelope>". + + % Insert carriage return into the line. +:- func insert_cr(string) = string. +insert_cr(Line) = Line ++ "\r\n". + +%------------------------------------------------------------------------% + + % Sends request to server using HTTP and receives response + % from server. +:- pred service_connection(tcp::in, string::in, string::out, + io__state::di, io__state::uo) is det. + +service_connection(TCP, Request, Response) --> + send_request(TCP, Request), + read_response(TCP, Response). + + % Sends the request to the server side. +:- pred send_request(S, string, io__state, io__state) <= stream__output(S). +:- mode send_request(in, in, di, uo) is det. + +send_request(S, Requests) --> + stream__write_string(S, Requests). + + % Reads responses from server. +:- pred read_response(S, string, io__state, io__state) <= stream__input(S). +:- mode read_response(in, out, di, uo) is det. + +read_response(S, Response) --> + stream__read_line(S, LineResult), + ( + { LineResult = ok(Line) }, + { string__from_char_list(Line, String) }, + { Response = String ++ Response0 }, + read_response(S, Response0) + ; + { LineResult = eof }, + { Response = "" } + ; + { LineResult = error(Error) }, + { error(string__format("read_response: %s.", [s(Error)])) } + ). + +%-------------------------------------------------------------------------% |