Project

plum

0.01
Repository is archived
No commit activity in last 3 years
No release in over 3 years
An HTTP/2 Library for Ruby
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

Plum: An HTTP/2 Library for Ruby

A pure Ruby HTTP/2 server and client implementation.

WARNING: Plum is currently under heavy development. You will encounter bugs when using it.

Circle CI Build Status Code Climate Test Coverage

Requirements

  • Ruby 2.3
  • OpenSSL 1.0.2 or newer
  • http_parser.rb gem - if you need HTTP/2 without TLS or HTTP/1.1 support
  • rack gem - if you use plum as a Rack server

Installation

You can install via rubygems:

gem install plum

then require it:

require "plum"

Usage

As a Rack-compatible server

Most existing Rack-based applications should work without modification.

# config.ru
App = -> env {
  [
    200,
    { "Content-Type" => "text/plain" },
    ["request: #{env["REQUEST_METHOD"]} #{env["PATH_INFO"]}"]
  ]
}

run App

You can run it:

% plum -e production -p 8080 --https --cert server.crt --key server.key config.ru

NOTE: If --cert and --key are omitted, a temporary dummy certificate will be generated.

My website https://rhe.jp is using plum as a Rack server.

As a HTTP/2 (HTTP/1.x) client library

If the server doesn't support HTTP/2, it falls back to HTTP/1.1 seamlessly.

   +-----------------+
   |:https option    | false
   |(default: true)  |-------> Try Upgrade from HTTP/1.1
   +-----------------+
            | true
            v
   +-----------------+
   | ALPN            | failed
   | negotiation     |-------> HTTP/1.x
   +-----------------+
            | "h2"
            v
          HTTP/2
Sequential request
client = Plum::Client.start("http2.rhe.jp", user_agent: "nyaan")
res1 = client.get("/", headers: { "accept" => "*/*" }).join
puts res1.body # => "..."
res2 = client.post("/post", "data").join
puts res2.body # => "..."

client.get("/clockstream").on_headers { |res|
  puts "status: #{res.status}, headers: #{res.headers}"
}.on_chunk { |chunk|
  puts chunk
}.on_finish {
  puts "finish!"
}.join

client.close
Parallel request
res1 = res2 = nil
Plum::Client.start("rhe.jp", 443, http2_settings: { max_frame_size: 32768 }) { |client|
  res1 = client.get("/")
  res2 = client.post("/post", "data")
  # res1.status == nil ; because it's async request
} # wait for response(s) and close

p res1.status # => "200"
Download a large file
# the value of hostname option will be used in SNI and :authority header
Plum::Client.start("http2.rhe.jp", 443, hostname: "assets.rhe.jp") { |client|
  client.get("/large") do |res| # called when received response headers
    p res.status # => "200"
    File.open("/tmp/large.file", "wb") { |file|
      res.on_chunk do |chunk| # called when each chunk of response body arrived
        file << chunk
      end
    }
  end
}

TODO

  • Better API design
  • Better server push support
  • Stream priority support

Of course ideas and pull requests are welcome.

Hacking

Clone this Git repository and run bundle install to install development dependencies. You can run test with rake test. The tests are written with Minitest.

License

MIT License