The following examples show how to make HTTP requests using the HttpRequest class.
Simple Get Request
Make a Get request to a server and retrieve the status code, the response headers, and the response text body:
response = HttpRequest.Get('https://httpbin.org/')
print, response.status_code
print, response.headers
print, response.text
Get Request and Save to a File
Make a Get request to a server and save the response to a file:
response = HttpRequest.Get('https://httpbin.org/', FILENAME='c:/temp/result.txt')
print, response.status_code
print, file_lines('c:/temp/result.txt')
Get Request with Parameters
Here we send a couple of parameters to a server. Since our parameters contain disallowed characters (such as spaces) we use the ESCAPE keyword:
params = hash('param1', 'my value', 'param2', -123.4)
response = HttpRequest.Get('https://httpbin.org/', params=params, /escape)
print, response.url
IDL prints:
http://myserver.com/?param1=my%20value¶m2=-123.400
Tip: You can also use the HttpRequest.Escape and Unescape methods to convert strings with disallowed or special characters.
Get Request with Basic Authentication
Make a request using basic authentication:
basic = 'Basic ' + idl_base64(byte('myname:mypass'))
response = HTTPRequest.Get('myserver.org', headers=hash('Authorization', basic))
print, `status_code = ${response.status_code}`
Note: This is a fake URL so this example will not actually run; you will need to use your own URL and the correct username and password.
Handling Errors
Make a request to a nonexistent server. In this case you will get an internal CURL error (response code < 100) and the error message will be in the response.text field:
response = HTTPRequest.Get('http://myfancyserver.org')
print, `status_code = ${response.status_code}`
print, response.text
IDL prints:
status_code = 6
Could not resolve host: myfancyserver.org
Now make a request to a real server but a nonexistent path. In this case you will get a 404 response code and the server's response will be in the response.text field:
response = HTTPRequest.Get('http://google.com/mybadpath')
print, `status_code = ${response.status_code}`
print, response.text
IDL prints:
status_code = 404
<!DOCTYPE html>
<html lang=en>
...
<a href=//www.google.com/><span id=logo aria-label=Google></span></a>
<p><b>404.</b> <ins>That's an error.</ins>
<p>The requested URL <code>/mybadpath</code> was not found on this server. <ins>That's all we know.</ins>
Post Request with Key/Value Pairs
Make a post request, passing in two key/value pairs as parameters:
params = dictionary('key1', 'value1', 'key2', 'value2')
response = HTTPRequest.Post('https://httpbin.org/post', params=params)
print, `status_code = ${response.status_code}`
print, response.text
IDL prints:
status_code = 200
{
...
"form": {
"key1": "value1",
"key2": "value2"
},
"headers": {
"Accept": "*/*",
"Content-Length": "23",
"Content-Type": "application/x-www-form-urlencoded",
...
},
...
}
Post Request with JSON Data
Make a post request and pass in two key/value pairs as JSON data. The response body is in JSON format so we use the HttpRequest.Json method:
formdata = dictionary('key1', 'value1', 'key2', 'value2')
response = HTTPRequest.Post('https://httpbin.org/post', json=formdata)
print, response.json(), /implied
IDL prints:
{
"data": "{\"key2\":\"value2\",\"key1\":\"value1\"}",
"headers": {
"Accept": "*/*",
"Content-Length": "33",
"Content-Type": "application/json",
...
},
"json": {
"key1": "value1",
"key2": "value2"
},
...
}
Post Request with Multipart Form and Files
Make a post request with a multipart form that includes a single file:
multipart= hash('myawesomedata', '@c:\users\chris\mytextfile.txt')
response = HTTPRequest.Post('https://myserver.org/', multipart=multipart)
Here we have a more complicated multipart where we need to specify the mimetype for a JPEG file and a different mimetype for some byte data:
myfile = filepath('md1107g8a.jpg', subdir=['examples', 'data'])
multipart = dictionary('name', 'Dave Stern', $
'mydata1', bindgen(10), $
'mydata2', {value: bindgen(10), mimetype: 'application/octet-stream'}, $
'myimage', {file: myfile, mimetype: 'image/jpeg'})
response = HTTPRequest.Post('https://myserver.org/', multipart=multipart)
Note: This is a fake URL so this example will not actually run; it is only here to show how to pass multipart data. To use the ::Post method, you will need to consult the documentation for your web server to determine the appropriate form fields and data/file formats.
Get Request with a Callback
First we define a callback function that will print out a progress status. Our callback also looks for a keypress: if you press "q" then the function will return zero and the request will be canceled.
function mycallback, downTotal, downNow, upTotal, upNow, callbackData
compile_opt idl2
if (get_kbrd(0) eq 'q') then return, 0
percent = round(100 * float(downNow) / (downTotal > 1))
if (callbackData eq percent) then return, 1
callbackData = percent
if (percent gt 0) then print, `${percent}% ${('*').dup(percent)}`
return, 1
end
Now download a large file, making sure to pass in our callback function and our initial percentage:
response = HTTPRequest.Get('http://mattmahoney.net/dc/enwik8.zip', $
CALLBACK_FUNCTION='mycallback', CALLBACK_DATA=0)
IDL prints:
1% *
...
50% **************************************************
...
100% ****************************************************************************************************
Patch Request
Unlike POST or PUT, a PATCH request allows you to update existing resources on the server. For example, to upload a file helloworld.txt that contains the text "Hello World":
IDL> data = hash("name", "patched file", "file", "@helloworld.txt")
IDL> response = HttpRequest.Patch('https://httpbin.org/patch', multipart = data)
IDL> response.status
200
IDL> response.json()
{
"args": {
},
"data": "",
"files": {
"file": "Hello World\r\n"
},
"form": {
"name": "patched file"
},
...
}
Note: This example assumes that the server is looking for form data with a field called "name" along with an associated file. To use the ::Patch method, you will need to consult the documentation for your web server to determine the appropriate form fields and data/file formats.
WebSocket Example
First, create a Python program to run a sample WebSocket server on localhost. Make sure your Python has the websockets package installed:
pip install websockets
Then create the following program:
Copy
import asyncio
import websockets
async def handler(websocket):
try:
print(f"New connection from {websocket.remote_address}", flush=True)
await websocket.send("Initial message from server")
async for message in websocket:
if isinstance(message, str): # Text data
msg = message
else: # Binary data
if len(message) > 10:
msg = f"{len(message)} bytes"
else:
msg = f"{message}"
print("Received: " + msg)
await websocket.send("Response: " + msg)
# Loop ended normally - client sent close frame
print(f"Client sent close frame: code={websocket.close_code}, reason={websocket.close_reason}", flush=True)
# Explicitly send close response
await websocket.close()
except websockets.exceptions.ConnectionClosed as e:
print(f"Connection closed: code={e.code}, reason={e.reason}", flush=True)
async def main():
async with websockets.serve(handler, "localhost", 8765, max_size=None):
print("WebSocket server running on ws://localhost:8765")
await asyncio.Future() # run forever
if __name__ == "__main__":
asyncio.run(main())
Save this file as sample_websocket.py and run it:
python sample_websocket.py
Next, create an IDL program which will open a connection to this WebSocket server, send some data, and wait for responses using an IDL Timer:
pro test_callsocket, id, ws
on_error, 2
catch, err
if (err ne 0 || ws.curl eq 0) then begin
catch, /cancel
!null = timer.cancel(id)
message, /reissue_last
return
endif
result = ws.Receive()
if isa(result, 'byte') || strlen(result) gt 0 then begin
print, result
if (isa(result, 'string') && result.contains("Close")) then begin
ws.Send, /CLOSE
!null = timer.cancel(id)
endif
endif
end
print, 'Connecting...'
ws = HttpRequest.WebSocket("ws://localhost:8765")
id = timer.set(0.5, 'test_callsocket', ws, /repeat)
ws.Send, 'Hello from IDL'
ws.Send, bindgen(10) + 1b
ws.Send, "Close"
end
Save this code in a file called test_websocket.pro, and then run it in IDL. You should see the following output:
IDL> .r test_websocket
Connecting...
Initial message from server
Response: Hello from IDL
Response: b'\x01\x02\x03\x04\x05\x06\x07\x08\t\n'
Response: Close
Over in the Python console, you should see the server output:
WebSocket server running on ws://localhost:8765
New connection from ('::1', 63535, 0, 0)
Received: Hello from IDL
Received: b'\x01\x02\x03\x04\x05\x06\x07\x08\t\n'
Received: Close
Client sent close frame: code=1000, reason=OK
Note: Most WebSocket servers (including this example Python one) send out "keepalive" pings at a regular interval. For example, the default for Python is 20 seconds. Your IDL code should call the Receive method at a regular interval that is shorter than this keepalive ping interval. Calling Receive will check for any existing data on the pipeline and will also automatically respond to pings, thus keeping the connection alive.
Version History
|
9.0 |
Introduced |
| 9.2 |
Added example of ::Get FILENAME keyword, added examples of using @ to upload files. Added PATCH example.
|
| 9.3 |
Added WebSocket example |
See Also
HttpRequest