<!-- Module User's Guide -->

<chapter>

	<title>&adminguide;</title>

	<section id="overview" xreflabel="Overview">
	<title>Overview</title>
	<para>
	The <emphasis>rest_client</emphasis> module provides a means of interacting
	with an HTTP server by doing RESTful queries, such as GET, POST and PUT.
	</para>
	</section>

	<section id="tcp-connection-reusage" xreflabel="TCP Connection Reusage">
	<title>TCP Connection Reusage</title>
	<para>
	Unless specified otherwise by the server through a "Connection: close"
	indication, the module will keep and reuse the TCP connections it creates
	as much as possible, regardless if the script writer performs blocking or
	asynchronous HTTP requests.  These connections are not shared among OpenSIPS
	workers — each worker maintains its own set of connections.
	</para>
	</section>

	<section id="dependencies" xreflabel="Dependencies">
	<title>Dependencies</title>
	<section>
		<title>&osips; Modules</title>
		<para>
		The following modules must be loaded before this module:
			<itemizedlist>
			<listitem>
			<para>
				<emphasis>No dependencies on other &osips; modules.</emphasis>.
			</para>
			</listitem>
			</itemizedlist>
		</para>
	</section>

	<section>
		<title>External Libraries or Applications</title>
		<para>
		The following libraries or applications must be installed before
		running &osips; with this module loaded:
			<itemizedlist>
			<listitem>
			<para>
				<emphasis>libcurl</emphasis>.
			</para>
			</listitem>
			</itemizedlist>
		</para>
	</section>
	</section>

	<section id="exported_parameters" xreflabel="Exported Parameters">
		<title>Exported Parameters</title>
	<section id="param_curl_timeout" xreflabel="curl_timeout">
		<title><varname>curl_timeout</varname> (integer)</title>
		<para>
		The maximum allowed time for any HTTP(S) transfer to complete.  This
		interval is inclusive of the initial connect time window, hence the value
		of this parameter must be greater than or equal to
		<xref linkend="param_connection_timeout"/>.
		</para>
		<para>
		<emphasis>
			Default value is <quote>20</quote> seconds.
		</emphasis>
		</para>
		<example>
		<title>Setting the <varname>curl_timeout</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("rest_client", "curl_timeout", 10)
...
</programlisting>
		</example>
	</section>

	<section id="param_connection_timeout" xreflabel="connection_timeout">
		<title><varname>connection_timeout</varname> (integer)</title>
		<para>
		The maximum allowed time to establish a connection with the server.
		</para>
		<para>
		<emphasis>
			Default value is <quote>20</quote> seconds.
		</emphasis>
		</para>
		<example>
		<title>Setting the <varname>connection_timeout</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("rest_client", "connection_timeout", 4)
...
</programlisting>
		</example>
	</section>

	<section id="param_connect_poll_interval" xreflabel="connect_poll_interval">
		<title><varname>connect_poll_interval</varname> (integer)</title>
		<para>
		Only relevant with async requests.  Allows complete control over how
		quickly we want to detect libcurl's completed blocking TCP/TLS handshakes,
		so the async transfers can be put in the background.  A lower
		<xref linkend="param_connect_poll_interval"/> may speed up all async
		HTTP transfers, but will also increase CPU usage.
		</para>
		<para>
		<emphasis>
			Default value is <quote>20</quote> milliseconds.
		</emphasis>
		</para>
		<example>
		<title>Setting the <varname>connect_poll_interval</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("rest_client", "connect_poll_interval", 2)
...
</programlisting>
		</example>
	</section>

	<section id="param_max_async_transfers" xreflabel="max_async_transfers">
		<title><varname>max_async_transfers</varname> (integer)</title>
		<para>
		Maximum number of asynchronous HTTP transfers <emphasis>a single</emphasis>
		OpenSIPS worker is allowed to run simultaneously. As long as this threshold
		is reached for a worker, all new async transfers it attempts to perform
		will be done in a blocking manner, with appropriate logging warnings.
		</para>
		<para>
		<emphasis>
			Default value is <quote>100</quote>.
		</emphasis>
		</para>
		<example>
		<title>Setting the <varname>max_async_transfers</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("rest_client", "max_async_transfers", 300)
