A web server for the examples
So that this lesson's examples run anywhere, even with no internet, each one starts a tiny web server on your own computer first, with the HTTP server library that comes with OXT-Beyond: httpdStart names the handler that answers each request and a port (0 lets the system choose a free one, which it puts in it), and the handler answers with httpdResponse. The address is then http://127.0.0.1: and the port: 127.0.0.1 always means this computer.
Everything on the client side works the same with any address on the internet, such as https://example.com/. And the server side is useful too: a program can serve a page to a phone on the same network, or answer another program.
Fetching: URL
URL with an http: or https: address is a container, like URL "file:" (Reading and writing files): reading it fetches the page. The script waits until it arrives, and the result says what went wrong, if anything:
on serve pSocket, pRequest
if pRequest["resource"] is "/hello" then
httpdResponse pSocket, 200, "Hello, web"
else
httpdResponse pSocket, 404, "No such page"
end if
end serve
httpdStart "serve", 0
put it into tPort
put URL ("http://127.0.0.1:" & tPort & "/hello")
put URL ("http://127.0.0.1:" & tPort & "/missing") into tPage
if the result is not empty then put "problem:" && the result
httpdStop tPort
Hello, web problem: error 404 Not Found
Asking with parameters
A request can carry values in its address, after a ?: ?q=tea&page=2. A value with spaces, & or accents must be encoded first. Encode text as UTF-8 and then with urlEncode, which is what servers expect today:
on serve pSocket, pRequest
httpdResponse pSocket, 200, "you searched for" && textDecode(pRequest["parameters"]["q"], "UTF-8") && \
"on page" && pRequest["parameters"]["page"]
end serve
httpdStart "serve", 0
put it into tPort
put urlEncode(textEncode("café & tea", "UTF-8")) into tQuery
put tQuery
put URL ("http://127.0.0.1:" & tPort & "/search?q=" & tQuery & "&page=2")
httpdStop tPort
caf%C3%A9+%26+tea you searched for café & tea on page 2
Sending: post
post sends data to a server, as a form does, and puts the server's answer in it. Form data is name=value pairs joined with &, each value encoded. To send JSON, say so with a Content-Type header in the httpHeaders, the extra headers sent with each request:
on serve pSocket, pRequest
if pRequest["content"] is empty then
httpdResponse pSocket, 200, "form: name is" && pRequest["parameters"]["name"]
else
httpdResponse pSocket, 200, "body:" && pRequest["content"]
end if
end serve
httpdStart "serve", 0
put it into tPort
post "name=" & urlEncode("Ada Lovelace") to URL ("http://127.0.0.1:" & tPort & "/save")
put it
set the httpHeaders to "Content-Type: application/json"
post "{" & quote & "name" & quote & ":" & quote & "Ada" & quote & "}" to URL ("http://127.0.0.1:" & tPort & "/save")
set the httpHeaders to empty
put it
httpdStop tPort
form: name is Ada Lovelace
body: {"name":"Ada"}
Set the httpHeaders back to empty when you are done, or the headers go with every later request too. A web service that needs a key usually takes it in a header: Authorization: Bearer and the key.
What the server sent back
The server's answer starts with headers of its own: the kind of data, its size, cookies. libURLLastRHHeaders() gives those of the last request, a header a line. HTTP ends each line with a carriage return before the line feed, so take those out first:
on serve pSocket, pRequest
httpdResponse pSocket, 200, "{}", "Content-Type: application/json"
end serve
httpdStart "serve", 0
put it into tPort
get URL ("http://127.0.0.1:" & tPort & "/data")
put libURLLastRHHeaders() into tHeaders
replace numToChar(13) with empty in tHeaders
put line 1 of tHeaders
filter tHeaders with "Content-Type:*"
put tHeaders
httpdStop tPort
HTTP/1.1 200 OK Content-Type: application/json
Loading in the background
Reading URL makes the script wait, and a slow server makes the program wait with it. load URL starts the download and returns at once; when it has finished, the message you name arrives with the address and its status, and the page is then in the cache, where URL reads it at once. unload URL frees the cache:
local sPort
on serve pSocket, pRequest
httpdResponse pSocket, 200, "loaded in the background"
end serve
on pageLoaded pAddress, pStatus
put "status:" && pStatus
put URL pAddress
unload URL pAddress
httpdStop sPort
end pageLoaded
httpdStart "serve", 0
put it into sPort
load URL ("http://127.0.0.1:" & sPort & "/news") with message "pageLoaded"
put "the program carries on while it loads"
the program carries on while it loads status: cached loaded in the background
The status is cached when the download worked, and error, timeout or contacted when it did not get that far. URLStatus(address) gives the same while it is still going, with how much has arrived, for a progress bar.
Downloading a file
A download is a fetch put straight into a binfile: URL, which keeps the bytes exactly as they came (Binary data):
on serve pSocket, pRequest
httpdResponse pSocket, 200, binaryEncode("CCa3", 0, 255, "PNG")
end serve
httpdStart "serve", 0
put it into tPort
put specialFolderPath("temporary") & "/oxt-lesson-download.bin" into tPath
put URL ("http://127.0.0.1:" & tPort & "/file") into tData
if the result is empty then
put tData into URL ("binfile:" & tPath)
put the number of bytes of URL ("binfile:" & tPath) && "bytes saved"
end if
delete file tPath
httpdStop tPort
5 bytes saved
Opening the user's browser
launch url opens an address in the user's own web browser, or a mailto: address in their email program. It does not fetch anything for the script:
on mouseUp
launch url "https://github.com/SethMorrowSoftware/OpenXTalk-Beyond"
end mouseUp
Try it yourself
Build a tiny web service and its client. The server answers /square?n= with JSON such as {"n": 12, "square": 144}, and a 400 error when n is not a number. The client asks for 12 and for twelve, reads the JSON, and puts the square or the problem.
Show a solution
on serve pSocket, pRequest
local tN, tAnswer
put pRequest["parameters"]["n"] into tN
if tN is not a number then
httpdResponse pSocket, 400, "n must be a number"
exit serve
end if
put tN + 0 into tAnswer["n"]
put tN * tN into tAnswer["square"]
httpdResponse pSocket, 200, JSONExport(tAnswer), "Content-Type: application/json"
end serve
httpdStart "serve", 0
put it into tPort
repeat for each item tAsk in "12,twelve"
put URL ("http://127.0.0.1:" & tPort & "/square?n=" & urlEncode(tAsk)) into tReply
if the result is not empty then
put tAsk & ":" && the result & "," && tReply
else
put JSONImport(tReply) into tData
put tData["n"] && "squared is" && tData["square"]
end if
end repeat
httpdStop tPort
12 squared is 144 twelve: error 400 Bad Request, n must be a number
Words in this lesson
URLhttp:https:the resulturlEncodepostthe httpHeaderslibURLLastRHHeadersload URLunload URLlaunch urlhttpdStarthttpdResponsehttpdStopEach one finds every lesson that uses it. The Dictionary has the whole story of each, with its syntax and more examples.