Halite
💎HTTP Requests Client with a chainable REST API, built-in sessions and middlewares.
Install / Use
/learn @icyleaf/HaliteREADME

Halite
HTTP Requests with a chainable REST API, built-in sessions and middleware written by Crystal. Inspired from the awesome Ruby's HTTP/RESTClient and Python's requests.
Build in Crystal version >= 1.0.0, this document valid with latest commit.
Index
<!-- TOC -->- Installation
- Usage
- Middleware
- Advanced Usage
Installation
Add this to your application's shard.yml:
dependencies:
halite:
github: icyleaf/halite
Usage
require "halite"
Making Requests
Make a GET request:
# Direct get url
Halite.get("http://httpbin.org/get")
# Support NamedTuple as query params
Halite.get("http://httpbin.org/get", params: {
language: "crystal",
shard: "halite"
})
# Also support Hash as query params
Halite.get("http://httpbin.org/get", headers: {
"Private-Token" => "T0k3n"
}, params: {
"language" => "crystal",
"shard" => "halite"
})
# And support chainable
Halite.header(private_token: "T0k3n")
.get("http://httpbin.org/get", params: {
"language" => "crystal",
"shard" => "halite"
})
See also all chainable methods.
Many other HTTP methods are available as well:
getheadpostputdeletepatchoptions
Passing Parameters
Query string parameters
Use the params argument to add query string parameters to requests:
Halite.get("http://httpbin.org/get", params: { "firstname" => "Olen", "lastname" => "Rosenbaum" })
Form data
Use the form argument to pass data serialized as form encoded:
Halite.post("http://httpbin.org/post", form: { "firstname" => "Olen", "lastname" => "Rosenbaum" })
File uploads (via form data)
To upload files as if form data, construct the form as follows:
Halite.post("http://httpbin.org/post", form: {
"username" => "Quincy",
"avatar" => File.open("/Users/icyleaf/quincy_avatar.png")
})
It is possible to upload multiple files:
Halite.post("http://httpbin.org/post", form: {
photos: [
File.open("/Users/icyleaf/photo1.png"),
File.open("/Users/icyleaf/photo2.png")
],
album_name: "samples"
})
Or pass the name with []:
Halite.post("http://httpbin.org/post", form: {
"photos[]" => [
File.open("/Users/icyleaf/photo1.png"),
File.open("/Users/icyleaf/photo2.png")
],
"album_name" => "samples"
})
Multiple files can also be uploaded using both ways above, it depend on web server.
JSON data
Use the json argument to pass data serialized as body encoded:
Halite.post("http://httpbin.org/post", json: { "firstname" => "Olen", "lastname" => "Rosenbaum" })
Raw String
Use the raw argument to pass raw string as body and set the Content-Type manually:
# Set content-type to "text/plain" by default
Halite.post("http://httpbin.org/post", raw: "name=Peter+Lee&address=%23123+Happy+Ave&language=C%2B%2B")
# Set content-type manually
Halite.post("http://httpbin.org/post",
headers: {
"content-type" => "application/json"
},
raw: %Q{{"name":"Peter Lee","address":"23123 Happy Ave","language":"C++"}}
)
Passing advanced options
Auth
Use the #basic_auth method to perform HTTP Basic Authentication using a username and password:
Halite.basic_auth(user: "user", pass: "p@ss").get("http://httpbin.org/get")
# We can pass a raw authorization header using the auth method:
Halite.auth("Bearer dXNlcjpwQHNz").get("http://httpbin.org/get")
User Agent
Use the #user_agent method to overwrite default one:
Halite.user_agent("Crystal Client").get("http://httpbin.org/user-agent")
Headers
Here are two way to passing headers data:
1. Use the #headers method
Halite.headers(private_token: "T0k3n").get("http://httpbin.org/get")
# Also support Hash or NamedTuple
Halite.headers({ "private_token" => "T0k3n" }).get("http://httpbin.org/get")
# Or
Halite.headers({ private_token: "T0k3n" }).get("http://httpbin.org/get")
2. Use the headers argument in the available request method:
Halite.get("http://httpbin.org/anything" , headers: { private_token: "T0k3n" })
Halite.post("http://httpbin.org/anything" , headers: { private_token: "T0k3n" })
Cookies
Passing cookies in requests
The Halite.cookies option can be used to configure cookies for a given request:
Halite.cookies(session_cookie: "6abaef100b77808ceb7fe26a3bcff1d0")
.get("http://httpbin.org/headers")
Get cookies in requests
To obtain the cookies(cookie jar) for a given response, call the #cookies method:
r = Halite.get("http://httpbin.org/cookies?set?session_cookie=6abaef100b77808ceb7fe26a3bcff1d0")
pp r.cookies
# => #<HTTP::Cookies:0x10dbed980 @cookies={"session_cookie" =>#<HTTP::Cookie:0x10ec20f00 @domain=nil, @expires=nil, @extension=nil, @http_only=false, @name="session_cookie", @path="/", @secure=false, @value="6abaef100b77808ceb7fe26a3bcff1d0">}>
Redirects and History
Automatically following redirects
The Halite.follow method can be used for automatically following redirects(Max up to 5 times):
# Set the cookie and redirect to http://httpbin.org/cookies
Halite.follow
.get("http://httpbin.org/cookies/set/name/foo")
Limiting number of redirects
As above, set over 5 times, it will raise a Halite::TooManyRedirectsError, but you can change less if you can:
Halite.follow(2)
.get("http://httpbin.org/relative-redirect/5")
Disabling unsafe redirects
It only redirects with GET, HEAD request and returns a 300, 301, 302 by default, otherwise it will raise a Halite::StateError.
We can disable it to set :strict to false if we want any method(verb) requests, in which case the GET method(verb) will be used for
that redirect:
Halite.follow(strict: false)
.post("http://httpbin.org/relative-redirect/5")
History
Response#history property list contains the Response objects that were created in order to complete the request.
The list is ordered from the oldest to most recent response.
r = Halite.follow
.get("http://httpbin.org/redirect/3")
r.uri
# => http://httpbin.org/get
r.status_code
# => 200
r.history
# => [
# #<Halite::Response HTTP/1.1 302 FOUND {"Location" => "/relative-redirect/2" ...>,
# #<Halite::Response HTTP/1.1 302 FOUND {"Location" => "/relative-redirect/1" ...>,
# #<Halite::Response HTTP/1.1 302 FOUND {"Location" => "/get" ...>,
# #<Halite::Response HTTP/1.1 200 OK {"Content-Type" => "application/json" ...>
# ]
NOTE: It contains the Response object if you use history and HTTP was not a 30x, For example:
r = Halite.get("http://httpbin.org/get")
r.history.size # => 0
r = Halite.follow
.get("http://httpbin.org/get")
r.history.size # => 1
Timeout
By default, the Halite does not enforce timeout on a request. We can enable per operation timeouts by configuring them through the chaining API.
The connect timeout is the number of seconds Halite will wait for our client to establish a connection to a remote server call on the socket.
Once our client has connected to the server and sent the HTTP request,
the read timeout is the number of seconds the client will wait for the server to send a response.
# Separate set connect and read timeout
Halite.timeout(connect: 3.0, read: 2.minutes)
.get("http://httpbin.org/anything")
# Boath set connect and read timeout
# The timeout value will be applied to both the connect and the read timeouts.
Halite.timeout(5)
.get("http://httpbin.org/anything")