...
</programlisting>
		</example>
	</section>

	<section id="param_max_transfer_size" xreflabel="max_transfer_size">
		<title><varname>max_transfer_size</varname> (integer)</title>
		<para>
		The maximum allowed size of a single transfer (download).  Reaching
		this limit during a transfer will cause the transfer to stop
		immediately, returning error -10 at script level.  A value of
		<emphasis role='bold'>0</emphasis> will disable the check.
		</para>
		<para>
		<emphasis>
			Default value is <quote>10240</quote> (KB).
		</emphasis>
		</para>
		<example>
		<title>Setting the <varname>max_transfer_size</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("rest_client", "max_transfer_size", 64)
...
</programlisting>
		</example>
	</section>

	<section id="param_ssl_verifypeer" xreflabel="ssl_verifypeer">
		<title><varname>ssl_verifypeer</varname> (integer)</title>
		<para>
		Set this to 0 in order to disable the verification of the remote peer's
		certificate. Verification is done using a default bundle of CA certificates
		which come with libcurl.
		</para>
		<para>
		<emphasis>
			Default value is <quote>1</quote> (enabled).
		</emphasis>
		</para>
		<example>
		<title>Setting the <varname>ssl_verifypeer</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("rest_client", "ssl_verifypeer", 0)
...
</programlisting>
		</example>
	</section>

	<section id="param_ssl_verifyhost" xreflabel="ssl_verifyhost">
		<title><varname>ssl_verifyhost</varname> (integer)</title>
		<para>
		Set this to 0 in order to disable the verification that the remote peer
		actually corresponds to the server listed in the certificate.
		</para>
		<para>
		<emphasis>
			Default value is <quote>1</quote> (enabled).
		</emphasis>
		</para>
		<example>
		<title>Setting the <varname>ssl_verifyhost</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("rest_client", "ssl_verifyhost", 0)
...
</programlisting>
		</example>
	</section>

	<section id="param_ssl_capath" xreflabel="ssl_capath">
		<title><varname>ssl_capath</varname> (integer)</title>
		<para>
		An optional path for CA certificates to be used for host verifications.
		</para>
		<example>
		<title>Setting the <varname>ssl_capath</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("rest_client", "ssl_capath", "/home/opensips/ca_certificates")
...
</programlisting>
		</example>
	</section>

	<section id="param_curl_http_version" xreflabel="curl_http_version">
		<title><varname>curl_http_version</varname> (integer)</title>
		<para>
		Use a specific HTTP version for all requests. Possible values:
		</para>
		<para>
		<itemizedlist>
		<listitem>
			<para>0 (default) - use whatever is deemed fit by libcurl</para>
		</listitem>
		<listitem>
			<para>1 - enforce HTTP 1.0 requests</para>
		</listitem>
		<listitem>
			<para>2 - enforce HTTP 1.1 requests</para>
		</listitem>
		<listitem>
			<para>3 - attempt HTTP 2 requests. Fall back to HTTP 1.1 if HTTP 2
				cannot be negotiated with the server. Requires libcurl 7.33.0+.
			</para>
		</listitem>
		<listitem>
			<para>4 - attempt HTTP 2 over TLS (HTTPS) only. Fall back to HTTP
				1.1 if HTTP 2 cannot be negotiated with the HTTPS server.
				For clear text HTTP servers, use HTTP 1.1.
				Requires libcurl 7.47.0+.
			</para>
		</listitem>
		<listitem>
			<para>5 - Issue non-TLS HTTP requests using HTTP 2 without HTTP 1.1
				Upgrade. It requires prior knowledge that the server supports
				HTTP 2 straight away. HTTPS requests will still do HTTP/2 the
				standard way with negotiated protocol version in the TLS
				handshake. Requires libcurl 7.49.0+.
			</para>
		</listitem>
		</itemizedlist>
		</para>
		<para>
			<emphasis>more details <ulink url="https://curl.haxx.se/libcurl/c/CURLOPT_HTTP_VERSION.html">
				<citetitle>here</citetitle></ulink>, where the documentation for
			this setting was inspired (read: pilfered) from</emphasis>
		</para>
		<example>
		<title>Setting the <varname>curl_http_version</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("rest_client", "curl_http_version", 3)
...
</programlisting>
		</example>
	</section>

	<section id="param_enable_expect_100" xreflabel="enable_expect_100">
		<title><varname>enable_expect_100</varname> (boolean)</title>
		<para>
		Include a "Expect: 100-continue" HTTP header field whenever the body
		size of a POST or PUT request exceeds 1024 bytes.  Once enabled, the
		timeout for waiting for a "100 Continue" reply from the server is 1
		second, after which the body upload will begin.
		</para>
		<para>
		<emphasis>
			Default value is <quote>false</quote> (disabled).
		</emphasis>
		</para>
		<example>
		<title>Setting the <varname>enable_expect_100</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("rest_client", "enable_expect_100", true)
