This provides a sample that shows how to provide a RESTful facade on top of a soap service using Xavi.
go get github.com/Sirupsen/logrus go get github.com/xtracdev/xavi go build
To simulate a soap service, we'll use mountebank
The imposters.json file contains a simple definition to simulate the stock quote services example from the SOAP 1.1 spec:
{
"port": 4545,
"protocol": "http",
"stubs": [
{
"responses": [
{
"is": {
"statusCode": 200,
"body": "<SOAP-ENV:Envelope
xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"
SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">
<SOAP-ENV:Body>
<m:GetLastTradePriceResponse xmlns:m=\"Some-URI\">
<Price>34.5</Price>
</m:GetLastTradePriceResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>\n"
}
}
],
"predicates": [
{
"and": [
{"equals": {"path": "/services/quote/getquote","method": "POST"}},
{ "contains": { "body": "Envelope" } }
]
}
]
}
]
}
Start mountebank, and configure it to expose the above endpoint:
curl -i -X POST -H 'Content-Type: application/json' -d@imposter.json http://127.0.0.1:2525/imposters
Based on the above, if a request is posted to the /services/quotes/getquote endpoint, and the body contains 'Envelope', a SOAP response is returned.
What if we wanted a simple RESTful endpoint, where we could obtain a quote for a symbol via a simple get on resource like /quote/symbol?
Doing this with Xavi is simple - we write a plug for the framework, register it on startup, and provide some configuration with a route .
For this example, you need to have golang 1.7.x.
First, we grab Xavi from github via go get.
go get github.com/xtracdev/xavi
Next, we create the plugin by creating a wrapper type, a method to instantiate the wrapper, and implement the Wrap method.
The details are in the quote package.
The plugin will be referenced by name in the Xavi route configuration. To make the plugin available to the configuration interface, it needs to be registered when the application is started.
Xavi provides a package that serves as an entry point to applications built using the toolkit. Applications pass the command line arguments and a function to register the plugins.
Refer to main.go for details.
Also of note is the use of some provided Xavi plugins. This sample shows how the recovery plugin can be used and extended via a custom wrapper factory method and registering the plugin in main.go.
This also shows the use of the timing plugin. The order of the plugins listed on the command line is significant: plugins are layered in order, with the earlier wrapped inside the later ones. The command line configuration below intentionally places the timer plugin outside the panic recovery plugin, so that we don't record timings for panic'd service calls.
Once the plugin and main function are available, build the application, and
use it to configure the servers, backends, routes, and listener. The below
configuration assumes the sample was built using go build
.
Note that before you run the commands below you need to set the value of the XAVI_KVSTORE_URL environment variable, e.g.
export XAVI_KVSTORE_URL=file:///`pwd`/config
./xavisample add-server -address localhost -port 4545 -name quotesvr1 ./xavisample add-backend -name quote-backend -servers quotesvr1 ./xavisample add-route -name quote-route -backends quote-backend -base-uri /quote/ -plugins Quote,SessionId,Timing,Recovery ./xavisample add-listener -name quote-listener -routes quote-route
At this point, the listener can be started.
Boot the listener:
./xavisample listen -ln quote-listener -address 0.0.0.0:8080
The xavi endpoint is now ready to go.
First, you can directly access the SOAP endpoint:
curl -X POST -d '
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<m:GetLastTradePrice xmlns:m="Some-URI">
<symbol>DIS</symbol>
</m:GetLastTradePrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
' localhost:4545/services/quote/getquote
This should produce the following response:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/
">
<SOAP-ENV:Body>
<m:GetLastTradePriceResponse xmlns:m="Some-URI">
<Price>34.5</Price>
</m:GetLastTradePriceResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
</pre>
Using the RESTful facade is much easier:
curl localhost:8080/quote/foo 34.5
To generate a panic, use this uri
curl localhost:8080/quote/XTRAC
Montebank Setup:
curl -i -X POST -H 'Content-Type: application/json' -d@imposter.json http://127.0.0.1:2525/imposters curl -i -X POST -H 'Content-Type: application/json' -d@imposter2.json http://127.0.0.1:2525/imposters
This is a modification to the above to add in a health check
./xavisample add-server -address localhost -port 4545 -name quotesvr1 -health-check http-get -ping-uri / ./xavisample add-server -address localhost -port 5454 -name quotesvr2 -health-check http-get -ping-uri / ./xavisample add-backend -name quote-backend -servers quotesvr1,quotesvr2 ./xavisample add-route -name quote-route -backends quote-backend -base-uri /quote/ -plugins Quote,SessionId,Timing,Recovery ./xavisample add-listener -name quote-listener -routes quote-route
Run it:
./xavisample listen -ln quote-listener -address 0.0.0.0:8080
Backend calls made via Xavi can be made with a context that allows cancellation or timeout. In this sample, random cancellations and timeouts can be injected by setting the MAYBE_TIMEOUT and MAYBE_CANCEL environment variables to a non-empty string, e.g.
MAYBE_CANCEL=1 MAYBE_TIMEOUT=1 ./xavisample listen -ln quote-listener -address 0.0.0.0:8080
The plugin author may insert a cancellable context into the call flow to allow explicit cancelling of the backend request, cancellation of the request based on a timeout, or a duration based timeout.
If the context times out or is cancelled while Xavi is invoking the backend service, Xavi will set the http response code based on the cancellation type, update the timing context for the backend with an error status, and return. While plugins can wait on the Done channel associated with the context, any manipulation of the http response should be done when call control returns to the plugin to avoid a race condition with Xavi's handling of the timeout/cancellation.
To run the HTTPs transport sample, you'll need to generate your own signing cert and key. The cert and key are both embedded in the mountebank configuration, and the cert file is referenced in the xavisample configuration.
Gophers can build src/crypto/tls/generate_cert.go
cd $GOROOT cd src/crypto/tls go build generate_cert.go
The generate the cert and key via:
$GOROOT/src/crypto/tls/generate_cert -ca -host `hostname`
Set up mb using imposters-https.json (after editing it to use your key and cert):
curl -i -X POST -H 'Content-Type: application/json' -d@imposter-https.json http://127.0.0.1:2525/imposters
Next then configure xavisample thusly:
NOTE: you will have to generate your own key and pem since the hostname on the cert needs to match your set up. You'll also need to change the hostname for the server to match your hostname and tls key.
export XAVI_KVSTORE_URL=file:///`pwd`/config
./xavisample add-server -address `hostname` -port 4443 -name quotesvr1 ./xavisample add-backend -name quote-backend -servers quotesvr1 -cacert-path ./cert.pem -tls-only=true ./xavisample add-route -name quote-route -backends quote-backend -base-uri /quote/ -plugins Quote,SessionId,Timing,Recovery ./xavisample add-listener -name quote-listener -routes quote-route
Boot the listener:
./xavisample listen -ln quote-listener -address 0.0.0.0:8080