iodine 0.1.21 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.travis.yml +23 -2
  4. data/CHANGELOG.md +9 -2
  5. data/README.md +232 -179
  6. data/Rakefile +13 -1
  7. data/bin/config.ru +63 -0
  8. data/bin/console +6 -0
  9. data/bin/echo +42 -32
  10. data/bin/http-hello +62 -0
  11. data/bin/http-playground +124 -0
  12. data/bin/playground +62 -0
  13. data/bin/poc/Gemfile.lock +23 -0
  14. data/bin/poc/README.md +37 -0
  15. data/bin/poc/config.ru +66 -0
  16. data/bin/poc/gemfile +1 -0
  17. data/bin/poc/www/index.html +57 -0
  18. data/bin/raw-rbhttp +35 -0
  19. data/bin/raw_broadcast +66 -0
  20. data/bin/test_with_faye +40 -0
  21. data/bin/ws-broadcast +108 -0
  22. data/bin/ws-echo +108 -0
  23. data/exe/iodine +59 -0
  24. data/ext/iodine/base64.c +264 -0
  25. data/ext/iodine/base64.h +72 -0
  26. data/ext/iodine/bscrypt-common.h +109 -0
  27. data/ext/iodine/bscrypt.h +49 -0
  28. data/ext/iodine/extconf.rb +41 -0
  29. data/ext/iodine/hex.c +123 -0
  30. data/ext/iodine/hex.h +70 -0
  31. data/ext/iodine/http.c +200 -0
  32. data/ext/iodine/http.h +128 -0
  33. data/ext/iodine/http1.c +402 -0
  34. data/ext/iodine/http1.h +56 -0
  35. data/ext/iodine/http1_simple_parser.c +473 -0
  36. data/ext/iodine/http1_simple_parser.h +59 -0
  37. data/ext/iodine/http_request.h +128 -0
  38. data/ext/iodine/http_response.c +1606 -0
  39. data/ext/iodine/http_response.h +393 -0
  40. data/ext/iodine/http_response_http1.h +374 -0
  41. data/ext/iodine/iodine_core.c +641 -0
  42. data/ext/iodine/iodine_core.h +70 -0
  43. data/ext/iodine/iodine_http.c +615 -0
  44. data/ext/iodine/iodine_http.h +19 -0
  45. data/ext/iodine/iodine_websocket.c +430 -0
  46. data/ext/iodine/iodine_websocket.h +21 -0
  47. data/ext/iodine/libasync.c +552 -0
  48. data/ext/iodine/libasync.h +117 -0
  49. data/ext/iodine/libreact.c +347 -0
  50. data/ext/iodine/libreact.h +244 -0
  51. data/ext/iodine/libserver.c +912 -0
  52. data/ext/iodine/libserver.h +435 -0
  53. data/ext/iodine/libsock.c +950 -0
  54. data/ext/iodine/libsock.h +478 -0
  55. data/ext/iodine/misc.c +181 -0
  56. data/ext/iodine/misc.h +76 -0
  57. data/ext/iodine/random.c +193 -0
  58. data/ext/iodine/random.h +48 -0
  59. data/ext/iodine/rb-call.c +127 -0
  60. data/ext/iodine/rb-call.h +60 -0
  61. data/ext/iodine/rb-libasync.h +79 -0
  62. data/ext/iodine/rb-rack-io.c +389 -0
  63. data/ext/iodine/rb-rack-io.h +17 -0
  64. data/ext/iodine/rb-registry.c +213 -0
  65. data/ext/iodine/rb-registry.h +33 -0
  66. data/ext/iodine/sha1.c +359 -0
  67. data/ext/iodine/sha1.h +85 -0
  68. data/ext/iodine/sha2.c +825 -0
  69. data/ext/iodine/sha2.h +138 -0
  70. data/ext/iodine/siphash.c +136 -0
  71. data/ext/iodine/siphash.h +15 -0
  72. data/ext/iodine/spnlock.h +235 -0
  73. data/ext/iodine/websockets.c +696 -0
  74. data/ext/iodine/websockets.h +120 -0
  75. data/ext/iodine/xor-crypt.c +189 -0
  76. data/ext/iodine/xor-crypt.h +107 -0
  77. data/iodine.gemspec +25 -18
  78. data/lib/iodine.rb +57 -58
  79. data/lib/iodine/http.rb +0 -189
  80. data/lib/iodine/protocol.rb +36 -245
  81. data/lib/iodine/version.rb +1 -1
  82. data/lib/rack/handler/iodine.rb +145 -2
  83. metadata +115 -37
  84. data/bin/core_http_test +0 -51
  85. data/bin/em playground +0 -56
  86. data/bin/hello_world +0 -75
  87. data/bin/setup +0 -7
  88. data/lib/iodine/client.rb +0 -5
  89. data/lib/iodine/core.rb +0 -102
  90. data/lib/iodine/core_init.rb +0 -143
  91. data/lib/iodine/http/hpack.rb +0 -553
  92. data/lib/iodine/http/http1.rb +0 -251
  93. data/lib/iodine/http/http2.rb +0 -507
  94. data/lib/iodine/http/rack_support.rb +0 -108
  95. data/lib/iodine/http/request.rb +0 -462
  96. data/lib/iodine/http/response.rb +0 -474
  97. data/lib/iodine/http/session.rb +0 -143
  98. data/lib/iodine/http/websocket_client.rb +0 -335
  99. data/lib/iodine/http/websocket_handler.rb +0 -101
  100. data/lib/iodine/http/websockets.rb +0 -336
  101. data/lib/iodine/io.rb +0 -56
  102. data/lib/iodine/logging.rb +0 -46
  103. data/lib/iodine/settings.rb +0 -158
  104. data/lib/iodine/ssl_connector.rb +0 -48
  105. data/lib/iodine/timers.rb +0 -95