...
</programlisting>
		</example>
	</section>

	<section id="param_no_concurrent_connects" xreflabel="no_concurrent_connects">
		<title><varname>no_concurrent_connects</varname> (boolean)</title>
		<para>
		Set to <emphasis>true</emphasis> in order to only allow one OpenSIPS
		worker to connect to a given URL hostname at a time.  While a worker
		is connecting, all other workers will receive error code
		<emphasis role='bold'>-4 (already connecting)</emphasis> when attempting
		to perform any rest_client operation to the same hostname, regardless if
		the operation is sync or async.
		</para>
		<para>
		For sync transfers, the scope of the worker process serialization
		extends to the entire cURL transfer (TCP connect + upload + download),
		as all three phases take place within a single cURL library call.
		</para>
		<para>
		This parameter may be useful in order to prevent system outages caused
		by concurrent blocking of all OpenSIPS workers on a failed (hanging)
		HTTP service, with no more free workers being left to process incoming
		SIP packets.
		</para>
		<para>
		<emphasis>
			Default value is <quote>false</quote> (disabled).
		</emphasis>
		</para>
		<example>
		<title>Setting the <varname>no_concurrent_connects</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("rest_client", "no_concurrent_connects", true)
...
</programlisting>
		</example>
	</section>

	<section id="param_curl_conn_lifetime" xreflabel="curl_conn_lifetime">
		<title><varname>curl_conn_lifetime</varname> (integer)</title>
		<para>
		Only relevant when <xref linkend="param_no_concurrent_connects"/> is enabled.
		By setting this parameter, script developers can leverage the connection
		reusage capabilities of libcURL and entirely skip the "no concurrent transfers"
		logic on a given SIP worker, should that worker already be known to have a TCP
		connection to the target URL hostname
		(established by a previous rest_xxx() function call).
		</para>
		<para>
		The parameter denotes the lifetime, in seconds, of TCP connections kept
		within libcURL for reusage, a setting which is often operating system
		dependant, and which may also be affected by enabling/disabling keepalives.
		Consult your operating system's and/or libcurl's documentation for further
		information on the max lifetime of your cURL TCP connections.
		</para>
		<para>
		<emphasis>
			Default value is <emphasis>0</emphasis> (disabled).
		</emphasis>
		</para>
		<example>
		<title>Setting the <varname>curl_conn_lifetime</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("rest_client", "curl_conn_lifetime", 1800)
...
</programlisting>
		</example>
	</section>

	</section>

	<section id="exported_functions" xreflabel="exported_functions">
	<title>Exported Functions</title>
	<section id="func_rest_get" xreflabel="rest_get()">
		<title>
		<function moreinfo="none">rest_get(url, body_pv, [ctype_pv], [retcode_pv])</function>
		</title>
		<para>
		Perform a blocking HTTP GET on the given <emphasis>url</emphasis> and
		return a representation of the resource.
		</para>
		<para>Parameters:</para>
		<itemizedlist>
			<listitem><para>
				<emphasis>url</emphasis> (string)
			</para></listitem>
			<listitem><para>
				<emphasis>body_pv</emphasis> (var) - output variable which will hold the
				body of the HTTP response.
			</para></listitem>
			<listitem><para>
				<emphasis>ctype_pv</emphasis> (var, optional) - output variable which will
				contain the value of the "Content-Type:" header of the response.
			</para></listitem>
			<listitem><para>
				<emphasis>retcode_pv</emphasis> (var, optional) - output variable which will
				retain the status code of the HTTP response.
				A <emphasis role='bold'>0</emphasis> status code value means no HTTP
				reply arrived at all.
			</para></listitem>
		</itemizedlist>

		&rest_return_codes;

		<para>
		This function can be used from any route.
		</para>
		<example>
		<title><function moreinfo="none">rest_get</function> usage</title>
		<programlisting format="linespecific">
...
# Example of querying a REST service to get the credit of an account
$var(rc) = rest_get("https://getcredit.org/?account=$fU",
                    $var(credit),
                    $var(ct),
                    $var(rcode));
if ($var(rc) &lt; 0) {
	xlog("rest_get() failed with $var(rc), acc=$fU\n");
	send_reply(500, "Server Internal Error");
	exit;
}

