Calling Web Services
@http sends an HTTP request from softcode. When the reply arrives, the game queues an action list
of your choosing and hands it the reply. Use it to post to a webhook, ask a dice or weather service
for data, or read from a site’s API.
@http <obj>/<attr>=<URL>[,<headers>]@http/<verb> <obj>/<attr>=<URL>[,<data>[,<headers>]]Without a switch the request is a GET. The other verbs are switches: /post, /put, /delete,
/patch, /head, /options, /trace and /connect.
Only Wizards and objects with the Can_HTTP power may use @http.
The examples on this page use postman-echo.com, which answers with the request it received, so the
reply shows what @http sent. SharpMUSH’s test suite runs them against that site, so the output shown
is what the game prints.
Reading the reply
Section titled “Reading the reply”The action list receives the reply as named arguments:
| Argument | Holds |
|---|---|
%0 | the body of the reply |
%<status> | the HTTP status, such as 200 |
%<content-type> | the reply’s Content-Type header |
%<headers> | every header of the reply, as a JSON object |
%<http-overflow> | 1 if the body was too long for one value and was cut short, otherwise 0 |
A body is cut at the most one value can hold (5 MiB). When that happens, the object holding the
attribute is also told Too much HTTP data received; excess truncated.
&SHOW me=think [if(%<http-overflow>,Reply too long.,Reply complete.)]@http me/SHOW=https://postman-echo.com/getOutput: Reply complete.
In %<headers> each header name is in lower case. Its value is a string, or a list of strings when
the site sent that header more than once. json_query() reads one; get returns it as a JSON
string, quotes and all, and unescape turns that back into text:
&SHOW me=think [json_query(json_query(%<headers>,get,content-type),unescape)]@http me/SHOW=https://postman-echo.com/getOutput: application/json; charset=utf-8
Sending data and headers
Section titled “Sending data and headers”<data> is the body of the request. <headers> is a JSON object: each member is a header name, and
its value is a string, a stored secret, or a list of those to send
that header once for each. json() builds one.
A GET has no body, so its headers come straight after the URL.
&SHOW me=think %<status> [json_query(%0,extract,$.json.bar)] [json_query(%0,extract,$.headers.game)]@http/post me/SHOW=https://postman-echo.com/post,json(object,bar,"baz"),json(object,Content-Type,"application/json",Game,"MyMUSH")Output: 200 baz MyMUSH
How the headers are used:
- Content-Type sets the type of
<data>. Without it, data is sent asapplication/x-www-form-urlencoded, the type of an HTML form. - The data is encoded in the charset its type names (
text/plain; charset=ISO-8859-1), or UTF-8 when it names none. - User-Agent replaces SharpMUSH’s own.
- Every other header is sent as given.
@http refuses the whole request when <headers> is not an object, a value is not a string, a
secret or a list of those, a header name has a space in it, or it sets Content-Length (the data sets
that). It says:
Headers must be a JSON object of header names and values. Aborting.A JSON object typed into the command needs a second pair of braces, because the outer pair only groups the argument and is removed:
&SHOW me=think %<status> [json_query(%0,extract,$.headers.game)]@http me/SHOW=https://postman-echo.com/get,{{"Game":"MyMUSH"}}Output: 200 MyMUSH
Logging in to a site
Section titled “Logging in to a site”Send an Authorization header. For HTTP Basic, the value is Basic followed by
encode64(<user>:<password>):
&SHOW me=think %<status> %0@http me/SHOW=https://postman-echo.com/basic-auth,json(object,Authorization,"Basic [encode64(postman:password)]")Output: 200 {"authenticated":true}
A site that issues API tokens takes them the same way, as json(object,Authorization,"Bearer <token>").
Logging in with a stored secret
Section titled “Logging in with a stored secret”A password or token written into softcode can be read by anyone who can read that attribute, and it is in every copy of the world. Store it as a secret instead. Softcode then names the secret, and the server puts the value into the header as the request leaves. No softcode ever holds the value.
A wizard stores the secret and names the hosts it may go to:
> @secret http.postman.login=postman:passwordhttp.postman.login is set. Its value is never shown again.> @secret/host http.postman.login=postman-echo.com@http may send http.postman.login to: postman-echo.comIn the headers, a secret is an object with secret, its name, and an optional scheme:
&SHOW me=think %<status> %0@http me/SHOW=https://postman-echo.com/basic-auth,json(object,Authorization,json(object,secret,"http.postman.login",scheme,"Basic"))Output: 200 {"authenticated":true}
The scheme decides how the value is written:
scheme | The header carries |
|---|---|
| none | the value alone |
Basic | Basic and the value, <user>:<password>, base64-encoded |
anything else, such as Bearer | the scheme, a space, and the value |
@http sends a secret only when all of these hold, and otherwise refuses the whole request with
A secret in the headers may not be sent to that URL. Aborting.:
- The secret’s name starts with
http.. Secrets the server keeps for itself, such assql.password, can never be sent. - The URL is
https. - The URL’s host is one
@secret/hostnamed for that secret.*.example.comcovers every host underexample.com, but notexample.comitself.
A request that carries a secret does not follow redirects, so a site cannot pass the secret on to
another host. The action list gets the redirect reply itself, with its Location in %<headers>.
Games imported from PennMUSH
Section titled “Games imported from PennMUSH”PennMUSH hands softcode q-registers instead of named arguments. A game imported from PennMUSH has
the http_qregisters option on, and then @http also:
- sets
%q<status>,%q<content-type>and%q<http-overflow>for the action list; - reads
%q<content-type>as the type of<data>when no<headers>are given; - reads
%q<userpass>, of the form<user>:<password>, and sends it as an HTTP Basic login.
So imported code such as this keeps working:
@http me/SHOW=[setq(userpass,postman:password)]https://postman-echo.com/basic-authWithout that option, @http reads no q-register. A q-register lasts until the end of the queue
entry, so one left over from other code could otherwise decide where a password is sent.
PennMUSH waits for the site to ask for a login before it sends userpass. SharpMUSH sends it as HTTP
Basic at once, so a site that accepts only Digest logins cannot be reached this way.
A PennMUSH build with a headers argument takes it as Name:value lines, where SharpMUSH takes a JSON
object. In both, <headers> replaces %q<content-type>.