data/bin/hello_world DELETED
@@ -1,75 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'pathname'
4
- Root ||= Pathname.new(File.dirname(__FILE__)).expand_path
5
- Dir.chdir Root.join('..').to_s
6
-
7
- require "bundler/setup"
8
- require "iodine/http"
9
-
10
-
11
- # ~/ruby/wrk/wrk -c400 -d10 -t12 http://localhost:3000/
12
-
13
-
14
- # Iodine.ssl = true
15
- # Iodine.treads = 8
16
- # Iodine.protocol.on_http do |req, res|
17
- # res.session[:count] ||= 0
18
- # res.session[:count] += 1
19
- # res << "Visits: #{res.session[:count]}\n\nRequest object:\n\n#{req.to_s}"
20
- # end
21
-
22
- # Iodine.processes = 4
23
-
24
- HELLO = "Hello World!"
25
- Iodine::Http.on_http { HELLO }
26
-
27
- Iodine.logger = nil
28
-
29
-
30
- class WSChatServer < Iodine::Http::WebsocketHandler
31
- def on_open
32
- @nickname = request.params[:nickname] || "unknown"
33
- broadcast "#{@nickname} has joined the chat!"
34
- write "Welcome #{@nickname}, you have joined the chat!"
35
- end
36
- def on_message data
37
- broadcast "#{@nickname} >> #{data}"
38
- write ">> #{data}"
39
- end
40
- def on_broadcast data
41
- write data
42
- end
43
- def on_close
44
- broadcast "#{@nickname} has left the chat!"
45
- end
46
- end
47
-
48
- Iodine::Http.on_websocket WSChatServer
49
-
50
- Iodine::Http.http2 = true
51
-
52
- Process.fork do
53
-
54
- Iodine.ssl = true
55
- Iodine.port = 3030
56
- Iodine::Http.on_http do |req, res|
57
- if req.path == '/stream'
58
- res.stream_async { sleep 0.2; res << "I was sleeping..."}
59
- next
60
- end
61
- if req.path == '/stream2'
62
- res.stream_async { sleep 2; res << "I was sleeping..."}
63
- next
64
- end
65
- res << "Old session visits: #{res.session_old[:count]}\n\n" if res.session_old
66
- res.session[:count] ||= 0
67
- res.session[:count] += 1
68
- res['content-type'] = 'text/plain'
69
- res.cookies['testing'] = !res.cookies['testing']
70
- res << "Visits: #{res.session[:count]}\n\nRequest object:\n\n#{req.to_s}"
71
- # puts "Setting :testing cookie was #{ res.cookies[:testing] }, setting to: #{res.cookies[:testing] = !res.cookies[:testing]}"
72
- # puts "Setting 'testing' cookie was #{ res.cookies['testing'] }, setting to: #{res.cookies['testing'] = !res.cookies['testing']}"
73
- end
74
-
75
- end
data/bin/setup DELETED
@@ -1,7 +0,0 @@
1
- #!/bin/bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
-
5
- bundle install
6
-
7
- # Do any other automated setup that you need to do here
data/lib/iodine/client.rb DELETED
@@ -1,5 +0,0 @@
1
- Iodine.protocol = :client if require('iodine/http') && Iodine.protocol == Iodine::Http::Http1
2
-
3
- module Iodine
4
- end
5
-
data/lib/iodine/core.rb DELETED
@@ -1,102 +0,0 @@
1
- module Iodine
2
- public
3
-
4
- #######################
5
- ## Events
6
-
7
- # Accepts a block and runs it asynchronously. This method runs asynchronously and returns immediately.
8
- #
9
- # use:
10
- #
11
- # Iodine.run(arg1, arg2, arg3 ...) { |arg1, arg2, arg3...| do_something }
12
- #
13
- # the block will be run within the current context, allowing access to current methods and variables.
14
- #
15
- # @return [Iodine] always returns the reactor object.
16
- def run *args, &block
17
- @queue << [block, args]
18
- self
19
- end
20
- alias :run_async :run
21
-
22
- # @return [Iodine] Adds a shutdown tasks. These tasks should be executed in order of creation.
23
- def on_shutdown *args, &block
24
- @shutdown_queue << [block, args]
25
- self
26
- end
27
-
28
- # @return [nil] Signals Iodine to exit if it was running on Server or Timer mode. Tasks will rundown pending timeout.
29
- def signal_exit
30
- Process.kill("INT", 0) unless @stop
31
- nil
32
- end
33
-
34
- # forces Iodine to start prematurely and asyncronously. This might cause Iodine's exit sequence to end abruptly, depending how the hosting application behaves.
35
- #
36
- # calling this method repeatedly will be ignored unless Iodine's threads have all died.
37
- #
38
- # @return [Iodine] the method will allways return `self` (Iodine).
39
- def force_start!
40
- return self if @force_running
41
- @force_running = true
42
- thread = Thread.new do
43
- startup true
44
- @force_running = false
45
- initialize_tasks
46
- @stop = false if @protocol
47
- end
48
- Kernel.at_exit {thread.raise("stop"); thread.join}
49
- self
50
- end
51
-
52
- protected
53
-
54
- def cycle
55
- work until @stop && @queue.empty?
56
- @shutdown_mutex.synchronize { shutdown }
57
- work until @queue.empty?
58
- run { true }
59
- end
60
-
61
- def work
62
- job = @queue && @queue.pop
63
- if job && job[0]
64
- begin
65
- job[0].call(*job[1])
66
- rescue => e
67
- error e
68
- end
69
- end
70
- end
71
-
72
- def startup use_rescue = false, hide_message = false
73
- @force_running = true
74
- threads = []
75
- (@thread_count ||= 1).times { threads << Thread.new { Thread.current[:buffer] ||= String.new; Thread.current[:write_buffer] ||= String.new; cycle } }
76
- unless @stop
77
- if use_rescue
78
- sleep rescue true
79
- else
80
- old_int_trap = trap('INT') { throw :stop; old_int_trap.respond_to?(:call) && old_int_trap.call }
81
- old_term_trap = trap('TERM') { throw :stop; old_term_trap.respond_to?(:call) && old_term_trap.call }
82
- catch(:stop) { sleep }
83
- end
84
- log "\nShutting down #{self == Iodine ? 'Iodine' : "#{self.name} (Iodine)"}. Setting shutdown timeout to 25 seconds.\n" unless hide_message
85
- @stop = true
86
- # setup exit timeout.
87
- threads.each {|t| Thread.new {sleep 25; t.kill; t.kill } }
88
- end
89
- threads.each {|t| t.join rescue true }
90
- end
91
-
92
- # performed once - the shutdown sequence.
93
- def shutdown
94
- return if @done
95
- @stop = @done = true
96
- arr = []
97
- arr.push @shutdown_queue.pop until @shutdown_queue.empty?
98
- @queue.push arr.pop while arr[0]
99
- @thread_count.times { run { true } }
100
- end
101
-
102
- end
@@ -1,143 +0,0 @@
1
- module Iodine
2
- protected
3
-
4
- def extended base
5
- base.instance_exec do
6
- initialize_core
7
- initialize_io
8
- initialize_timers
9
- initialize_tasks
10
- end
11
- base.protocol = :cycle unless base == Iodine
12
- end
13
-
14
- def initialize_core
15
- # initializes all the core components
16
- # referenced in `core.rb`
17
- @queue = Queue.new
18
- @shutdown_queue = Queue.new
19
- @stop = true
20
- @done = false
21
- @logger = Logger.new(STDOUT)
22
- @spawn_count = 1
23
- @thread_count = nil
24
- @ios = {}
25
- @io_in = Queue.new
26
- @io_out = Queue.new
27
- @shutdown_mutex = Mutex.new
28
- @servers = {}
29
- Kernel.at_exit do
30
- if self == Iodine
31
- startup
32
- else
33
- Iodine.protocol ||= :cycle if @timers.any? || @protocol
34
- thread = Thread.new { startup true }
35
- Iodine.on_shutdown { thread.raise 'stop' ; thread.join }
36
- end
37
- end
38
- end
39
-
40
- def initialize_io
41
- # initializes all the IO components
42
- # referenced in `io.rb`
43
- @port = ((ARGV.index('-p') && ARGV[ARGV.index('-p') + 1]) || ENV['PORT'] || 3000).to_i
44
- @bind = (ARGV.index('-ip') && ARGV[ARGV.index('-ip') + 1]) || ENV['IP'] || "0.0.0.0"
45
- @ssl = (ARGV.index('ssl') && true) || (@port == 443)
46
- @protocol = nil
47
- @ssl_context = nil
48
- @ssl_protocols = {}
49
- @time = Time.now
50
-
51
- @timeout_proc = Proc.new {|prot| prot.timeout?(@time) }
52
- @status_loop = Proc.new {|io| @io_out << io if io.closed? || !(io.stat.readable? rescue false) }
53
- @close_callback = Proc.new {|prot| prot.on_close if prot }
54
- @reactor = [ (Proc.new do
55
- @ios.keys.each(&@status_loop)
56
- @ios.values.each(&@timeout_proc)
57
- until @io_in.empty?
58
- n_io = @io_in.pop
59
- @ios[n_io[0]] = n_io[1]
60
- end
61
- until @io_out.empty?
62
- o_io = @io_out.pop
63
- o_io.close unless o_io.closed?
64
- run @ios.delete(o_io), &@close_callback
65
- end
66
- if @queue.empty?
67
- #clear any closed IO objects.
68
- @time = Time.now
69
- # react to IO events
70
- begin
71
- r = IO.select(@ios.keys, nil, nil, 0.15)
72
- r[0].each {|io| @queue << [@ios[io]] } if r
73
- rescue
74
-
75
- end
76
- unless @stop && @queue.empty?
77
- # @ios.values.each &@timeout_loop
78
- @check_timers && (@queue << @check_timers)
79
- @queue << @reactor
80
- end
81
- else
82
- @queue << @reactor
83
- end
84
- end )]
85
- end
86
-
87
- def initialize_timers
88
- @timer_locker = Mutex.new
89
- @timers = []
90
- # cycles through timed jobs, executing and/or deleting them if their time has come.
91
- @check_timers = [(Proc.new do
92
- @timer_locker.synchronize { @timers.delete_if {|t| t.done? } }
93
- end)]
94
- end
95
-
96
- def initialize_tasks
97
- # initializes actions to be taken when starting to run
98
- run do
99
- @protocol ||= :cycle if @timers.any?
100
- next unless @protocol
101
- if @protocol.is_a?( ::Class ) && @protocol.ancestors.include?( ::Iodine::Protocol )
102
- begin
103
- @server = ::TCPServer.new(@bind, @port)
104
- rescue => e
105
- fatal e.message
106
- fatal "Running existing tasks with #{@thread_count} thread(s) and exiting."
107
- @queue << @reactor
108
- Process.kill("INT", 0)
109
- next
110
- end
111
- on_shutdown do
112
- @server.close unless @server.nil? || @server.closed?
113
- log "Stopped listening to port #{@port}.\n"
114
- end
115
- ::Iodine::Base::Listener.accept(@server, false)
116
- log "Iodine #{VERSION} is listening on port #{@port}#{ ' (SSL/TLS)' if @ssl} with #{@thread_count} thread(s).\n"
117
- if @spawn_count && @spawn_count.to_i > 1 && Process.respond_to?(:fork)
118
- log "Server will run using #{@spawn_count.to_i} processes - Spawning #{@spawn_count.to_i - 1 } more processes.\n"
119
- (@spawn_count.to_i - 1).times do
120
- Process.fork do
121
- log "Spawned process: #{Process.pid}.\n"
122
- on_shutdown { log "Shutting down process #{Process.pid}.\n" }
123
- @queue.clear
124
- @queue << @reactor
125
- startup false, true
126
- end
127
- end
128
-
129
- end
130
- log "Press ^C to stop the server.\n"
131
- else
132
- log "#{self == Iodine ? 'Iodine' : "#{self.name} (Iodine)"} #{VERSION} is running with #{@thread_count} thread(s).\n"
133
- log "Press ^C to stop the cycling.\n"
134
- end
135
- on_shutdown do
136
- shut_down_proc = Proc.new {|prot| prot.on_shutdown ; prot.close }
137
- @ios.values.each {|p| run p, &shut_down_proc }
138
- @queue << @reactor
139
- end
140
- @queue << @reactor
141
- end
142
- end
143
- end
@@ -1,553 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- module Iodine
4
- module Http
5
- class Http2 < ::Iodine::Protocol
6
- class HPACK
7
- class IndexTable
8
- attr_reader :size
9
- attr_accessor :max_size
10
- def initialize
11
- @list = []
12
- @size = 4_096 # initial defaul size by standard
13
- @actual_size = 0
14
- end
15
- def [] index
16
- raise "HPACK Error - invalid header index: 0" if index == 0
17
- return STATIC_LIST[index] if index < STATIC_LENGTH
18
- raise "HPACK Error - invalid header index: #{index}" if index >= ( @list.count + STATIC_LENGTH )
19
- @list[index - STATIC_LENGTH]
20
- end
21
- alias :get_index :[]
22
- def get_name index
23
- get_index(index)[0]
24
- end
25
- def insert *field
26
- @list.unshift field
27
- field.each {|f| @actual_size += f.to_s.bytesize}; @actual_size += 32
28
- resize
29
- field
30
- end
31
- def find *field
32
- index = STATIC_LIST.index(field)
33
- return index if index
34
- index = @list.index(field)
35
- index ? (index + STATIC_LENGTH) : nil
36
- end
37
- def find_name name
38
- index = 1
39
- while STATIC_LIST[index]
40
- return index if STATIC_LIST[index][0] == name
41
- index += 1
42
- end
43
- index = 0
44
- while @list[index]
45
- return index+STATIC_LENGTH if @list[index][0] == name
46
- index += 1
47
- end
48
- nil
49
- end
50
- def resize value = nil
51
- @size = value if value && value <= 4_096
52
- while (@actual_size > @size) && @list.any?
53
- @list.pop.each {|i| @actual_size -= i.to_s.bytesize}
54
- @actual_size -= 32
55
- end
56
- self
57
- end
58
- end
59
-
60
- def initialize
61
- @decoding_list = IndexTable.new
62
- @encoding_list = IndexTable.new
63
- end
64
-
65
- def decode data
66
- data = StringIO.new data
67
- results = {}
68
- while (field = decode_field(data))
69
- name = (field[0].is_a?(String) && field[0][0] == ':'.freeze) ? field[0][1..-1].to_sym : field[0]
70
- results[name] ? (results[name].is_a?(String) ? (results[name] = [results[name], field[1]]) : (results[name] << field[1]) ) : (results[name] = field[1]) if field[1]
71
- end
72
- results
73
- end
74
- def encode headers = {}
75
- buffer = String.new.force_encoding(::Encoding::ASCII_8BIT)
76
- headers.each {|k, v| buffer << encode_field( (k.is_a?(String) ? k : ":#{k.to_s}".freeze) ,v) if v}
77
- buffer.force_encoding(::Encoding::ASCII_8BIT)
78
- end
79
- def resize max
80
- @decoding_list.resize max
81
- @encoding_list.resize max
82
- end
83
-
84
- protected
85
- def decode_field data # expects a StringIO or other IO object
86
- byte = data.getbyte
87
- return nil unless byte
88
- if byte[7] == 1 # 0b1000_0000 == 0b1000_0000
89
- # An indexed header field starts with the '1' 1-bit pattern, followed by the index of the matching header field, represented as an integer with a 7-bit prefix (see Section 5.1).
90
- num = extract_number data, byte, 1
91
- @decoding_list[num]
92
- elsif byte & 192 == 64 # 0b1100_0000 == 0b0100_0000
93
- # A literal header field with incremental indexing representation starts with the '01' 2-bit pattern.
94
- # If the header field name matches the header field name of an entry stored in the static table or the dynamic table, the header field name can be represented using the index of that entry. In this case, the index of the entry is represented as an integer with a 6-bit prefix (see Section 5.1). This value is always non-zero.
95
- # Otherwise, the header field name is represented as a string literal (see Section 5.2). A value 0 is used in place of the 6-bit index, followed by the header field name.
96
- num = extract_number data, byte, 2
97
- field_name = (num == 0) ? extract_string(data) : @decoding_list.get_name(num)
98
- field_value = extract_string(data)
99
- @decoding_list.insert field_name, field_value
100
- elsif byte & 224 # 0b1110_0000 == 0
101
- # A literal header field without indexing representation starts with the '0000' 4-bit pattern.
102
- # If the header field name matches the header field name of an entry stored in the static table or the dynamic table, the header field name can be represented using the index of that entry.
103
- # In this case, the index of the entry is represented as an integer with a 4-bit prefix (see Section 5.1). This value is always non-zero.
104
- # Otherwise, the header field name is represented as a string literal (see Section 5.2) and a value 0 is used in place of the 4-bit index, followed by the header field name.
105
- # OR
106
- # A literal header field never-indexed representation starts with the '0001' 4-bit pattern + 4+ bits for index
107
- num = extract_number data, byte, 4
108
- field_name = (num == 0) ? extract_string(data) : @decoding_list.get_name(num)
109
- field_value = extract_string(data)
110
- [field_name, field_value]
111
- elsif byte & 224 == 32 # 0b1110_0000 == 0b0010_0000
112
- # A dynamic table size update starts with the '001' 3-bit pattern
113
- # followed by the new maximum size, represented as an integer with a 5-bit prefix (see Section 5.1).
114
- @decoding_list.resize extract_number(data, byte, 5)
115
- [].freeze
116
- else
117
- raise "HPACK Error - invalid field indicator."
118
- end
119
- end
120
- def encode_field name, value
121
- if value.is_a?(Array)
122
- return (value.map {|v| encode_field name, v} .join)
123
- end
124
- raise "Http/2 headers must be LOWERCASE Strings!" if name[0] =~ /[A-Z]/n.freeze
125
- value = value.to_s
126
- if name == 'set-cookie'.freeze
127
- buffer = String.new.force_encoding ::Encoding::ASCII_8BIT
128
- buffer << pack_number( 55, 1, 4)
129
- buffer << pack_string(value)
130
- return buffer
131
- end
132
- index = @encoding_list.find(name, value)
133
- return pack_number( index, 1, 1) if index
134
- index = @encoding_list.find_name name
135
- buffer = String.new.force_encoding(::Encoding::ASCII_8BIT)
136
- if index
137
- buffer << pack_number( index, 1, 2)
138
- else
139
- raise "Http/2 headers whould be Strings! or allowed Psedo-Header Symbol Only!" if name[0] == ':'.freeze
140
- buffer << pack_number( 0, 1, 2)
141
- buffer << pack_string(name)
142
- end
143
- buffer << pack_string(value)
144
- @encoding_list.insert name, value
145
- buffer
146
- rescue
147
- puts "HPACK failure data dump:"
148
- puts "buffer: #{buffer} - #{buffer.encoding}" if buffer
149
- puts "name: #{name} - #{name.encoding}" if name.is_a? String
150
- puts "value: #{value} - #{value.encoding}" if value.is_a? String
151
- puts "packed #{pack_string(name)} - #{pack_string(value)}" if value
152
- raise
153
- end
154
- def extract_number data, prefix, prefix_length
155
- mask = 255 >> prefix_length
156
- return prefix & mask unless (prefix & mask) == mask
157
- count = prefix = 0
158
- loop do
159
- c = data.getbyte
160
- prefix = prefix | ((c & 127) << (7*count))
161
- break if c[7] == 0
162
- count += 1
163
- end
164
- prefix + mask
165
- end
166
- def pack_number number, prefix, prefix_length
167
- n_length = 8-prefix_length
168
- if (number + 1 ).bit_length <= n_length
169
- return ((prefix << n_length) | number).chr.force_encoding(::Encoding::ASCII_8BIT)
170
- end
171
- prefix = [(prefix << n_length) | (2**n_length - 1)]
172
- number -= 2**n_length - 1
173
- loop do
174
- prefix << ((number & 127) | 128)
175
- number = number >> 7
176
- break if number == 0
177
- end
178
- (prefix << (prefix.pop & 127)).pack('C*'.freeze).force_encoding(::Encoding::ASCII_8BIT)
179
- end
180
- def pack_string string, deflate = true
181
- string = deflate ? deflate(string) : string.dup.force_encoding(::Encoding::ASCII_8BIT)
182
- (pack_number(string.bytesize, (deflate ? 1 : 0), 1) + string ).force_encoding ::Encoding::ASCII_8BIT
183
- end
184
- def extract_string data
185
- byte = data.getbyte
186
- hoffman = byte[7] == 1
187
- length = extract_number data, byte, 1
188
- if hoffman
189
- inflate data.read(length)
190
- else
191
- data.read length
192
- end
193
- end
194
- def inflate data
195
- data = StringIO.new data
196
- str = String.new
197
- buffer = String.new
198
- until data.eof?
199
- byte = data.getbyte
200
- 8.times do |i|
201
- buffer << byte[7-i].to_s
202
- if HUFFMAN[buffer]
203
- str << HUFFMAN[buffer].chr rescue raise("HPACK Error - Huffman EOS found")
204
- buffer.clear
205
- end
206
- end
207
- end
208
- raise "HPACK Error - Huffman padding too long (#{buffer.length}): #{buffer}" if buffer.length > 29
209
- str
210
- end
211
- def deflate data
212
- str = String.new.force_encoding ::Encoding::ASCII_8BIT
213
- buffer = String.new.force_encoding ::Encoding::ASCII_8BIT
214
- data.bytes.each do |i|
215
- buffer << HUFFMAN.key(i)
216
- if (buffer.bytesize % 8) == 0
217
- str << [buffer].pack('B*'.freeze)
218
- buffer.clear
219
- end
220
- end
221
- unless buffer.empty?
222
- (8-(buffer.bytesize % 8)).times { buffer << '1'.freeze} if (buffer.bytesize % 8)
223
- str << [buffer].pack('B*'.freeze)
224
- buffer.clear
225
- end
226
- str.force_encoding ::Encoding::ASCII_8BIT
227
- end
228
- STATIC_LIST = [ nil,
229
- [":authority"],
230
- [":method", "GET" ],
231
- [":method", "POST" ],
232
- [":path", "/" ],
233
- [":path", "/index.html" ],
234
- [":scheme", "http" ],
235
- [":scheme", "https" ],
236
- [":status", "200" ],
237
- [":status", "204" ],
238
- [":status", "206" ],
239
- [":status", "304" ],
240
- [":status", "400" ],
241
- [":status", "404" ],
242
- [":status", "500" ],
243
- ["accept-charset"],
244
- ["accept-encoding", "gzip, deflate" ],
245
- ["accept-language"],
246
- ["accept-ranges"],
247
- ["accept"],
248
- ["access-control-allow-origin"],
249
- ["age"],
250
- ["allow"],
251
- ["authorization"],
252
- ["cache-control"],
253
- ["content-disposition"],
254
- ["content-encoding"],
255
- ["content-language"],
256
- ["content-length"],
257
- ["content-location"],
258
- ["content-range"],
259
- ["content-type"],
260
- ["cookie"],
261
- ["date"],
262
- ["etag"],
263
- ["expect"],
264
- ["expires"],
265
- ["from"],
266
- ["host"],
267
- ["if-match"],
268
- ["if-modified-since"],
269
- ["if-none-match"],
270
- ["if-range"],
271
- ["if-unmodified-since"],
272
- ["last-modified"],
273
- ["link"],
274
- ["location"],
275
- ["max-forwards"],
276
- ["proxy-authenticate"],
277
- ["proxy-authorization"],
278
- ["range"],
279
- ["referer"],
280
- ["refresh"],
281
- ["retry-after"],
282
- ["server"],
283
- ["set-cookie"],
284
- ["strict-transport-security"],
285
- ["transfer-encoding"],
286
- ["user-agent"],
287
- ["vary"],
288
- ["via"],
289
- ["www-authenticate"] ].map! {|a| a.map! {|s| s.is_a?(String) ? s.freeze : s } && a.freeze if a}
290
- STATIC_LENGTH = STATIC_LIST.length
291
-
292
- HUFFMAN = [
293
- "1111111111000",
294
- "11111111111111111011000",
295
- "1111111111111111111111100010",
296
- "1111111111111111111111100011",
297
- "1111111111111111111111100100",
298
- "1111111111111111111111100101",
299
- "1111111111111111111111100110",
300
- "1111111111111111111111100111",
301
- "1111111111111111111111101000",
302
- "111111111111111111101010",
303
- "111111111111111111111111111100",
304
- "1111111111111111111111101001",
305
- "1111111111111111111111101010",
306
- "111111111111111111111111111101",
307
- "1111111111111111111111101011",
308
- "1111111111111111111111101100",
309
- "1111111111111111111111101101",
310
- "1111111111111111111111101110",
311
- "1111111111111111111111101111",
312
- "1111111111111111111111110000",
313
- "1111111111111111111111110001",
314
- "1111111111111111111111110010",
315
- "111111111111111111111111111110",
316
- "1111111111111111111111110011",
317
- "1111111111111111111111110100",
318
- "1111111111111111111111110101",
319
- "1111111111111111111111110110",
320
- "1111111111111111111111110111",
321
- "1111111111111111111111111000",
322
- "1111111111111111111111111001",
323
- "1111111111111111111111111010",
324
- "1111111111111111111111111011",
325
- "010100",
326
- "1111111000",
327
- "1111111001",
328
- "111111111010",
329
- "1111111111001",
330
- "010101",
331
- "11111000",
332
- "11111111010",
333
- "1111111010",
334
- "1111111011",
335
- "11111001",
336
- "11111111011",
337
- "11111010",
338
- "010110",
339
- "010111",
340
- "011000",
341
- "00000",
342
- "00001",
343
- "00010",
344
- "011001",
345
- "011010",
346
- "011011",
347
- "011100",
348
- "011101",
349
- "011110",
350
- "011111",
351
- "1011100",
352
- "11111011",
353
- "111111111111100",
354
- "100000",
355
- "111111111011",
356
- "1111111100",
357
- "1111111111010",
358
- "100001",
359
- "1011101",
360
- "1011110",
361
- "1011111",
362
- "1100000",
363
- "1100001",
364
- "1100010",
365
- "1100011",
366
- "1100100",
367
- "1100101",
368
- "1100110",
369
- "1100111",
370
- "1101000",
371
- "1101001",
372
- "1101010",
373
- "1101011",
374
- "1101100",
375
- "1101101",
376
- "1101110",
377
- "1101111",
378
- "1110000",
379
- "1110001",
380
- "1110010",
381
- "11111100",
382
- "1110011",
383
- "11111101",
384
- "1111111111011",
385
- "1111111111111110000",
386
- "1111111111100",
387
- "11111111111100",
388
- "100010",
389
- "111111111111101",
390
- "00011",
391
- "100011",
392
- "00100",
393
- "100100",
394
- "00101",
395
- "100101",
396
- "100110",
397
- "100111",
398
- "00110",
399
- "1110100",
400
- "1110101",
401
- "101000",
402
- "101001",
403
- "101010",
404
- "00111",
405
- "101011",
406
- "1110110",
407
- "101100",
408
- "01000",
409
- "01001",
410
- "101101",
411
- "1110111",
412
- "1111000",
413
- "1111001",
414
- "1111010",
415
- "1111011",
416
- "111111111111110",
417
- "'",
418
- "11111111111101",
419
- "1111111111101",
420
- "1111111111111111111111111100",
421
- "11111111111111100110",
422
- "1111111111111111010010",
423
- "11111111111111100111",
424
- "11111111111111101000",
425
- "1111111111111111010011",
426
- "1111111111111111010100",
427
- "1111111111111111010101",
428
- "11111111111111111011001",
429
- "1111111111111111010110",
430
- "11111111111111111011010",
431
- "11111111111111111011011",
432
- "11111111111111111011100",
433
- "11111111111111111011101",
434
- "11111111111111111011110",
435
- "111111111111111111101011",
436
- "11111111111111111011111",
437
- "111111111111111111101100",
438
- "111111111111111111101101",
439
- "1111111111111111010111",
440
- "11111111111111111100000",
441
- "111111111111111111101110",
442
- "11111111111111111100001",
443
- "11111111111111111100010",
444
- "11111111111111111100011",
445
- "11111111111111111100100",
446
- "111111111111111011100",
447
- "1111111111111111011000",
448
- "11111111111111111100101",
449
- "1111111111111111011001",
450
- "11111111111111111100110",
451
- "11111111111111111100111",
452
- "111111111111111111101111",
453
- "1111111111111111011010",
454
- "111111111111111011101",
455
- "11111111111111101001",
456
- "1111111111111111011011",
457
- "1111111111111111011100",
458
- "11111111111111111101000",
459
- "11111111111111111101001",
460
- "111111111111111011110",
461
- "11111111111111111101010",
462
- "1111111111111111011101",
463
- "1111111111111111011110",
464
- "111111111111111111110000",
465
- "111111111111111011111",
466
- "1111111111111111011111",
467
- "11111111111111111101011",
468
- "11111111111111111101100",
469
- "111111111111111100000",
470
- "111111111111111100001",
471
- "1111111111111111100000",
472
- "111111111111111100010",
473
- "11111111111111111101101",
474
- "1111111111111111100001",
475
- "11111111111111111101110",
476
- "11111111111111111101111",
477
- "11111111111111101010",
478
- "1111111111111111100010",
479
- "1111111111111111100011",
480
- "1111111111111111100100",
481
- "11111111111111111110000",
482
- "1111111111111111100101",
483
- "1111111111111111100110",
484
- "11111111111111111110001",
485
- "11111111111111111111100000",
486
- "11111111111111111111100001",
487
- "11111111111111101011",
488
- "1111111111111110001",
489
- "1111111111111111100111",
490
- "11111111111111111110010",
491
- "1111111111111111101000",
492
- "1111111111111111111101100",
493
- "11111111111111111111100010",
494
- "11111111111111111111100011",
495
- "11111111111111111111100100",
496
- "111111111111111111111011110",
497
- "111111111111111111111011111",
498
- "11111111111111111111100101",
499
- "111111111111111111110001",
500
- "1111111111111111111101101",
501
- "1111111111111110010",
502
- "111111111111111100011",
503
- "11111111111111111111100110",
504
- "111111111111111111111100000",
505
- "111111111111111111111100001",
506
- "11111111111111111111100111",
507
- "111111111111111111111100010",
508
- "111111111111111111110010",
509
- "111111111111111100100",
510
- "111111111111111100101",
511
- "11111111111111111111101000",
512
- "11111111111111111111101001",
513
- "1111111111111111111111111101",
514
- "111111111111111111111100011",
515
- "111111111111111111111100100",
516
- "111111111111111111111100101",
517
- "11111111111111101100",
518
- "111111111111111111110011",
519
- "11111111111111101101",
520
- "111111111111111100110",
521
- "1111111111111111101001",
522
- "111111111111111100111",
523
- "111111111111111101000",
524
- "11111111111111111110011",
525
- "1111111111111111101010",
526
- "1111111111111111101011",
527
- "1111111111111111111101110",
528
- "1111111111111111111101111",
529
- "111111111111111111110100",
530
- "111111111111111111110101",
531
- "11111111111111111111101010",
532
- "11111111111111111110100",
533
- "11111111111111111111101011",
534
- "111111111111111111111100110",
535
- "11111111111111111111101100",
536
- "11111111111111111111101101",
537
- "111111111111111111111100111",
538
- "111111111111111111111101000",
539
- "111111111111111111111101001",
540
- "111111111111111111111101010",
541
- "111111111111111111111101011",
542
- "1111111111111111111111111110",
543
- "111111111111111111111101100",
544
- "111111111111111111111101101",
545
- "111111111111111111111101110",
546
- "111111111111111111111101111",
547
- "111111111111111111111110000",
548
- "11111111111111111111101110",
549
- "111111111111111111111111111111"].each_with_index.with_object({}) {|a, h| h[a[0]] = a[1] }
550
- end
551
- end
552
- end
553
- end