I am trying to use the new Apple Push Notification API, which is based on HTTP/2.
I found the http-2 Ruby gem, but the documentation isn’t clear about how to make requests as a client.
How to make a HTTP/2 request in Ruby/Rails?
I am trying to use the new Apple Push Notification API, which is based on HTTP/2.
I found the http-2 Ruby gem, but the documentation isn’t clear about how to make requests as a client.
How to make a HTTP/2 request in Ruby/Rails?
DISCLAIMER: I'm the author of the two gems listed here below.
If you want to issue HTTP/2 calls, you may consider NetHttp2, a HTTP/2 client for Ruby.
Usage example for sync calls:
require 'net-http2'
# create a client
client = NetHttp2::Client.new("http://106.186.112.116")
# send request
response = client.call(:get, '/')
# read the response
response.ok? # => true
response.status # => '200'
response.headers # => {":status"=>"200"}
response.body # => "A body"
# close the connection
client.close
On top of writing the HTTP/2 calls yourself, if you want an Apple Push Notification gem that uses the new HTTP/2 specifics and can be embedded in a Rails environment then you may also consider Apnotic.
Usage is very simple:
require 'apnotic'
# create a persistent connection
connection = Apnotic::Connection.new(cert_path: "apns_certificate.pem", cert_pass: "pass")
# create a notification for a specific device token
token = "6c267f26b173cd9595ae2f6702b1ab560371a60e7c8a9e27419bd0fa4a42e58f"
notification = Apnotic::Notification.new(token)
notification.alert = "Notification from Apnotic!"
# send (this is a blocking call)
response = connection.push(notification)
# read the response
response.ok? # => true
response.status # => '200'
response.headers # => {":status"=>"200", "apns-id"=>"6f2cd350-bfad-4af0-a8bc-0d501e9e1799"}
response.body # => ""
# close the connection
connection.close
There is an example of creating HTTP/2 client in this file. It could be adapted to APN requests like this:
require 'socket'
require 'http/2'
payload = '{"foo":"bar"}'
device_token = '00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0' # example
sock = TCPSocket.new('api.development.push.apple.com', 443)
conn = HTTP2::Client.new
conn.on(:frame) do |bytes|
puts "Sending bytes: #{bytes.unpack("H*").first}"
sock.print bytes
sock.flush
end
stream = conn.new_stream
stream.on(:close) do
sock.close
end
head = {
':scheme' => 'https',
':method' => 'POST',
':path' => "/3/device/#{device_token}",
'apns-id' => '123e4567-e89b-12d3-a456-42665544000', # or you could omit this header
'content-length' => payload.bytesize.to_s # should be less than or equal to 4096 bytes
}
puts 'Sending HTTP 2.0 request'
stream.headers(head, end_stream: false)
stream.data(payload)
while !sock.closed? && !sock.eof?
data = sock.read_nonblock(1024)
puts "Received bytes: #{data.unpack("H*").first}"
begin
conn << data
rescue => e
puts "Exception: #{e}, #{e.message} - closing socket."
sock.close
end
end
I’ve been working on a client implementation for this: https://github.com/alloy/lowdown.
Thus far I have been extensively testing it, but it won’t be deployed into production until next week. I’d love for it to get more testing, feedback, etc.
Here is how you do it using async-http
:
#!/usr/bin/env ruby
require 'async'
require 'async/logger'
require 'async/http/client'
require 'async/http/url_endpoint'
Async.run do |task|
endpoint = Async::HTTP::URLEndpoint.parse("https://www.google.com")
client = Async::HTTP::Client.new(endpoint)
headers = {
'accept' => 'text/html',
}
request = Async::HTTP::Request.new("www.google.com", "GET", "/search?q=cats", headers)
response = client.call(request)
body = response.read
end
This will negotiate HTTP/1, HTTP/2 and SSL as required.