if ($var(rcode) != 200) {
	xlog("L_INFO", "rest_get() rcode=$var(rcode), acc=$fU\n");
	send_reply(403, "Forbidden");
	exit;
}
...
</programlisting>
		</example>
	</section>
	<section id="func_rest_post" xreflabel="rest_post()">
		<title>
		<function moreinfo="none">rest_post(url, send_body, [send_ctype],
				recv_body_pv, [recv_ctype_pv], [retcode_pv])
		</function>
		</title>
		<para>
		Perform a blocking HTTP POST on the given <emphasis>url</emphasis>.
		</para>
		<para>
		Note that the <emphasis>send_body</emphasis> parameter can also accept a format-string
		but it cannot be larger than 1024 bytes. For larger messages, you must build them in a
		pseudo-variable and pass it to the function.
		</para>

		<para>Parameters:</para>
		<itemizedlist>
			<listitem><para>
				<emphasis>url</emphasis> (string)
			</para></listitem>
			<listitem><para>
				<emphasis>send_body</emphasis> (string) - The request body.
			</para></listitem>
			<listitem><para>
				<emphasis>send_ctype</emphasis> (string, optional) - The MIME
				Content-Type header for the request. The default is
				<emphasis>"application/x-www-form-urlencoded"</emphasis>
			</para></listitem>
			<listitem><para>
				<emphasis>recv_body_pv</emphasis> (var) - output variable which
				will hold the body of the HTTP response.
			</para></listitem>
			<listitem><para>
				<emphasis>recv_ctype_pv</emphasis> (var, optional) - output
				variable which will contain the value of the "Content-Type"
				header of the response
			</para></listitem>
			<listitem><para>
				<emphasis>retcode_pv</emphasis> (var, optional) - output variable
				which will retain the status code of the HTTP response.
				A <emphasis role='bold'>0</emphasis> status code value means no HTTP
				reply arrived at all.
			</para></listitem>
		</itemizedlist>

		&rest_return_codes;

		<para>
		This function can be used from any route.
		</para>
		<example>
		<title><function moreinfo="none">rest_post</function> usage</title>
		<programlisting format="linespecific">
...
# Creating a resource using a RESTful service with an HTTP POST request
$var(rc) = rest_post("https://myserver.org/register_user",
                     $fU, , $var(body), $var(ct), $var(rcode));
if ($var(rc) &lt; 0) {
	xlog("rest_post() failed with $var(rc), user=$fU\n");
	send_reply(500, "Server Internal Error 1");
	exit;
}

if ($var(rcode) != 200) {
	xlog("rest_post() rcode=$var(rcode), user=$fU\n");
	send_reply(500, "Server Internal Error 2");
	exit;
}
...

</programlisting>
		</example>
	</section>
	<section id="func_rest_put" xreflabel="rest_put()">
		<title>
		<function moreinfo="none">rest_put(url, send_body, [send_ctype],
				recv_body_pv[, [recv_ctype_pv][, [retcode_pv]]])
		</function>
		</title>
		<para>
		Perform a blocking HTTP PUT on the given <emphasis>url</emphasis>.
		</para>
		<para>
		Similar to <xref linkend="func_rest_post"/>, the <emphasis>send_body_pv</emphasis>
		parameter can also accept a format-string but it cannot be larger than 1024 bytes. For
		larger messages, you must build them in a pseudo-variable and pass it to the function.
		</para>

		<para>Parameters:</para>
		<itemizedlist>
			<listitem><para>
				<emphasis>url</emphasis> (string)
			</para></listitem>
			<listitem><para>
				<emphasis>send_body</emphasis> (string) - The request body.
			</para></listitem>
			<listitem><para>
				<emphasis>send_ctype</emphasis> (string, optional) - The MIME
				Content-Type header for the request. The default is
				<emphasis>"application/x-www-form-urlencoded"</emphasis>
			</para></listitem>
			<listitem><para>
				<emphasis>recv_body_pv</emphasis> (var) - output variable which
				will hold the body of the HTTP response.
			</para></listitem>
			<listitem><para>
				<emphasis>recv_ctype_pv</emphasis> (var, optional) - output variable
				which will contain the value of the "Content-Type" header of the response
			</para></listitem>
			<listitem><para>
				<emphasis>retcode_pv</emphasis> (var, optional) - output variable
				which will retain the status code of the HTTP response.
				A <emphasis role='bold'>0</emphasis> status code value means no HTTP
				reply arrived at all.
			</para></listitem>
		</itemizedlist>

		&rest_return_codes;

		<para>
		This function can be used from any route.
		</para>
		<example>
		<title><function moreinfo="none">rest_put</function> usage</title>
		<programlisting format="linespecific">
