Recursive URL Substitution Syntax

Like any substitution syntax, using untrusted formatted strings or untrusted sources of data can lead to exploits. Be careful when designing the usage so that no tainted input winds up in the context.
= Syntax =
The syntax is based on placing curly braces inside an URL to indicate that the curly brace and everything inside should be replaced with a substitution. Each type of substitution will have it's own rules but every time you see { and } then all contents inside those braces are to be replaced.
== Strings ==
String substitution uses an open curly brace '{' followed by a dollar symbol '$' followed by the name of the substitution and a closing curly brace '}'. The name is used as a key into a map whose value will be replaced into the original format string. The key must only contain alphanumeric characters and '+', '-', '_', and spaces.
Values which appear in the map but not referenced in the format string are ignored.
 String Substitution Examples
Format 	Map Contents 	Result
{$bucket}.agent.lindenlab.com 	'bucket':'a' 	a.agent.lindenlab.com
agent/{$agent-id}/inventory/item/{$item-id}	'agent-id':foo, 'item-id':bar	agent/foo/inventory/item/bar
Values which are lists/tuples will have their elements urlquoted and joined with '/'. I.e. they are assumed to be exploded paths. E.g.:
Format 	Map Contents 	Result
foo/{$stuff}/baz	'stuff':['one','two','three']	foo/one/two/three/baz
== Query Strings ==
Query strings are a specialized url syntax for representing the contents of a map as an ampersand delimited url query string including the leading '?'. Query string use an open curly brace '{' followed by a percent symbol '%' followed by the name of a dictionary in the replacement context followed by a closing curly brace '}'.
Unlike string substitution, if the named value is not a key in the map contents, the russ directive will be stripped.
 Query String Substitution Examples
Format 	Map Contents 	Result
/sql/{$query}{%params}	{ 'query':'get-agent-name-by-id', 'params':{'agent_id':123}} 	/sql/get-agent-name-by-id?agent_id=123
/proc/{$proc}{%params}	{ 'proc':'messages/dir/DirFindPeople', 'params':{'estate':1, 'query':'designer clothing'}}	/proc/messages/dir/DirFindPeople?estate=1&query=designer+clothing
== Functions ==
Function substitution uses an open curly brace '{' followed by a bang symbol '!' followed by the name of the substitution function and a closing curly brace '}'. The name is a function that is available to the substitution engine, and may be implementation specific. The function is prototyped as accepting a map context and returning a string.
For example, {!region-host-port} could be a function which takes a map which is assumed to contain a key 'region-id'. The function will perform a service lookup for region presence with the region-id in the service builder, and then parse the return document for the region host and port.
== REST Requests ==
For REST Request substitution, the embedded URL is fetched, parsed for a single LLSD document which is embedded inside the curly braces. 
The simple syntax is an opening curly brace '{', followed by the URL, and terminated with a closing curly brace '}'. The document at the url is parsed and treated as a string. Containers, the undefined element, and non-parseable documents are considered errors.
For http request, no instructions to the protocol means to perform a normal GET.
 Rest Request Substitution Examples
Format 	Results 	
{http://gridtool.agni.lindenlab.com/agents/online-now} 	58582 	
{http://blondie.lindenlab.com/lookup/agentname/phoenix/linden	agent-id}	3c115e51-04f4-523c-9fa6-98aff1034730
Supported protocols: http, https 
We should probably support file.
== Recursive Substitution ==
In theory, this grammar can be used recursively, though only python currently supports this feature.
 Formating Examples
Format
{http://sim1028.{$grid}.{$domain}/agent/{$agent-id}/location}
{{$node}{$domain}}
= Syntax Specification =
The full syntax is an opening curly brace '{', followed by the URL, followed by a vline '|', followed by protocol specific instructions, followed by a 'vline', followed by a path to follow through the document to the element which will be treated as a string. 
russ-statement : open-russ substitution close-russ
open-russ: '{'
close-russ: '}'
substituion: string-substitution| query-string-substitution | url-substitution | function-substitution
string-substition: '$' llsd-key
query-string-substition: '%' llsd-key
function-substitution: '!' function-name
function-name: llsd-key
url-substitution: url [ vline [ url-parameters ] vline llsd-path]
url: protocol ':' (normal url authority & host & path & parameters & opaque parts)
protocol: 'http' | 'https'
vline: '|'
url-parameters: verb [ body ]
verb: 'GET' | 'PUT' | 'POST' | 'DELETE'
body: ???
llsd-path: segment * [ "/" segment ]
segment: llsd-key
Please note that the following elements in the syntax rules are not well defined:
* llsd-key
* body
* function-name
= Implementations =
== python ==
Our indra python libs have a russ implementation in russ.py which supports:
* string substitution
* query string substitution
* url substitution
* recursive substitution
== c++ ==
In indra/llmessage libs has a russ implementation inside of the LLServiceBuilder class which supports:
* string substitution
* url substitution
== perl ==
In Indra::WebServices, you can call buildService($service_name) to make a client to that service. The current implementation supports:
* string substitution
* query string substitution
* recursive substitution