...
# Creating/Updating a resource using a RESTful service with an HTTP PUT request
$var(rc) = rest_put("https://myserver.org/users/$fU",
                    $var(userinfo), , $var(body), $var(ct), $var(rcode));
if ($var(rc) &lt; 0) {
	xlog("rest_put() failed with $var(rc), user=$fU\n");
	send_reply(500, "Server Internal Error 3");
	exit;
}

if ($var(rcode) != 200) {
	xlog("rest_put() rcode=$var(rcode), user=$fU\n");
	send_reply(500, "Server Internal Error 4");
	exit;
}
...
</programlisting>
		</example>
	</section>
	<section id="func_rest_append_hf" xreflabel="rest_append_hf()">
		<title>
		<function moreinfo="none">rest_append_hf(txt)</function>
		</title>
		<para>
		Append <emphasis>txt</emphasis> to the HTTP headers of the subsequent request.
		Multiple headers can be appended by making multiple calls
		before executing a request.
		</para>
		<para>
		The contents of <emphasis>txt</emphasis> should adhere to the
		specification for HTTP headers (ex. Field: Value)
		</para>
		<para>Parameters</para>
		<itemizedlist>
		<listitem>
			<para><emphasis>txt</emphasis> (string)</para>
		</listitem>
		</itemizedlist>
		<para>
		This function can be used from any route.
		</para>
		<example>
		<title><function moreinfo="none">rest_append_hf</function> usage</title>
		<programlisting format="linespecific">
...
# Example of querying a REST service requiring additional headers

rest_append_hf("Authorization: Bearer mF_9.B5f-4.1JqM");
$var(rc) = rest_get("http://getcredit.org/?account=$fU", $var(credit));
...
		</programlisting>
		</example>
	</section>
	<section id="func_rest_init_client_tls" xreflabel="rest_init_client_tls()">
		<title>
		<function moreinfo="none">rest_init_client_tls(tls_client_domain)</function>
		</title>
		<para>
		Force a specific TLS domain to be used at most once, during the next
		GET/POST/PUT request.  Refer to the tls_mgm module for additional info
		regarding TLS client domains.
		</para>
		<para>
		If using this function, you must also ensure that tls_mgm is loaded
		and properly configured.
		</para>
		<para>Parameters</para>
		<itemizedlist>
		<listitem>
			<para><emphasis>tls_client_domain</emphasis> (string)</para>
		</listitem>
		</itemizedlist>
		<para>
		This function can be used from any route.
		</para>
		<example>
		<title><function moreinfo="none">rest_init_client_tls</function> usage</title>
		<programlisting format="linespecific">
...
rest_init_client_tls("dom1");
if (!rest_get("https://example.com"))
    xlog("query failed\n");
...
		</programlisting>
		</example>
	</section>
	</section>

	<section>
	<title>Exported Asynchronous Functions</title>
	<section id="afunc_rest_get" xreflabel="rest_get()">
		<title>
		<function moreinfo="none">rest_get(url, body_pv[, [ctype_pv][, [retcode_pv]]])
		</function>
		</title>
		<para>
		Perform an asynchronous HTTP GET.  This function behaves exactly the same as
		<emphasis role='bold'><xref linkend="func_rest_get"/></emphasis>
		(in terms of input, output and processing),
		but in a non-blocking manner.  Script execution is suspended until the
		entire content of the HTTP response is available.
		</para>
		<example>
		<title><function moreinfo="none">async rest_get</function> usage</title>
		<programlisting format="linespecific">
route {
	...
	async(rest_get("http://getcredit.org/?account=$fU",
	               $var(credit), , $var(rcode)), resume);
}

route [resume] {
	$var(rc) = $rc;
	if ($var(rc) &lt; 0) {
		xlog("async rest_get() failed with $var(rc), acc=$fU\n");
		send_reply(500, "Server Internal Error");
		exit;
	}

	if ($var(rcode) != 200) {
		xlog("L_INFO", "async rest_get() rcode=$var(rcode), acc=$fU\n");
		send_reply(403, "Forbidden");
		exit;
	}

	...
}
</programlisting>
		</example>
	</section>

	<section id="afunc_rest_post" xreflabel="rest_post()">
		<title>
		<function moreinfo="none">rest_post(url, send_body_pv, [send_ctype_pv],
				recv_body_pv[, [recv_ctype_pv][, [retcode_pv]]])
		</function>
		</title>
		<para>
		Perform an asynchronous HTTP POST.  This function behaves exactly the same as
		<emphasis role='bold'><xref linkend="func_rest_post"/></emphasis> (in
		terms of input, output and processing), but in a non-blocking manner.
		Script execution is suspended until the entire content of the HTTP
		response is available.
		</para>
		<example>
		<title><function moreinfo="none">async rest_post</function> usage</title>
		<programlisting format="linespecific">
route {
	...
	async(rest_post("http://myserver.org/register_user",
	                $fU, , $var(body), $var(ct), $var(rcode)), resume);
}

route [resume] {
	$var(rc) = $rc;
	if ($var(rc) &lt; 0) {
		xlog("async rest_post() failed with $var(rc), user=$fU\n");
		send_reply(500, "Server Internal Error 1");
		exit;
	}
	if ($var(rcode) != 200) {
		xlog("async rest_post() rcode=$var(rcode), user=$fU\n");
		send_reply(500, "Server Internal Error 2");
		exit;
	}

	...
}

</programlisting>
		</example>
	</section>

	<section id="afunc_rest_put" xreflabel="rest_put()">
		<title>
		<function moreinfo="none">rest_put(url, send_body_pv, [send_ctype_pv],
				recv_body_pv[, [recv_ctype_pv][, [retcode_pv]]])
		</function>
		</title>
		<para>
		Perform an asynchronous HTTP PUT.  This function behaves exactly the same as
		<emphasis role='bold'><xref linkend="func_rest_put"/></emphasis> (in
		terms of input, output and processing), but in a non-blocking manner.
		Script execution is suspended until the entire content of the HTTP
		response is available.
		</para>
		<example>
		<title><function moreinfo="none">async rest_put</function> usage</title>
		<programlisting format="linespecific">
route {
	...
	async(rest_put("http://myserver.org/users/$fU", $var(userinfo), ,
	               $var(body), $var(ct), $var(rcode)), resume);
}

route [resume] {
	$var(rc) = $rc;
	if ($var(rc) &lt; 0) {
		xlog("async rest_put() failed with $var(rc), user=$fU\n");
		send_reply(500, "Server Internal Error 3");
		exit;
	}
	if ($var(rcode) != 200) {
		xlog("async rest_put() rcode=$var(rcode), user=$fU\n");
		send_reply(500, "Server Internal Error 4");
		exit;
	}

	...
}
</programlisting>
		</example>
	</section>

	</section>

    <section id="exported_transformations">
        <title>Exported script transformations</title>
        <para>
			The module also provides a way for encoding and decoding parameters
			contained in an arbitrary script variable, in accordance with
			RFC3986. This is done by applying a transformation to a script
			variable containing the data to be encoded. The value of the
			original variable is not altered and a corresponding string value
			is returned. The transformation is performed through libcurl API
			method curl_easy_escape (or curl_escape for libcurl &lt; 7.15.4).
        </para>

        <section id="tran_rest.escape" xreflabel="rest.escape">
            <title>
                <varname>{rest.escape}</varname>
            </title>
            <para>
                The result of this transformation is to produce percent encoded string value which can be safely used in URI construction.
            </para>

            <para>There are no parameters for this transformation.</para>

            <example>
                <title><varname>rest.escape</varname> usage</title>
                <programlisting format="linespecific">
...
# This example would produce log entry: "Output: call%40example.com%26safe%3Dfalse"
$var(tmp) = "call@example.com&amp;safe=false";
xlog("Output: $(var(tmp){rest.escape})\n");

# Encode call ID before transmission:
$var(rc) = rest_get("https://call-info.org/?id=$(ci{rest.escape})", $var(body_pv));
...
                </programlisting>
            </example>

        </section>

        <section id="tran_rest.unescape" xreflabel="rest.unescape">
            <title>
                <varname>{rest.unescape}</varname>
            </title>
            <para>
                The result of this transformation is to decode percent encoded string values.
            </para>

            <para>There are no parameters for this transformation.</para>

            <example>
                <title><varname>rest.unescape</varname> usage</title>
                <programlisting format="linespecific">
...
# This example would produce log entry: "Output: 1+1=2!"
$var(tmp) = "1%2B1%3D2%21";
xlog("Output: $(var(tmp){rest.unescape})\n");

# This example would produce log entry: "OpenSIPs, tastes better with every SIP!"
$var(tmp) = "OpenSIPs%2C%20tastes%20better%20with%20every%20SIP%21";
xlog("$(var(tmp){rest.unescape})\n");
...
                </programlisting>
            </example>

        </section>

    </section>

</chapter>
