From dc3630f64ca1376aa27a8fd69cfb95181825ebea Mon Sep 17 00:00:00 2001 From: HoneyryderChuck Date: Thu, 13 Jun 2024 17:25:46 +0100 Subject: [PATCH] ported back remaining changes from http-2-next --- .github/workflows/ci.yml | 35 +- .rubocop.yml | 104 ++- .rubocop_todo.yml | 116 +--- .simplecov | 11 + Gemfile | 28 +- Rakefile | 57 +- example/Gemfile | 4 +- example/client.rb | 32 +- example/helper.rb | 18 +- example/raw.rb | 4 +- example/server.rb | 62 +- example/upgrade_client.rb | 38 +- example/upgrade_server.rb | 72 +- http-2.gemspec | 33 +- lib/http/2.rb | 23 +- lib/http/2/base64.rb | 45 ++ lib/http/2/client.rb | 4 +- lib/http/2/connection.rb | 28 +- lib/http/2/emitter.rb | 6 +- lib/http/2/error.rb | 2 +- lib/http/2/extensions.rb | 6 +- lib/http/2/flow_buffer.rb | 8 +- lib/http/2/framer.rb | 35 +- lib/http/2/header.rb | 10 +- lib/http/2/header/compressor.rb | 12 +- lib/http/2/header/decompressor.rb | 16 +- lib/http/2/header/encoding_context.rb | 135 ++-- lib/http/2/header/huffman.rb | 16 +- lib/http/2/header/huffman_statemachine.rb | 768 ++++++++-------------- lib/http/2/server.rb | 4 +- lib/http/2/stream.rb | 10 +- lib/http/2/version.rb | 2 +- spec/client_spec.rb | 118 ++-- spec/compressor_spec.rb | 402 +++++------ spec/connection_spec.rb | 28 +- spec/emitter_spec.rb | 14 +- spec/framer_spec.rb | 210 +++--- spec/helper.rb | 32 +- spec/hpack_test_spec.rb | 34 +- spec/huffman_spec.rb | 52 +- spec/server_spec.rb | 50 +- spec/shared_examples/connection.rb | 164 ++--- spec/stream_spec.rb | 264 ++++---- tasks/generate_huffman_table.rb | 22 +- 44 files changed, 1484 insertions(+), 1650 deletions(-) create mode 100644 .simplecov create mode 100644 lib/http/2/base64.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b99a4a3..bf291fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: ['3.0', 3.1, 3.2, 3.3, jruby-9.4] + ruby: [2.7 ,'3.0', 3.1, 3.2, 3.3, jruby, truffleruby] steps: - uses: actions/checkout@v4 @@ -27,7 +27,11 @@ jobs: bundler-cache: true - name: Tests + env: + CI: 1 run: | + echo $(pwd) + RUBY_VERSION=`ruby -e 'puts RUBY_VERSION'` RUBY_PLATFORM=`ruby -e 'puts RUBY_PLATFORM'` RUBY_ENGINE=`ruby -e 'puts RUBY_ENGINE'` if [[ "$RUBY_ENGINE" = "ruby" ]] && [[ ${RUBY_VERSION:0:1} = "3" ]] && [[ ! $RUBYOPT =~ "jit" ]]; then @@ -36,14 +40,14 @@ jobs: export RBS_TEST_RAISE="true" export RBS_TEST_LOGLEVEL="error" export RBS_TEST_OPT="-Isig -rbase64" - export RBS_TEST_TARGET="HTTP2Next*" + export RBS_TEST_TARGET="HTTP2*" fi - ENV=CI bundle exec rake + bundle exec rake - name: Upload coverage - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 if: always() with: - name: coverage-report + name: coverage-report-${{matrix.ruby}} path: coverage/ coverage: @@ -53,27 +57,30 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/download-artifact@master - with: - name: coverage-report - path: path/to/artifact - - name: Setup Ruby uses: ruby/setup-ruby@v1 with: ruby-version: 3.3 bundler-cache: true + - name: Download coverage results + uses: actions/download-artifact@v4 + with: + pattern: coverage-report-* + path: coverage + - name: coverage + env: + CI: 1 run: | + echo ${{ github.workspace }} gem install simplecov --no-doc - ls - find coverage -name "*resultset.json" -exec sed -i 's?/home?'`pwd`'?' {} \; + find coverage -name "*resultset.json" + find coverage -name "*resultset.json" -exec sed -i 's?${{ github.workspace }}?'`pwd`'?' {} \; rake coverage:report - name: Upload coverage - uses: actions/upload-artifact@v2 - if: always() + uses: actions/upload-artifact@v4 with: name: coverage-report path: coverage/ diff --git a/.rubocop.yml b/.rubocop.yml index 9b654e6..9362244 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,15 +1,107 @@ inherit_from: .rubocop_todo.yml +require: + - rubocop-performance + AllCops: NewCops: enable - SuggestExtensions: false - TargetRubyVersion: 3.0 + TargetRubyVersion: 2.7 + DisplayCopNames: true Exclude: - - 'vendor/bundle/**/*' # see https://github.com/rubocop/rubocop/issues/9832 + - "bin/**" + - "vendor/**/*" + - "**/huffman_statemachine.rb" + - "lib/tasks/**" -Gemspec/RequireMFA: - Enabled: false Metrics/BlockLength: + Enabled: false + +Lint/EmptyWhen: + Enabled: false + +Style/StringLiterals: + EnforcedStyle: double_quotes + +Style/NumericPredicate: + Enabled: false + +Gemspec/RequiredRubyVersion: + Enabled: false + +Bundler/DuplicatedGem: + Enabled: false + +Style/OptionalBooleanParameter: + Enabled: false + +Style/ArgumentsForwarding: + Enabled: false + +Lint/MissingSuper: + Exclude: + - 'lib/httpx/io/unix.rb' + +Style/HashTransformValues: + Exclude: + - 'lib/httpx/plugins/digest_authentication.rb' + +Lint/ConstantDefinitionInBlock: + Exclude: + - 'spec/**/*' + +# TODO: remove this if min supported version of ruby is 2.3 +Style/HashSyntax: + Enabled: false + +Style/AndOr: + Enabled: false + +Style/SafeNavigation: + Enabled: false + +Layout/HeredocIndentation: + Exclude: + - "lib/tasks/generate_huffman_table.rb" + - "example/*" + +Naming/MethodParameterName: + Enabled: false + +Naming/VariableNumber: + Exclude: + - example/server.rb + +Layout/LineLength: + Max: 128 + +Style/HashEachMethods: + Enabled: true + +Style/HashTransformKeys: + Enabled: true + +Style/CommentAnnotation: + Enabled: false + +Style/SlicingWithRange: + Enabled: false + +Lint/SuppressedException: + Exclude: + - Rakefile + +Lint/EmptyBlock: + Exclude: + - spec/* + +Performance/CollectionLiteralInLoop: + Exclude: + - spec/* + +Performance/MethodObjectAsBlock: + Enabled: false + +Metrics/CollectionLiteralLength: Exclude: - - 'spec/**/*.rb' + - lib/http/2/header/huffman.rb \ No newline at end of file diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index a9a9631..506d57c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,121 +1,47 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2024-06-13 02:27:14 UTC using RuboCop version 1.64.1. +# on 2016-06-09 10:57:54 -0400 using RuboCop version 0.40.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 1 -# Configuration parameters: AllowedMethods. -# AllowedMethods: enums -Lint/ConstantDefinitionInBlock: - Exclude: - - 'spec/emitter_spec.rb' - -# Offense count: 3 -# Configuration parameters: AllowComments, AllowEmptyLambdas. -Lint/EmptyBlock: - Exclude: - - 'spec/client_spec.rb' - - 'spec/emitter_spec.rb' - - 'spec/server_spec.rb' - -# Offense count: 3 -# Configuration parameters: AllowComments, AllowNil. -Lint/SuppressedException: - Exclude: - - 'Rakefile' - -# Offense count: 27 -# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. +# Offense count: 24 Metrics/AbcSize: - Max: 172 + Max: 200 -# Offense count: 8 -# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. -# AllowedMethods: refine -Metrics/BlockLength: - Max: 82 - -# Offense count: 19 -# Configuration parameters: CountBlocks. +# Offense count: 16 Metrics/BlockNesting: - Max: 6 + Max: 5 + Exclude: + - "lib/http/2/connection.rb" -# Offense count: 7 -# Configuration parameters: CountComments, CountAsOne. +# Offense count: 5 +# Configuration parameters: CountComments. Metrics/ClassLength: - Max: 516 + Max: 371 -# Offense count: 2 -# Configuration parameters: LengthThreshold. -Metrics/CollectionLiteralLength: - Exclude: - - 'lib/http/2/header/huffman.rb' - - 'lib/http/2/header/huffman_statemachine.rb' +Metrics/ModuleLength: + Max: 120 -# Offense count: 14 -# Configuration parameters: AllowedMethods, AllowedPatterns. +# Offense count: 12 Metrics/CyclomaticComplexity: Max: 60 -# Offense count: 33 -# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. +# Offense count: 29 +# Configuration parameters: CountComments. Metrics/MethodLength: - Max: 132 + Max: 134 # Offense count: 1 -# Configuration parameters: CountComments, CountAsOne. -Metrics/ModuleLength: - Max: 113 - -# Offense count: 1 -# Configuration parameters: CountKeywordArgs, MaxOptionalParameters. +# Configuration parameters: CountKeywordArgs. Metrics/ParameterLists: Max: 7 -# Offense count: 12 -# Configuration parameters: AllowedMethods, AllowedPatterns. +# Offense count: 10 Metrics/PerceivedComplexity: - Max: 48 + Max: 50 -# Offense count: 6 -# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. -# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to -Naming/MethodParameterName: - Exclude: - - 'lib/http/2/connection.rb' - - 'lib/http/2/extensions.rb' - - 'lib/http/2/header/compressor.rb' - - 'lib/http/2/header/decompressor.rb' - -# Offense count: 1 -# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns. -# SupportedStyles: snake_case, normalcase, non_integer -# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64 -Naming/VariableNumber: - Exclude: - - 'example/server.rb' - -# Offense count: 10 -# Configuration parameters: AllowedConstants. +# Offense count: 4 Style/Documentation: - Exclude: - - 'spec/**/*' - - 'test/**/*' - - 'example/helper.rb' - - 'example/upgrade_server.rb' - - 'lib/http/2/error.rb' - - 'lib/http/2/extensions.rb' - - 'lib/http/2/flow_buffer.rb' - - 'lib/http/2/header/decompressor.rb' - - 'lib/http/2/header/encoding_context.rb' - - 'tasks/generate_huffman_table.rb' - -# Offense count: 1 -# Configuration parameters: AllowedMethods. -# AllowedMethods: respond_to_missing? -Style/OptionalBooleanParameter: - Exclude: - - 'lib/http/2/flow_buffer.rb' + Enabled: false diff --git a/.simplecov b/.simplecov new file mode 100644 index 0000000..0d8ef3a --- /dev/null +++ b/.simplecov @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +SimpleCov.start do + command_name "Spec" + add_filter "/.bundle/" + add_filter "/vendor/" + add_filter "/spec/" + add_filter "/lib/http/2/base64" + coverage_dir "coverage" + minimum_coverage(RUBY_ENGINE == "truffleruby" ? 85 : 90) +end diff --git a/Gemfile b/Gemfile index 3280b2f..9545daf 100644 --- a/Gemfile +++ b/Gemfile @@ -1,31 +1,35 @@ # frozen_string_literal: true -source 'https://rubygems.org' +source "https://rubygems.org" gemspec -gem 'rake', require: false +gem "rake", require: false group :development do - gem 'pry' - gem 'pry-byebug', platform: :mri - gem 'rubocop' - gem 'rubocop-performance' + gem "pry" + gem "pry-byebug", platform: :mri + if RUBY_VERSION >= "3.0.0" + gem "rubocop" + gem "rubocop-performance" + end end group :docs do - gem 'yard' + gem "yard" end group :test do - gem 'rspec' - gem 'simplecov', require: false + gem "rspec" + gem "simplecov", require: false end group :types do platform :mri do - gem 'rbs' - gem 'steep' - gem 'typeprof' + if RUBY_VERSION >= "3.0.0" + gem "rbs" + gem "steep" + gem "typeprof" + end end end diff --git a/Rakefile b/Rakefile index efd48b6..d8cb632 100644 --- a/Rakefile +++ b/Rakefile @@ -1,60 +1,63 @@ # frozen_string_literal: true -require 'English' -require 'bundler/gem_tasks' -require 'open3' +require "English" +require "bundler/gem_tasks" +require "open3" -require_relative 'tasks/generate_huffman_table' +require_relative "tasks/generate_huffman_table" + +RUBY_MAJOR_MINOR = RUBY_VERSION.split(".").first(2).join(".") begin - require 'rspec/core/rake_task' + require "rspec/core/rake_task" RSpec::Core::RakeTask.new(:spec) do |t| - t.exclude_pattern = './spec/hpack_test_spec.rb' + t.exclude_pattern = "./spec/hpack_test_spec.rb" end RSpec::Core::RakeTask.new(:hpack) do |t| - t.pattern = './spec/hpack_test_spec.rb' + t.pattern = "./spec/hpack_test_spec.rb" end rescue LoadError end begin - require 'rubocop/rake_task' - desc 'Run rubocop' + require "rubocop/rake_task" + desc "Run rubocop" RuboCop::RakeTask.new rescue LoadError end begin - require 'yard' + require "yard" YARD::Rake::YardocTask.new rescue LoadError end namespace :coverage do - desc 'Aggregates coverage reports' + desc "Aggregates coverage reports" task :report do - return unless ENV.key?('CI') + return unless ENV.key?("CI") - require 'simplecov' + require "simplecov" - SimpleCov.collate Dir['coverage/**/.resultset.json'] + puts Dir["coverage/**/.resultset.json"].inspect + SimpleCov.collate Dir["coverage/**/.resultset.json"] end end -desc 'install h2spec' +desc "install h2spec" task :h2spec_install do platform = case RUBY_PLATFORM when /darwin/ - 'h2spec_darwin_amd64.tar.gz' + "h2spec_darwin_amd64.tar.gz" when /cygwin|mswin|mingw|bccwin|wince|emx/ - 'h2spec_windows_amd64.zip' + "h2spec_windows_amd64.zip" else - 'h2spec_linux_amd64.tar.gz' + "h2spec_linux_amd64.tar.gz" end # uri = "https://github.com/summerwind/h2spec/releases/download/v2.3.0/#{platform}" - tar_location = File.join(__dir__, 'h2spec-releases', platform) + tar_location = File.join(__dir__, "h2spec-releases", platform) # require "net/http" # File.open(tar_location, "wb") do |file| # response = nil @@ -82,24 +85,24 @@ task :h2spec_install do # FileUtils.rm(tar_location) end -desc 'run h2spec' +desc "run h2spec" task :h2spec do - h2spec = File.join(__dir__, 'h2spec') + h2spec = File.join(__dir__, "h2spec") unless File.exist?(h2spec) abort 'Please install h2spec first.\n' \ 'Run "rake h2spec_install",\n' \ - 'Or Download the binary from https://github.com/summerwind/h2spec/releases' + "Or Download the binary from https://github.com/summerwind/h2spec/releases" end - server_pid = Process.spawn('ruby example/server.rb -p 9000', out: File::NULL) - sleep RUBY_ENGINE == 'ruby' ? 5 : 20 + server_pid = Process.spawn("ruby example/server.rb -p 9000", out: File::NULL) + sleep RUBY_ENGINE == "ruby" ? 5 : 20 system("#{h2spec} -p 9000 -o 2 --strict") - Process.kill('TERM', server_pid) + Process.kill("TERM", server_pid) exit($CHILD_STATUS.exitstatus) end default_tasks = %i[spec] -default_tasks << :rubocop if defined?(RuboCop) && RUBY_ENGINE == 'ruby' -default_tasks += %i[h2spec_install h2spec] if ENV.key?('CI') +default_tasks << :rubocop if defined?(RuboCop) && RUBY_ENGINE == "ruby" +default_tasks += %i[h2spec_install h2spec] if ENV.key?("CI") task default: default_tasks task all: %i[default hpack] diff --git a/example/Gemfile b/example/Gemfile index 8ba1c28..81721b3 100644 --- a/example/Gemfile +++ b/example/Gemfile @@ -1,5 +1,5 @@ # frozen_string_literal: true -source 'https://rubygems.org' +source "https://rubygems.org" -gem 'http_parser.rb' +gem "http_parser.rb" diff --git a/example/client.rb b/example/client.rb index aca1c4c..9b4b635 100644 --- a/example/client.rb +++ b/example/client.rb @@ -1,21 +1,21 @@ # frozen_string_literal: true -require_relative 'helper' +require_relative "helper" options = {} OptionParser.new do |opts| - opts.banner = 'Usage: client.rb [options]' + opts.banner = "Usage: client.rb [options]" - opts.on('-d', '--data [String]', 'HTTP payload') do |v| + opts.on("-d", "--data [String]", "HTTP payload") do |v| options[:payload] = v end end.parse! -uri = URI.parse(ARGV[0] || 'http://localhost:8080/') +uri = URI.parse(ARGV[0] || "http://localhost:8080/") tcp = TCPSocket.new(uri.host, uri.port) sock = nil -if uri.scheme == 'https' +if uri.scheme == "https" ctx = OpenSSL::SSL::SSLContext.new ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE @@ -75,12 +75,12 @@ end stream.on(:close) do - log.info 'stream closed' + log.info "stream closed" conn.goaway end stream.on(:half_close) do - log.info 'closing client-end of the stream' + log.info "closing client-end of the stream" end stream.on(:headers) do |h| @@ -96,23 +96,23 @@ end head = { - ':scheme' => uri.scheme, - ':method' => (options[:payload].nil? ? 'GET' : 'POST'), - ':authority' => [uri.host, uri.port].join(':'), - ':path' => uri.path, - 'accept' => '*/*' + ":scheme" => uri.scheme, + ":method" => (options[:payload].nil? ? "GET" : "POST"), + ":authority" => [uri.host, uri.port].join(":"), + ":path" => uri.path, + "accept" => "*/*" } -puts 'Sending HTTP 2.0 request' -if head[':method'] == 'GET' +puts "Sending HTTP 2.0 request" +if head[":method"] == "GET" stream.headers(head, end_stream: true) else stream.headers(head, end_stream: false) stream.data(options[:payload]) end -require 'memory_profiler' -report = MemoryProfiler.report(allow_files: 'http-2-next') do +require "memory_profiler" +report = MemoryProfiler.report(allow_files: "http-2-next") do while !sock.closed? && !sock.eof? data = sock.read_nonblock(1024) # puts "Received bytes: #{data.unpack("H*").first}" diff --git a/example/helper.rb b/example/helper.rb index 29416d2..ac1c727 100644 --- a/example/helper.rb +++ b/example/helper.rb @@ -1,22 +1,22 @@ # frozen_string_literal: true -$LOAD_PATH << 'lib' << '../lib' +$LOAD_PATH << "lib" << "../lib" -require 'optparse' -require 'socket' -require 'openssl' -require 'uri' +require "optparse" +require "socket" +require "openssl" +require "uri" # This will enable coverage within the CI environment -if ENV.key?('CI') - require 'simplecov' +if ENV.key?("CI") + require "simplecov" SimpleCov.command_name "#{RUBY_ENGINE}-#{RUBY_VERSION}-h2spec" SimpleCov.coverage_dir "coverage/#{RUBY_ENGINE}-#{RUBY_VERSION}-h2spec" end -require 'http/2' +require "http/2" -DRAFT = 'h2' +DRAFT = "h2" class Logger def initialize(id) diff --git a/example/raw.rb b/example/raw.rb index 852e610..2d3ea20 100644 --- a/example/raw.rb +++ b/example/raw.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require 'socket' +require "socket" -puts 'Starting server on port 9000' +puts "Starting server on port 9000" server = TCPServer.new(9000) loop do diff --git a/example/server.rb b/example/server.rb index 5ab3de3..cb97d83 100644 --- a/example/server.rb +++ b/example/server.rb @@ -1,20 +1,20 @@ # frozen_string_literal: true -require_relative 'helper' +require_relative "helper" options = { port: 8080 } OptionParser.new do |opts| - opts.banner = 'Usage: server.rb [options]' + opts.banner = "Usage: server.rb [options]" - opts.on('-s', '--secure', 'HTTPS mode') do |v| + opts.on("-s", "--secure", "HTTPS mode") do |v| options[:secure] = v end - opts.on('-p', '--port [Integer]', 'listen port') do |v| + opts.on("-p", "--port [Integer]", "listen port") do |v| options[:port] = v end - opts.on('-u', '--push', 'Push message') do |_v| + opts.on("-u", "--push", "Push message") do |_v| options[:push] = true end end.parse! @@ -24,14 +24,14 @@ if options[:secure] ctx = OpenSSL::SSL::SSLContext.new - ctx.cert = OpenSSL::X509::Certificate.new(File.open('keys/server.crt')) - ctx.key = OpenSSL::PKey::RSA.new(File.open('keys/server.key')) + ctx.cert = OpenSSL::X509::Certificate.new(File.open("keys/server.crt")) + ctx.key = OpenSSL::PKey::RSA.new(File.open("keys/server.key")) ctx.ssl_version = :TLSv1_2 ctx.options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] ctx.ciphers = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:ciphers] - ctx.alpn_protocols = ['h2'] + ctx.alpn_protocols = ["h2"] ctx.alpn_select_cb = lambda do |protocols| raise "Protocol #{DRAFT} is required" if protocols.index(DRAFT).nil? @@ -39,14 +39,15 @@ DRAFT end - ctx.ecdh_curves = 'P-256' + ctx.ecdh_curves = "P-256" server = OpenSSL::SSL::SSLServer.new(server, ctx) end loop do sock = server.accept - puts 'New TCP connection!' + sock.setsockopt(:SOCKET, :RCVBUF, 2048) + puts "New TCP connection!" conn = HTTP2::Server.new conn.on(:frame) do |bytes| @@ -61,16 +62,19 @@ end conn.on(:goaway) do - sock.close + Thread.start do + sleep(1) + sock.close + end end conn.on(:stream) do |stream| log = Logger.new(stream.id) req = {} - buffer = ''.b + buffer = "".b - stream.on(:active) { log.info 'client opened new stream' } - stream.on(:close) { log.info 'stream closed' } + stream.on(:active) { log.info "client opened new stream" } + stream.on(:close) { log.info "stream closed" } stream.on(:headers) do |h| req = Hash[*h.flatten] @@ -83,21 +87,21 @@ end stream.on(:half_close) do - log.info 'client closed its end of the stream' + log.info "client closed its end of the stream" response = nil - if req[':method'] == 'POST' + if req[":method"] == "POST" log.info "Received POST request, payload: #{buffer}" response = "Hello HTTP 2.0! POST payload: #{buffer}" else - log.info 'Received GET request' - response = 'Hello HTTP 2.0! GET request' + log.info "Received GET request" + response = "Hello HTTP 2.0! GET request" end stream.headers({ - ':status' => '200', - 'content-length' => response.bytesize.to_s, - 'content-type' => 'text/plain' + ":status" => "200", + "content-length" => response.bytesize.to_s, + "content-type" => "text/plain" }, end_stream: false) if options[:push] @@ -105,15 +109,15 @@ # send 10 promises 10.times do |i| - puts 'sending push' + puts "sending push" - head = { ':method' => 'GET', - ':authority' => 'localhost', - ':scheme' => 'https', - ':path' => "/other_resource/#{i}" } + head = { ":method" => "GET", + ":authority" => "localhost", + ":scheme" => "https", + ":path" => "/other_resource/#{i}" } stream.promise(head) do |push| - push.headers({ ':status' => '200', 'content-type' => 'text/plain', 'content-length' => '11' }) + push.headers({ ":status" => "200", "content-type" => "text/plain", "content-length" => "11" }) push_streams << push end end @@ -121,7 +125,7 @@ # split response into multiple DATA frames stream.data(response[0, 5], end_stream: false) - stream.data(response[5, -1] || '') + stream.data(response[5, -1] || "") if options[:push] push_streams.each_with_index do |push, i| @@ -133,8 +137,8 @@ end while !sock.closed? && !(sock.eof? rescue true) # rubocop:disable Style/RescueModifier - data = sock.readpartial(1024) # puts "Received bytes: #{data.unpack("H*").first}" + data = sock.readpartial(16_384) begin conn << data diff --git a/example/upgrade_client.rb b/example/upgrade_client.rb index f942168..b93fccc 100644 --- a/example/upgrade_client.rb +++ b/example/upgrade_client.rb @@ -2,14 +2,14 @@ # frozen_string_literals: true -require_relative 'helper' -require 'http_parser' +require_relative "helper" +require "http_parser" OptionParser.new do |opts| - opts.banner = 'Usage: upgrade_client.rb [options]' + opts.banner = "Usage: upgrade_client.rb [options]" end.parse! -uri = URI.parse(ARGV[0] || 'http://localhost:8080/') +uri = URI.parse(ARGV[0] || "http://localhost:8080/") sock = TCPSocket.new(uri.host, uri.port) conn = HTTP2::Client.new @@ -17,7 +17,7 @@ def request_header_hash Hash.new do |hash, key| k = key.to_s.downcase - k.tr! '_', '-' + k.tr! "_", "-" _, value = hash.find { |header_key, _| header_key.downcase == k } hash[key] = value if value end @@ -36,16 +36,16 @@ def request_header_hash # upgrader module class UpgradeHandler - UPGRADE_REQUEST = <<~RESP.freeze - GET %s HTTP/1.1 - Connection: Upgrade, HTTP2-Settings - HTTP2-Settings: #{HTTP2::Client.settings_header(settings_max_concurrent_streams: 100)} - Upgrade: h2c - Host: %s - User-Agent: http-2 upgrade - Accept: */* + UPGRADE_REQUEST = < 'http', - ':method' => @parser.http_method, - ':authority' => headers['Host'], - ':path' => @parser.request_url + ":scheme" => "http", + ":method" => @parser.http_method, + ":authority" => headers["Host"], + ":path" => @parser.request_url }.merge(headers) @conn.upgrade(settings, request, @body) @@ -100,7 +100,7 @@ def on_message_complete loop do sock = server.accept - puts 'New TCP connection!' + puts "New TCP connection!" conn = HTTP2::Server.new conn.on(:frame) do |bytes| @@ -117,11 +117,11 @@ def on_message_complete conn.on(:stream) do |stream| log = Logger.new(stream.id) req = request_header_hash - buffer = '' + buffer = "" - stream.on(:active) { log.info 'client opened new stream' } + stream.on(:active) { log.info "client opened new stream" } stream.on(:close) do - log.info 'stream closed' + log.info "stream closed" end stream.on(:headers) do |h| @@ -135,34 +135,34 @@ def on_message_complete end stream.on(:half_close) do - log.info 'client closed its end of the stream' + log.info "client closed its end of the stream" - if req['Upgrade'] + if req["Upgrade"] log.info "Processing h2c Upgrade request: #{req}" - if req[':method'] != 'OPTIONS' # Don't respond to OPTIONS... - response = 'Hello h2c world!' + if req[":method"] != "OPTIONS" # Don't respond to OPTIONS... + response = "Hello h2c world!" stream.headers({ - ':status' => '200', - 'content-length' => response.bytesize.to_s, - 'content-type' => 'text/plain' + ":status" => "200", + "content-length" => response.bytesize.to_s, + "content-type" => "text/plain" }, end_stream: false) stream.data(response) end else response = nil - if req[':method'] == 'POST' + if req[":method"] == "POST" log.info "Received POST request, payload: #{buffer}" response = "Hello HTTP 2.0! POST payload: #{buffer}" else - log.info 'Received GET request' - response = 'Hello HTTP 2.0! GET request' + log.info "Received GET request" + response = "Hello HTTP 2.0! GET request" end stream.headers({ - ':status' => '200', - 'content-length' => response.bytesize.to_s, - 'content-type' => 'text/plain' + ":status" => "200", + "content-length" => response.bytesize.to_s, + "content-type" => "text/plain" }, end_stream: false) # split response into multiple DATA frames diff --git a/http-2.gemspec b/http-2.gemspec index c5e02cb..98f3557 100644 --- a/http-2.gemspec +++ b/http-2.gemspec @@ -1,19 +1,28 @@ # frozen_string_literal: true -require_relative 'lib/http/2/version' +lib = File.expand_path("./lib", __dir__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require "http/2/version" Gem::Specification.new do |spec| - spec.name = 'http-2' - spec.version = HTTP2::VERSION - spec.authors = ['Tiago Cardoso', 'Ilya Grigorik', 'Kaoru Maeda'] - spec.email = ['ilya@igvita.com', 'cardoso_tiago@hotmail.com'] - spec.description = 'Pure-ruby HTTP 2.0 protocol implementation' - spec.summary = spec.description - spec.homepage = 'https://github.com/igrigorik/http-2' - spec.license = 'MIT' - spec.files = Dir['LICENSE', 'README.md', 'lib/**/*.rb', 'sig/**/*.rbs'] + spec.name = "http-2" + spec.version = HTTP2::VERSION + spec.authors = ["Tiago Cardoso", "Ilya Grigorik", "Kaoru Maeda"] + spec.email = ["cardoso_tiago@hotmail.com"] + spec.description = "Pure-ruby HTTP 2.0 protocol implementation" + spec.summary = spec.description + spec.homepage = "https://github.com/igrigorik/http-2" + spec.license = "MIT" + spec.required_ruby_version = ">=2.7.0" - spec.add_runtime_dependency 'base64' + spec.metadata = { + "bug_tracker_uri" => "https://github.com/igrigorik/http-2/issues", + "changelog_uri" => "https://github.com/igrigorik/http-2/blob/main/CHANGELOG.md", + "source_code_uri" => "https://github.com/igrigorik/http-2", + "homepage_uri" => "https://github.com/igrigorik/http-2", + "rubygems_mfa_required" => "true" + } - spec.required_ruby_version = '>= 3.0' + spec.files = Dir["LICENSE.txt", "README.md", "lib/**/*.rb", "sig/**/*.rbs"] + spec.require_paths = ["lib"] end diff --git a/lib/http/2.rb b/lib/http/2.rb index c925982..e6c44e5 100644 --- a/lib/http/2.rb +++ b/lib/http/2.rb @@ -1,13 +1,14 @@ # frozen_string_literal: true -require 'http/2/version' -require 'http/2/extensions' -require 'http/2/error' -require 'http/2/emitter' -require 'http/2/flow_buffer' -require 'http/2/header' -require 'http/2/framer' -require 'http/2/connection' -require 'http/2/client' -require 'http/2/server' -require 'http/2/stream' +require "http/2/version" +require "http/2/extensions" +require "http/2/base64" +require "http/2/error" +require "http/2/emitter" +require "http/2/flow_buffer" +require "http/2/header" +require "http/2/framer" +require "http/2/connection" +require "http/2/client" +require "http/2/server" +require "http/2/stream" diff --git a/lib/http/2/base64.rb b/lib/http/2/base64.rb new file mode 100644 index 0000000..3b97091 --- /dev/null +++ b/lib/http/2/base64.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +if RUBY_VERSION < "3.3.0" + require "base64" +elsif !defined?(Base64) + module HTTP2 + # require "base64" will not be a default gem after ruby 3.4.0 + module Base64 + module_function + + def encode64(bin) + [bin].pack("m") + end + + def decode64(str) + str.unpack1("m") + end + + def strict_encode64(bin) + [bin].pack("m0") + end + + def strict_decode64(str) + str.unpack1("m0") + end + + def urlsafe_encode64(bin, padding: true) + str = strict_encode64(bin) + str.chomp!("==") or str.chomp!("=") unless padding + str.tr!("+/", "-_") + str + end + end + + def urlsafe_decode64(str) + if !str.end_with?("=") && str.length % 4 != 0 + str = str.ljust((str.length + 3) & ~3, "=") + str.tr!("-_", "+/") + else + str = str.tr("-_", "+/") + end + strict_decode64(str) + end + end +end diff --git a/lib/http/2/client.rb b/lib/http/2/client.rb index 41c477d..20fc53d 100644 --- a/lib/http/2/client.rb +++ b/lib/http/2/client.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'base64' - module HTTP2 # HTTP 2.0 client connection class that implements appropriate header # compression / decompression algorithms and stream management logic. @@ -71,7 +69,7 @@ def send_connection_preface def self.settings_header(settings) frame = Framer.new.generate(type: :settings, stream: 0, payload: settings) - Base64.urlsafe_encode64(frame[9..]) + Base64.urlsafe_encode64(frame[9..-1]) end private diff --git a/lib/http/2/connection.rb b/lib/http/2/connection.rb index 4d04b9e..5bd73d5 100644 --- a/lib/http/2/connection.rb +++ b/lib/http/2/connection.rb @@ -45,6 +45,7 @@ module HTTP2 # Note that this class should not be used directly. Instead, you want to # use either Client or Server class to drive the HTTP 2.0 exchange. # + # rubocop:disable Metrics/ClassLength class Connection include FlowBuffer include Emitter @@ -95,7 +96,7 @@ def initialize(settings = {}) @remote_window_limit = @remote_settings[:settings_initial_window_size] @remote_window = @remote_window_limit - @recv_buffer = ''.b + @recv_buffer = "".b @continuation = [] @error = nil @@ -117,7 +118,7 @@ def new_stream(**args) raise ConnectionClosed if @state == :closed raise StreamLimitExceeded if @active_stream_count >= @remote_settings[:settings_max_concurrent_streams] - connection_error(:protocol_error, msg: 'id is smaller than previous') if @stream_id < @last_activated_stream + connection_error(:protocol_error, msg: "id is smaller than previous") if @stream_id < @last_activated_stream stream = activate_stream(id: @stream_id, **args) @last_activated_stream = stream.id @@ -322,7 +323,7 @@ def receive(data) return end - connection_error(msg: 'missing parent ID') if parent.nil? + connection_error(msg: "missing parent ID") if parent.nil? unless parent.state == :open || parent.state == :half_closed_local # An endpoint might receive a PUSH_PROMISE frame after it sends @@ -375,12 +376,12 @@ def receive(data) # (see Section 5.1). when :window_update stream = @streams_recently_closed[frame[:stream]] - connection_error(:protocol_error, msg: 'sent window update on idle stream') unless stream + connection_error(:protocol_error, msg: "sent window update on idle stream") unless stream process_window_update(frame: frame, encode: true) else # An endpoint that receives an unexpected stream identifier # MUST respond with a connection error of type PROTOCOL_ERROR. - connection_error(msg: 'stream does not exist') + connection_error(msg: "stream does not exist") end end end @@ -688,7 +689,7 @@ def encode_headers(frame) frames = [] begin - while payload&.bytesize&.positive? + while payload && payload.bytesize > 0 cont = frame.dup cont[:type] = :continuation cont[:flags] = [] @@ -719,7 +720,7 @@ def encode_headers(frame) # @param window [Integer] # @param parent [Stream] def activate_stream(id:, **args) - connection_error(msg: 'Stream ID already exists') if @streams.key?(id) + connection_error(msg: "Stream ID already exists") if @streams.key?(id) raise StreamLimitExceeded if @active_stream_count >= @local_settings[:settings_max_concurrent_streams] @@ -749,7 +750,7 @@ def activate_stream(id:, **args) def verify_stream_order(id) return unless id.odd? - connection_error(msg: 'Stream ID smaller than previous') if @last_stream_id > id + connection_error(msg: "Stream ID smaller than previous") if @last_stream_id > id @last_stream_id = id end @@ -759,13 +760,13 @@ def _verify_pseudo_headers(frame, mandatory_headers) pseudo_headers = headers.take_while do |field, value| # use this loop to validate pseudo-headers - connection_error(:protocol_error, msg: 'path is empty') if field == ':path' && value.empty? - field.start_with?(':') + connection_error(:protocol_error, msg: "path is empty") if field == ":path" && value.empty? + field.start_with?(":") end.map(&:first) return if mandatory_headers.size == pseudo_headers.size && (mandatory_headers - pseudo_headers).empty? - connection_error(:protocol_error, msg: 'invalid pseudo-headers') + connection_error(:protocol_error, msg: "invalid pseudo-headers") end # Emit GOAWAY error indicating to peer that the connection is being @@ -784,8 +785,8 @@ def connection_error(error = :protocol_error, msg: nil, e: nil) @state = :closed @error = error - msg ||= e ? e.message : 'protocol error' - backtrace = e&.backtrace + msg ||= e ? e.message : "protocol error" + backtrace = e ? e.backtrace : nil raise Error.types[error], msg, backtrace end alias error connection_error @@ -794,4 +795,5 @@ def manage_state(_) yield end end + # rubocop:enable Metrics/ClassLength end diff --git a/lib/http/2/emitter.rb b/lib/http/2/emitter.rb index 566f380..ad4db9e 100644 --- a/lib/http/2/emitter.rb +++ b/lib/http/2/emitter.rb @@ -10,7 +10,7 @@ module Emitter # @param event [Symbol] # @param block [Proc] callback function def on(event, &block) - raise ArgumentError, 'must provide callback' unless block + raise ArgumentError, "must provide callback" unless block listeners(event.to_sym).push block end @@ -31,9 +31,9 @@ def once(event, &block) # @param event [Symbol] # @param args [Array] arguments to be passed to the callbacks # @param block [Proc] callback function - def emit(event, ...) + def emit(event, *args, &block) listeners(event).delete_if do |cb| - :delete == cb.call(...) # rubocop:disable Style/YodaCondition + :delete == cb.call(*args, &block) # rubocop:disable Style/YodaCondition end end diff --git a/lib/http/2/error.rb b/lib/http/2/error.rb index 8e54d89..95f736a 100644 --- a/lib/http/2/error.rb +++ b/lib/http/2/error.rb @@ -15,7 +15,7 @@ def self.inherited(klass) type = klass.name or return - type = type.split('::').last or return + type = type.split("::").last or return type = type.gsub(/([^\^])([A-Z])/, '\1_\2').downcase.to_sym HTTP2::Error.types[type] = klass diff --git a/lib/http/2/extensions.rb b/lib/http/2/extensions.rb index a982497..fac8994 100644 --- a/lib/http/2/extensions.rb +++ b/lib/http/2/extensions.rb @@ -4,7 +4,7 @@ module HTTP2 module StringExtensions refine String do def read(n) - return ''.b if n.zero? + return "".b if n == 0 chunk = byteslice(0..n - 1) remaining = byteslice(n..-1) @@ -13,7 +13,7 @@ def read(n) end def read_uint32 - read(4).unpack1('N') + read(4).unpack1("N") end def shift_byte @@ -25,7 +25,7 @@ def shift_byte # this mixin handles backwards-compatibility for the new packing options # shipping with ruby 3.3 (see https://docs.ruby-lang.org/en/3.3/packed_data_rdoc.html) module PackingExtensions - if RUBY_VERSION < '3.3.0' + if RUBY_VERSION < "3.3.0" def pack(array_to_pack, template, buffer:, offset: -1) packed_str = array_to_pack.pack(template) case offset diff --git a/lib/http/2/flow_buffer.rb b/lib/http/2/flow_buffer.rb index 8a53062..61665d9 100644 --- a/lib/http/2/flow_buffer.rb +++ b/lib/http/2/flow_buffer.rb @@ -38,7 +38,7 @@ def calculate_window_update(window_max_size) # current received window size + delta length is strictly larger than # local window size, it throws a flow control error. # - error(:flow_control_error) if @local_window.negative? + error(:flow_control_error) if @local_window < 0 # Send WINDOW_UPDATE if the received window size goes over # the local window size / 2. @@ -91,10 +91,10 @@ def process_window_update(frame:, encode: false) return if frame[:ignore] if frame[:increment] - raise ProtocolError, 'increment MUST be higher than zero' if frame[:increment].zero? + raise ProtocolError, "increment MUST be higher than zero" if frame[:increment].zero? @remote_window += frame[:increment] - error(:flow_control_error, msg: 'window size too large') if @remote_window > MAX_WINDOW_SIZE + error(:flow_control_error, msg: "window size too large") if @remote_window > MAX_WINDOW_SIZE end send_data(nil, encode) end @@ -126,7 +126,7 @@ def retrieve(window_size) # Frames with zero length with the END_STREAM flag set (that # is, an empty DATA frame) MAY be sent if there is no available space # in either flow control window. - return if window_size <= 0 && !(frame_size.zero? && end_stream) + return if window_size <= 0 && !(frame_size == 0 && end_stream) @buffer.shift diff --git a/lib/http/2/framer.rb b/lib/http/2/framer.rb index 137018d..68bba8a 100644 --- a/lib/http/2/framer.rb +++ b/lib/http/2/framer.rb @@ -103,9 +103,9 @@ class Framer RBIT = 0x7fffffff RBYTE = 0x0fffffff EBIT = 0x80000000 - UINT32 = 'N' - UINT16 = 'n' - UINT8 = 'C' + UINT32 = "N" + UINT16 = "n" + UINT8 = "C" HEADERPACK = (UINT8 + UINT16 + UINT8 + UINT8 + UINT32).freeze FRAME_LENGTH_HISHIFT = 16 FRAME_LENGTH_LOMASK = 0xFFFF @@ -131,7 +131,7 @@ def common_header(frame, buffer:) raise CompressionError, "Frame size is too large: #{frame[:length]}" if frame[:length] > @remote_max_frame_size - raise CompressionError, "Frame size is invalid: #{frame[:length]}" if (frame[:length]).negative? + raise CompressionError, "Frame size is invalid: #{frame[:length]}" if frame[:length] < 0 raise CompressionError, "Stream ID (#{frame[:stream]}) is too large" if frame[:stream] > MAX_STREAM_ID @@ -165,7 +165,7 @@ def read_common_header(buf) frame[:type], = FRAME_TYPES.find { |_t, pos| type == pos } if frame[:type] frame[:flags] = FRAME_FLAGS[frame[:type]].each_with_object([]) do |(name, pos), acc| - acc << name if (flags & (1 << pos)).positive? + acc << name if (flags & (1 << pos)) > 0 end end @@ -178,7 +178,7 @@ def read_common_header(buf) # # @param frame [Hash] def generate(frame) - bytes = ''.b + bytes = "".b length = 0 frame[:flags] ||= [] @@ -244,10 +244,7 @@ def generate(frame) length += 4 + frame[:payload].bytesize when :ping - if frame[:payload].bytesize != 8 - raise CompressionError, - "Invalid payload size (#{frame[:payload].size} != 8 bytes)" - end + raise CompressionError, "Invalid payload size (#{frame[:payload].size} != 8 bytes)" if frame[:payload].bytesize != 8 bytes << frame[:payload] length += 8 @@ -274,7 +271,7 @@ def generate(frame) pack([frame[:max_age], frame[:port]], UINT32 + UINT16, buffer: bytes) length += 6 if frame[:proto] - raise CompressionError, 'Proto too long' if frame[:proto].bytesize > 255 + raise CompressionError, "Proto too long" if frame[:proto].bytesize > 255 pack([frame[:proto].bytesize], UINT8, buffer: bytes) bytes << frame[:proto] @@ -284,7 +281,7 @@ def generate(frame) length += 1 end if frame[:host] - raise CompressionError, 'Host too long' if frame[:host].bytesize > 255 + raise CompressionError, "Host too long" if frame[:host].bytesize > 255 pack([frame[:host].bytesize], UINT8, buffer: bytes) bytes << frame[:host] @@ -345,7 +342,7 @@ def parse(buf) frame = read_common_header(buf) return if buf.size < 9 + frame[:length] - raise ProtocolError, 'payload too large' if frame[:length] > @local_max_frame_size + raise ProtocolError, "payload too large" if frame[:length] > @local_max_frame_size buf.read(9) payload = buf.read(frame[:length]) @@ -362,9 +359,9 @@ def parse(buf) if padded padlen = payload.read(1).unpack1(UINT8) frame[:padding] = padlen + 1 - raise ProtocolError, 'padding too long' if padlen > payload.bytesize + raise ProtocolError, "padding too long" if padlen > payload.bytesize - payload = payload.byteslice(0, payload.bytesize - padlen) if padlen.positive? + payload = payload.byteslice(0, payload.bytesize - padlen) if padlen > 0 frame[:length] -= frame[:padding] frame[:flags].delete(:padded) end @@ -401,7 +398,7 @@ def parse(buf) # NOTE: frame[:length] might not match the number of frame[:payload] # because unknown extensions are ignored. frame[:payload] = [] - raise ProtocolError, 'Invalid settings payload length' unless (frame[:length] % 6).zero? + raise ProtocolError, "Invalid settings payload length" unless (frame[:length] % 6).zero? raise ProtocolError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero? @@ -422,7 +419,7 @@ def parse(buf) frame[:error] = unpack_error payload.read_uint32 size = frame[:length] - 8 # for last_stream and error - frame[:payload] = payload.read(size) if size.positive? + frame[:payload] = payload.read(size) if size > 0 when :window_update if frame[:length] % 4 != 0 raise FrameSizeError, "Invalid length for WINDOW_UPDATE (#{frame[:length]} not multiple of 4)" @@ -434,11 +431,11 @@ def parse(buf) len = payload.byteslice(0, 1).ord payload = payload.byteslice(1..-1) - frame[:proto] = payload.read(len) if len.positive? + frame[:proto] = payload.read(len) if len > 0 len = payload.byteslice(0, 1).ord payload = payload.byteslice(1..-1) - frame[:host] = payload.read(len) if len.positive? + frame[:host] = payload.read(len) if len > 0 frame[:origin] = payload.read(payload.size) unless payload.empty? diff --git a/lib/http/2/header.rb b/lib/http/2/header.rb index ff6f24e..df6d38e 100644 --- a/lib/http/2/header.rb +++ b/lib/http/2/header.rb @@ -28,8 +28,8 @@ module Header end end -require 'http/2/header/huffman' -require 'http/2/header/huffman_statemachine' -require 'http/2/header/encoding_context' -require 'http/2/header/compressor' -require 'http/2/header/decompressor' +require "http/2/header/huffman" +require "http/2/header/huffman_statemachine" +require "http/2/header/encoding_context" +require "http/2/header/compressor" +require "http/2/header/decompressor" diff --git a/lib/http/2/header/compressor.rb b/lib/http/2/header/compressor.rb index 90db8b8..0a51218 100644 --- a/lib/http/2/header/compressor.rb +++ b/lib/http/2/header/compressor.rb @@ -36,7 +36,7 @@ def table_size=(size) # @return [String] binary string def integer(i, n, buffer:, offset: 0) limit = (2**n) - 1 - return pack([i], 'C', buffer: buffer, offset: offset) if i < limit + return pack([i], "C", buffer: buffer, offset: offset) if i < limit bytes = [] bytes.push limit unless n.zero? @@ -48,7 +48,7 @@ def integer(i, n, buffer:, offset: 0) end bytes.push i - pack(bytes, 'C*', buffer: buffer, offset: offset) + pack(bytes, "C*", buffer: buffer, offset: offset) end # Encodes provided value via string literal representation. @@ -91,7 +91,7 @@ def string(str) # @param h [Hash] header command # @param buffer [String] # @return [Buffer] - def header(h, buffer = ''.b) + def header(h, buffer = "".b) rep = HEADREP[h[:type]] case h[:type] @@ -122,8 +122,8 @@ def header(h, buffer = ''.b) # @param headers [Array] +[[name, value], ...]+ # @return [Buffer] def encode(headers) - buffer = ''.b - pseudo_headers, regular_headers = headers.partition { |f, _| f.start_with? ':' } + buffer = "".b + pseudo_headers, regular_headers = headers.partition { |f, _| f.start_with? ":" } headers = [*pseudo_headers, *regular_headers] commands = @cc.encode(headers) commands.each do |cmd| @@ -147,7 +147,7 @@ def huffman_string(str) # @param str [String] # @return [String] binary string def plain_string(str) - plain = ''.b + plain = "".b integer(str.bytesize, 7, buffer: plain) plain << str.dup.force_encoding(Encoding::BINARY) plain diff --git a/lib/http/2/header/decompressor.rb b/lib/http/2/header/decompressor.rb index 66e1ac4..8b14013 100644 --- a/lib/http/2/header/decompressor.rb +++ b/lib/http/2/header/decompressor.rb @@ -52,12 +52,12 @@ def integer(buf, n) # @return [String] UTF-8 encoded string # @raise [CompressionError] when input is malformed def string(buf) - raise CompressionError, 'invalid header block fragment' if buf.empty? + raise CompressionError, "invalid header block fragment" if buf.empty? huffman = (buf.getbyte(0) & 0x80) == 0x80 len = integer(buf, 7) str = buf.read(len) - raise CompressionError, 'string too short' unless str.bytesize == len + raise CompressionError, "string too short" unless str.bytesize == len str = Huffman.new.decode(str) if huffman str.force_encoding(Encoding::UTF_8) @@ -114,9 +114,9 @@ def decode(buf, frame = nil) field, value = @cc.process(header(buf)) next if field.nil? - is_pseudo_header = field.start_with? ':' + is_pseudo_header = field.start_with? ":" if !decoding_pseudo_headers && is_pseudo_header - raise ProtocolError, 'one or more pseudo headers encountered after regular headers' + raise ProtocolError, "one or more pseudo headers encountered after regular headers" end decoding_pseudo_headers = is_pseudo_header @@ -124,13 +124,13 @@ def decode(buf, frame = nil) if frame case field - when ':status' + when ":status" frame[:status] = Integer(value) - when ':method' + when ":method" frame[:method] = value - when 'content-length' + when "content-length" frame[:content_length] = Integer(value) - when 'trailer' + when "trailer" (frame[:trailer] ||= []) << value end end diff --git a/lib/http/2/header/encoding_context.rb b/lib/http/2/header/encoding_context.rb index 764ab31..fa74881 100644 --- a/lib/http/2/header/encoding_context.rb +++ b/lib/http/2/header/encoding_context.rb @@ -8,73 +8,73 @@ module Header class EncodingContext include Error - UPPER = /[[:upper:]]/ + UPPER = /[[:upper:]]/.freeze # @private # Static table # - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10#appendix-A STATIC_TABLE = [ - [':authority', ''], - [':method', 'GET'], - [':method', 'POST'], - [':path', '/'], - [':path', '/index.html'], - [':scheme', 'http'], - [':scheme', 'https'], - [':status', '200'], - [':status', '204'], - [':status', '206'], - [':status', '304'], - [':status', '400'], - [':status', '404'], - [':status', '500'], - ['accept-charset', ''], - ['accept-encoding', 'gzip, deflate'], - ['accept-language', ''], - ['accept-ranges', ''], - ['accept', ''], - ['access-control-allow-origin', ''], - ['age', ''], - ['allow', ''], - ['authorization', ''], - ['cache-control', ''], - ['content-disposition', ''], - ['content-encoding', ''], - ['content-language', ''], - ['content-length', ''], - ['content-location', ''], - ['content-range', ''], - ['content-type', ''], - ['cookie', ''], - ['date', ''], - ['etag', ''], - ['expect', ''], - ['expires', ''], - ['from', ''], - ['host', ''], - ['if-match', ''], - ['if-modified-since', ''], - ['if-none-match', ''], - ['if-range', ''], - ['if-unmodified-since', ''], - ['last-modified', ''], - ['link', ''], - ['location', ''], - ['max-forwards', ''], - ['proxy-authenticate', ''], - ['proxy-authorization', ''], - ['range', ''], - ['referer', ''], - ['refresh', ''], - ['retry-after', ''], - ['server', ''], - ['set-cookie', ''], - ['strict-transport-security', ''], - ['transfer-encoding', ''], - ['user-agent', ''], - ['vary', ''], - ['via', ''], - ['www-authenticate', ''] + [":authority", ""], + [":method", "GET"], + [":method", "POST"], + [":path", "/"], + [":path", "/index.html"], + [":scheme", "http"], + [":scheme", "https"], + [":status", "200"], + [":status", "204"], + [":status", "206"], + [":status", "304"], + [":status", "400"], + [":status", "404"], + [":status", "500"], + ["accept-charset", ""], + ["accept-encoding", "gzip, deflate"], + ["accept-language", ""], + ["accept-ranges", ""], + ["accept", ""], + ["access-control-allow-origin", ""], + ["age", ""], + ["allow", ""], + ["authorization", ""], + ["cache-control", ""], + ["content-disposition", ""], + ["content-encoding", ""], + ["content-language", ""], + ["content-length", ""], + ["content-location", ""], + ["content-range", ""], + ["content-type", ""], + ["cookie", ""], + ["date", ""], + ["etag", ""], + ["expect", ""], + ["expires", ""], + ["from", ""], + ["host", ""], + ["if-match", ""], + ["if-modified-since", ""], + ["if-none-match", ""], + ["if-range", ""], + ["if-unmodified-since", ""], + ["last-modified", ""], + ["link", ""], + ["location", ""], + ["max-forwards", ""], + ["proxy-authenticate", ""], + ["proxy-authorization", ""], + ["range", ""], + ["referer", ""], + ["refresh", ""], + ["retry-after", ""], + ["server", ""], + ["set-cookie", ""], + ["strict-transport-security", ""], + ["transfer-encoding", ""], + ["user-agent", ""], + ["vary", ""], + ["via", ""], + ["www-authenticate", ""] ].each { |pair| pair.each(&:freeze).freeze }.freeze STATIC_TABLE_BY_FIELD = STATIC_TABLE @@ -139,7 +139,7 @@ def dup def dereference(index) # NOTE: index is zero-based in this module. value = STATIC_TABLE[index] || @table[index - STATIC_TABLE_SIZE] - raise CompressionError, 'Index too large' unless value + raise CompressionError, "Index too large" unless value value end @@ -155,16 +155,13 @@ def process(cmd) case cmd[:type] when :changetablesize - raise CompressionError, 'tried to change table size after adding elements to table' if @_table_updated + raise CompressionError, "tried to change table size after adding elements to table" if @_table_updated # we can receive multiple table size change commands inside a header frame. However, # we should blow up if we receive another frame where the new table size is bigger. table_size_updated = @limit != @options[:table_size] - if !table_size_updated && cmd[:value] > @limit - raise CompressionError, - 'dynamic table size update exceed limit' - end + raise CompressionError, "dynamic table size update exceed limit" if !table_size_updated && cmd[:value] > @limit self.table_size = cmd[:value] @@ -227,7 +224,7 @@ def encode(headers) # Literal header names MUST be translated to lowercase before # encoding and transmission. field = field.downcase if UPPER.match?(field) - value = '/' if field == ':path' && value.empty? + value = "/" if field == ":path" && value.empty? cmd = addcmd(field, value) cmd[:type] = :noindex if noindex && cmd[:type] == :incremental commands << cmd diff --git a/lib/http/2/header/huffman.rb b/lib/http/2/header/huffman.rb index dbf2b8d..5edcac1 100644 --- a/lib/http/2/header/huffman.rb +++ b/lib/http/2/header/huffman.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require_relative '../error' -require_relative '../extensions' +require_relative "../error" +require_relative "../extensions" module HTTP2 # Implementation of huffman encoding for HPACK @@ -26,8 +26,8 @@ class Huffman # @return [String] binary string def encode(str) bitstring = str.each_byte.map { |chr| ENCODE_TABLE[chr] }.join - bitstring << ('1' * ((8 - bitstring.size) % 8)) - [bitstring].pack('B*') + bitstring << ("1" * ((8 - bitstring.size) % 8)) + [bitstring].pack("B*") end # Decodes provided Huffman coded string. @@ -36,7 +36,7 @@ def encode(str) # @return [String] binary string # @raise [CompressionError] when Huffman coded string is malformed def decode(buf) - emit = ''.b + emit = "".b state = 0 # start state mask = (1 << BITS_AT_ONCE) - 1 @@ -49,14 +49,14 @@ def decode(buf) # [emit] character to be emitted on this transition, empty string, or EOS. # [next] next state number. trans = MACHINE[state][branch] - raise CompressionError, 'Huffman decode error (EOS found)' if trans.first == EOS + raise CompressionError, "Huffman decode error (EOS found)" if trans.first == EOS emit << trans.first.chr if trans.first state = trans.last end end # Check whether partial input is correctly filled - raise CompressionError, 'Huffman decode error (EOS invalid)' unless state <= MAX_FINAL_STATE + raise CompressionError, "Huffman decode error (EOS invalid)" unless state <= MAX_FINAL_STATE emit.force_encoding(Encoding::BINARY) end @@ -323,7 +323,7 @@ def decode(buf) [0x3fffffff, 30] ].each(&:freeze).freeze - ENCODE_TABLE = CODES.map { |c, l| [c].pack('N').unpack1('B*')[-l..] }.each(&:freeze).freeze + ENCODE_TABLE = CODES.map { |c, l| [c].pack("N").unpack1("B*")[-l..-1] }.each(&:freeze).freeze end end end diff --git a/lib/http/2/header/huffman_statemachine.rb b/lib/http/2/header/huffman_statemachine.rb index 3a908e4..6aff2b0 100644 --- a/lib/http/2/header/huffman_statemachine.rb +++ b/lib/http/2/header/huffman_statemachine.rb @@ -12,518 +12,262 @@ class Huffman # :nodoc: MAX_FINAL_STATE = 7 MACHINE = [ - [[nil, 16], [nil, 76], [nil, 54], [nil, 36], [nil, 30], [nil, 28], [nil, 24], [nil, 221], [nil, 9], [nil, 10], - [nil, 11], [nil, 12], [nil, 13], [nil, 14], [nil, 15], [nil, 1]], - [[119, 29], [119, 5], [120, 29], [120, 5], [121, 29], [121, 5], [122, 29], [122, 5], [38, 0], [42, 0], [44, 0], - [59, 0], [88, 0], [90, 0], [nil, 31], [nil, 32]], - [[38, 29], [38, 5], [42, 29], [42, 5], [44, 29], [44, 5], [59, 29], [59, 5], [88, 29], [88, 5], [90, 29], - [90, 5], [nil, 87], [nil, 88], [nil, 89], [nil, 90]], - [[88, 25], [88, 26], [88, 27], [88, 6], [90, 25], [90, 26], [90, 27], [90, 6], [33, 0], [34, 0], [40, 0], - [41, 0], [63, 0], [nil, 84], [nil, 85], [nil, 86]], - [[33, 29], [33, 5], [34, 29], [34, 5], [40, 29], [40, 5], [41, 29], [41, 5], [63, 29], [63, 5], [39, 0], - [43, 0], [124, 0], [nil, 81], [nil, 82], [nil, 83]], - [[nil, 61], [nil, 62], [nil, 63], [nil, 64], [nil, 65], [nil, 66], [nil, 67], [nil, 68], [nil, 69], [nil, 70], - [nil, 71], [nil, 72], [nil, 73], [nil, 74], [nil, 75], [nil, 2]], - [[nil, 39], [nil, 40], [nil, 41], [nil, 42], [nil, 43], [nil, 44], [nil, 45], [nil, 46], [nil, 47], [nil, 48], - [nil, 49], [nil, 50], [nil, 51], [nil, 52], [nil, 53], [nil, 3]], - [[85, 0], [86, 0], [87, 0], [89, 0], [106, 0], [107, 0], [113, 0], [118, 0], [119, 0], [120, 0], [121, 0], - [122, 0], [nil, 33], [nil, 34], [nil, 35], [nil, 4]], - [[203, 17], [203, 18], [203, 19], [203, 20], [203, 21], [203, 22], [203, 23], [203, 7], [204, 17], [204, 18], - [204, 19], [204, 20], [204, 21], [204, 22], [204, 23], [204, 7]], - [[61, 25], [61, 26], [61, 27], [61, 6], [65, 25], [65, 26], [65, 27], [65, 6], [95, 25], [95, 26], [95, 27], - [95, 6], [98, 25], [98, 26], [98, 27], [98, 6]], - [[100, 25], [100, 26], [100, 27], [100, 6], [102, 25], [102, 26], [102, 27], [102, 6], [103, 25], [103, 26], - [103, 27], [103, 6], [104, 25], [104, 26], [104, 27], [104, 6]], - [[108, 25], [108, 26], [108, 27], [108, 6], [109, 25], [109, 26], [109, 27], [109, 6], [110, 25], [110, 26], - [110, 27], [110, 6], [112, 25], [112, 26], [112, 27], [112, 6]], - [[114, 25], [114, 26], [114, 27], [114, 6], [117, 25], [117, 26], [117, 27], [117, 6], [58, 29], [58, 5], - [66, 29], [66, 5], [67, 29], [67, 5], [68, 29], [68, 5]], - [[69, 29], [69, 5], [70, 29], [70, 5], [71, 29], [71, 5], [72, 29], [72, 5], [73, 29], [73, 5], [74, 29], - [74, 5], [75, 29], [75, 5], [76, 29], [76, 5]], - [[77, 29], [77, 5], [78, 29], [78, 5], [79, 29], [79, 5], [80, 29], [80, 5], [81, 29], [81, 5], [82, 29], - [82, 5], [83, 29], [83, 5], [84, 29], [84, 5]], - [[85, 29], [85, 5], [86, 29], [86, 5], [87, 29], [87, 5], [89, 29], [89, 5], [106, 29], [106, 5], [107, 29], - [107, 5], [113, 29], [113, 5], [118, 29], [118, 5]], - [[48, 17], [48, 18], [48, 19], [48, 20], [48, 21], [48, 22], [48, 23], [48, 7], [49, 17], [49, 18], [49, 19], - [49, 20], [49, 21], [49, 22], [49, 23], [49, 7]], - [[48, 25], [48, 26], [48, 27], [48, 6], [49, 25], [49, 26], [49, 27], [49, 6], [50, 25], [50, 26], [50, 27], - [50, 6], [97, 25], [97, 26], [97, 27], [97, 6]], - [[99, 25], [99, 26], [99, 27], [99, 6], [101, 25], [101, 26], [101, 27], [101, 6], [105, 25], [105, 26], - [105, 27], [105, 6], [111, 25], [111, 26], [111, 27], [111, 6]], - [[115, 25], [115, 26], [115, 27], [115, 6], [116, 25], [116, 26], [116, 27], [116, 6], [32, 29], [32, 5], - [37, 29], [37, 5], [45, 29], [45, 5], [46, 29], [46, 5]], - [[47, 29], [47, 5], [51, 29], [51, 5], [52, 29], [52, 5], [53, 29], [53, 5], [54, 29], [54, 5], [55, 29], - [55, 5], [56, 29], [56, 5], [57, 29], [57, 5]], - [[61, 29], [61, 5], [65, 29], [65, 5], [95, 29], [95, 5], [98, 29], [98, 5], [100, 29], [100, 5], [102, 29], - [102, 5], [103, 29], [103, 5], [104, 29], [104, 5]], - [[108, 29], [108, 5], [109, 29], [109, 5], [110, 29], [110, 5], [112, 29], [112, 5], [114, 29], [114, 5], - [117, 29], [117, 5], [58, 0], [66, 0], [67, 0], [68, 0]], - [[69, 0], [70, 0], [71, 0], [72, 0], [73, 0], [74, 0], [75, 0], [76, 0], [77, 0], [78, 0], [79, 0], [80, 0], - [81, 0], [82, 0], [83, 0], [84, 0]], - [[47, 25], [47, 26], [47, 27], [47, 6], [51, 25], [51, 26], [51, 27], [51, 6], [52, 25], [52, 26], [52, 27], - [52, 6], [53, 25], [53, 26], [53, 27], [53, 6]], - [[48, 29], [48, 5], [49, 29], [49, 5], [50, 29], [50, 5], [97, 29], [97, 5], [99, 29], [99, 5], [101, 29], - [101, 5], [105, 29], [105, 5], [111, 29], [111, 5]], - [[115, 29], [115, 5], [116, 29], [116, 5], [32, 0], [37, 0], [45, 0], [46, 0], [47, 0], [51, 0], [52, 0], - [53, 0], [54, 0], [55, 0], [56, 0], [57, 0]], - [[61, 0], [65, 0], [95, 0], [98, 0], [100, 0], [102, 0], [103, 0], [104, 0], [108, 0], [109, 0], [110, 0], - [112, 0], [114, 0], [117, 0], [nil, 37], [nil, 38]], - [[32, 25], [32, 26], [32, 27], [32, 6], [37, 25], [37, 26], [37, 27], [37, 6], [45, 25], [45, 26], [45, 27], - [45, 6], [46, 25], [46, 26], [46, 27], [46, 6]], - [[48, 0], [49, 0], [50, 0], [97, 0], [99, 0], [101, 0], [105, 0], [111, 0], [115, 0], [116, 0], [nil, 55], - [nil, 56], [nil, 57], [nil, 58], [nil, 59], [nil, 60]], - [[115, 17], [115, 18], [115, 19], [115, 20], [115, 21], [115, 22], [115, 23], [115, 7], [116, 17], [116, 18], - [116, 19], [116, 20], [116, 21], [116, 22], [116, 23], [116, 7]], - [[33, 25], [33, 26], [33, 27], [33, 6], [34, 25], [34, 26], [34, 27], [34, 6], [40, 25], [40, 26], [40, 27], - [40, 6], [41, 25], [41, 26], [41, 27], [41, 6]], - [[63, 25], [63, 26], [63, 27], [63, 6], [39, 29], [39, 5], [43, 29], [43, 5], [124, 29], [124, 5], [35, 0], - [62, 0], [nil, 77], [nil, 78], [nil, 79], [nil, 80]], - [[38, 17], [38, 18], [38, 19], [38, 20], [38, 21], [38, 22], [38, 23], [38, 7], [42, 17], [42, 18], [42, 19], - [42, 20], [42, 21], [42, 22], [42, 23], [42, 7]], - [[44, 17], [44, 18], [44, 19], [44, 20], [44, 21], [44, 22], [44, 23], [44, 7], [59, 17], [59, 18], [59, 19], - [59, 20], [59, 21], [59, 22], [59, 23], [59, 7]], - [[88, 17], [88, 18], [88, 19], [88, 20], [88, 21], [88, 22], [88, 23], [88, 7], [90, 17], [90, 18], [90, 19], - [90, 20], [90, 21], [90, 22], [90, 23], [90, 7]], - [[105, 17], [105, 18], [105, 19], [105, 20], [105, 21], [105, 22], [105, 23], [105, 7], [111, 17], [111, 18], - [111, 19], [111, 20], [111, 21], [111, 22], [111, 23], [111, 7]], - [[58, 17], [58, 18], [58, 19], [58, 20], [58, 21], [58, 22], [58, 23], [58, 7], [66, 17], [66, 18], [66, 19], - [66, 20], [66, 21], [66, 22], [66, 23], [66, 7]], - [[67, 17], [67, 18], [67, 19], [67, 20], [67, 21], [67, 22], [67, 23], [67, 7], [68, 17], [68, 18], [68, 19], - [68, 20], [68, 21], [68, 22], [68, 23], [68, 7]], - [[69, 17], [69, 18], [69, 19], [69, 20], [69, 21], [69, 22], [69, 23], [69, 7], [70, 17], [70, 18], [70, 19], - [70, 20], [70, 21], [70, 22], [70, 23], [70, 7]], - [[71, 17], [71, 18], [71, 19], [71, 20], [71, 21], [71, 22], [71, 23], [71, 7], [72, 17], [72, 18], [72, 19], - [72, 20], [72, 21], [72, 22], [72, 23], [72, 7]], - [[73, 17], [73, 18], [73, 19], [73, 20], [73, 21], [73, 22], [73, 23], [73, 7], [74, 17], [74, 18], [74, 19], - [74, 20], [74, 21], [74, 22], [74, 23], [74, 7]], - [[75, 17], [75, 18], [75, 19], [75, 20], [75, 21], [75, 22], [75, 23], [75, 7], [76, 17], [76, 18], [76, 19], - [76, 20], [76, 21], [76, 22], [76, 23], [76, 7]], - [[77, 17], [77, 18], [77, 19], [77, 20], [77, 21], [77, 22], [77, 23], [77, 7], [78, 17], [78, 18], [78, 19], - [78, 20], [78, 21], [78, 22], [78, 23], [78, 7]], - [[79, 17], [79, 18], [79, 19], [79, 20], [79, 21], [79, 22], [79, 23], [79, 7], [80, 17], [80, 18], [80, 19], - [80, 20], [80, 21], [80, 22], [80, 23], [80, 7]], - [[81, 17], [81, 18], [81, 19], [81, 20], [81, 21], [81, 22], [81, 23], [81, 7], [82, 17], [82, 18], [82, 19], - [82, 20], [82, 21], [82, 22], [82, 23], [82, 7]], - [[83, 17], [83, 18], [83, 19], [83, 20], [83, 21], [83, 22], [83, 23], [83, 7], [84, 17], [84, 18], [84, 19], - [84, 20], [84, 21], [84, 22], [84, 23], [84, 7]], - [[85, 17], [85, 18], [85, 19], [85, 20], [85, 21], [85, 22], [85, 23], [85, 7], [86, 17], [86, 18], [86, 19], - [86, 20], [86, 21], [86, 22], [86, 23], [86, 7]], - [[87, 17], [87, 18], [87, 19], [87, 20], [87, 21], [87, 22], [87, 23], [87, 7], [89, 17], [89, 18], [89, 19], - [89, 20], [89, 21], [89, 22], [89, 23], [89, 7]], - [[106, 17], [106, 18], [106, 19], [106, 20], [106, 21], [106, 22], [106, 23], [106, 7], [107, 17], [107, 18], - [107, 19], [107, 20], [107, 21], [107, 22], [107, 23], [107, 7]], - [[113, 17], [113, 18], [113, 19], [113, 20], [113, 21], [113, 22], [113, 23], [113, 7], [118, 17], [118, 18], - [118, 19], [118, 20], [118, 21], [118, 22], [118, 23], [118, 7]], - [[119, 17], [119, 18], [119, 19], [119, 20], [119, 21], [119, 22], [119, 23], [119, 7], [120, 17], [120, 18], - [120, 19], [120, 20], [120, 21], [120, 22], [120, 23], [120, 7]], - [[121, 17], [121, 18], [121, 19], [121, 20], [121, 21], [121, 22], [121, 23], [121, 7], [122, 17], [122, 18], - [122, 19], [122, 20], [122, 21], [122, 22], [122, 23], [122, 7]], - [[38, 25], [38, 26], [38, 27], [38, 6], [42, 25], [42, 26], [42, 27], [42, 6], [44, 25], [44, 26], [44, 27], - [44, 6], [59, 25], [59, 26], [59, 27], [59, 6]], - [[99, 17], [99, 18], [99, 19], [99, 20], [99, 21], [99, 22], [99, 23], [99, 7], [101, 17], [101, 18], - [101, 19], [101, 20], [101, 21], [101, 22], [101, 23], [101, 7]], - [[32, 17], [32, 18], [32, 19], [32, 20], [32, 21], [32, 22], [32, 23], [32, 7], [37, 17], [37, 18], [37, 19], - [37, 20], [37, 21], [37, 22], [37, 23], [37, 7]], - [[45, 17], [45, 18], [45, 19], [45, 20], [45, 21], [45, 22], [45, 23], [45, 7], [46, 17], [46, 18], [46, 19], - [46, 20], [46, 21], [46, 22], [46, 23], [46, 7]], - [[47, 17], [47, 18], [47, 19], [47, 20], [47, 21], [47, 22], [47, 23], [47, 7], [51, 17], [51, 18], [51, 19], - [51, 20], [51, 21], [51, 22], [51, 23], [51, 7]], - [[52, 17], [52, 18], [52, 19], [52, 20], [52, 21], [52, 22], [52, 23], [52, 7], [53, 17], [53, 18], [53, 19], - [53, 20], [53, 21], [53, 22], [53, 23], [53, 7]], - [[54, 17], [54, 18], [54, 19], [54, 20], [54, 21], [54, 22], [54, 23], [54, 7], [55, 17], [55, 18], [55, 19], - [55, 20], [55, 21], [55, 22], [55, 23], [55, 7]], - [[56, 17], [56, 18], [56, 19], [56, 20], [56, 21], [56, 22], [56, 23], [56, 7], [57, 17], [57, 18], [57, 19], - [57, 20], [57, 21], [57, 22], [57, 23], [57, 7]], - [[61, 17], [61, 18], [61, 19], [61, 20], [61, 21], [61, 22], [61, 23], [61, 7], [65, 17], [65, 18], [65, 19], - [65, 20], [65, 21], [65, 22], [65, 23], [65, 7]], - [[95, 17], [95, 18], [95, 19], [95, 20], [95, 21], [95, 22], [95, 23], [95, 7], [98, 17], [98, 18], [98, 19], - [98, 20], [98, 21], [98, 22], [98, 23], [98, 7]], - [[100, 17], [100, 18], [100, 19], [100, 20], [100, 21], [100, 22], [100, 23], [100, 7], [102, 17], [102, 18], - [102, 19], [102, 20], [102, 21], [102, 22], [102, 23], [102, 7]], - [[103, 17], [103, 18], [103, 19], [103, 20], [103, 21], [103, 22], [103, 23], [103, 7], [104, 17], [104, 18], - [104, 19], [104, 20], [104, 21], [104, 22], [104, 23], [104, 7]], - [[108, 17], [108, 18], [108, 19], [108, 20], [108, 21], [108, 22], [108, 23], [108, 7], [109, 17], [109, 18], - [109, 19], [109, 20], [109, 21], [109, 22], [109, 23], [109, 7]], - [[110, 17], [110, 18], [110, 19], [110, 20], [110, 21], [110, 22], [110, 23], [110, 7], [112, 17], [112, 18], - [112, 19], [112, 20], [112, 21], [112, 22], [112, 23], [112, 7]], - [[114, 17], [114, 18], [114, 19], [114, 20], [114, 21], [114, 22], [114, 23], [114, 7], [117, 17], [117, 18], - [117, 19], [117, 20], [117, 21], [117, 22], [117, 23], [117, 7]], - [[58, 25], [58, 26], [58, 27], [58, 6], [66, 25], [66, 26], [66, 27], [66, 6], [67, 25], [67, 26], [67, 27], - [67, 6], [68, 25], [68, 26], [68, 27], [68, 6]], - [[69, 25], [69, 26], [69, 27], [69, 6], [70, 25], [70, 26], [70, 27], [70, 6], [71, 25], [71, 26], [71, 27], - [71, 6], [72, 25], [72, 26], [72, 27], [72, 6]], - [[73, 25], [73, 26], [73, 27], [73, 6], [74, 25], [74, 26], [74, 27], [74, 6], [75, 25], [75, 26], [75, 27], - [75, 6], [76, 25], [76, 26], [76, 27], [76, 6]], - [[77, 25], [77, 26], [77, 27], [77, 6], [78, 25], [78, 26], [78, 27], [78, 6], [79, 25], [79, 26], [79, 27], - [79, 6], [80, 25], [80, 26], [80, 27], [80, 6]], - [[81, 25], [81, 26], [81, 27], [81, 6], [82, 25], [82, 26], [82, 27], [82, 6], [83, 25], [83, 26], [83, 27], - [83, 6], [84, 25], [84, 26], [84, 27], [84, 6]], - [[85, 25], [85, 26], [85, 27], [85, 6], [86, 25], [86, 26], [86, 27], [86, 6], [87, 25], [87, 26], [87, 27], - [87, 6], [89, 25], [89, 26], [89, 27], [89, 6]], - [[106, 25], [106, 26], [106, 27], [106, 6], [107, 25], [107, 26], [107, 27], [107, 6], [113, 25], [113, 26], - [113, 27], [113, 6], [118, 25], [118, 26], [118, 27], [118, 6]], - [[119, 25], [119, 26], [119, 27], [119, 6], [120, 25], [120, 26], [120, 27], [120, 6], [121, 25], [121, 26], - [121, 27], [121, 6], [122, 25], [122, 26], [122, 27], [122, 6]], - [[50, 17], [50, 18], [50, 19], [50, 20], [50, 21], [50, 22], [50, 23], [50, 7], [97, 17], [97, 18], [97, 19], - [97, 20], [97, 21], [97, 22], [97, 23], [97, 7]], - [[0, 17], [0, 18], [0, 19], [0, 20], [0, 21], [0, 22], [0, 23], [0, 7], [36, 17], [36, 18], [36, 19], [36, 20], - [36, 21], [36, 22], [36, 23], [36, 7]], - [[64, 17], [64, 18], [64, 19], [64, 20], [64, 21], [64, 22], [64, 23], [64, 7], [91, 17], [91, 18], [91, 19], - [91, 20], [91, 21], [91, 22], [91, 23], [91, 7]], - [[93, 17], [93, 18], [93, 19], [93, 20], [93, 21], [93, 22], [93, 23], [93, 7], [126, 17], [126, 18], - [126, 19], [126, 20], [126, 21], [126, 22], [126, 23], [126, 7]], - [[94, 25], [94, 26], [94, 27], [94, 6], [125, 25], [125, 26], [125, 27], [125, 6], [60, 29], [60, 5], [96, 29], - [96, 5], [123, 29], [123, 5], [nil, 91], [nil, 92]], - [[35, 17], [35, 18], [35, 19], [35, 20], [35, 21], [35, 22], [35, 23], [35, 7], [62, 17], [62, 18], [62, 19], - [62, 20], [62, 21], [62, 22], [62, 23], [62, 7]], - [[0, 25], [0, 26], [0, 27], [0, 6], [36, 25], [36, 26], [36, 27], [36, 6], [64, 25], [64, 26], [64, 27], - [64, 6], [91, 25], [91, 26], [91, 27], [91, 6]], - [[93, 25], [93, 26], [93, 27], [93, 6], [126, 25], [126, 26], [126, 27], [126, 6], [94, 29], [94, 5], - [125, 29], [125, 5], [60, 0], [96, 0], [123, 0], [nil, 93]], - [[39, 17], [39, 18], [39, 19], [39, 20], [39, 21], [39, 22], [39, 23], [39, 7], [43, 17], [43, 18], [43, 19], - [43, 20], [43, 21], [43, 22], [43, 23], [43, 7]], - [[124, 17], [124, 18], [124, 19], [124, 20], [124, 21], [124, 22], [124, 23], [124, 7], [35, 25], [35, 26], - [35, 27], [35, 6], [62, 25], [62, 26], [62, 27], [62, 6]], - [[0, 29], [0, 5], [36, 29], [36, 5], [64, 29], [64, 5], [91, 29], [91, 5], [93, 29], [93, 5], [126, 29], - [126, 5], [94, 0], [125, 0], [nil, 94], [nil, 95]], - [[33, 17], [33, 18], [33, 19], [33, 20], [33, 21], [33, 22], [33, 23], [33, 7], [34, 17], [34, 18], [34, 19], - [34, 20], [34, 21], [34, 22], [34, 23], [34, 7]], - [[40, 17], [40, 18], [40, 19], [40, 20], [40, 21], [40, 22], [40, 23], [40, 7], [41, 17], [41, 18], [41, 19], - [41, 20], [41, 21], [41, 22], [41, 23], [41, 7]], - [[63, 17], [63, 18], [63, 19], [63, 20], [63, 21], [63, 22], [63, 23], [63, 7], [39, 25], [39, 26], [39, 27], - [39, 6], [43, 25], [43, 26], [43, 27], [43, 6]], - [[124, 25], [124, 26], [124, 27], [124, 6], [35, 29], [35, 5], [62, 29], [62, 5], [0, 0], [36, 0], [64, 0], - [91, 0], [93, 0], [126, 0], [nil, 96], [nil, 97]], - [[92, 29], [92, 5], [195, 29], [195, 5], [208, 29], [208, 5], [128, 0], [130, 0], [131, 0], [162, 0], [184, 0], - [194, 0], [224, 0], [226, 0], [nil, 98], [nil, 99]], - [[nil, 100], [nil, 101], [nil, 102], [nil, 103], [nil, 104], [nil, 105], [nil, 106], [nil, 107], [nil, 108], - [nil, 109], [nil, 110], [nil, 111], [nil, 112], [nil, 113], [nil, 114], [nil, 115]], - [[92, 0], [195, 0], [208, 0], [nil, 116], [nil, 117], [nil, 118], [nil, 119], [nil, 120], [nil, 121], - [nil, 122], [nil, 123], [nil, 124], [nil, 125], [nil, 126], [nil, 127], [nil, 128]], - [[60, 17], [60, 18], [60, 19], [60, 20], [60, 21], [60, 22], [60, 23], [60, 7], [96, 17], [96, 18], [96, 19], - [96, 20], [96, 21], [96, 22], [96, 23], [96, 7]], - [[123, 17], [123, 18], [123, 19], [123, 20], [123, 21], [123, 22], [123, 23], [123, 7], [nil, 129], [nil, 130], - [nil, 131], [nil, 132], [nil, 133], [nil, 134], [nil, 135], [nil, 136]], - [[94, 17], [94, 18], [94, 19], [94, 20], [94, 21], [94, 22], [94, 23], [94, 7], [125, 17], [125, 18], - [125, 19], [125, 20], [125, 21], [125, 22], [125, 23], [125, 7]], - [[60, 25], [60, 26], [60, 27], [60, 6], [96, 25], [96, 26], [96, 27], [96, 6], [123, 25], [123, 26], [123, 27], - [123, 6], [nil, 137], [nil, 138], [nil, 139], [nil, 140]], - [[153, 17], [153, 18], [153, 19], [153, 20], [153, 21], [153, 22], [153, 23], [153, 7], [161, 17], [161, 18], - [161, 19], [161, 20], [161, 21], [161, 22], [161, 23], [161, 7]], - [[167, 17], [167, 18], [167, 19], [167, 20], [167, 21], [167, 22], [167, 23], [167, 7], [172, 17], [172, 18], - [172, 19], [172, 20], [172, 21], [172, 22], [172, 23], [172, 7]], - [[176, 17], [176, 18], [176, 19], [176, 20], [176, 21], [176, 22], [176, 23], [176, 7], [177, 17], [177, 18], - [177, 19], [177, 20], [177, 21], [177, 22], [177, 23], [177, 7]], - [[179, 17], [179, 18], [179, 19], [179, 20], [179, 21], [179, 22], [179, 23], [179, 7], [209, 17], [209, 18], - [209, 19], [209, 20], [209, 21], [209, 22], [209, 23], [209, 7]], - [[216, 17], [216, 18], [216, 19], [216, 20], [216, 21], [216, 22], [216, 23], [216, 7], [217, 17], [217, 18], - [217, 19], [217, 20], [217, 21], [217, 22], [217, 23], [217, 7]], - [[227, 17], [227, 18], [227, 19], [227, 20], [227, 21], [227, 22], [227, 23], [227, 7], [229, 17], [229, 18], - [229, 19], [229, 20], [229, 21], [229, 22], [229, 23], [229, 7]], - [[230, 17], [230, 18], [230, 19], [230, 20], [230, 21], [230, 22], [230, 23], [230, 7], [129, 25], [129, 26], - [129, 27], [129, 6], [132, 25], [132, 26], [132, 27], [132, 6]], - [[133, 25], [133, 26], [133, 27], [133, 6], [134, 25], [134, 26], [134, 27], [134, 6], [136, 25], [136, 26], - [136, 27], [136, 6], [146, 25], [146, 26], [146, 27], [146, 6]], - [[154, 25], [154, 26], [154, 27], [154, 6], [156, 25], [156, 26], [156, 27], [156, 6], [160, 25], [160, 26], - [160, 27], [160, 6], [163, 25], [163, 26], [163, 27], [163, 6]], - [[164, 25], [164, 26], [164, 27], [164, 6], [169, 25], [169, 26], [169, 27], [169, 6], [170, 25], [170, 26], - [170, 27], [170, 6], [173, 25], [173, 26], [173, 27], [173, 6]], - [[178, 25], [178, 26], [178, 27], [178, 6], [181, 25], [181, 26], [181, 27], [181, 6], [185, 25], [185, 26], - [185, 27], [185, 6], [186, 25], [186, 26], [186, 27], [186, 6]], - [[187, 25], [187, 26], [187, 27], [187, 6], [189, 25], [189, 26], [189, 27], [189, 6], [190, 25], [190, 26], - [190, 27], [190, 6], [196, 25], [196, 26], [196, 27], [196, 6]], - [[198, 25], [198, 26], [198, 27], [198, 6], [228, 25], [228, 26], [228, 27], [228, 6], [232, 25], [232, 26], - [232, 27], [232, 6], [233, 25], [233, 26], [233, 27], [233, 6]], - [[1, 29], [1, 5], [135, 29], [135, 5], [137, 29], [137, 5], [138, 29], [138, 5], [139, 29], [139, 5], - [140, 29], [140, 5], [141, 29], [141, 5], [143, 29], [143, 5]], - [[147, 29], [147, 5], [149, 29], [149, 5], [150, 29], [150, 5], [151, 29], [151, 5], [152, 29], [152, 5], - [155, 29], [155, 5], [157, 29], [157, 5], [158, 29], [158, 5]], - [[165, 29], [165, 5], [166, 29], [166, 5], [168, 29], [168, 5], [174, 29], [174, 5], [175, 29], [175, 5], - [180, 29], [180, 5], [182, 29], [182, 5], [183, 29], [183, 5]], - [[188, 29], [188, 5], [191, 29], [191, 5], [197, 29], [197, 5], [231, 29], [231, 5], [239, 29], [239, 5], - [9, 0], [142, 0], [144, 0], [145, 0], [148, 0], [159, 0]], - [[171, 0], [206, 0], [215, 0], [225, 0], [236, 0], [237, 0], [nil, 141], [nil, 142], [nil, 143], [nil, 144], - [nil, 145], [nil, 146], [nil, 147], [nil, 148], [nil, 149], [nil, 150]], - [[128, 17], [128, 18], [128, 19], [128, 20], [128, 21], [128, 22], [128, 23], [128, 7], [130, 17], [130, 18], - [130, 19], [130, 20], [130, 21], [130, 22], [130, 23], [130, 7]], - [[131, 17], [131, 18], [131, 19], [131, 20], [131, 21], [131, 22], [131, 23], [131, 7], [162, 17], [162, 18], - [162, 19], [162, 20], [162, 21], [162, 22], [162, 23], [162, 7]], - [[184, 17], [184, 18], [184, 19], [184, 20], [184, 21], [184, 22], [184, 23], [184, 7], [194, 17], [194, 18], - [194, 19], [194, 20], [194, 21], [194, 22], [194, 23], [194, 7]], - [[224, 17], [224, 18], [224, 19], [224, 20], [224, 21], [224, 22], [224, 23], [224, 7], [226, 17], [226, 18], - [226, 19], [226, 20], [226, 21], [226, 22], [226, 23], [226, 7]], - [[153, 25], [153, 26], [153, 27], [153, 6], [161, 25], [161, 26], [161, 27], [161, 6], [167, 25], [167, 26], - [167, 27], [167, 6], [172, 25], [172, 26], [172, 27], [172, 6]], - [[176, 25], [176, 26], [176, 27], [176, 6], [177, 25], [177, 26], [177, 27], [177, 6], [179, 25], [179, 26], - [179, 27], [179, 6], [209, 25], [209, 26], [209, 27], [209, 6]], - [[216, 25], [216, 26], [216, 27], [216, 6], [217, 25], [217, 26], [217, 27], [217, 6], [227, 25], [227, 26], - [227, 27], [227, 6], [229, 25], [229, 26], [229, 27], [229, 6]], - [[230, 25], [230, 26], [230, 27], [230, 6], [129, 29], [129, 5], [132, 29], [132, 5], [133, 29], [133, 5], - [134, 29], [134, 5], [136, 29], [136, 5], [146, 29], [146, 5]], - [[154, 29], [154, 5], [156, 29], [156, 5], [160, 29], [160, 5], [163, 29], [163, 5], [164, 29], [164, 5], - [169, 29], [169, 5], [170, 29], [170, 5], [173, 29], [173, 5]], - [[178, 29], [178, 5], [181, 29], [181, 5], [185, 29], [185, 5], [186, 29], [186, 5], [187, 29], [187, 5], - [189, 29], [189, 5], [190, 29], [190, 5], [196, 29], [196, 5]], - [[198, 29], [198, 5], [228, 29], [228, 5], [232, 29], [232, 5], [233, 29], [233, 5], [1, 0], [135, 0], - [137, 0], [138, 0], [139, 0], [140, 0], [141, 0], [143, 0]], - [[147, 0], [149, 0], [150, 0], [151, 0], [152, 0], [155, 0], [157, 0], [158, 0], [165, 0], [166, 0], [168, 0], - [174, 0], [175, 0], [180, 0], [182, 0], [183, 0]], - [[188, 0], [191, 0], [197, 0], [231, 0], [239, 0], [nil, 151], [nil, 152], [nil, 153], [nil, 154], [nil, 155], - [nil, 156], [nil, 157], [nil, 158], [nil, 159], [nil, 160], [nil, 161]], - [[92, 17], [92, 18], [92, 19], [92, 20], [92, 21], [92, 22], [92, 23], [92, 7], [195, 17], [195, 18], - [195, 19], [195, 20], [195, 21], [195, 22], [195, 23], [195, 7]], - [[208, 17], [208, 18], [208, 19], [208, 20], [208, 21], [208, 22], [208, 23], [208, 7], [128, 25], [128, 26], - [128, 27], [128, 6], [130, 25], [130, 26], [130, 27], [130, 6]], - [[131, 25], [131, 26], [131, 27], [131, 6], [162, 25], [162, 26], [162, 27], [162, 6], [184, 25], [184, 26], - [184, 27], [184, 6], [194, 25], [194, 26], [194, 27], [194, 6]], - [[224, 25], [224, 26], [224, 27], [224, 6], [226, 25], [226, 26], [226, 27], [226, 6], [153, 29], [153, 5], - [161, 29], [161, 5], [167, 29], [167, 5], [172, 29], [172, 5]], - [[176, 29], [176, 5], [177, 29], [177, 5], [179, 29], [179, 5], [209, 29], [209, 5], [216, 29], [216, 5], - [217, 29], [217, 5], [227, 29], [227, 5], [229, 29], [229, 5]], - [[230, 29], [230, 5], [129, 0], [132, 0], [133, 0], [134, 0], [136, 0], [146, 0], [154, 0], [156, 0], [160, 0], - [163, 0], [164, 0], [169, 0], [170, 0], [173, 0]], - [[178, 0], [181, 0], [185, 0], [186, 0], [187, 0], [189, 0], [190, 0], [196, 0], [198, 0], [228, 0], [232, 0], - [233, 0], [nil, 162], [nil, 163], [nil, 164], [nil, 165]], - [[nil, 166], [nil, 167], [nil, 168], [nil, 169], [nil, 170], [nil, 171], [nil, 172], [nil, 173], [nil, 174], - [nil, 175], [nil, 176], [nil, 177], [nil, 178], [nil, 179], [nil, 180], [nil, 181]], - [[92, 25], [92, 26], [92, 27], [92, 6], [195, 25], [195, 26], [195, 27], [195, 6], [208, 25], [208, 26], - [208, 27], [208, 6], [128, 29], [128, 5], [130, 29], [130, 5]], - [[131, 29], [131, 5], [162, 29], [162, 5], [184, 29], [184, 5], [194, 29], [194, 5], [224, 29], [224, 5], - [226, 29], [226, 5], [153, 0], [161, 0], [167, 0], [172, 0]], - [[176, 0], [177, 0], [179, 0], [209, 0], [216, 0], [217, 0], [227, 0], [229, 0], [230, 0], [nil, 182], - [nil, 183], [nil, 184], [nil, 185], [nil, 186], [nil, 187], [nil, 188]], - [[nil, 189], [nil, 190], [nil, 191], [nil, 192], [nil, 193], [nil, 194], [nil, 195], [nil, 196], [nil, 197], - [nil, 198], [nil, 199], [nil, 200], [nil, 201], [nil, 202], [nil, 203], [nil, 204]], - [[199, 17], [199, 18], [199, 19], [199, 20], [199, 21], [199, 22], [199, 23], [199, 7], [207, 17], [207, 18], - [207, 19], [207, 20], [207, 21], [207, 22], [207, 23], [207, 7]], - [[234, 17], [234, 18], [234, 19], [234, 20], [234, 21], [234, 22], [234, 23], [234, 7], [235, 17], [235, 18], - [235, 19], [235, 20], [235, 21], [235, 22], [235, 23], [235, 7]], - [[192, 25], [192, 26], [192, 27], [192, 6], [193, 25], [193, 26], [193, 27], [193, 6], [200, 25], [200, 26], - [200, 27], [200, 6], [201, 25], [201, 26], [201, 27], [201, 6]], - [[202, 25], [202, 26], [202, 27], [202, 6], [205, 25], [205, 26], [205, 27], [205, 6], [210, 25], [210, 26], - [210, 27], [210, 6], [213, 25], [213, 26], [213, 27], [213, 6]], - [[218, 25], [218, 26], [218, 27], [218, 6], [219, 25], [219, 26], [219, 27], [219, 6], [238, 25], [238, 26], - [238, 27], [238, 6], [240, 25], [240, 26], [240, 27], [240, 6]], - [[242, 25], [242, 26], [242, 27], [242, 6], [243, 25], [243, 26], [243, 27], [243, 6], [255, 25], [255, 26], - [255, 27], [255, 6], [203, 29], [203, 5], [204, 29], [204, 5]], - [[211, 29], [211, 5], [212, 29], [212, 5], [214, 29], [214, 5], [221, 29], [221, 5], [222, 29], [222, 5], - [223, 29], [223, 5], [241, 29], [241, 5], [244, 29], [244, 5]], - [[245, 29], [245, 5], [246, 29], [246, 5], [247, 29], [247, 5], [248, 29], [248, 5], [250, 29], [250, 5], - [251, 29], [251, 5], [252, 29], [252, 5], [253, 29], [253, 5]], - [[254, 29], [254, 5], [2, 0], [3, 0], [4, 0], [5, 0], [6, 0], [7, 0], [8, 0], [11, 0], [12, 0], [14, 0], - [15, 0], [16, 0], [17, 0], [18, 0]], - [[19, 0], [20, 0], [21, 0], [23, 0], [24, 0], [25, 0], [26, 0], [27, 0], [28, 0], [29, 0], [30, 0], [31, 0], - [127, 0], [220, 0], [249, 0], [nil, 205]], - [[9, 17], [9, 18], [9, 19], [9, 20], [9, 21], [9, 22], [9, 23], [9, 7], [142, 17], [142, 18], [142, 19], - [142, 20], [142, 21], [142, 22], [142, 23], [142, 7]], - [[144, 17], [144, 18], [144, 19], [144, 20], [144, 21], [144, 22], [144, 23], [144, 7], [145, 17], [145, 18], - [145, 19], [145, 20], [145, 21], [145, 22], [145, 23], [145, 7]], - [[148, 17], [148, 18], [148, 19], [148, 20], [148, 21], [148, 22], [148, 23], [148, 7], [159, 17], [159, 18], - [159, 19], [159, 20], [159, 21], [159, 22], [159, 23], [159, 7]], - [[171, 17], [171, 18], [171, 19], [171, 20], [171, 21], [171, 22], [171, 23], [171, 7], [206, 17], [206, 18], - [206, 19], [206, 20], [206, 21], [206, 22], [206, 23], [206, 7]], - [[215, 17], [215, 18], [215, 19], [215, 20], [215, 21], [215, 22], [215, 23], [215, 7], [225, 17], [225, 18], - [225, 19], [225, 20], [225, 21], [225, 22], [225, 23], [225, 7]], - [[236, 17], [236, 18], [236, 19], [236, 20], [236, 21], [236, 22], [236, 23], [236, 7], [237, 17], [237, 18], - [237, 19], [237, 20], [237, 21], [237, 22], [237, 23], [237, 7]], - [[199, 25], [199, 26], [199, 27], [199, 6], [207, 25], [207, 26], [207, 27], [207, 6], [234, 25], [234, 26], - [234, 27], [234, 6], [235, 25], [235, 26], [235, 27], [235, 6]], - [[192, 29], [192, 5], [193, 29], [193, 5], [200, 29], [200, 5], [201, 29], [201, 5], [202, 29], [202, 5], - [205, 29], [205, 5], [210, 29], [210, 5], [213, 29], [213, 5]], - [[218, 29], [218, 5], [219, 29], [219, 5], [238, 29], [238, 5], [240, 29], [240, 5], [242, 29], [242, 5], - [243, 29], [243, 5], [255, 29], [255, 5], [203, 0], [204, 0]], - [[211, 0], [212, 0], [214, 0], [221, 0], [222, 0], [223, 0], [241, 0], [244, 0], [245, 0], [246, 0], [247, 0], - [248, 0], [250, 0], [251, 0], [252, 0], [253, 0]], - [[254, 0], [nil, 206], [nil, 207], [nil, 208], [nil, 209], [nil, 210], [nil, 211], [nil, 212], [nil, 213], - [nil, 214], [nil, 215], [nil, 216], [nil, 217], [nil, 218], [nil, 219], [nil, 220]], - [[1, 17], [1, 18], [1, 19], [1, 20], [1, 21], [1, 22], [1, 23], [1, 7], [135, 17], [135, 18], [135, 19], - [135, 20], [135, 21], [135, 22], [135, 23], [135, 7]], - [[137, 17], [137, 18], [137, 19], [137, 20], [137, 21], [137, 22], [137, 23], [137, 7], [138, 17], [138, 18], - [138, 19], [138, 20], [138, 21], [138, 22], [138, 23], [138, 7]], - [[139, 17], [139, 18], [139, 19], [139, 20], [139, 21], [139, 22], [139, 23], [139, 7], [140, 17], [140, 18], - [140, 19], [140, 20], [140, 21], [140, 22], [140, 23], [140, 7]], - [[141, 17], [141, 18], [141, 19], [141, 20], [141, 21], [141, 22], [141, 23], [141, 7], [143, 17], [143, 18], - [143, 19], [143, 20], [143, 21], [143, 22], [143, 23], [143, 7]], - [[147, 17], [147, 18], [147, 19], [147, 20], [147, 21], [147, 22], [147, 23], [147, 7], [149, 17], [149, 18], - [149, 19], [149, 20], [149, 21], [149, 22], [149, 23], [149, 7]], - [[150, 17], [150, 18], [150, 19], [150, 20], [150, 21], [150, 22], [150, 23], [150, 7], [151, 17], [151, 18], - [151, 19], [151, 20], [151, 21], [151, 22], [151, 23], [151, 7]], - [[152, 17], [152, 18], [152, 19], [152, 20], [152, 21], [152, 22], [152, 23], [152, 7], [155, 17], [155, 18], - [155, 19], [155, 20], [155, 21], [155, 22], [155, 23], [155, 7]], - [[157, 17], [157, 18], [157, 19], [157, 20], [157, 21], [157, 22], [157, 23], [157, 7], [158, 17], [158, 18], - [158, 19], [158, 20], [158, 21], [158, 22], [158, 23], [158, 7]], - [[165, 17], [165, 18], [165, 19], [165, 20], [165, 21], [165, 22], [165, 23], [165, 7], [166, 17], [166, 18], - [166, 19], [166, 20], [166, 21], [166, 22], [166, 23], [166, 7]], - [[168, 17], [168, 18], [168, 19], [168, 20], [168, 21], [168, 22], [168, 23], [168, 7], [174, 17], [174, 18], - [174, 19], [174, 20], [174, 21], [174, 22], [174, 23], [174, 7]], - [[175, 17], [175, 18], [175, 19], [175, 20], [175, 21], [175, 22], [175, 23], [175, 7], [180, 17], [180, 18], - [180, 19], [180, 20], [180, 21], [180, 22], [180, 23], [180, 7]], - [[182, 17], [182, 18], [182, 19], [182, 20], [182, 21], [182, 22], [182, 23], [182, 7], [183, 17], [183, 18], - [183, 19], [183, 20], [183, 21], [183, 22], [183, 23], [183, 7]], - [[188, 17], [188, 18], [188, 19], [188, 20], [188, 21], [188, 22], [188, 23], [188, 7], [191, 17], [191, 18], - [191, 19], [191, 20], [191, 21], [191, 22], [191, 23], [191, 7]], - [[197, 17], [197, 18], [197, 19], [197, 20], [197, 21], [197, 22], [197, 23], [197, 7], [231, 17], [231, 18], - [231, 19], [231, 20], [231, 21], [231, 22], [231, 23], [231, 7]], - [[239, 17], [239, 18], [239, 19], [239, 20], [239, 21], [239, 22], [239, 23], [239, 7], [9, 25], [9, 26], - [9, 27], [9, 6], [142, 25], [142, 26], [142, 27], [142, 6]], - [[144, 25], [144, 26], [144, 27], [144, 6], [145, 25], [145, 26], [145, 27], [145, 6], [148, 25], [148, 26], - [148, 27], [148, 6], [159, 25], [159, 26], [159, 27], [159, 6]], - [[171, 25], [171, 26], [171, 27], [171, 6], [206, 25], [206, 26], [206, 27], [206, 6], [215, 25], [215, 26], - [215, 27], [215, 6], [225, 25], [225, 26], [225, 27], [225, 6]], - [[236, 25], [236, 26], [236, 27], [236, 6], [237, 25], [237, 26], [237, 27], [237, 6], [199, 29], [199, 5], - [207, 29], [207, 5], [234, 29], [234, 5], [235, 29], [235, 5]], - [[192, 0], [193, 0], [200, 0], [201, 0], [202, 0], [205, 0], [210, 0], [213, 0], [218, 0], [219, 0], [238, 0], - [240, 0], [242, 0], [243, 0], [255, 0], [nil, 8]], - [[nil, 222], [nil, 223], [nil, 224], [nil, 225], [nil, 226], [nil, 227], [nil, 228], [nil, 229], [nil, 230], - [nil, 231], [nil, 232], [nil, 233], [nil, 234], [nil, 235], [nil, 236], [nil, 237]], - [[129, 17], [129, 18], [129, 19], [129, 20], [129, 21], [129, 22], [129, 23], [129, 7], [132, 17], [132, 18], - [132, 19], [132, 20], [132, 21], [132, 22], [132, 23], [132, 7]], - [[133, 17], [133, 18], [133, 19], [133, 20], [133, 21], [133, 22], [133, 23], [133, 7], [134, 17], [134, 18], - [134, 19], [134, 20], [134, 21], [134, 22], [134, 23], [134, 7]], - [[136, 17], [136, 18], [136, 19], [136, 20], [136, 21], [136, 22], [136, 23], [136, 7], [146, 17], [146, 18], - [146, 19], [146, 20], [146, 21], [146, 22], [146, 23], [146, 7]], - [[154, 17], [154, 18], [154, 19], [154, 20], [154, 21], [154, 22], [154, 23], [154, 7], [156, 17], [156, 18], - [156, 19], [156, 20], [156, 21], [156, 22], [156, 23], [156, 7]], - [[160, 17], [160, 18], [160, 19], [160, 20], [160, 21], [160, 22], [160, 23], [160, 7], [163, 17], [163, 18], - [163, 19], [163, 20], [163, 21], [163, 22], [163, 23], [163, 7]], - [[164, 17], [164, 18], [164, 19], [164, 20], [164, 21], [164, 22], [164, 23], [164, 7], [169, 17], [169, 18], - [169, 19], [169, 20], [169, 21], [169, 22], [169, 23], [169, 7]], - [[170, 17], [170, 18], [170, 19], [170, 20], [170, 21], [170, 22], [170, 23], [170, 7], [173, 17], [173, 18], - [173, 19], [173, 20], [173, 21], [173, 22], [173, 23], [173, 7]], - [[178, 17], [178, 18], [178, 19], [178, 20], [178, 21], [178, 22], [178, 23], [178, 7], [181, 17], [181, 18], - [181, 19], [181, 20], [181, 21], [181, 22], [181, 23], [181, 7]], - [[185, 17], [185, 18], [185, 19], [185, 20], [185, 21], [185, 22], [185, 23], [185, 7], [186, 17], [186, 18], - [186, 19], [186, 20], [186, 21], [186, 22], [186, 23], [186, 7]], - [[187, 17], [187, 18], [187, 19], [187, 20], [187, 21], [187, 22], [187, 23], [187, 7], [189, 17], [189, 18], - [189, 19], [189, 20], [189, 21], [189, 22], [189, 23], [189, 7]], - [[190, 17], [190, 18], [190, 19], [190, 20], [190, 21], [190, 22], [190, 23], [190, 7], [196, 17], [196, 18], - [196, 19], [196, 20], [196, 21], [196, 22], [196, 23], [196, 7]], - [[198, 17], [198, 18], [198, 19], [198, 20], [198, 21], [198, 22], [198, 23], [198, 7], [228, 17], [228, 18], - [228, 19], [228, 20], [228, 21], [228, 22], [228, 23], [228, 7]], - [[232, 17], [232, 18], [232, 19], [232, 20], [232, 21], [232, 22], [232, 23], [232, 7], [233, 17], [233, 18], - [233, 19], [233, 20], [233, 21], [233, 22], [233, 23], [233, 7]], - [[1, 25], [1, 26], [1, 27], [1, 6], [135, 25], [135, 26], [135, 27], [135, 6], [137, 25], [137, 26], [137, 27], - [137, 6], [138, 25], [138, 26], [138, 27], [138, 6]], - [[139, 25], [139, 26], [139, 27], [139, 6], [140, 25], [140, 26], [140, 27], [140, 6], [141, 25], [141, 26], - [141, 27], [141, 6], [143, 25], [143, 26], [143, 27], [143, 6]], - [[147, 25], [147, 26], [147, 27], [147, 6], [149, 25], [149, 26], [149, 27], [149, 6], [150, 25], [150, 26], - [150, 27], [150, 6], [151, 25], [151, 26], [151, 27], [151, 6]], - [[152, 25], [152, 26], [152, 27], [152, 6], [155, 25], [155, 26], [155, 27], [155, 6], [157, 25], [157, 26], - [157, 27], [157, 6], [158, 25], [158, 26], [158, 27], [158, 6]], - [[165, 25], [165, 26], [165, 27], [165, 6], [166, 25], [166, 26], [166, 27], [166, 6], [168, 25], [168, 26], - [168, 27], [168, 6], [174, 25], [174, 26], [174, 27], [174, 6]], - [[175, 25], [175, 26], [175, 27], [175, 6], [180, 25], [180, 26], [180, 27], [180, 6], [182, 25], [182, 26], - [182, 27], [182, 6], [183, 25], [183, 26], [183, 27], [183, 6]], - [[188, 25], [188, 26], [188, 27], [188, 6], [191, 25], [191, 26], [191, 27], [191, 6], [197, 25], [197, 26], - [197, 27], [197, 6], [231, 25], [231, 26], [231, 27], [231, 6]], - [[239, 25], [239, 26], [239, 27], [239, 6], [9, 29], [9, 5], [142, 29], [142, 5], [144, 29], [144, 5], - [145, 29], [145, 5], [148, 29], [148, 5], [159, 29], [159, 5]], - [[171, 29], [171, 5], [206, 29], [206, 5], [215, 29], [215, 5], [225, 29], [225, 5], [236, 29], [236, 5], - [237, 29], [237, 5], [199, 0], [207, 0], [234, 0], [235, 0]], - [[nil, 238], [nil, 239], [nil, 240], [nil, 241], [nil, 242], [nil, 243], [nil, 244], [nil, 245], [nil, 246], - [nil, 247], [nil, 248], [nil, 249], [nil, 250], [nil, 251], [nil, 252], [nil, 253]], - [[10, 25], [10, 26], [10, 27], [10, 6], [13, 25], [13, 26], [13, 27], [13, 6], [22, 25], [22, 26], [22, 27], - [22, 6], [256, 25], [256, 26], [256, 27], [256, 6]], - [[2, 17], [2, 18], [2, 19], [2, 20], [2, 21], [2, 22], [2, 23], [2, 7], [3, 17], [3, 18], [3, 19], [3, 20], - [3, 21], [3, 22], [3, 23], [3, 7]], - [[4, 17], [4, 18], [4, 19], [4, 20], [4, 21], [4, 22], [4, 23], [4, 7], [5, 17], [5, 18], [5, 19], [5, 20], - [5, 21], [5, 22], [5, 23], [5, 7]], - [[6, 17], [6, 18], [6, 19], [6, 20], [6, 21], [6, 22], [6, 23], [6, 7], [7, 17], [7, 18], [7, 19], [7, 20], - [7, 21], [7, 22], [7, 23], [7, 7]], - [[8, 17], [8, 18], [8, 19], [8, 20], [8, 21], [8, 22], [8, 23], [8, 7], [11, 17], [11, 18], [11, 19], [11, 20], - [11, 21], [11, 22], [11, 23], [11, 7]], - [[12, 17], [12, 18], [12, 19], [12, 20], [12, 21], [12, 22], [12, 23], [12, 7], [14, 17], [14, 18], [14, 19], - [14, 20], [14, 21], [14, 22], [14, 23], [14, 7]], - [[15, 17], [15, 18], [15, 19], [15, 20], [15, 21], [15, 22], [15, 23], [15, 7], [16, 17], [16, 18], [16, 19], - [16, 20], [16, 21], [16, 22], [16, 23], [16, 7]], - [[17, 17], [17, 18], [17, 19], [17, 20], [17, 21], [17, 22], [17, 23], [17, 7], [18, 17], [18, 18], [18, 19], - [18, 20], [18, 21], [18, 22], [18, 23], [18, 7]], - [[19, 17], [19, 18], [19, 19], [19, 20], [19, 21], [19, 22], [19, 23], [19, 7], [20, 17], [20, 18], [20, 19], - [20, 20], [20, 21], [20, 22], [20, 23], [20, 7]], - [[21, 17], [21, 18], [21, 19], [21, 20], [21, 21], [21, 22], [21, 23], [21, 7], [23, 17], [23, 18], [23, 19], - [23, 20], [23, 21], [23, 22], [23, 23], [23, 7]], - [[24, 17], [24, 18], [24, 19], [24, 20], [24, 21], [24, 22], [24, 23], [24, 7], [25, 17], [25, 18], [25, 19], - [25, 20], [25, 21], [25, 22], [25, 23], [25, 7]], - [[26, 17], [26, 18], [26, 19], [26, 20], [26, 21], [26, 22], [26, 23], [26, 7], [27, 17], [27, 18], [27, 19], - [27, 20], [27, 21], [27, 22], [27, 23], [27, 7]], - [[28, 17], [28, 18], [28, 19], [28, 20], [28, 21], [28, 22], [28, 23], [28, 7], [29, 17], [29, 18], [29, 19], - [29, 20], [29, 21], [29, 22], [29, 23], [29, 7]], - [[30, 17], [30, 18], [30, 19], [30, 20], [30, 21], [30, 22], [30, 23], [30, 7], [31, 17], [31, 18], [31, 19], - [31, 20], [31, 21], [31, 22], [31, 23], [31, 7]], - [[127, 17], [127, 18], [127, 19], [127, 20], [127, 21], [127, 22], [127, 23], [127, 7], [220, 17], [220, 18], - [220, 19], [220, 20], [220, 21], [220, 22], [220, 23], [220, 7]], - [[249, 17], [249, 18], [249, 19], [249, 20], [249, 21], [249, 22], [249, 23], [249, 7], [10, 29], [10, 5], - [13, 29], [13, 5], [22, 29], [22, 5], [256, 29], [256, 5]], - [[54, 25], [54, 26], [54, 27], [54, 6], [55, 25], [55, 26], [55, 27], [55, 6], [56, 25], [56, 26], [56, 27], - [56, 6], [57, 25], [57, 26], [57, 27], [57, 6]], - [[211, 17], [211, 18], [211, 19], [211, 20], [211, 21], [211, 22], [211, 23], [211, 7], [212, 17], [212, 18], - [212, 19], [212, 20], [212, 21], [212, 22], [212, 23], [212, 7]], - [[214, 17], [214, 18], [214, 19], [214, 20], [214, 21], [214, 22], [214, 23], [214, 7], [221, 17], [221, 18], - [221, 19], [221, 20], [221, 21], [221, 22], [221, 23], [221, 7]], - [[222, 17], [222, 18], [222, 19], [222, 20], [222, 21], [222, 22], [222, 23], [222, 7], [223, 17], [223, 18], - [223, 19], [223, 20], [223, 21], [223, 22], [223, 23], [223, 7]], - [[241, 17], [241, 18], [241, 19], [241, 20], [241, 21], [241, 22], [241, 23], [241, 7], [244, 17], [244, 18], - [244, 19], [244, 20], [244, 21], [244, 22], [244, 23], [244, 7]], - [[245, 17], [245, 18], [245, 19], [245, 20], [245, 21], [245, 22], [245, 23], [245, 7], [246, 17], [246, 18], - [246, 19], [246, 20], [246, 21], [246, 22], [246, 23], [246, 7]], - [[247, 17], [247, 18], [247, 19], [247, 20], [247, 21], [247, 22], [247, 23], [247, 7], [248, 17], [248, 18], - [248, 19], [248, 20], [248, 21], [248, 22], [248, 23], [248, 7]], - [[250, 17], [250, 18], [250, 19], [250, 20], [250, 21], [250, 22], [250, 23], [250, 7], [251, 17], [251, 18], - [251, 19], [251, 20], [251, 21], [251, 22], [251, 23], [251, 7]], - [[252, 17], [252, 18], [252, 19], [252, 20], [252, 21], [252, 22], [252, 23], [252, 7], [253, 17], [253, 18], - [253, 19], [253, 20], [253, 21], [253, 22], [253, 23], [253, 7]], - [[254, 17], [254, 18], [254, 19], [254, 20], [254, 21], [254, 22], [254, 23], [254, 7], [2, 25], [2, 26], - [2, 27], [2, 6], [3, 25], [3, 26], [3, 27], [3, 6]], - [[4, 25], [4, 26], [4, 27], [4, 6], [5, 25], [5, 26], [5, 27], [5, 6], [6, 25], [6, 26], [6, 27], [6, 6], - [7, 25], [7, 26], [7, 27], [7, 6]], - [[8, 25], [8, 26], [8, 27], [8, 6], [11, 25], [11, 26], [11, 27], [11, 6], [12, 25], [12, 26], [12, 27], - [12, 6], [14, 25], [14, 26], [14, 27], [14, 6]], - [[15, 25], [15, 26], [15, 27], [15, 6], [16, 25], [16, 26], [16, 27], [16, 6], [17, 25], [17, 26], [17, 27], - [17, 6], [18, 25], [18, 26], [18, 27], [18, 6]], - [[19, 25], [19, 26], [19, 27], [19, 6], [20, 25], [20, 26], [20, 27], [20, 6], [21, 25], [21, 26], [21, 27], - [21, 6], [23, 25], [23, 26], [23, 27], [23, 6]], - [[24, 25], [24, 26], [24, 27], [24, 6], [25, 25], [25, 26], [25, 27], [25, 6], [26, 25], [26, 26], [26, 27], - [26, 6], [27, 25], [27, 26], [27, 27], [27, 6]], - [[28, 25], [28, 26], [28, 27], [28, 6], [29, 25], [29, 26], [29, 27], [29, 6], [30, 25], [30, 26], [30, 27], - [30, 6], [31, 25], [31, 26], [31, 27], [31, 6]], - [[127, 25], [127, 26], [127, 27], [127, 6], [220, 25], [220, 26], [220, 27], [220, 6], [249, 25], [249, 26], - [249, 27], [249, 6], [10, 0], [13, 0], [22, 0], [256, 0]], - [[192, 17], [192, 18], [192, 19], [192, 20], [192, 21], [192, 22], [192, 23], [192, 7], [193, 17], [193, 18], - [193, 19], [193, 20], [193, 21], [193, 22], [193, 23], [193, 7]], - [[200, 17], [200, 18], [200, 19], [200, 20], [200, 21], [200, 22], [200, 23], [200, 7], [201, 17], [201, 18], - [201, 19], [201, 20], [201, 21], [201, 22], [201, 23], [201, 7]], - [[202, 17], [202, 18], [202, 19], [202, 20], [202, 21], [202, 22], [202, 23], [202, 7], [205, 17], [205, 18], - [205, 19], [205, 20], [205, 21], [205, 22], [205, 23], [205, 7]], - [[210, 17], [210, 18], [210, 19], [210, 20], [210, 21], [210, 22], [210, 23], [210, 7], [213, 17], [213, 18], - [213, 19], [213, 20], [213, 21], [213, 22], [213, 23], [213, 7]], - [[218, 17], [218, 18], [218, 19], [218, 20], [218, 21], [218, 22], [218, 23], [218, 7], [219, 17], [219, 18], - [219, 19], [219, 20], [219, 21], [219, 22], [219, 23], [219, 7]], - [[238, 17], [238, 18], [238, 19], [238, 20], [238, 21], [238, 22], [238, 23], [238, 7], [240, 17], [240, 18], - [240, 19], [240, 20], [240, 21], [240, 22], [240, 23], [240, 7]], - [[242, 17], [242, 18], [242, 19], [242, 20], [242, 21], [242, 22], [242, 23], [242, 7], [243, 17], [243, 18], - [243, 19], [243, 20], [243, 21], [243, 22], [243, 23], [243, 7]], - [[255, 17], [255, 18], [255, 19], [255, 20], [255, 21], [255, 22], [255, 23], [255, 7], [203, 25], [203, 26], - [203, 27], [203, 6], [204, 25], [204, 26], [204, 27], [204, 6]], - [[211, 25], [211, 26], [211, 27], [211, 6], [212, 25], [212, 26], [212, 27], [212, 6], [214, 25], [214, 26], - [214, 27], [214, 6], [221, 25], [221, 26], [221, 27], [221, 6]], - [[222, 25], [222, 26], [222, 27], [222, 6], [223, 25], [223, 26], [223, 27], [223, 6], [241, 25], [241, 26], - [241, 27], [241, 6], [244, 25], [244, 26], [244, 27], [244, 6]], - [[245, 25], [245, 26], [245, 27], [245, 6], [246, 25], [246, 26], [246, 27], [246, 6], [247, 25], [247, 26], - [247, 27], [247, 6], [248, 25], [248, 26], [248, 27], [248, 6]], - [[250, 25], [250, 26], [250, 27], [250, 6], [251, 25], [251, 26], [251, 27], [251, 6], [252, 25], [252, 26], - [252, 27], [252, 6], [253, 25], [253, 26], [253, 27], [253, 6]], - [[254, 25], [254, 26], [254, 27], [254, 6], [2, 29], [2, 5], [3, 29], [3, 5], [4, 29], [4, 5], [5, 29], [5, 5], - [6, 29], [6, 5], [7, 29], [7, 5]], - [[8, 29], [8, 5], [11, 29], [11, 5], [12, 29], [12, 5], [14, 29], [14, 5], [15, 29], [15, 5], [16, 29], - [16, 5], [17, 29], [17, 5], [18, 29], [18, 5]], - [[19, 29], [19, 5], [20, 29], [20, 5], [21, 29], [21, 5], [23, 29], [23, 5], [24, 29], [24, 5], [25, 29], - [25, 5], [26, 29], [26, 5], [27, 29], [27, 5]], - [[28, 29], [28, 5], [29, 29], [29, 5], [30, 29], [30, 5], [31, 29], [31, 5], [127, 29], [127, 5], [220, 29], - [220, 5], [249, 29], [249, 5], [nil, 254], [nil, 255]], - [[10, 17], [10, 18], [10, 19], [10, 20], [10, 21], [10, 22], [10, 23], [10, 7], [13, 17], [13, 18], [13, 19], - [13, 20], [13, 21], [13, 22], [13, 23], [13, 7]], - [[22, 17], [22, 18], [22, 19], [22, 20], [22, 21], [22, 22], [22, 23], [22, 7], [256, 17], [256, 18], - [256, 19], [256, 20], [256, 21], [256, 22], [256, 23], [256, 7]] + [[nil, 16], [nil, 76], [nil, 54], [nil, 36], [nil, 30], [nil, 28], [nil, 24], [nil, 221], [nil, 9], [nil, 10], [nil, 11], [nil, 12], [nil, 13], [nil, 14], [nil, 15], [nil, 1]], + [[119, 29], [119, 5], [120, 29], [120, 5], [121, 29], [121, 5], [122, 29], [122, 5], [38, 0], [42, 0], [44, 0], [59, 0], [88, 0], [90, 0], [nil, 31], [nil, 32]], + [[38, 29], [38, 5], [42, 29], [42, 5], [44, 29], [44, 5], [59, 29], [59, 5], [88, 29], [88, 5], [90, 29], [90, 5], [nil, 87], [nil, 88], [nil, 89], [nil, 90]], + [[88, 25], [88, 26], [88, 27], [88, 6], [90, 25], [90, 26], [90, 27], [90, 6], [33, 0], [34, 0], [40, 0], [41, 0], [63, 0], [nil, 84], [nil, 85], [nil, 86]], + [[33, 29], [33, 5], [34, 29], [34, 5], [40, 29], [40, 5], [41, 29], [41, 5], [63, 29], [63, 5], [39, 0], [43, 0], [124, 0], [nil, 81], [nil, 82], [nil, 83]], + [[nil, 61], [nil, 62], [nil, 63], [nil, 64], [nil, 65], [nil, 66], [nil, 67], [nil, 68], [nil, 69], [nil, 70], [nil, 71], [nil, 72], [nil, 73], [nil, 74], [nil, 75], [nil, 2]], + [[nil, 39], [nil, 40], [nil, 41], [nil, 42], [nil, 43], [nil, 44], [nil, 45], [nil, 46], [nil, 47], [nil, 48], [nil, 49], [nil, 50], [nil, 51], [nil, 52], [nil, 53], [nil, 3]], + [[85, 0], [86, 0], [87, 0], [89, 0], [106, 0], [107, 0], [113, 0], [118, 0], [119, 0], [120, 0], [121, 0], [122, 0], [nil, 33], [nil, 34], [nil, 35], [nil, 4]], + [[203, 17], [203, 18], [203, 19], [203, 20], [203, 21], [203, 22], [203, 23], [203, 7], [204, 17], [204, 18], [204, 19], [204, 20], [204, 21], [204, 22], [204, 23], [204, 7]], + [[61, 25], [61, 26], [61, 27], [61, 6], [65, 25], [65, 26], [65, 27], [65, 6], [95, 25], [95, 26], [95, 27], [95, 6], [98, 25], [98, 26], [98, 27], [98, 6]], + [[100, 25], [100, 26], [100, 27], [100, 6], [102, 25], [102, 26], [102, 27], [102, 6], [103, 25], [103, 26], [103, 27], [103, 6], [104, 25], [104, 26], [104, 27], [104, 6]], + [[108, 25], [108, 26], [108, 27], [108, 6], [109, 25], [109, 26], [109, 27], [109, 6], [110, 25], [110, 26], [110, 27], [110, 6], [112, 25], [112, 26], [112, 27], [112, 6]], + [[114, 25], [114, 26], [114, 27], [114, 6], [117, 25], [117, 26], [117, 27], [117, 6], [58, 29], [58, 5], [66, 29], [66, 5], [67, 29], [67, 5], [68, 29], [68, 5]], + [[69, 29], [69, 5], [70, 29], [70, 5], [71, 29], [71, 5], [72, 29], [72, 5], [73, 29], [73, 5], [74, 29], [74, 5], [75, 29], [75, 5], [76, 29], [76, 5]], + [[77, 29], [77, 5], [78, 29], [78, 5], [79, 29], [79, 5], [80, 29], [80, 5], [81, 29], [81, 5], [82, 29], [82, 5], [83, 29], [83, 5], [84, 29], [84, 5]], + [[85, 29], [85, 5], [86, 29], [86, 5], [87, 29], [87, 5], [89, 29], [89, 5], [106, 29], [106, 5], [107, 29], [107, 5], [113, 29], [113, 5], [118, 29], [118, 5]], + [[48, 17], [48, 18], [48, 19], [48, 20], [48, 21], [48, 22], [48, 23], [48, 7], [49, 17], [49, 18], [49, 19], [49, 20], [49, 21], [49, 22], [49, 23], [49, 7]], + [[48, 25], [48, 26], [48, 27], [48, 6], [49, 25], [49, 26], [49, 27], [49, 6], [50, 25], [50, 26], [50, 27], [50, 6], [97, 25], [97, 26], [97, 27], [97, 6]], + [[99, 25], [99, 26], [99, 27], [99, 6], [101, 25], [101, 26], [101, 27], [101, 6], [105, 25], [105, 26], [105, 27], [105, 6], [111, 25], [111, 26], [111, 27], [111, 6]], + [[115, 25], [115, 26], [115, 27], [115, 6], [116, 25], [116, 26], [116, 27], [116, 6], [32, 29], [32, 5], [37, 29], [37, 5], [45, 29], [45, 5], [46, 29], [46, 5]], + [[47, 29], [47, 5], [51, 29], [51, 5], [52, 29], [52, 5], [53, 29], [53, 5], [54, 29], [54, 5], [55, 29], [55, 5], [56, 29], [56, 5], [57, 29], [57, 5]], + [[61, 29], [61, 5], [65, 29], [65, 5], [95, 29], [95, 5], [98, 29], [98, 5], [100, 29], [100, 5], [102, 29], [102, 5], [103, 29], [103, 5], [104, 29], [104, 5]], + [[108, 29], [108, 5], [109, 29], [109, 5], [110, 29], [110, 5], [112, 29], [112, 5], [114, 29], [114, 5], [117, 29], [117, 5], [58, 0], [66, 0], [67, 0], [68, 0]], + [[69, 0], [70, 0], [71, 0], [72, 0], [73, 0], [74, 0], [75, 0], [76, 0], [77, 0], [78, 0], [79, 0], [80, 0], [81, 0], [82, 0], [83, 0], [84, 0]], + [[47, 25], [47, 26], [47, 27], [47, 6], [51, 25], [51, 26], [51, 27], [51, 6], [52, 25], [52, 26], [52, 27], [52, 6], [53, 25], [53, 26], [53, 27], [53, 6]], + [[48, 29], [48, 5], [49, 29], [49, 5], [50, 29], [50, 5], [97, 29], [97, 5], [99, 29], [99, 5], [101, 29], [101, 5], [105, 29], [105, 5], [111, 29], [111, 5]], + [[115, 29], [115, 5], [116, 29], [116, 5], [32, 0], [37, 0], [45, 0], [46, 0], [47, 0], [51, 0], [52, 0], [53, 0], [54, 0], [55, 0], [56, 0], [57, 0]], + [[61, 0], [65, 0], [95, 0], [98, 0], [100, 0], [102, 0], [103, 0], [104, 0], [108, 0], [109, 0], [110, 0], [112, 0], [114, 0], [117, 0], [nil, 37], [nil, 38]], + [[32, 25], [32, 26], [32, 27], [32, 6], [37, 25], [37, 26], [37, 27], [37, 6], [45, 25], [45, 26], [45, 27], [45, 6], [46, 25], [46, 26], [46, 27], [46, 6]], + [[48, 0], [49, 0], [50, 0], [97, 0], [99, 0], [101, 0], [105, 0], [111, 0], [115, 0], [116, 0], [nil, 55], [nil, 56], [nil, 57], [nil, 58], [nil, 59], [nil, 60]], + [[115, 17], [115, 18], [115, 19], [115, 20], [115, 21], [115, 22], [115, 23], [115, 7], [116, 17], [116, 18], [116, 19], [116, 20], [116, 21], [116, 22], [116, 23], [116, 7]], + [[33, 25], [33, 26], [33, 27], [33, 6], [34, 25], [34, 26], [34, 27], [34, 6], [40, 25], [40, 26], [40, 27], [40, 6], [41, 25], [41, 26], [41, 27], [41, 6]], + [[63, 25], [63, 26], [63, 27], [63, 6], [39, 29], [39, 5], [43, 29], [43, 5], [124, 29], [124, 5], [35, 0], [62, 0], [nil, 77], [nil, 78], [nil, 79], [nil, 80]], + [[38, 17], [38, 18], [38, 19], [38, 20], [38, 21], [38, 22], [38, 23], [38, 7], [42, 17], [42, 18], [42, 19], [42, 20], [42, 21], [42, 22], [42, 23], [42, 7]], + [[44, 17], [44, 18], [44, 19], [44, 20], [44, 21], [44, 22], [44, 23], [44, 7], [59, 17], [59, 18], [59, 19], [59, 20], [59, 21], [59, 22], [59, 23], [59, 7]], + [[88, 17], [88, 18], [88, 19], [88, 20], [88, 21], [88, 22], [88, 23], [88, 7], [90, 17], [90, 18], [90, 19], [90, 20], [90, 21], [90, 22], [90, 23], [90, 7]], + [[105, 17], [105, 18], [105, 19], [105, 20], [105, 21], [105, 22], [105, 23], [105, 7], [111, 17], [111, 18], [111, 19], [111, 20], [111, 21], [111, 22], [111, 23], [111, 7]], + [[58, 17], [58, 18], [58, 19], [58, 20], [58, 21], [58, 22], [58, 23], [58, 7], [66, 17], [66, 18], [66, 19], [66, 20], [66, 21], [66, 22], [66, 23], [66, 7]], + [[67, 17], [67, 18], [67, 19], [67, 20], [67, 21], [67, 22], [67, 23], [67, 7], [68, 17], [68, 18], [68, 19], [68, 20], [68, 21], [68, 22], [68, 23], [68, 7]], + [[69, 17], [69, 18], [69, 19], [69, 20], [69, 21], [69, 22], [69, 23], [69, 7], [70, 17], [70, 18], [70, 19], [70, 20], [70, 21], [70, 22], [70, 23], [70, 7]], + [[71, 17], [71, 18], [71, 19], [71, 20], [71, 21], [71, 22], [71, 23], [71, 7], [72, 17], [72, 18], [72, 19], [72, 20], [72, 21], [72, 22], [72, 23], [72, 7]], + [[73, 17], [73, 18], [73, 19], [73, 20], [73, 21], [73, 22], [73, 23], [73, 7], [74, 17], [74, 18], [74, 19], [74, 20], [74, 21], [74, 22], [74, 23], [74, 7]], + [[75, 17], [75, 18], [75, 19], [75, 20], [75, 21], [75, 22], [75, 23], [75, 7], [76, 17], [76, 18], [76, 19], [76, 20], [76, 21], [76, 22], [76, 23], [76, 7]], + [[77, 17], [77, 18], [77, 19], [77, 20], [77, 21], [77, 22], [77, 23], [77, 7], [78, 17], [78, 18], [78, 19], [78, 20], [78, 21], [78, 22], [78, 23], [78, 7]], + [[79, 17], [79, 18], [79, 19], [79, 20], [79, 21], [79, 22], [79, 23], [79, 7], [80, 17], [80, 18], [80, 19], [80, 20], [80, 21], [80, 22], [80, 23], [80, 7]], + [[81, 17], [81, 18], [81, 19], [81, 20], [81, 21], [81, 22], [81, 23], [81, 7], [82, 17], [82, 18], [82, 19], [82, 20], [82, 21], [82, 22], [82, 23], [82, 7]], + [[83, 17], [83, 18], [83, 19], [83, 20], [83, 21], [83, 22], [83, 23], [83, 7], [84, 17], [84, 18], [84, 19], [84, 20], [84, 21], [84, 22], [84, 23], [84, 7]], + [[85, 17], [85, 18], [85, 19], [85, 20], [85, 21], [85, 22], [85, 23], [85, 7], [86, 17], [86, 18], [86, 19], [86, 20], [86, 21], [86, 22], [86, 23], [86, 7]], + [[87, 17], [87, 18], [87, 19], [87, 20], [87, 21], [87, 22], [87, 23], [87, 7], [89, 17], [89, 18], [89, 19], [89, 20], [89, 21], [89, 22], [89, 23], [89, 7]], + [[106, 17], [106, 18], [106, 19], [106, 20], [106, 21], [106, 22], [106, 23], [106, 7], [107, 17], [107, 18], [107, 19], [107, 20], [107, 21], [107, 22], [107, 23], [107, 7]], + [[113, 17], [113, 18], [113, 19], [113, 20], [113, 21], [113, 22], [113, 23], [113, 7], [118, 17], [118, 18], [118, 19], [118, 20], [118, 21], [118, 22], [118, 23], [118, 7]], + [[119, 17], [119, 18], [119, 19], [119, 20], [119, 21], [119, 22], [119, 23], [119, 7], [120, 17], [120, 18], [120, 19], [120, 20], [120, 21], [120, 22], [120, 23], [120, 7]], + [[121, 17], [121, 18], [121, 19], [121, 20], [121, 21], [121, 22], [121, 23], [121, 7], [122, 17], [122, 18], [122, 19], [122, 20], [122, 21], [122, 22], [122, 23], [122, 7]], + [[38, 25], [38, 26], [38, 27], [38, 6], [42, 25], [42, 26], [42, 27], [42, 6], [44, 25], [44, 26], [44, 27], [44, 6], [59, 25], [59, 26], [59, 27], [59, 6]], + [[99, 17], [99, 18], [99, 19], [99, 20], [99, 21], [99, 22], [99, 23], [99, 7], [101, 17], [101, 18], [101, 19], [101, 20], [101, 21], [101, 22], [101, 23], [101, 7]], + [[32, 17], [32, 18], [32, 19], [32, 20], [32, 21], [32, 22], [32, 23], [32, 7], [37, 17], [37, 18], [37, 19], [37, 20], [37, 21], [37, 22], [37, 23], [37, 7]], + [[45, 17], [45, 18], [45, 19], [45, 20], [45, 21], [45, 22], [45, 23], [45, 7], [46, 17], [46, 18], [46, 19], [46, 20], [46, 21], [46, 22], [46, 23], [46, 7]], + [[47, 17], [47, 18], [47, 19], [47, 20], [47, 21], [47, 22], [47, 23], [47, 7], [51, 17], [51, 18], [51, 19], [51, 20], [51, 21], [51, 22], [51, 23], [51, 7]], + [[52, 17], [52, 18], [52, 19], [52, 20], [52, 21], [52, 22], [52, 23], [52, 7], [53, 17], [53, 18], [53, 19], [53, 20], [53, 21], [53, 22], [53, 23], [53, 7]], + [[54, 17], [54, 18], [54, 19], [54, 20], [54, 21], [54, 22], [54, 23], [54, 7], [55, 17], [55, 18], [55, 19], [55, 20], [55, 21], [55, 22], [55, 23], [55, 7]], + [[56, 17], [56, 18], [56, 19], [56, 20], [56, 21], [56, 22], [56, 23], [56, 7], [57, 17], [57, 18], [57, 19], [57, 20], [57, 21], [57, 22], [57, 23], [57, 7]], + [[61, 17], [61, 18], [61, 19], [61, 20], [61, 21], [61, 22], [61, 23], [61, 7], [65, 17], [65, 18], [65, 19], [65, 20], [65, 21], [65, 22], [65, 23], [65, 7]], + [[95, 17], [95, 18], [95, 19], [95, 20], [95, 21], [95, 22], [95, 23], [95, 7], [98, 17], [98, 18], [98, 19], [98, 20], [98, 21], [98, 22], [98, 23], [98, 7]], + [[100, 17], [100, 18], [100, 19], [100, 20], [100, 21], [100, 22], [100, 23], [100, 7], [102, 17], [102, 18], [102, 19], [102, 20], [102, 21], [102, 22], [102, 23], [102, 7]], + [[103, 17], [103, 18], [103, 19], [103, 20], [103, 21], [103, 22], [103, 23], [103, 7], [104, 17], [104, 18], [104, 19], [104, 20], [104, 21], [104, 22], [104, 23], [104, 7]], + [[108, 17], [108, 18], [108, 19], [108, 20], [108, 21], [108, 22], [108, 23], [108, 7], [109, 17], [109, 18], [109, 19], [109, 20], [109, 21], [109, 22], [109, 23], [109, 7]], + [[110, 17], [110, 18], [110, 19], [110, 20], [110, 21], [110, 22], [110, 23], [110, 7], [112, 17], [112, 18], [112, 19], [112, 20], [112, 21], [112, 22], [112, 23], [112, 7]], + [[114, 17], [114, 18], [114, 19], [114, 20], [114, 21], [114, 22], [114, 23], [114, 7], [117, 17], [117, 18], [117, 19], [117, 20], [117, 21], [117, 22], [117, 23], [117, 7]], + [[58, 25], [58, 26], [58, 27], [58, 6], [66, 25], [66, 26], [66, 27], [66, 6], [67, 25], [67, 26], [67, 27], [67, 6], [68, 25], [68, 26], [68, 27], [68, 6]], + [[69, 25], [69, 26], [69, 27], [69, 6], [70, 25], [70, 26], [70, 27], [70, 6], [71, 25], [71, 26], [71, 27], [71, 6], [72, 25], [72, 26], [72, 27], [72, 6]], + [[73, 25], [73, 26], [73, 27], [73, 6], [74, 25], [74, 26], [74, 27], [74, 6], [75, 25], [75, 26], [75, 27], [75, 6], [76, 25], [76, 26], [76, 27], [76, 6]], + [[77, 25], [77, 26], [77, 27], [77, 6], [78, 25], [78, 26], [78, 27], [78, 6], [79, 25], [79, 26], [79, 27], [79, 6], [80, 25], [80, 26], [80, 27], [80, 6]], + [[81, 25], [81, 26], [81, 27], [81, 6], [82, 25], [82, 26], [82, 27], [82, 6], [83, 25], [83, 26], [83, 27], [83, 6], [84, 25], [84, 26], [84, 27], [84, 6]], + [[85, 25], [85, 26], [85, 27], [85, 6], [86, 25], [86, 26], [86, 27], [86, 6], [87, 25], [87, 26], [87, 27], [87, 6], [89, 25], [89, 26], [89, 27], [89, 6]], + [[106, 25], [106, 26], [106, 27], [106, 6], [107, 25], [107, 26], [107, 27], [107, 6], [113, 25], [113, 26], [113, 27], [113, 6], [118, 25], [118, 26], [118, 27], [118, 6]], + [[119, 25], [119, 26], [119, 27], [119, 6], [120, 25], [120, 26], [120, 27], [120, 6], [121, 25], [121, 26], [121, 27], [121, 6], [122, 25], [122, 26], [122, 27], [122, 6]], + [[50, 17], [50, 18], [50, 19], [50, 20], [50, 21], [50, 22], [50, 23], [50, 7], [97, 17], [97, 18], [97, 19], [97, 20], [97, 21], [97, 22], [97, 23], [97, 7]], + [[0, 17], [0, 18], [0, 19], [0, 20], [0, 21], [0, 22], [0, 23], [0, 7], [36, 17], [36, 18], [36, 19], [36, 20], [36, 21], [36, 22], [36, 23], [36, 7]], + [[64, 17], [64, 18], [64, 19], [64, 20], [64, 21], [64, 22], [64, 23], [64, 7], [91, 17], [91, 18], [91, 19], [91, 20], [91, 21], [91, 22], [91, 23], [91, 7]], + [[93, 17], [93, 18], [93, 19], [93, 20], [93, 21], [93, 22], [93, 23], [93, 7], [126, 17], [126, 18], [126, 19], [126, 20], [126, 21], [126, 22], [126, 23], [126, 7]], + [[94, 25], [94, 26], [94, 27], [94, 6], [125, 25], [125, 26], [125, 27], [125, 6], [60, 29], [60, 5], [96, 29], [96, 5], [123, 29], [123, 5], [nil, 91], [nil, 92]], + [[35, 17], [35, 18], [35, 19], [35, 20], [35, 21], [35, 22], [35, 23], [35, 7], [62, 17], [62, 18], [62, 19], [62, 20], [62, 21], [62, 22], [62, 23], [62, 7]], + [[0, 25], [0, 26], [0, 27], [0, 6], [36, 25], [36, 26], [36, 27], [36, 6], [64, 25], [64, 26], [64, 27], [64, 6], [91, 25], [91, 26], [91, 27], [91, 6]], + [[93, 25], [93, 26], [93, 27], [93, 6], [126, 25], [126, 26], [126, 27], [126, 6], [94, 29], [94, 5], [125, 29], [125, 5], [60, 0], [96, 0], [123, 0], [nil, 93]], + [[39, 17], [39, 18], [39, 19], [39, 20], [39, 21], [39, 22], [39, 23], [39, 7], [43, 17], [43, 18], [43, 19], [43, 20], [43, 21], [43, 22], [43, 23], [43, 7]], + [[124, 17], [124, 18], [124, 19], [124, 20], [124, 21], [124, 22], [124, 23], [124, 7], [35, 25], [35, 26], [35, 27], [35, 6], [62, 25], [62, 26], [62, 27], [62, 6]], + [[0, 29], [0, 5], [36, 29], [36, 5], [64, 29], [64, 5], [91, 29], [91, 5], [93, 29], [93, 5], [126, 29], [126, 5], [94, 0], [125, 0], [nil, 94], [nil, 95]], + [[33, 17], [33, 18], [33, 19], [33, 20], [33, 21], [33, 22], [33, 23], [33, 7], [34, 17], [34, 18], [34, 19], [34, 20], [34, 21], [34, 22], [34, 23], [34, 7]], + [[40, 17], [40, 18], [40, 19], [40, 20], [40, 21], [40, 22], [40, 23], [40, 7], [41, 17], [41, 18], [41, 19], [41, 20], [41, 21], [41, 22], [41, 23], [41, 7]], + [[63, 17], [63, 18], [63, 19], [63, 20], [63, 21], [63, 22], [63, 23], [63, 7], [39, 25], [39, 26], [39, 27], [39, 6], [43, 25], [43, 26], [43, 27], [43, 6]], + [[124, 25], [124, 26], [124, 27], [124, 6], [35, 29], [35, 5], [62, 29], [62, 5], [0, 0], [36, 0], [64, 0], [91, 0], [93, 0], [126, 0], [nil, 96], [nil, 97]], + [[92, 29], [92, 5], [195, 29], [195, 5], [208, 29], [208, 5], [128, 0], [130, 0], [131, 0], [162, 0], [184, 0], [194, 0], [224, 0], [226, 0], [nil, 98], [nil, 99]], + [[nil, 100], [nil, 101], [nil, 102], [nil, 103], [nil, 104], [nil, 105], [nil, 106], [nil, 107], [nil, 108], [nil, 109], [nil, 110], [nil, 111], [nil, 112], [nil, 113], [nil, 114], [nil, 115]], + [[92, 0], [195, 0], [208, 0], [nil, 116], [nil, 117], [nil, 118], [nil, 119], [nil, 120], [nil, 121], [nil, 122], [nil, 123], [nil, 124], [nil, 125], [nil, 126], [nil, 127], [nil, 128]], + [[60, 17], [60, 18], [60, 19], [60, 20], [60, 21], [60, 22], [60, 23], [60, 7], [96, 17], [96, 18], [96, 19], [96, 20], [96, 21], [96, 22], [96, 23], [96, 7]], + [[123, 17], [123, 18], [123, 19], [123, 20], [123, 21], [123, 22], [123, 23], [123, 7], [nil, 129], [nil, 130], [nil, 131], [nil, 132], [nil, 133], [nil, 134], [nil, 135], [nil, 136]], + [[94, 17], [94, 18], [94, 19], [94, 20], [94, 21], [94, 22], [94, 23], [94, 7], [125, 17], [125, 18], [125, 19], [125, 20], [125, 21], [125, 22], [125, 23], [125, 7]], + [[60, 25], [60, 26], [60, 27], [60, 6], [96, 25], [96, 26], [96, 27], [96, 6], [123, 25], [123, 26], [123, 27], [123, 6], [nil, 137], [nil, 138], [nil, 139], [nil, 140]], + [[153, 17], [153, 18], [153, 19], [153, 20], [153, 21], [153, 22], [153, 23], [153, 7], [161, 17], [161, 18], [161, 19], [161, 20], [161, 21], [161, 22], [161, 23], [161, 7]], + [[167, 17], [167, 18], [167, 19], [167, 20], [167, 21], [167, 22], [167, 23], [167, 7], [172, 17], [172, 18], [172, 19], [172, 20], [172, 21], [172, 22], [172, 23], [172, 7]], + [[176, 17], [176, 18], [176, 19], [176, 20], [176, 21], [176, 22], [176, 23], [176, 7], [177, 17], [177, 18], [177, 19], [177, 20], [177, 21], [177, 22], [177, 23], [177, 7]], + [[179, 17], [179, 18], [179, 19], [179, 20], [179, 21], [179, 22], [179, 23], [179, 7], [209, 17], [209, 18], [209, 19], [209, 20], [209, 21], [209, 22], [209, 23], [209, 7]], + [[216, 17], [216, 18], [216, 19], [216, 20], [216, 21], [216, 22], [216, 23], [216, 7], [217, 17], [217, 18], [217, 19], [217, 20], [217, 21], [217, 22], [217, 23], [217, 7]], + [[227, 17], [227, 18], [227, 19], [227, 20], [227, 21], [227, 22], [227, 23], [227, 7], [229, 17], [229, 18], [229, 19], [229, 20], [229, 21], [229, 22], [229, 23], [229, 7]], + [[230, 17], [230, 18], [230, 19], [230, 20], [230, 21], [230, 22], [230, 23], [230, 7], [129, 25], [129, 26], [129, 27], [129, 6], [132, 25], [132, 26], [132, 27], [132, 6]], + [[133, 25], [133, 26], [133, 27], [133, 6], [134, 25], [134, 26], [134, 27], [134, 6], [136, 25], [136, 26], [136, 27], [136, 6], [146, 25], [146, 26], [146, 27], [146, 6]], + [[154, 25], [154, 26], [154, 27], [154, 6], [156, 25], [156, 26], [156, 27], [156, 6], [160, 25], [160, 26], [160, 27], [160, 6], [163, 25], [163, 26], [163, 27], [163, 6]], + [[164, 25], [164, 26], [164, 27], [164, 6], [169, 25], [169, 26], [169, 27], [169, 6], [170, 25], [170, 26], [170, 27], [170, 6], [173, 25], [173, 26], [173, 27], [173, 6]], + [[178, 25], [178, 26], [178, 27], [178, 6], [181, 25], [181, 26], [181, 27], [181, 6], [185, 25], [185, 26], [185, 27], [185, 6], [186, 25], [186, 26], [186, 27], [186, 6]], + [[187, 25], [187, 26], [187, 27], [187, 6], [189, 25], [189, 26], [189, 27], [189, 6], [190, 25], [190, 26], [190, 27], [190, 6], [196, 25], [196, 26], [196, 27], [196, 6]], + [[198, 25], [198, 26], [198, 27], [198, 6], [228, 25], [228, 26], [228, 27], [228, 6], [232, 25], [232, 26], [232, 27], [232, 6], [233, 25], [233, 26], [233, 27], [233, 6]], + [[1, 29], [1, 5], [135, 29], [135, 5], [137, 29], [137, 5], [138, 29], [138, 5], [139, 29], [139, 5], [140, 29], [140, 5], [141, 29], [141, 5], [143, 29], [143, 5]], + [[147, 29], [147, 5], [149, 29], [149, 5], [150, 29], [150, 5], [151, 29], [151, 5], [152, 29], [152, 5], [155, 29], [155, 5], [157, 29], [157, 5], [158, 29], [158, 5]], + [[165, 29], [165, 5], [166, 29], [166, 5], [168, 29], [168, 5], [174, 29], [174, 5], [175, 29], [175, 5], [180, 29], [180, 5], [182, 29], [182, 5], [183, 29], [183, 5]], + [[188, 29], [188, 5], [191, 29], [191, 5], [197, 29], [197, 5], [231, 29], [231, 5], [239, 29], [239, 5], [9, 0], [142, 0], [144, 0], [145, 0], [148, 0], [159, 0]], + [[171, 0], [206, 0], [215, 0], [225, 0], [236, 0], [237, 0], [nil, 141], [nil, 142], [nil, 143], [nil, 144], [nil, 145], [nil, 146], [nil, 147], [nil, 148], [nil, 149], [nil, 150]], + [[128, 17], [128, 18], [128, 19], [128, 20], [128, 21], [128, 22], [128, 23], [128, 7], [130, 17], [130, 18], [130, 19], [130, 20], [130, 21], [130, 22], [130, 23], [130, 7]], + [[131, 17], [131, 18], [131, 19], [131, 20], [131, 21], [131, 22], [131, 23], [131, 7], [162, 17], [162, 18], [162, 19], [162, 20], [162, 21], [162, 22], [162, 23], [162, 7]], + [[184, 17], [184, 18], [184, 19], [184, 20], [184, 21], [184, 22], [184, 23], [184, 7], [194, 17], [194, 18], [194, 19], [194, 20], [194, 21], [194, 22], [194, 23], [194, 7]], + [[224, 17], [224, 18], [224, 19], [224, 20], [224, 21], [224, 22], [224, 23], [224, 7], [226, 17], [226, 18], [226, 19], [226, 20], [226, 21], [226, 22], [226, 23], [226, 7]], + [[153, 25], [153, 26], [153, 27], [153, 6], [161, 25], [161, 26], [161, 27], [161, 6], [167, 25], [167, 26], [167, 27], [167, 6], [172, 25], [172, 26], [172, 27], [172, 6]], + [[176, 25], [176, 26], [176, 27], [176, 6], [177, 25], [177, 26], [177, 27], [177, 6], [179, 25], [179, 26], [179, 27], [179, 6], [209, 25], [209, 26], [209, 27], [209, 6]], + [[216, 25], [216, 26], [216, 27], [216, 6], [217, 25], [217, 26], [217, 27], [217, 6], [227, 25], [227, 26], [227, 27], [227, 6], [229, 25], [229, 26], [229, 27], [229, 6]], + [[230, 25], [230, 26], [230, 27], [230, 6], [129, 29], [129, 5], [132, 29], [132, 5], [133, 29], [133, 5], [134, 29], [134, 5], [136, 29], [136, 5], [146, 29], [146, 5]], + [[154, 29], [154, 5], [156, 29], [156, 5], [160, 29], [160, 5], [163, 29], [163, 5], [164, 29], [164, 5], [169, 29], [169, 5], [170, 29], [170, 5], [173, 29], [173, 5]], + [[178, 29], [178, 5], [181, 29], [181, 5], [185, 29], [185, 5], [186, 29], [186, 5], [187, 29], [187, 5], [189, 29], [189, 5], [190, 29], [190, 5], [196, 29], [196, 5]], + [[198, 29], [198, 5], [228, 29], [228, 5], [232, 29], [232, 5], [233, 29], [233, 5], [1, 0], [135, 0], [137, 0], [138, 0], [139, 0], [140, 0], [141, 0], [143, 0]], + [[147, 0], [149, 0], [150, 0], [151, 0], [152, 0], [155, 0], [157, 0], [158, 0], [165, 0], [166, 0], [168, 0], [174, 0], [175, 0], [180, 0], [182, 0], [183, 0]], + [[188, 0], [191, 0], [197, 0], [231, 0], [239, 0], [nil, 151], [nil, 152], [nil, 153], [nil, 154], [nil, 155], [nil, 156], [nil, 157], [nil, 158], [nil, 159], [nil, 160], [nil, 161]], + [[92, 17], [92, 18], [92, 19], [92, 20], [92, 21], [92, 22], [92, 23], [92, 7], [195, 17], [195, 18], [195, 19], [195, 20], [195, 21], [195, 22], [195, 23], [195, 7]], + [[208, 17], [208, 18], [208, 19], [208, 20], [208, 21], [208, 22], [208, 23], [208, 7], [128, 25], [128, 26], [128, 27], [128, 6], [130, 25], [130, 26], [130, 27], [130, 6]], + [[131, 25], [131, 26], [131, 27], [131, 6], [162, 25], [162, 26], [162, 27], [162, 6], [184, 25], [184, 26], [184, 27], [184, 6], [194, 25], [194, 26], [194, 27], [194, 6]], + [[224, 25], [224, 26], [224, 27], [224, 6], [226, 25], [226, 26], [226, 27], [226, 6], [153, 29], [153, 5], [161, 29], [161, 5], [167, 29], [167, 5], [172, 29], [172, 5]], + [[176, 29], [176, 5], [177, 29], [177, 5], [179, 29], [179, 5], [209, 29], [209, 5], [216, 29], [216, 5], [217, 29], [217, 5], [227, 29], [227, 5], [229, 29], [229, 5]], + [[230, 29], [230, 5], [129, 0], [132, 0], [133, 0], [134, 0], [136, 0], [146, 0], [154, 0], [156, 0], [160, 0], [163, 0], [164, 0], [169, 0], [170, 0], [173, 0]], + [[178, 0], [181, 0], [185, 0], [186, 0], [187, 0], [189, 0], [190, 0], [196, 0], [198, 0], [228, 0], [232, 0], [233, 0], [nil, 162], [nil, 163], [nil, 164], [nil, 165]], + [[nil, 166], [nil, 167], [nil, 168], [nil, 169], [nil, 170], [nil, 171], [nil, 172], [nil, 173], [nil, 174], [nil, 175], [nil, 176], [nil, 177], [nil, 178], [nil, 179], [nil, 180], [nil, 181]], + [[92, 25], [92, 26], [92, 27], [92, 6], [195, 25], [195, 26], [195, 27], [195, 6], [208, 25], [208, 26], [208, 27], [208, 6], [128, 29], [128, 5], [130, 29], [130, 5]], + [[131, 29], [131, 5], [162, 29], [162, 5], [184, 29], [184, 5], [194, 29], [194, 5], [224, 29], [224, 5], [226, 29], [226, 5], [153, 0], [161, 0], [167, 0], [172, 0]], + [[176, 0], [177, 0], [179, 0], [209, 0], [216, 0], [217, 0], [227, 0], [229, 0], [230, 0], [nil, 182], [nil, 183], [nil, 184], [nil, 185], [nil, 186], [nil, 187], [nil, 188]], + [[nil, 189], [nil, 190], [nil, 191], [nil, 192], [nil, 193], [nil, 194], [nil, 195], [nil, 196], [nil, 197], [nil, 198], [nil, 199], [nil, 200], [nil, 201], [nil, 202], [nil, 203], [nil, 204]], + [[199, 17], [199, 18], [199, 19], [199, 20], [199, 21], [199, 22], [199, 23], [199, 7], [207, 17], [207, 18], [207, 19], [207, 20], [207, 21], [207, 22], [207, 23], [207, 7]], + [[234, 17], [234, 18], [234, 19], [234, 20], [234, 21], [234, 22], [234, 23], [234, 7], [235, 17], [235, 18], [235, 19], [235, 20], [235, 21], [235, 22], [235, 23], [235, 7]], + [[192, 25], [192, 26], [192, 27], [192, 6], [193, 25], [193, 26], [193, 27], [193, 6], [200, 25], [200, 26], [200, 27], [200, 6], [201, 25], [201, 26], [201, 27], [201, 6]], + [[202, 25], [202, 26], [202, 27], [202, 6], [205, 25], [205, 26], [205, 27], [205, 6], [210, 25], [210, 26], [210, 27], [210, 6], [213, 25], [213, 26], [213, 27], [213, 6]], + [[218, 25], [218, 26], [218, 27], [218, 6], [219, 25], [219, 26], [219, 27], [219, 6], [238, 25], [238, 26], [238, 27], [238, 6], [240, 25], [240, 26], [240, 27], [240, 6]], + [[242, 25], [242, 26], [242, 27], [242, 6], [243, 25], [243, 26], [243, 27], [243, 6], [255, 25], [255, 26], [255, 27], [255, 6], [203, 29], [203, 5], [204, 29], [204, 5]], + [[211, 29], [211, 5], [212, 29], [212, 5], [214, 29], [214, 5], [221, 29], [221, 5], [222, 29], [222, 5], [223, 29], [223, 5], [241, 29], [241, 5], [244, 29], [244, 5]], + [[245, 29], [245, 5], [246, 29], [246, 5], [247, 29], [247, 5], [248, 29], [248, 5], [250, 29], [250, 5], [251, 29], [251, 5], [252, 29], [252, 5], [253, 29], [253, 5]], + [[254, 29], [254, 5], [2, 0], [3, 0], [4, 0], [5, 0], [6, 0], [7, 0], [8, 0], [11, 0], [12, 0], [14, 0], [15, 0], [16, 0], [17, 0], [18, 0]], + [[19, 0], [20, 0], [21, 0], [23, 0], [24, 0], [25, 0], [26, 0], [27, 0], [28, 0], [29, 0], [30, 0], [31, 0], [127, 0], [220, 0], [249, 0], [nil, 205]], + [[9, 17], [9, 18], [9, 19], [9, 20], [9, 21], [9, 22], [9, 23], [9, 7], [142, 17], [142, 18], [142, 19], [142, 20], [142, 21], [142, 22], [142, 23], [142, 7]], + [[144, 17], [144, 18], [144, 19], [144, 20], [144, 21], [144, 22], [144, 23], [144, 7], [145, 17], [145, 18], [145, 19], [145, 20], [145, 21], [145, 22], [145, 23], [145, 7]], + [[148, 17], [148, 18], [148, 19], [148, 20], [148, 21], [148, 22], [148, 23], [148, 7], [159, 17], [159, 18], [159, 19], [159, 20], [159, 21], [159, 22], [159, 23], [159, 7]], + [[171, 17], [171, 18], [171, 19], [171, 20], [171, 21], [171, 22], [171, 23], [171, 7], [206, 17], [206, 18], [206, 19], [206, 20], [206, 21], [206, 22], [206, 23], [206, 7]], + [[215, 17], [215, 18], [215, 19], [215, 20], [215, 21], [215, 22], [215, 23], [215, 7], [225, 17], [225, 18], [225, 19], [225, 20], [225, 21], [225, 22], [225, 23], [225, 7]], + [[236, 17], [236, 18], [236, 19], [236, 20], [236, 21], [236, 22], [236, 23], [236, 7], [237, 17], [237, 18], [237, 19], [237, 20], [237, 21], [237, 22], [237, 23], [237, 7]], + [[199, 25], [199, 26], [199, 27], [199, 6], [207, 25], [207, 26], [207, 27], [207, 6], [234, 25], [234, 26], [234, 27], [234, 6], [235, 25], [235, 26], [235, 27], [235, 6]], + [[192, 29], [192, 5], [193, 29], [193, 5], [200, 29], [200, 5], [201, 29], [201, 5], [202, 29], [202, 5], [205, 29], [205, 5], [210, 29], [210, 5], [213, 29], [213, 5]], + [[218, 29], [218, 5], [219, 29], [219, 5], [238, 29], [238, 5], [240, 29], [240, 5], [242, 29], [242, 5], [243, 29], [243, 5], [255, 29], [255, 5], [203, 0], [204, 0]], + [[211, 0], [212, 0], [214, 0], [221, 0], [222, 0], [223, 0], [241, 0], [244, 0], [245, 0], [246, 0], [247, 0], [248, 0], [250, 0], [251, 0], [252, 0], [253, 0]], + [[254, 0], [nil, 206], [nil, 207], [nil, 208], [nil, 209], [nil, 210], [nil, 211], [nil, 212], [nil, 213], [nil, 214], [nil, 215], [nil, 216], [nil, 217], [nil, 218], [nil, 219], [nil, 220]], + [[1, 17], [1, 18], [1, 19], [1, 20], [1, 21], [1, 22], [1, 23], [1, 7], [135, 17], [135, 18], [135, 19], [135, 20], [135, 21], [135, 22], [135, 23], [135, 7]], + [[137, 17], [137, 18], [137, 19], [137, 20], [137, 21], [137, 22], [137, 23], [137, 7], [138, 17], [138, 18], [138, 19], [138, 20], [138, 21], [138, 22], [138, 23], [138, 7]], + [[139, 17], [139, 18], [139, 19], [139, 20], [139, 21], [139, 22], [139, 23], [139, 7], [140, 17], [140, 18], [140, 19], [140, 20], [140, 21], [140, 22], [140, 23], [140, 7]], + [[141, 17], [141, 18], [141, 19], [141, 20], [141, 21], [141, 22], [141, 23], [141, 7], [143, 17], [143, 18], [143, 19], [143, 20], [143, 21], [143, 22], [143, 23], [143, 7]], + [[147, 17], [147, 18], [147, 19], [147, 20], [147, 21], [147, 22], [147, 23], [147, 7], [149, 17], [149, 18], [149, 19], [149, 20], [149, 21], [149, 22], [149, 23], [149, 7]], + [[150, 17], [150, 18], [150, 19], [150, 20], [150, 21], [150, 22], [150, 23], [150, 7], [151, 17], [151, 18], [151, 19], [151, 20], [151, 21], [151, 22], [151, 23], [151, 7]], + [[152, 17], [152, 18], [152, 19], [152, 20], [152, 21], [152, 22], [152, 23], [152, 7], [155, 17], [155, 18], [155, 19], [155, 20], [155, 21], [155, 22], [155, 23], [155, 7]], + [[157, 17], [157, 18], [157, 19], [157, 20], [157, 21], [157, 22], [157, 23], [157, 7], [158, 17], [158, 18], [158, 19], [158, 20], [158, 21], [158, 22], [158, 23], [158, 7]], + [[165, 17], [165, 18], [165, 19], [165, 20], [165, 21], [165, 22], [165, 23], [165, 7], [166, 17], [166, 18], [166, 19], [166, 20], [166, 21], [166, 22], [166, 23], [166, 7]], + [[168, 17], [168, 18], [168, 19], [168, 20], [168, 21], [168, 22], [168, 23], [168, 7], [174, 17], [174, 18], [174, 19], [174, 20], [174, 21], [174, 22], [174, 23], [174, 7]], + [[175, 17], [175, 18], [175, 19], [175, 20], [175, 21], [175, 22], [175, 23], [175, 7], [180, 17], [180, 18], [180, 19], [180, 20], [180, 21], [180, 22], [180, 23], [180, 7]], + [[182, 17], [182, 18], [182, 19], [182, 20], [182, 21], [182, 22], [182, 23], [182, 7], [183, 17], [183, 18], [183, 19], [183, 20], [183, 21], [183, 22], [183, 23], [183, 7]], + [[188, 17], [188, 18], [188, 19], [188, 20], [188, 21], [188, 22], [188, 23], [188, 7], [191, 17], [191, 18], [191, 19], [191, 20], [191, 21], [191, 22], [191, 23], [191, 7]], + [[197, 17], [197, 18], [197, 19], [197, 20], [197, 21], [197, 22], [197, 23], [197, 7], [231, 17], [231, 18], [231, 19], [231, 20], [231, 21], [231, 22], [231, 23], [231, 7]], + [[239, 17], [239, 18], [239, 19], [239, 20], [239, 21], [239, 22], [239, 23], [239, 7], [9, 25], [9, 26], [9, 27], [9, 6], [142, 25], [142, 26], [142, 27], [142, 6]], + [[144, 25], [144, 26], [144, 27], [144, 6], [145, 25], [145, 26], [145, 27], [145, 6], [148, 25], [148, 26], [148, 27], [148, 6], [159, 25], [159, 26], [159, 27], [159, 6]], + [[171, 25], [171, 26], [171, 27], [171, 6], [206, 25], [206, 26], [206, 27], [206, 6], [215, 25], [215, 26], [215, 27], [215, 6], [225, 25], [225, 26], [225, 27], [225, 6]], + [[236, 25], [236, 26], [236, 27], [236, 6], [237, 25], [237, 26], [237, 27], [237, 6], [199, 29], [199, 5], [207, 29], [207, 5], [234, 29], [234, 5], [235, 29], [235, 5]], + [[192, 0], [193, 0], [200, 0], [201, 0], [202, 0], [205, 0], [210, 0], [213, 0], [218, 0], [219, 0], [238, 0], [240, 0], [242, 0], [243, 0], [255, 0], [nil, 8]], + [[nil, 222], [nil, 223], [nil, 224], [nil, 225], [nil, 226], [nil, 227], [nil, 228], [nil, 229], [nil, 230], [nil, 231], [nil, 232], [nil, 233], [nil, 234], [nil, 235], [nil, 236], [nil, 237]], + [[129, 17], [129, 18], [129, 19], [129, 20], [129, 21], [129, 22], [129, 23], [129, 7], [132, 17], [132, 18], [132, 19], [132, 20], [132, 21], [132, 22], [132, 23], [132, 7]], + [[133, 17], [133, 18], [133, 19], [133, 20], [133, 21], [133, 22], [133, 23], [133, 7], [134, 17], [134, 18], [134, 19], [134, 20], [134, 21], [134, 22], [134, 23], [134, 7]], + [[136, 17], [136, 18], [136, 19], [136, 20], [136, 21], [136, 22], [136, 23], [136, 7], [146, 17], [146, 18], [146, 19], [146, 20], [146, 21], [146, 22], [146, 23], [146, 7]], + [[154, 17], [154, 18], [154, 19], [154, 20], [154, 21], [154, 22], [154, 23], [154, 7], [156, 17], [156, 18], [156, 19], [156, 20], [156, 21], [156, 22], [156, 23], [156, 7]], + [[160, 17], [160, 18], [160, 19], [160, 20], [160, 21], [160, 22], [160, 23], [160, 7], [163, 17], [163, 18], [163, 19], [163, 20], [163, 21], [163, 22], [163, 23], [163, 7]], + [[164, 17], [164, 18], [164, 19], [164, 20], [164, 21], [164, 22], [164, 23], [164, 7], [169, 17], [169, 18], [169, 19], [169, 20], [169, 21], [169, 22], [169, 23], [169, 7]], + [[170, 17], [170, 18], [170, 19], [170, 20], [170, 21], [170, 22], [170, 23], [170, 7], [173, 17], [173, 18], [173, 19], [173, 20], [173, 21], [173, 22], [173, 23], [173, 7]], + [[178, 17], [178, 18], [178, 19], [178, 20], [178, 21], [178, 22], [178, 23], [178, 7], [181, 17], [181, 18], [181, 19], [181, 20], [181, 21], [181, 22], [181, 23], [181, 7]], + [[185, 17], [185, 18], [185, 19], [185, 20], [185, 21], [185, 22], [185, 23], [185, 7], [186, 17], [186, 18], [186, 19], [186, 20], [186, 21], [186, 22], [186, 23], [186, 7]], + [[187, 17], [187, 18], [187, 19], [187, 20], [187, 21], [187, 22], [187, 23], [187, 7], [189, 17], [189, 18], [189, 19], [189, 20], [189, 21], [189, 22], [189, 23], [189, 7]], + [[190, 17], [190, 18], [190, 19], [190, 20], [190, 21], [190, 22], [190, 23], [190, 7], [196, 17], [196, 18], [196, 19], [196, 20], [196, 21], [196, 22], [196, 23], [196, 7]], + [[198, 17], [198, 18], [198, 19], [198, 20], [198, 21], [198, 22], [198, 23], [198, 7], [228, 17], [228, 18], [228, 19], [228, 20], [228, 21], [228, 22], [228, 23], [228, 7]], + [[232, 17], [232, 18], [232, 19], [232, 20], [232, 21], [232, 22], [232, 23], [232, 7], [233, 17], [233, 18], [233, 19], [233, 20], [233, 21], [233, 22], [233, 23], [233, 7]], + [[1, 25], [1, 26], [1, 27], [1, 6], [135, 25], [135, 26], [135, 27], [135, 6], [137, 25], [137, 26], [137, 27], [137, 6], [138, 25], [138, 26], [138, 27], [138, 6]], + [[139, 25], [139, 26], [139, 27], [139, 6], [140, 25], [140, 26], [140, 27], [140, 6], [141, 25], [141, 26], [141, 27], [141, 6], [143, 25], [143, 26], [143, 27], [143, 6]], + [[147, 25], [147, 26], [147, 27], [147, 6], [149, 25], [149, 26], [149, 27], [149, 6], [150, 25], [150, 26], [150, 27], [150, 6], [151, 25], [151, 26], [151, 27], [151, 6]], + [[152, 25], [152, 26], [152, 27], [152, 6], [155, 25], [155, 26], [155, 27], [155, 6], [157, 25], [157, 26], [157, 27], [157, 6], [158, 25], [158, 26], [158, 27], [158, 6]], + [[165, 25], [165, 26], [165, 27], [165, 6], [166, 25], [166, 26], [166, 27], [166, 6], [168, 25], [168, 26], [168, 27], [168, 6], [174, 25], [174, 26], [174, 27], [174, 6]], + [[175, 25], [175, 26], [175, 27], [175, 6], [180, 25], [180, 26], [180, 27], [180, 6], [182, 25], [182, 26], [182, 27], [182, 6], [183, 25], [183, 26], [183, 27], [183, 6]], + [[188, 25], [188, 26], [188, 27], [188, 6], [191, 25], [191, 26], [191, 27], [191, 6], [197, 25], [197, 26], [197, 27], [197, 6], [231, 25], [231, 26], [231, 27], [231, 6]], + [[239, 25], [239, 26], [239, 27], [239, 6], [9, 29], [9, 5], [142, 29], [142, 5], [144, 29], [144, 5], [145, 29], [145, 5], [148, 29], [148, 5], [159, 29], [159, 5]], + [[171, 29], [171, 5], [206, 29], [206, 5], [215, 29], [215, 5], [225, 29], [225, 5], [236, 29], [236, 5], [237, 29], [237, 5], [199, 0], [207, 0], [234, 0], [235, 0]], + [[nil, 238], [nil, 239], [nil, 240], [nil, 241], [nil, 242], [nil, 243], [nil, 244], [nil, 245], [nil, 246], [nil, 247], [nil, 248], [nil, 249], [nil, 250], [nil, 251], [nil, 252], [nil, 253]], + [[10, 25], [10, 26], [10, 27], [10, 6], [13, 25], [13, 26], [13, 27], [13, 6], [22, 25], [22, 26], [22, 27], [22, 6], [256, 25], [256, 26], [256, 27], [256, 6]], + [[2, 17], [2, 18], [2, 19], [2, 20], [2, 21], [2, 22], [2, 23], [2, 7], [3, 17], [3, 18], [3, 19], [3, 20], [3, 21], [3, 22], [3, 23], [3, 7]], + [[4, 17], [4, 18], [4, 19], [4, 20], [4, 21], [4, 22], [4, 23], [4, 7], [5, 17], [5, 18], [5, 19], [5, 20], [5, 21], [5, 22], [5, 23], [5, 7]], + [[6, 17], [6, 18], [6, 19], [6, 20], [6, 21], [6, 22], [6, 23], [6, 7], [7, 17], [7, 18], [7, 19], [7, 20], [7, 21], [7, 22], [7, 23], [7, 7]], + [[8, 17], [8, 18], [8, 19], [8, 20], [8, 21], [8, 22], [8, 23], [8, 7], [11, 17], [11, 18], [11, 19], [11, 20], [11, 21], [11, 22], [11, 23], [11, 7]], + [[12, 17], [12, 18], [12, 19], [12, 20], [12, 21], [12, 22], [12, 23], [12, 7], [14, 17], [14, 18], [14, 19], [14, 20], [14, 21], [14, 22], [14, 23], [14, 7]], + [[15, 17], [15, 18], [15, 19], [15, 20], [15, 21], [15, 22], [15, 23], [15, 7], [16, 17], [16, 18], [16, 19], [16, 20], [16, 21], [16, 22], [16, 23], [16, 7]], + [[17, 17], [17, 18], [17, 19], [17, 20], [17, 21], [17, 22], [17, 23], [17, 7], [18, 17], [18, 18], [18, 19], [18, 20], [18, 21], [18, 22], [18, 23], [18, 7]], + [[19, 17], [19, 18], [19, 19], [19, 20], [19, 21], [19, 22], [19, 23], [19, 7], [20, 17], [20, 18], [20, 19], [20, 20], [20, 21], [20, 22], [20, 23], [20, 7]], + [[21, 17], [21, 18], [21, 19], [21, 20], [21, 21], [21, 22], [21, 23], [21, 7], [23, 17], [23, 18], [23, 19], [23, 20], [23, 21], [23, 22], [23, 23], [23, 7]], + [[24, 17], [24, 18], [24, 19], [24, 20], [24, 21], [24, 22], [24, 23], [24, 7], [25, 17], [25, 18], [25, 19], [25, 20], [25, 21], [25, 22], [25, 23], [25, 7]], + [[26, 17], [26, 18], [26, 19], [26, 20], [26, 21], [26, 22], [26, 23], [26, 7], [27, 17], [27, 18], [27, 19], [27, 20], [27, 21], [27, 22], [27, 23], [27, 7]], + [[28, 17], [28, 18], [28, 19], [28, 20], [28, 21], [28, 22], [28, 23], [28, 7], [29, 17], [29, 18], [29, 19], [29, 20], [29, 21], [29, 22], [29, 23], [29, 7]], + [[30, 17], [30, 18], [30, 19], [30, 20], [30, 21], [30, 22], [30, 23], [30, 7], [31, 17], [31, 18], [31, 19], [31, 20], [31, 21], [31, 22], [31, 23], [31, 7]], + [[127, 17], [127, 18], [127, 19], [127, 20], [127, 21], [127, 22], [127, 23], [127, 7], [220, 17], [220, 18], [220, 19], [220, 20], [220, 21], [220, 22], [220, 23], [220, 7]], + [[249, 17], [249, 18], [249, 19], [249, 20], [249, 21], [249, 22], [249, 23], [249, 7], [10, 29], [10, 5], [13, 29], [13, 5], [22, 29], [22, 5], [256, 29], [256, 5]], + [[54, 25], [54, 26], [54, 27], [54, 6], [55, 25], [55, 26], [55, 27], [55, 6], [56, 25], [56, 26], [56, 27], [56, 6], [57, 25], [57, 26], [57, 27], [57, 6]], + [[211, 17], [211, 18], [211, 19], [211, 20], [211, 21], [211, 22], [211, 23], [211, 7], [212, 17], [212, 18], [212, 19], [212, 20], [212, 21], [212, 22], [212, 23], [212, 7]], + [[214, 17], [214, 18], [214, 19], [214, 20], [214, 21], [214, 22], [214, 23], [214, 7], [221, 17], [221, 18], [221, 19], [221, 20], [221, 21], [221, 22], [221, 23], [221, 7]], + [[222, 17], [222, 18], [222, 19], [222, 20], [222, 21], [222, 22], [222, 23], [222, 7], [223, 17], [223, 18], [223, 19], [223, 20], [223, 21], [223, 22], [223, 23], [223, 7]], + [[241, 17], [241, 18], [241, 19], [241, 20], [241, 21], [241, 22], [241, 23], [241, 7], [244, 17], [244, 18], [244, 19], [244, 20], [244, 21], [244, 22], [244, 23], [244, 7]], + [[245, 17], [245, 18], [245, 19], [245, 20], [245, 21], [245, 22], [245, 23], [245, 7], [246, 17], [246, 18], [246, 19], [246, 20], [246, 21], [246, 22], [246, 23], [246, 7]], + [[247, 17], [247, 18], [247, 19], [247, 20], [247, 21], [247, 22], [247, 23], [247, 7], [248, 17], [248, 18], [248, 19], [248, 20], [248, 21], [248, 22], [248, 23], [248, 7]], + [[250, 17], [250, 18], [250, 19], [250, 20], [250, 21], [250, 22], [250, 23], [250, 7], [251, 17], [251, 18], [251, 19], [251, 20], [251, 21], [251, 22], [251, 23], [251, 7]], + [[252, 17], [252, 18], [252, 19], [252, 20], [252, 21], [252, 22], [252, 23], [252, 7], [253, 17], [253, 18], [253, 19], [253, 20], [253, 21], [253, 22], [253, 23], [253, 7]], + [[254, 17], [254, 18], [254, 19], [254, 20], [254, 21], [254, 22], [254, 23], [254, 7], [2, 25], [2, 26], [2, 27], [2, 6], [3, 25], [3, 26], [3, 27], [3, 6]], + [[4, 25], [4, 26], [4, 27], [4, 6], [5, 25], [5, 26], [5, 27], [5, 6], [6, 25], [6, 26], [6, 27], [6, 6], [7, 25], [7, 26], [7, 27], [7, 6]], + [[8, 25], [8, 26], [8, 27], [8, 6], [11, 25], [11, 26], [11, 27], [11, 6], [12, 25], [12, 26], [12, 27], [12, 6], [14, 25], [14, 26], [14, 27], [14, 6]], + [[15, 25], [15, 26], [15, 27], [15, 6], [16, 25], [16, 26], [16, 27], [16, 6], [17, 25], [17, 26], [17, 27], [17, 6], [18, 25], [18, 26], [18, 27], [18, 6]], + [[19, 25], [19, 26], [19, 27], [19, 6], [20, 25], [20, 26], [20, 27], [20, 6], [21, 25], [21, 26], [21, 27], [21, 6], [23, 25], [23, 26], [23, 27], [23, 6]], + [[24, 25], [24, 26], [24, 27], [24, 6], [25, 25], [25, 26], [25, 27], [25, 6], [26, 25], [26, 26], [26, 27], [26, 6], [27, 25], [27, 26], [27, 27], [27, 6]], + [[28, 25], [28, 26], [28, 27], [28, 6], [29, 25], [29, 26], [29, 27], [29, 6], [30, 25], [30, 26], [30, 27], [30, 6], [31, 25], [31, 26], [31, 27], [31, 6]], + [[127, 25], [127, 26], [127, 27], [127, 6], [220, 25], [220, 26], [220, 27], [220, 6], [249, 25], [249, 26], [249, 27], [249, 6], [10, 0], [13, 0], [22, 0], [256, 0]], + [[192, 17], [192, 18], [192, 19], [192, 20], [192, 21], [192, 22], [192, 23], [192, 7], [193, 17], [193, 18], [193, 19], [193, 20], [193, 21], [193, 22], [193, 23], [193, 7]], + [[200, 17], [200, 18], [200, 19], [200, 20], [200, 21], [200, 22], [200, 23], [200, 7], [201, 17], [201, 18], [201, 19], [201, 20], [201, 21], [201, 22], [201, 23], [201, 7]], + [[202, 17], [202, 18], [202, 19], [202, 20], [202, 21], [202, 22], [202, 23], [202, 7], [205, 17], [205, 18], [205, 19], [205, 20], [205, 21], [205, 22], [205, 23], [205, 7]], + [[210, 17], [210, 18], [210, 19], [210, 20], [210, 21], [210, 22], [210, 23], [210, 7], [213, 17], [213, 18], [213, 19], [213, 20], [213, 21], [213, 22], [213, 23], [213, 7]], + [[218, 17], [218, 18], [218, 19], [218, 20], [218, 21], [218, 22], [218, 23], [218, 7], [219, 17], [219, 18], [219, 19], [219, 20], [219, 21], [219, 22], [219, 23], [219, 7]], + [[238, 17], [238, 18], [238, 19], [238, 20], [238, 21], [238, 22], [238, 23], [238, 7], [240, 17], [240, 18], [240, 19], [240, 20], [240, 21], [240, 22], [240, 23], [240, 7]], + [[242, 17], [242, 18], [242, 19], [242, 20], [242, 21], [242, 22], [242, 23], [242, 7], [243, 17], [243, 18], [243, 19], [243, 20], [243, 21], [243, 22], [243, 23], [243, 7]], + [[255, 17], [255, 18], [255, 19], [255, 20], [255, 21], [255, 22], [255, 23], [255, 7], [203, 25], [203, 26], [203, 27], [203, 6], [204, 25], [204, 26], [204, 27], [204, 6]], + [[211, 25], [211, 26], [211, 27], [211, 6], [212, 25], [212, 26], [212, 27], [212, 6], [214, 25], [214, 26], [214, 27], [214, 6], [221, 25], [221, 26], [221, 27], [221, 6]], + [[222, 25], [222, 26], [222, 27], [222, 6], [223, 25], [223, 26], [223, 27], [223, 6], [241, 25], [241, 26], [241, 27], [241, 6], [244, 25], [244, 26], [244, 27], [244, 6]], + [[245, 25], [245, 26], [245, 27], [245, 6], [246, 25], [246, 26], [246, 27], [246, 6], [247, 25], [247, 26], [247, 27], [247, 6], [248, 25], [248, 26], [248, 27], [248, 6]], + [[250, 25], [250, 26], [250, 27], [250, 6], [251, 25], [251, 26], [251, 27], [251, 6], [252, 25], [252, 26], [252, 27], [252, 6], [253, 25], [253, 26], [253, 27], [253, 6]], + [[254, 25], [254, 26], [254, 27], [254, 6], [2, 29], [2, 5], [3, 29], [3, 5], [4, 29], [4, 5], [5, 29], [5, 5], [6, 29], [6, 5], [7, 29], [7, 5]], + [[8, 29], [8, 5], [11, 29], [11, 5], [12, 29], [12, 5], [14, 29], [14, 5], [15, 29], [15, 5], [16, 29], [16, 5], [17, 29], [17, 5], [18, 29], [18, 5]], + [[19, 29], [19, 5], [20, 29], [20, 5], [21, 29], [21, 5], [23, 29], [23, 5], [24, 29], [24, 5], [25, 29], [25, 5], [26, 29], [26, 5], [27, 29], [27, 5]], + [[28, 29], [28, 5], [29, 29], [29, 5], [30, 29], [30, 5], [31, 29], [31, 5], [127, 29], [127, 5], [220, 29], [220, 5], [249, 29], [249, 5], [nil, 254], [nil, 255]], + [[10, 17], [10, 18], [10, 19], [10, 20], [10, 21], [10, 22], [10, 23], [10, 7], [13, 17], [13, 18], [13, 19], [13, 20], [13, 21], [13, 22], [13, 23], [13, 7]], + [[22, 17], [22, 18], [22, 19], [22, 20], [22, 21], [22, 22], [22, 23], [22, 7], [256, 17], [256, 18], [256, 19], [256, 20], [256, 21], [256, 22], [256, 23], [256, 7]], ].each { |arr| arr.each { |subarr| subarr.each(&:freeze) }.freeze }.freeze end end diff --git a/lib/http/2/server.rb b/lib/http/2/server.rb index de006ca..707355f 100644 --- a/lib/http/2/server.rb +++ b/lib/http/2/server.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'base64' - module HTTP2 # HTTP 2.0 server connection class that implements appropriate header # compression / decompression algorithms and stream management logic. @@ -80,7 +78,7 @@ def upgrade(settings, headers, body) receive(CONNECTION_PREFACE_MAGIC) # Process received HTTP2-Settings payload - buf = ''.b + buf = "".b buf << Base64.urlsafe_decode64(settings.to_s) @framer.common_header( { diff --git a/lib/http/2/stream.rb b/lib/http/2/stream.rb index 3d92e8e..0fd26a0 100644 --- a/lib/http/2/stream.rb +++ b/lib/http/2/stream.rb @@ -164,13 +164,13 @@ def receive(frame) alias << receive def verify_trailers(frame) - stream_error(:protocol_error, msg: 'trailer headers frame must close the stream') unless end_stream?(frame) + stream_error(:protocol_error, msg: "trailer headers frame must close the stream") unless end_stream?(frame) return unless @_trailers trailers = frame[:payload] return unless trailers.respond_to?(:each) - trailers.each_key do |field| + trailers.each do |field, _| # rubocop:disable Style/HashEachMethods @_trailers.delete(field) break if @_trailers.empty? end @@ -183,7 +183,7 @@ def calculate_content_length(data_length) @_content_length -= data_length return if @_content_length >= 0 - stream_error(:protocol_error, msg: 'received more data than what was defined in content-length') + stream_error(:protocol_error, msg: "received more data than what was defined in content-length") end # Processes outgoing HTTP 2.0 frames. Data frames may be automatically @@ -219,13 +219,13 @@ def send(frame) def headers(headers, end_headers: true, end_stream: false) flags = [] flags << :end_headers if end_headers - flags << :end_stream if end_stream || @_method == 'HEAD' + flags << :end_stream if end_stream || @_method == "HEAD" send(type: :headers, flags: flags, payload: headers) end def promise(headers, end_headers: true, &block) - raise ArgumentError, 'must provide callback' unless block + raise ArgumentError, "must provide callback" unless block flags = end_headers ? [:end_headers] : [] emit(:promise, self, headers, flags, &block) diff --git a/lib/http/2/version.rb b/lib/http/2/version.rb index d988875..8d29781 100644 --- a/lib/http/2/version.rb +++ b/lib/http/2/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module HTTP2 - VERSION = '1.0.3' + VERSION = "1.0.0" end diff --git a/spec/client_spec.rb b/spec/client_spec.rb index 32653c2..640ecd0 100644 --- a/spec/client_spec.rb +++ b/spec/client_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'helper' -require 'shared_examples/connection' +require "helper" +require "shared_examples/connection" RSpec.describe HTTP2::Client do include FrameHelpers @@ -13,13 +13,13 @@ client end - it_behaves_like 'a connection' do + it_behaves_like "a connection" do let(:connected_conn) { client } end - context 'initialization and settings' do + context "initialization and settings" do let(:client) { Client.new } - it 'should raise error if first frame is not settings' do + it "should raise error if first frame is not settings" do (frame_types - [settings_frame]).each do |frame| conn = Client.new expect { conn << f.generate(frame) }.to raise_error(ProtocolError) @@ -27,43 +27,43 @@ end end - it 'should not raise error if first frame is SETTINGS' do + it "should not raise error if first frame is SETTINGS" do expect { client << f.generate(settings_frame) }.to_not raise_error expect(client.state).to eq :connected expect(client).to_not be_closed end - it 'should raise error if SETTINGS stream != 0' do + it "should raise error if SETTINGS stream != 0" do frame = set_stream_id(f.generate(settings_frame), 0x1) expect { client << frame }.to raise_error(ProtocolError) end - it 'should return odd stream IDs' do + it "should return odd stream IDs" do expect(client.new_stream.id).not_to be_even end - it 'should emit connection header and SETTINGS on new client connection' do + it "should emit connection header and SETTINGS on new client connection" do frames = [] client.on(:frame) { |bytes| frames << bytes } - client.ping('12345678') + client.ping("12345678") expect(frames[0]).to eq CONNECTION_PREFACE_MAGIC expect(f.parse(frames[1])[:type]).to eq :settings end - it 'should initialize client with custom connection settings' do + it "should initialize client with custom connection settings" do frames = [] client = Client.new(settings_max_concurrent_streams: 200) client.on(:frame) { |bytes| frames << bytes } - client.ping('12345678') + client.ping("12345678") frame = f.parse(frames[1]) expect(frame[:type]).to eq :settings expect(frame[:payload]).to include([:settings_max_concurrent_streams, 200]) end - it 'should initialize client when receiving server settings before sending ack' do + it "should initialize client when receiving server settings before sending ack" do frames = [] client.on(:frame) { |bytes| frames << bytes } client << f.generate(settings_frame) @@ -76,9 +76,9 @@ end end - context 'settings synchronization' do + context "settings synchronization" do let(:client) { Client.new } - it 'should reflect outgoing settings when ack is received' do + it "should reflect outgoing settings when ack is received" do expect(client.local_settings[:settings_header_table_size]).to eq 4096 client.settings(settings_header_table_size: 256) expect(client.local_settings[:settings_header_table_size]).to eq 4096 @@ -90,43 +90,43 @@ end end - context 'upgrade' do - it 'fails when client has already created streams' do + context "upgrade" do + it "fails when client has already created streams" do client.new_stream expect { client.upgrade }.to raise_error(HTTP2::Error::ProtocolError) end - it 'sends the preface' do + it "sends the preface" do expect(client).to receive(:send_connection_preface) client.upgrade end - it 'initializes the first stream in the half-closed state' do + it "initializes the first stream in the half-closed state" do stream = client.upgrade expect(stream.state).to be(:half_closed_local) end end - context 'push' do - it 'should disallow client initiated push' do + context "push" do + it "should disallow client initiated push" do expect do client.promise({}) {} end.to raise_error(NoMethodError) end - it 'should raise error on PUSH_PROMISE against stream 0' do + it "should raise error on PUSH_PROMISE against stream 0" do expect do client << set_stream_id(f.generate(push_promise_frame), 0) end.to raise_error(ProtocolError) end - it 'should raise error on PUSH_PROMISE against bogus stream' do + it "should raise error on PUSH_PROMISE against bogus stream" do expect do client << set_stream_id(f.generate(push_promise_frame), 31_415) end.to raise_error(ProtocolError) end - it 'should raise error on PUSH_PROMISE against non-idle stream' do + it "should raise error on PUSH_PROMISE against non-idle stream" do expect do s = client.new_stream s.send headers_frame @@ -136,7 +136,7 @@ end.to raise_error(ProtocolError) end - it 'should emit stream object for received PUSH_PROMISE' do + it "should emit stream object for received PUSH_PROMISE" do s = client.new_stream s.send headers_frame @@ -148,7 +148,7 @@ expect(promise.state).to eq :reserved_remote end - it 'should emit promise headers for received PUSH_PROMISE' do + it "should emit promise headers for received PUSH_PROMISE" do header = nil s = client.new_stream s.send headers_frame @@ -164,7 +164,7 @@ # expect(header).to eq([%w(a b)]) end - it 'should auto RST_STREAM promises against locally-RST stream' do + it "should auto RST_STREAM promises against locally-RST stream" do s = client.new_stream s.send headers_frame s.close @@ -179,9 +179,9 @@ end end - context 'alt-svc' do - context 'received in the connection' do - it 'should emit :altsvc when receiving one' do + context "alt-svc" do + context "received in the connection" do + it "should emit :altsvc when receiving one" do client << f.generate(settings_frame) frame = nil client.on(:altsvc) do |f| @@ -190,7 +190,7 @@ client << f.generate(altsvc_frame) expect(frame).to be_a(Hash) end - it 'should not emit :altsvc when the frame when contains no host' do + it "should not emit :altsvc when the frame when contains no host" do client << f.generate(settings_frame) frame = nil client.on(:altsvc) do |f| @@ -201,8 +201,8 @@ expect(frame).to be_nil end end - context 'received in a stream' do - it 'should emit :altsvc' do + context "received in a stream" do + it "should emit :altsvc" do s = client.new_stream s.send headers_frame s.close @@ -214,7 +214,7 @@ expect(frame).to be_a(Hash) end - it 'should not emit :alt_svc when the frame when contains a origin' do + it "should not emit :alt_svc when the frame when contains a origin" do s = client.new_stream s.send headers_frame s.close @@ -229,21 +229,21 @@ end end - context 'origin' do + context "origin" do let(:orig_frame) { origin_frame.merge(payload: %w[https://www.google.com https://www.youtube.com]) } - context 'received in the connection' do - it 'should emit :origin when receiving one' do + context "received in the connection" do + it "should emit :origin when receiving one" do client << f.generate(settings_frame) origins = [] client.on(:origin) do |origin| origins << origin end client << f.generate(orig_frame) - expect(origins).to include('https://www.google.com') - expect(origins).to include('https://www.youtube.com') + expect(origins).to include("https://www.google.com") + expect(origins).to include("https://www.youtube.com") end - context 'initialized as h2c' do - it 'should be ignored' do + context "initialized as h2c" do + it "should be ignored" do client.upgrade origins = [] client.on(:origin) do |origin| @@ -253,9 +253,9 @@ expect(origins).to be_empty end end - context 'when receiving a reserved flag' do + context "when receiving a reserved flag" do let(:orig_frame) { origin_frame.merge(flags: [:reserved]) } - it 'should be ignored' do + it "should be ignored" do client << f.generate(settings_frame) origins = [] client.on(:origin) do |origin| @@ -266,8 +266,8 @@ end end end - context 'received in a stream' do - it 'should be ignored' do + context "received in a stream" do + it "should be ignored" do s = client.new_stream s.send headers_frame s.close @@ -279,9 +279,9 @@ end end - context 'connection management' do + context "connection management" do let(:conn) { Client.new } - it 'should send GOAWAY frame on connection error' do + it "should send GOAWAY frame on connection error" do stream = conn.new_stream expect(conn).to receive(:encode) do |frame| @@ -299,8 +299,8 @@ end end - context 'stream management' do - it 'should process connection management frames after GOAWAY' do + context "stream management" do + it "should process connection management frames after GOAWAY" do stream = client.new_stream stream.send headers_frame client << f.generate(goaway_frame) @@ -309,8 +309,8 @@ end end - context 'framing' do - it 'should buffer incomplete frames' do + context "framing" do + it "should buffer incomplete frames" do frame = f.generate(window_update_frame.merge(stream: 0, increment: 1000)) client << frame expect(client.remote_window).to eq DEFAULT_FLOW_WINDOW + 1000 @@ -320,7 +320,7 @@ expect(client.remote_window).to eq DEFAULT_FLOW_WINDOW + 2000 end - it 'should decompress header blocks regardless of stream state' do + it "should decompress header blocks regardless of stream state" do req_headers = [ %w[:status 200], %w[x-my-header first] @@ -339,7 +339,7 @@ client << f.generate(headers) end - it 'should decode non-contiguous header blocks' do + it "should decode non-contiguous header blocks" do req_headers = [ %w[:status 200], %w[x-my-header first] @@ -369,8 +369,8 @@ end end - context 'API' do - it '.goaway should generate GOAWAY frame with last processed stream ID' do + context "API" do + it ".goaway should generate GOAWAY frame with last processed stream ID" do stream = client.new_stream stream.send headers_frame @@ -378,15 +378,15 @@ expect(frame[:type]).to eq :goaway expect(frame[:last_stream]).to eq 1 expect(frame[:error]).to eq :internal_error - expect(frame[:payload]).to eq 'payload' + expect(frame[:payload]).to eq "payload" end - client.goaway(:internal_error, 'payload') + client.goaway(:internal_error, "payload") end end - context '.settings_header' do - it 'encodes the settings frame in base64' do + context ".settings_header" do + it "encodes the settings frame in base64" do settings_header = described_class.settings_header(settings_frame[:payload]) expect(f.generate(settings_frame)).to end_with(Base64.urlsafe_decode64(settings_header)) end diff --git a/spec/compressor_spec.rb b/spec/compressor_spec.rb index 33cf83c..b3c32f3 100644 --- a/spec/compressor_spec.rb +++ b/spec/compressor_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'helper' +require "helper" RSpec.describe HTTP2::Header do using StringExtensions @@ -8,44 +8,44 @@ let(:c) { Compressor.new } let(:d) { Decompressor.new } - context 'literal representation' do - context 'integer' do - it 'should encode 10 using a 5-bit prefix' do - buf = c.integer(10, 5, buffer: ''.b) - expect(buf).to eq [10].pack('C') + context "literal representation" do + context "integer" do + it "should encode 10 using a 5-bit prefix" do + buf = c.integer(10, 5, buffer: "".b) + expect(buf).to eq [10].pack("C") expect(d.integer(buf, 5)).to eq 10 end - it 'should encode 10 using a 0-bit prefix' do - buf = c.integer(10, 0, buffer: ''.b) - expect(buf).to eq [10].pack('C') + it "should encode 10 using a 0-bit prefix" do + buf = c.integer(10, 0, buffer: "".b) + expect(buf).to eq [10].pack("C") expect(d.integer(buf, 0)).to eq 10 end - it 'should encode 1337 using a 5-bit prefix' do - buf = c.integer(1337, 5, buffer: ''.b) - expect(buf).to eq [31, 128 + 26, 10].pack('C*') + it "should encode 1337 using a 5-bit prefix" do + buf = c.integer(1337, 5, buffer: "".b) + expect(buf).to eq [31, 128 + 26, 10].pack("C*") expect(d.integer(buf, 5)).to eq 1337 end - it 'should encode 1337 using a 0-bit prefix' do - buf = c.integer(1337, 0, buffer: ''.b) - expect(buf).to eq [128 + 57, 10].pack('C*') + it "should encode 1337 using a 0-bit prefix" do + buf = c.integer(1337, 0, buffer: "".b) + expect(buf).to eq [128 + 57, 10].pack("C*") expect(d.integer(buf, 0)).to eq 1337 end end - context 'string' do + context "string" do [ - ['with huffman', :always, 0x80], - ['without huffman', :never, 0] + ["with huffman", :always, 0x80], + ["without huffman", :never, 0] ].each do |desc, option, msb| - let(:trailer) { 'trailer' } + let(:trailer) { "trailer" } [ - ['ascii codepoints', 'abcdefghij'], - ['utf-8 codepoints', 'éáűőúöüó€'], - ['long utf-8 strings', 'éáűőúöüó€' * 100] + ["ascii codepoints", "abcdefghij"], + ["utf-8 codepoints", "éáűőúöüó€"], + ["long utf-8 strings", "éáűőúöüó€" * 100] ].each do |datatype, plain| it "should handle #{datatype} #{desc}" do # NOTE: don't put this new in before{} because of test case shuffling @@ -59,10 +59,10 @@ end end end - context 'choosing shorter representation' do - [['日本語', :plain], - ['200', :huffman], - ['xq', :plain]].each do |string, choice| + context "choosing shorter representation" do + [["日本語", :plain], + ["200", :huffman], + ["xq", :plain]].each do |string, choice| it "should return #{choice} representation" do c = Compressor.new(huffman: :shorter) wire = c.string(string) @@ -73,32 +73,32 @@ end end - context 'header representation' do - it 'should handle indexed representation' do + context "header representation" do + it "should handle indexed representation" do h = { name: 10, type: :indexed } wire = c.header(h) expect(wire.getbyte(0) & 0x80).to eq 0x80 expect(wire.getbyte(0) & 0x7f).to eq h[:name] + 1 expect(d.header(wire)).to eq h end - it 'should raise when decoding indexed representation with index zero' do + it "should raise when decoding indexed representation with index zero" do h = { name: 10, type: :indexed } wire = c.header(h) wire[0] = 0x80.chr(Encoding::BINARY) expect { d.header(wire) }.to raise_error CompressionError end - context 'literal w/o indexing representation' do - it 'should handle indexed header' do - h = { name: 10, value: 'my-value', type: :noindex } + context "literal w/o indexing representation" do + it "should handle indexed header" do + h = { name: 10, value: "my-value", type: :noindex } wire = c.header(h) expect(wire.getbyte(0) & 0xf0).to eq 0x0 expect(wire.getbyte(0) & 0x0f).to eq h[:name] + 1 expect(d.header(wire)).to eq h end - it 'should handle literal header' do - h = { name: 'x-custom', value: 'my-value', type: :noindex } + it "should handle literal header" do + h = { name: "x-custom", value: "my-value", type: :noindex } wire = c.header(h) expect(wire.getbyte(0) & 0xf0).to eq 0x0 expect(wire.getbyte(0) & 0x0f).to eq 0 @@ -106,17 +106,17 @@ end end - context 'literal w/ incremental indexing' do - it 'should handle indexed header' do - h = { name: 10, value: 'my-value', type: :incremental } + context "literal w/ incremental indexing" do + it "should handle indexed header" do + h = { name: 10, value: "my-value", type: :incremental } wire = c.header(h) expect(wire.getbyte(0) & 0xc0).to eq 0x40 expect(wire.getbyte(0) & 0x3f).to eq h[:name] + 1 expect(d.header(wire)).to eq h end - it 'should handle literal header' do - h = { name: 'x-custom', value: 'my-value', type: :incremental } + it "should handle literal header" do + h = { name: "x-custom", value: "my-value", type: :incremental } wire = c.header(h) expect(wire.getbyte(0) & 0xc0).to eq 0x40 expect(wire.getbyte(0) & 0x3f).to eq 0 @@ -124,17 +124,17 @@ end end - context 'literal never indexed' do - it 'should handle indexed header' do - h = { name: 10, value: 'my-value', type: :neverindexed } + context "literal never indexed" do + it "should handle indexed header" do + h = { name: 10, value: "my-value", type: :neverindexed } wire = c.header(h) expect(wire.getbyte(0) & 0xf0).to eq 0x10 expect(wire.getbyte(0) & 0x0f).to eq h[:name] + 1 expect(d.header(wire)).to eq h end - it 'should handle literal header' do - h = { name: 'x-custom', value: 'my-value', type: :neverindexed } + it "should handle literal header" do + h = { name: "x-custom", value: "my-value", type: :neverindexed } wire = c.header(h) expect(wire.getbyte(0) & 0xf0).to eq 0x10 expect(wire.getbyte(0) & 0x0f).to eq 0 @@ -143,125 +143,125 @@ end end - context 'shared compression context' do + context "shared compression context" do let(:cc) { EncodingContext.new } - it 'should be initialized with empty headers' do + it "should be initialized with empty headers" do expect(cc.table).to be_empty end - context 'processing' do + context "processing" do [ - ['no indexing', :noindex], - ['never indexed', :neverindexed] + ["no indexing", :noindex], + ["never indexed", :neverindexed] ].each do |desc, type| context desc.to_s do - it 'should process indexed header with literal value' do + it "should process indexed header with literal value" do original_table = cc.table - emit = cc.process(name: 4, value: '/path', type: type) - expect(emit).to eq [':path', '/path'] + emit = cc.process(name: 4, value: "/path", type: type) + expect(emit).to eq [":path", "/path"] expect(cc.table).to eq original_table end - it 'should process literal header with literal value' do + it "should process literal header with literal value" do original_table = cc.table - emit = cc.process(name: 'x-custom', value: 'random', type: type) + emit = cc.process(name: "x-custom", value: "random", type: type) expect(emit).to eq %w[x-custom random] expect(cc.table).to eq original_table end end end - context 'incremental indexing' do - it 'should process indexed header with literal value' do + context "incremental indexing" do + it "should process indexed header with literal value" do original_table = cc.table.dup - emit = cc.process(name: 4, value: '/path', type: :incremental) - expect(emit).to eq [':path', '/path'] - expect(cc.table - original_table).to eq [[':path', '/path']] + emit = cc.process(name: 4, value: "/path", type: :incremental) + expect(emit).to eq [":path", "/path"] + expect(cc.table - original_table).to eq [[":path", "/path"]] end - it 'should process literal header with literal value' do + it "should process literal header with literal value" do original_table = cc.table.dup - cc.process(name: 'x-custom', value: 'random', type: :incremental) + cc.process(name: "x-custom", value: "random", type: :incremental) expect(cc.table - original_table).to eq [%w[x-custom random]] end end - context 'size bounds' do - it 'should drop headers from end of table' do + context "size bounds" do + it "should drop headers from end of table" do cc = EncodingContext.new(table_size: 2048) cc.instance_eval do - add_to_table(['test1', '1' * 1024]) - add_to_table(['test2', '2' * 500]) + add_to_table(["test1", "1" * 1024]) + add_to_table(["test2", "2" * 500]) end original_table = cc.table.dup original_size = original_table.join.bytesize + (original_table.size * 32) - cc.process(name: 'x-custom', - value: 'a' * (2048 - original_size), + cc.process(name: "x-custom", + value: "a" * (2048 - original_size), type: :incremental) - expect(cc.table.first[0]).to eq 'x-custom' + expect(cc.table.first[0]).to eq "x-custom" expect(cc.table.size).to eq original_table.size # number of entries end end - it 'should clear table if entry exceeds table size' do + it "should clear table if entry exceeds table size" do cc = EncodingContext.new(table_size: 2048) cc.instance_eval do - add_to_table(['test1', '1' * 1024]) - add_to_table(['test2', '2' * 500]) + add_to_table(["test1", "1" * 1024]) + add_to_table(["test2", "2" * 500]) end - h = { name: 'x-custom', value: 'a', index: 0, type: :incremental } - e = { name: 'large', value: 'a' * 2048, index: 0 } + h = { name: "x-custom", value: "a", index: 0, type: :incremental } + e = { name: "large", value: "a" * 2048, index: 0 } cc.process(h) cc.process(e.merge(type: :incremental)) expect(cc.table).to be_empty end - it 'should shrink table if set smaller size' do + it "should shrink table if set smaller size" do cc = EncodingContext.new(table_size: 2048) cc.listen_on_table do cc.instance_eval do - add_to_table(['test1', '1' * 1024]) - add_to_table(['test2', '2' * 500]) + add_to_table(["test1", "1" * 1024]) + add_to_table(["test2", "2" * 500]) end end cc.process(type: :changetablesize, value: 1500) expect(cc.table.size).to be 1 - expect(cc.table.first[0]).to eq 'test2' + expect(cc.table.first[0]).to eq "test2" end - it 'should reject table size update if exceed limit' do + it "should reject table size update if exceed limit" do cc = EncodingContext.new(table_size: 4096) expect { cc.process(type: :changetablesize, value: 150_000_000) }.to raise_error(CompressionError) end end - context 'encode' do - it 'downcases the field' do + context "encode" do + it "downcases the field" do expect(EncodingContext.new.encode([%w[Content-Length 5]])) .to eq(EncodingContext.new.encode([%w[content-length 5]])) end - it 'fills :path if empty' do - expect(EncodingContext.new.encode([[':path', '']])) - .to eq(EncodingContext.new.encode([[':path', '/']])) + it "fills :path if empty" do + expect(EncodingContext.new.encode([[":path", ""]])) + .to eq(EncodingContext.new.encode([[":path", "/"]])) end end end spec_examples = [ - { title: 'D.3. Request Examples without Huffman', + { title: "D.3. Request Examples without Huffman", type: :request, table_size: 4096, huffman: :never, @@ -269,156 +269,156 @@ { wire: "8286 8441 0f77 7777 2e65 7861 6d70 6c65 2e63 6f6d", emitted: [ - [':method', 'GET'], - [':scheme', 'http'], - [':path', '/'], - [':authority', 'www.example.com'] + [":method", "GET"], + [":scheme", "http"], + [":path", "/"], + [":authority", "www.example.com"] ], table: [ - [':authority', 'www.example.com'] + [":authority", "www.example.com"] ], table_size: 57 }, - { wire: '8286 84be 5808 6e6f 2d63 6163 6865', + { wire: "8286 84be 5808 6e6f 2d63 6163 6865", emitted: [ - [':method', 'GET'], - [':scheme', 'http'], - [':path', '/'], - [':authority', 'www.example.com'], + [":method", "GET"], + [":scheme", "http"], + [":path", "/"], + [":authority", "www.example.com"], %w[cache-control no-cache] ], table: [ %w[cache-control no-cache], - [':authority', 'www.example.com'] + [":authority", "www.example.com"] ], table_size: 110 }, { wire: "8287 85bf 400a 6375 7374 6f6d 2d6b 6579 0c63 7573 746f 6d2d 7661 6c75 65", emitted: [ - [':method', 'GET'], - [':scheme', 'https'], - [':path', '/index.html'], - [':authority', 'www.example.com'], + [":method", "GET"], + [":scheme", "https"], + [":path", "/index.html"], + [":authority", "www.example.com"], %w[custom-key custom-value] ], table: [ %w[custom-key custom-value], %w[cache-control no-cache], - [':authority', 'www.example.com'] + [":authority", "www.example.com"] ], table_size: 164 } ] }, - { title: 'D.4. Request Examples with Huffman', + { title: "D.4. Request Examples with Huffman", type: :request, table_size: 4096, huffman: :always, streams: [ - { wire: '8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff', + { wire: "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff", emitted: [ - [':method', 'GET'], - [':scheme', 'http'], - [':path', '/'], - [':authority', 'www.example.com'] + [":method", "GET"], + [":scheme", "http"], + [":path", "/"], + [":authority", "www.example.com"] ], table: [ - [':authority', 'www.example.com'] + [":authority", "www.example.com"] ], table_size: 57 }, - { wire: '8286 84be 5886 a8eb 1064 9cbf', + { wire: "8286 84be 5886 a8eb 1064 9cbf", emitted: [ - [':method', 'GET'], - [':scheme', 'http'], - [':path', '/'], - [':authority', 'www.example.com'], + [":method", "GET"], + [":scheme", "http"], + [":path", "/"], + [":authority", "www.example.com"], %w[cache-control no-cache] ], table: [ %w[cache-control no-cache], - [':authority', 'www.example.com'] + [":authority", "www.example.com"] ], table_size: 110 }, { wire: "8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925 a849 e95b b8e8 b4bf", emitted: [ - [':method', 'GET'], - [':scheme', 'https'], - [':path', '/index.html'], - [':authority', 'www.example.com'], + [":method", "GET"], + [":scheme", "https"], + [":path", "/index.html"], + [":authority", "www.example.com"], %w[custom-key custom-value] ], table: [ %w[custom-key custom-value], %w[cache-control no-cache], - [':authority', 'www.example.com'] + [":authority", "www.example.com"] ], table_size: 164 } ] }, - { title: 'D.4.a. Request Examples with Huffman - Client Handling of Improperly Ordered Headers', + { title: "D.4.a. Request Examples with Huffman - Client Handling of Improperly Ordered Headers", type: :request, table_size: 4096, huffman: :always, streams: [ - { wire: '8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff', + { wire: "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff", emitted: [ - [':method', 'GET'], - [':scheme', 'http'], - [':path', '/'], - [':authority', 'www.example.com'] + [":method", "GET"], + [":scheme", "http"], + [":path", "/"], + [":authority", "www.example.com"] ], table: [ - [':authority', 'www.example.com'] + [":authority", "www.example.com"] ], table_size: 57 }, - { wire: '8286 84be 5886 a8eb 1064 9cbf', + { wire: "8286 84be 5886 a8eb 1064 9cbf", emitted: [ - [':method', 'GET'], - [':scheme', 'http'], + [":method", "GET"], + [":scheme", "http"], %w[cache-control no-cache], - [':path', '/'], - [':authority', 'www.example.com'] + [":path", "/"], + [":authority", "www.example.com"] ], table: [ %w[cache-control no-cache], - [':authority', 'www.example.com'] + [":authority", "www.example.com"] ], table_size: 110 }, { wire: "8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925 a849 e95b b8e8 b4bf", emitted: [ - [':method', 'GET'], - [':scheme', 'https'], + [":method", "GET"], + [":scheme", "https"], %w[custom-key custom-value], - [':path', '/index.html'], - [':authority', 'www.example.com'] + [":path", "/index.html"], + [":authority", "www.example.com"] ], table: [ %w[custom-key custom-value], %w[cache-control no-cache], - [':authority', 'www.example.com'] + [":authority", "www.example.com"] ], table_size: 164 } ] }, - { title: 'D.4.b. Request Examples with Huffman - Server Handling of Improperly Ordered Headers', + { title: "D.4.b. Request Examples with Huffman - Server Handling of Improperly Ordered Headers", type: :request, bypass_encoder: true, table_size: 4096, huffman: :always, streams: [ - { wire: '8286408825a849e95ba97d7f8925a849e95bb8e8b4bf84418cf1e3c2e5f23a6ba0ab90f4ff', + { wire: "8286408825a849e95ba97d7f8925a849e95bb8e8b4bf84418cf1e3c2e5f23a6ba0ab90f4ff", emitted: [ - [':method', 'GET'], - [':scheme', 'http'], + [":method", "GET"], + [":scheme", "http"], %w[custom-key custom-value], - [':path', '/'], - [':authority', 'www.example.com'] + [":path", "/"], + [":authority", "www.example.com"] ], table: [ %w[custom-key custom-value], - [':authority', 'www.example.com'] + [":authority", "www.example.com"] ], table_size: 111, has_bad_headers: true } ] }, - { title: 'D.5. Response Examples without Huffman', + { title: "D.5. Response Examples without Huffman", type: :response, table_size: 256, huffman: :never, @@ -429,29 +429,29 @@ 7474 7073 3a2f 2f77 7777 2e65 7861 6d70 6c65 2e63 6f6d", emitted: [ - [':status', '302'], + [":status", "302"], %w[cache-control private], - ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'], - ['location', 'https://www.example.com'] + ["date", "Mon, 21 Oct 2013 20:13:21 GMT"], + ["location", "https://www.example.com"] ], table: [ - ['location', 'https://www.example.com'], - ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'], + ["location", "https://www.example.com"], + ["date", "Mon, 21 Oct 2013 20:13:21 GMT"], %w[cache-control private], - [':status', '302'] + [":status", "302"] ], table_size: 222 }, - { wire: '4803 3330 37c1 c0bf', + { wire: "4803 3330 37c1 c0bf", emitted: [ - [':status', '307'], + [":status", "307"], %w[cache-control private], - ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'], - ['location', 'https://www.example.com'] + ["date", "Mon, 21 Oct 2013 20:13:21 GMT"], + ["location", "https://www.example.com"] ], table: [ - [':status', '307'], - ['location', 'https://www.example.com'], - ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'], + [":status", "307"], + ["location", "https://www.example.com"], + ["date", "Mon, 21 Oct 2013 20:13:21 GMT"], %w[cache-control private] ], table_size: 222 }, @@ -463,21 +463,21 @@ 6765 3d33 3630 303b 2076 6572 7369 6f6e 3d31", emitted: [ - [':status', '200'], + [":status", "200"], %w[cache-control private], - ['date', 'Mon, 21 Oct 2013 20:13:22 GMT'], - ['location', 'https://www.example.com'], + ["date", "Mon, 21 Oct 2013 20:13:22 GMT"], + ["location", "https://www.example.com"], %w[content-encoding gzip], - ['set-cookie', 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1'] + ["set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"] ], table: [ - ['set-cookie', 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1'], + ["set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"], %w[content-encoding gzip], - ['date', 'Mon, 21 Oct 2013 20:13:22 GMT'] + ["date", "Mon, 21 Oct 2013 20:13:22 GMT"] ], table_size: 215 } ] }, - { title: 'D.6. Response Examples with Huffman', + { title: "D.6. Response Examples with Huffman", type: :response, table_size: 256, huffman: :always, @@ -487,29 +487,29 @@ 2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8 e9ae 82ae 43d3", emitted: [ - [':status', '302'], + [":status", "302"], %w[cache-control private], - ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'], - ['location', 'https://www.example.com'] + ["date", "Mon, 21 Oct 2013 20:13:21 GMT"], + ["location", "https://www.example.com"] ], table: [ - ['location', 'https://www.example.com'], - ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'], + ["location", "https://www.example.com"], + ["date", "Mon, 21 Oct 2013 20:13:21 GMT"], %w[cache-control private], - [':status', '302'] + [":status", "302"] ], table_size: 222 }, - { wire: '4883 640e ffc1 c0bf', + { wire: "4883 640e ffc1 c0bf", emitted: [ - [':status', '307'], + [":status", "307"], %w[cache-control private], - ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'], - ['location', 'https://www.example.com'] + ["date", "Mon, 21 Oct 2013 20:13:21 GMT"], + ["location", "https://www.example.com"] ], table: [ - [':status', '307'], - ['location', 'https://www.example.com'], - ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'], + [":status", "307"], + ["location", "https://www.example.com"], + ["date", "Mon, 21 Oct 2013 20:13:21 GMT"], %w[cache-control private] ], table_size: 222 }, @@ -519,37 +519,37 @@ 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07", emitted: [ - [':status', '200'], + [":status", "200"], %w[cache-control private], - ['date', 'Mon, 21 Oct 2013 20:13:22 GMT'], - ['location', 'https://www.example.com'], + ["date", "Mon, 21 Oct 2013 20:13:22 GMT"], + ["location", "https://www.example.com"], %w[content-encoding gzip], - ['set-cookie', 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1'] + ["set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"] ], table: [ - ['set-cookie', 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1'], + ["set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"], %w[content-encoding gzip], - ['date', 'Mon, 21 Oct 2013 20:13:22 GMT'] + ["date", "Mon, 21 Oct 2013 20:13:22 GMT"] ], table_size: 215 } ] }, - { title: 'D.6.a. Response Examples with Huffman - dynamic table size updates should not trigger exceptions', + { title: "D.6.a. Response Examples with Huffman - dynamic table size updates should not trigger exceptions", type: :response, table_size: 4096, huffman: :always, bypass_encoder: true, streams: [ - { wire: '2088 7689 aa63 55e5 80ae 16d7 17', + { wire: "2088 7689 aa63 55e5 80ae 16d7 17", emitted: [ - [':status', '200'], - ['server', 'nginx/1.15.2'] + [":status", "200"], + ["server", "nginx/1.15.2"] ], table: [], table_size: 0 } ] } ] - context 'decode' do + context "decode" do spec_examples.each do |ex| context "spec example #{ex[:title]}" do ex[:streams].size.times do |nth| @@ -557,7 +557,7 @@ let(:dc) { Decompressor.new(table_size: ex[:table_size]) } before do (0...nth).each do |i| - bytes = [ex[:streams][i][:wire].delete(" \n")].pack('H*') + bytes = [ex[:streams][i][:wire].delete(" \n")].pack("H*") if ex[:streams][i][:has_bad_headers] expect { dc.decode(bytes) }.to raise_error ProtocolError else @@ -566,25 +566,25 @@ end end if ex[:streams][nth][:has_bad_headers] - it 'should raise CompressionError' do - bytes = [ex[:streams][nth][:wire].delete(" \n")].pack('H*') + it "should raise CompressionError" do + bytes = [ex[:streams][nth][:wire].delete(" \n")].pack("H*") expect { dc.decode(bytes) }.to raise_error ProtocolError end else let!(:emitted) do - bytes = [ex[:streams][nth][:wire].delete(" \n")].pack('H*') + bytes = [ex[:streams][nth][:wire].delete(" \n")].pack("H*") dc.decode(bytes) end - it 'should emit expected headers' do + it "should emit expected headers" do # partitioned compare - pseudo_headers, headers = ex[:streams][nth][:emitted].partition { |f, _| f.start_with? ':' } + pseudo_headers, headers = ex[:streams][nth][:emitted].partition { |f, _| f.start_with? ":" } partitioned_headers = pseudo_headers + headers expect(emitted).to eq partitioned_headers end - it 'should update header table' do + it "should update header table" do expect(dc.instance_eval { @cc.table }).to eq ex[:streams][nth][:table] end - it 'should compute header table size' do + it "should compute header table size" do expect(dc.instance_eval { @cc.current_table_size }).to eq ex[:streams][nth][:table_size] end end @@ -594,7 +594,7 @@ end end - context 'encode' do + context "encode" do spec_examples.each do |ex| next if ex[:bypass_encoder] @@ -621,15 +621,15 @@ cc.encode(ex[:streams][nth][:emitted]) end end - it 'should emit expected bytes on wire' do - expect(subject.unpack1('H*')).to eq ex[:streams][nth][:wire].delete(" \n") + it "should emit expected bytes on wire" do + expect(subject.unpack1("H*")).to eq ex[:streams][nth][:wire].delete(" \n") end unless ex[:streams][nth][:has_bad_headers] - it 'should update header table' do + it "should update header table" do subject expect(cc.instance_eval { @cc.table }).to eq ex[:streams][nth][:table] end - it 'should compute header table size' do + it "should compute header table size" do subject expect(cc.instance_eval { @cc.current_table_size }).to eq ex[:streams][nth][:table_size] end diff --git a/spec/connection_spec.rb b/spec/connection_spec.rb index bde0207..e238aa2 100644 --- a/spec/connection_spec.rb +++ b/spec/connection_spec.rb @@ -1,28 +1,28 @@ # frozen_string_literal: true -require 'helper' +require "helper" RSpec.describe HTTP2::Connection do include FrameHelpers let(:conn) { Client.new } let(:f) { Framer.new } - context 'Headers pre/post processing' do + context "Headers pre/post processing" do let(:conn) do client = Client.new client << f.generate(settings_frame) client end - it 'should not concatenate multiple occurences of a header field with the same name' do + it "should not concatenate multiple occurences of a header field with the same name" do input = [ - ['Content-Type', 'text/html'], - ['Cache-Control', 'max-age=60, private'], + ["Content-Type", "text/html"], + ["Cache-Control", "max-age=60, private"], %w[Cache-Control must-revalidate] ] expected = [ - ['content-type', 'text/html'], - ['cache-control', 'max-age=60, private'], + ["content-type", "text/html"], + ["cache-control", "max-age=60, private"], %w[cache-control must-revalidate] ] headers = [] @@ -38,15 +38,15 @@ expect(emitted).to match_array(expected) end - it 'should not split zero-concatenated header field values' do + it "should not split zero-concatenated header field values" do input = [*RESPONSE_HEADERS, - ['cache-control', "max-age=60, private\0must-revalidate"], - ['content-type', 'text/html'], - ['cookie', "a=b\0c=d; e=f"]] + ["cache-control", "max-age=60, private\0must-revalidate"], + ["content-type", "text/html"], + ["cookie", "a=b\0c=d; e=f"]] expected = [*RESPONSE_HEADERS, - ['cache-control', "max-age=60, private\0must-revalidate"], - ['content-type', 'text/html'], - ['cookie', "a=b\0c=d; e=f"]] + ["cache-control", "max-age=60, private\0must-revalidate"], + ["content-type", "text/html"], + ["cookie", "a=b\0c=d; e=f"]] result = nil conn.on(:stream) do |stream| diff --git a/spec/emitter_spec.rb b/spec/emitter_spec.rb index ea64446..c36979a 100644 --- a/spec/emitter_spec.rb +++ b/spec/emitter_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'helper' +require "helper" RSpec.describe HTTP2::Emitter do class Worker @@ -12,12 +12,12 @@ class Worker @cnt = 0 end - it 'should raise error on missing callback' do + it "should raise error on missing callback" do expect { w.on(:a) {} }.to_not raise_error expect { w.on(:a) }.to raise_error end - it 'should allow multiple callbacks on single event' do + it "should allow multiple callbacks on single event" do cnt = 0 w.on(:a) { cnt += 1 } w.on(:a) { cnt += 1 } @@ -26,7 +26,7 @@ class Worker expect(cnt).to eq 2 end - it 'should execute callback with optional args' do + it "should execute callback with optional args" do args = nil w.on(:a) { |a| args = a } w.emit(:a, 123) @@ -34,7 +34,7 @@ class Worker expect(args).to eq 123 end - it 'should pass emitted callbacks to listeners' do + it "should pass emitted callbacks to listeners" do cnt = 0 w.on(:a) { |&block| block.call } w.once(:a) { |&block| block.call } @@ -43,11 +43,11 @@ class Worker expect(cnt).to eq 2 end - it 'should allow events with no callbacks' do + it "should allow events with no callbacks" do expect { w.emit(:missing) }.to_not raise_error end - it 'should execute callback exactly once' do + it "should execute callback exactly once" do cnt = 0 w.on(:a) { cnt += 1 } w.once(:a) { cnt += 1 } diff --git a/spec/framer_spec.rb b/spec/framer_spec.rb index 6d0f0a1..5b39b22 100644 --- a/spec/framer_spec.rb +++ b/spec/framer_spec.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true -require 'helper' +require "helper" RSpec.describe HTTP2::Framer do using StringExtensions let(:f) { Framer.new } - context 'common header' do + context "common header" do let(:frame) do { length: 4, @@ -17,17 +17,17 @@ } end - let(:bytes) { [0, 0x04, 0x01, 0x5, 0x0000000F].pack('CnCCN') } + let(:bytes) { [0, 0x04, 0x01, 0x5, 0x0000000F].pack("CnCCN") } - it 'should generate common 9 byte header' do - expect(f.common_header(frame, buffer: ''.b)).to eq bytes + it "should generate common 9 byte header" do + expect(f.common_header(frame, buffer: "".b)).to eq bytes end - it 'should parse common 9 byte header' do + it "should parse common 9 byte header" do expect(f.read_common_header(bytes)).to eq frame end - it 'should generate a large frame' do + it "should generate a large frame" do f = Framer.new f.remote_max_frame_size = (2**24) - 1 frame = { @@ -36,73 +36,73 @@ flags: %i[end_stream end_headers], stream: 15 } - bytes = [5, 17, 0x01, 0x5, 0x0000000F].pack('CnCCN') - expect(f.common_header(frame, buffer: ''.b)).to eq bytes + bytes = [5, 17, 0x01, 0x5, 0x0000000F].pack("CnCCN") + expect(f.common_header(frame, buffer: "".b)).to eq bytes expect(f.read_common_header(bytes)).to eq frame end - it 'should raise exception on invalid frame type when sending' do + it "should raise exception on invalid frame type when sending" do expect do frame[:type] = :bogus - f.common_header(frame, buffer: ''.b) + f.common_header(frame, buffer: "".b) end.to raise_error(CompressionError, /invalid.*type/i) end - it 'should raise exception on invalid stream ID' do + it "should raise exception on invalid stream ID" do expect do frame[:stream] = Framer::MAX_STREAM_ID + 1 - f.common_header(frame, buffer: ''.b) + f.common_header(frame, buffer: "".b) end.to raise_error(CompressionError, /stream/i) end - it 'should raise exception on invalid frame flag' do + it "should raise exception on invalid frame flag" do expect do frame[:flags] = [:bogus] - f.common_header(frame, buffer: ''.b) + f.common_header(frame, buffer: "".b) end.to raise_error(CompressionError, /frame flag/) end - it 'should raise exception on invalid frame size' do + it "should raise exception on invalid frame size" do expect do frame[:length] = 2**24 - f.common_header(frame, buffer: ''.b) + f.common_header(frame, buffer: "".b) end.to raise_error(CompressionError, /too large/) end end - context 'DATA' do - it 'should generate and parse bytes' do + context "DATA" do + it "should generate and parse bytes" do frame = { length: 4, type: :data, flags: [:end_stream], stream: 1, - payload: 'text' + payload: "text" } bytes = f.generate(frame) - expect(bytes).to eq [0, 0x4, 0x0, 0x1, 0x1, *'text'.bytes].pack('CnCCNC*') + expect(bytes).to eq [0, 0x4, 0x0, 0x1, 0x1, *"text".bytes].pack("CnCCNC*") expect(f.parse(bytes)).to eq frame end end - context 'HEADERS' do - it 'should generate and parse bytes' do + context "HEADERS" do + it "should generate and parse bytes" do frame = { length: 12, type: :headers, flags: %i[end_stream end_headers], stream: 1, - payload: 'header-block' + payload: "header-block" } bytes = f.generate(frame) - expect(bytes).to eq [0, 0xc, 0x1, 0x5, 0x1, *'header-block'.bytes].pack('CnCCNC*') + expect(bytes).to eq [0, 0xc, 0x1, 0x5, 0x1, *"header-block".bytes].pack("CnCCNC*") expect(f.parse(bytes)).to eq frame end - it 'should carry an optional stream priority' do + it "should carry an optional stream priority" do frame = { length: 16, type: :headers, @@ -111,17 +111,17 @@ dependency: 15, weight: 12, exclusive: false, - payload: 'header-block' + payload: "header-block" } bytes = f.generate(frame) - expect(bytes).to eq [0, 0x11, 0x1, 0x24, 0x1, 0xf, 0xb, *'header-block'.bytes].pack('CnCCNNCC*') + expect(bytes).to eq [0, 0x11, 0x1, 0x24, 0x1, 0xf, 0xb, *"header-block".bytes].pack("CnCCNNCC*") expect(f.parse(bytes)).to eq frame end end - context 'PRIORITY' do - it 'should generate and parse bytes' do + context "PRIORITY" do + it "should generate and parse bytes" do frame = { length: 5, type: :priority, @@ -132,13 +132,13 @@ } bytes = f.generate(frame) - expect(bytes).to eq [0, 0x5, 0x2, 0x0, 0x1, 0x8000000f, 0xb].pack('CnCCNNC') + expect(bytes).to eq [0, 0x5, 0x2, 0x0, 0x1, 0x8000000f, 0xb].pack("CnCCNNC") expect(f.parse(bytes)).to eq frame end end - context 'RST_STREAM' do - it 'should generate and parse bytes' do + context "RST_STREAM" do + it "should generate and parse bytes" do frame = { length: 4, type: :rst_stream, @@ -147,12 +147,12 @@ } bytes = f.generate(frame) - expect(bytes).to eq [0, 0x4, 0x3, 0x0, 0x1, 0x5].pack('CnCCNN') + expect(bytes).to eq [0, 0x4, 0x3, 0x0, 0x1, 0x5].pack("CnCCNN") expect(f.parse(bytes)).to eq frame end end - context 'SETTINGS' do + context "SETTINGS" do let(:frame) do { type: :settings, @@ -165,22 +165,22 @@ } end - it 'should generate and parse bytes' do + it "should generate and parse bytes" do bytes = f.generate(frame) - expect(bytes).to eq [0, 12, 0x4, 0x0, 0x0, 3, 10, 1, 2048].pack('CnCCNnNnN') + expect(bytes).to eq [0, 12, 0x4, 0x0, 0x0, 3, 10, 1, 2048].pack("CnCCNnNnN") parsed = f.parse(bytes) parsed.delete(:length) frame.delete(:length) expect(parsed).to eq frame end - it 'should generate settings when id is given as an integer' do + it "should generate settings when id is given as an integer" do frame[:payload][1][0] = 1 bytes = f.generate(frame) - expect(bytes).to eq [0, 12, 0x4, 0x0, 0x0, 3, 10, 1, 2048].pack('CnCCNnNnN') + expect(bytes).to eq [0, 12, 0x4, 0x0, 0x0, 3, 10, 1, 2048].pack("CnCCNnNnN") end - it 'should ignore custom settings when sending' do + it "should ignore custom settings when sending" do frame[:payload] = [ [:settings_max_concurrent_streams, 10], [:settings_initial_window_size, 20], @@ -193,7 +193,7 @@ expect(f.parse(buf)).to eq frame end - it 'should ignore custom settings when receiving' do + it "should ignore custom settings when receiving" do frame[:payload] = [ [:settings_max_concurrent_streams, 10], [:settings_initial_window_size, 20] @@ -208,14 +208,14 @@ expect(parsed).to eq frame end - it 'should raise exception on sending invalid stream ID' do + it "should raise exception on sending invalid stream ID" do expect do frame[:stream] = 1 f.generate(frame) end.to raise_error(CompressionError, /Invalid stream ID/) end - it 'should raise exception on receiving invalid stream ID' do + it "should raise exception on receiving invalid stream ID" do expect do buf = f.generate(frame) buf.setbyte(8, 1) @@ -223,14 +223,14 @@ end.to raise_error(ProtocolError, /Invalid stream ID/) end - it 'should raise exception on sending invalid setting' do + it "should raise exception on sending invalid setting" do expect do frame[:payload] = [[:random, 23]] f.generate(frame) end.to raise_error(CompressionError, /Unknown settings ID/) end - it 'should raise exception on receiving invalid payload length' do + it "should raise exception on receiving invalid payload length" do expect do buf = f.generate(frame) buf.setbyte(2, 11) # change payload length @@ -239,49 +239,49 @@ end end - context 'PUSH_PROMISE' do - it 'should generate and parse bytes' do + context "PUSH_PROMISE" do + it "should generate and parse bytes" do frame = { length: 11, type: :push_promise, flags: [:end_headers], stream: 1, promise_stream: 2, - payload: 'headers' + payload: "headers" } bytes = f.generate(frame) - expect(bytes).to eq [0, 0xb, 0x5, 0x4, 0x1, 0x2, *'headers'.bytes].pack('CnCCNNC*') + expect(bytes).to eq [0, 0xb, 0x5, 0x4, 0x1, 0x2, *"headers".bytes].pack("CnCCNNC*") expect(f.parse(bytes)).to eq frame end end - context 'PING' do + context "PING" do let(:frame) do { length: 8, stream: 1, type: :ping, flags: [:ack], - payload: '12345678' + payload: "12345678" } end - it 'should generate and parse bytes' do + it "should generate and parse bytes" do bytes = f.generate(frame) - expect(bytes).to eq [0, 0x8, 0x6, 0x1, 0x1, *'12345678'.bytes].pack('CnCCNC*') + expect(bytes).to eq [0, 0x8, 0x6, 0x1, 0x1, *"12345678".bytes].pack("CnCCNC*") expect(f.parse(bytes)).to eq frame end - it 'should raise exception on invalid payload' do + it "should raise exception on invalid payload" do expect do - frame[:payload] = '1234' + frame[:payload] = "1234" f.generate(frame) end.to raise_error(CompressionError, /Invalid payload size/) end end - context 'GOAWAY' do + context "GOAWAY" do let(:frame) do { length: 13, @@ -289,28 +289,28 @@ type: :goaway, last_stream: 2, error: :no_error, - payload: 'debug' + payload: "debug" } end - it 'should generate and parse bytes' do + it "should generate and parse bytes" do bytes = f.generate(frame) - expect(bytes).to eq [0, 0xd, 0x7, 0x0, 0x1, 0x2, 0x0, *'debug'.bytes].pack('CnCCNNNC*') + expect(bytes).to eq [0, 0xd, 0x7, 0x0, 0x1, 0x2, 0x0, *"debug".bytes].pack("CnCCNNNC*") expect(f.parse(bytes)).to eq frame end - it 'should treat debug payload as optional' do + it "should treat debug payload as optional" do frame.delete :payload frame[:length] = 0x8 bytes = f.generate(frame) - expect(bytes).to eq [0, 0x8, 0x7, 0x0, 0x1, 0x2, 0x0].pack('CnCCNNN') + expect(bytes).to eq [0, 0x8, 0x7, 0x0, 0x1, 0x2, 0x0].pack("CnCCNNN") expect(f.parse(bytes)).to eq frame end end - context 'WINDOW_UPDATE' do - it 'should generate and parse bytes' do + context "WINDOW_UPDATE" do + it "should generate and parse bytes" do frame = { length: 4, type: :window_update, @@ -318,11 +318,11 @@ } bytes = f.generate(frame) - expect(bytes).to eq [0, 0x4, 0x8, 0x0, 0x0, 0xa].pack('CnCCNN') + expect(bytes).to eq [0, 0x4, 0x8, 0x0, 0x0, 0xa].pack("CnCCNN") expect(f.parse(bytes)).to eq frame end - it 'should break when the increment is too large' do + it "should break when the increment is too large" do frame = { length: 4, type: :window_update, @@ -333,51 +333,51 @@ end end - context 'CONTINUATION' do - it 'should generate and parse bytes' do + context "CONTINUATION" do + it "should generate and parse bytes" do frame = { length: 12, type: :continuation, stream: 1, flags: [:end_headers], - payload: 'header-block' + payload: "header-block" } bytes = f.generate(frame) - expect(bytes).to eq [0, 0xc, 0x9, 0x4, 0x1, *'header-block'.bytes].pack('CnCCNC*') + expect(bytes).to eq [0, 0xc, 0x9, 0x4, 0x1, *"header-block".bytes].pack("CnCCNC*") expect(f.parse(bytes)).to eq frame end end - context 'ALTSVC' do - it 'should generate and parse bytes' do + context "ALTSVC" do + it "should generate and parse bytes" do frame = { length: 44, type: :altsvc, stream: 1, max_age: 1_402_290_402, # 4 port: 8080, # 2 - proto: 'h2-13', # 1 + 5 - host: 'www.example.com', # 1 + 15 - origin: 'www.example.com' # 15 + proto: "h2-13", # 1 + 5 + host: "www.example.com", # 1 + 15 + origin: "www.example.com" # 15 } bytes = f.generate(frame) - expected = [0, 43, 0xa, 0, 1, 1_402_290_402, 8080].pack('CnCCNNn') - expected << [5, *'h2-13'.bytes].pack('CC*') - expected << [15, *'www.example.com'.bytes].pack('CC*') - expected << [*'www.example.com'.bytes].pack('C*') + expected = [0, 43, 0xa, 0, 1, 1_402_290_402, 8080].pack("CnCCNNn") + expected << [5, *"h2-13".bytes].pack("CC*") + expected << [15, *"www.example.com".bytes].pack("CC*") + expected << [*"www.example.com".bytes].pack("C*") expect(bytes).to eq expected expect(f.parse(bytes)).to eq frame end end - context 'Padding' do + context "Padding" do let(:frame) do { length: 12, type: type, stream: 1, - payload: 'example data' + payload: "example data" } end %i[data headers push_promise].each do |type| @@ -388,7 +388,7 @@ length: 12, type: type, stream: 1, - payload: 'example data', + payload: "example data", promise_stream: 2 } end @@ -397,24 +397,24 @@ context "generating #{type} frame padded #{padlen}" do let(:normal) { f.generate(frame) } let(:padded) { f.generate(frame.merge(padding: padlen)) } - it 'should generate a frame with padding' do + it "should generate a frame with padding" do expect(padded.bytesize).to eq normal.bytesize + padlen end - it 'should fill padded octets with zero' do + it "should fill padded octets with zero" do trailer_len = padlen - 1 expect(padded[-trailer_len, trailer_len]).to match(/\A\0*\z/) end - it 'should parse a frame with padding' do + it "should parse a frame with padding" do expect(f.parse(padded)).to eq \ f.parse(normal).merge(padding: padlen) end - it 'should preserve payload' do + it "should preserve payload" do expect(f.parse(padded)[:payload]).to eq frame[:payload] end end end end - context 'generating with invalid padding length' do + context "generating with invalid padding length" do [0, 257, 1334].each do |padlen| it "should raise error on trying to generate data frame padded with invalid #{padlen}" do expect do @@ -422,8 +422,8 @@ end.to raise_error(CompressionError, /padding/i) end end - it 'should raise error when adding a padding would make frame too large' do - frame[:payload] = 'q' * (f.remote_max_frame_size - 200) + it "should raise error when adding a padding would make frame too large" do + frame[:payload] = "q" * (f.remote_max_frame_size - 200) frame[:length] = frame[:payload].size frame[:padding] = 210 # would exceed 4096 expect do @@ -431,40 +431,40 @@ end.to raise_error(CompressionError, /padding/i) end end - context 'parsing frames with invalid paddings' do + context "parsing frames with invalid paddings" do let(:padded) { f.generate(frame.merge(padding: 123)) } - it 'should raise exception when the given padding is longer than the payload' do + it "should raise exception when the given padding is longer than the payload" do padded.setbyte(9, 240) expect { f.parse(padded) }.to raise_error(ProtocolError, /padding/) end end end - it 'should determine frame length' do + it "should determine frame length" do frames = [ - [{ type: :data, stream: 1, flags: [:end_stream], payload: 'abc' }, 3], - [{ type: :headers, stream: 1, payload: 'abc' }, 3], + [{ type: :data, stream: 1, flags: [:end_stream], payload: "abc" }, 3], + [{ type: :headers, stream: 1, payload: "abc" }, 3], [{ type: :priority, stream: 3, dependency: 30, exclusive: false, weight: 1 }, 5], [{ type: :rst_stream, stream: 3, error: 100 }, 4], [{ type: :settings, payload: [[:settings_max_concurrent_streams, 10]] }, 6], - [{ type: :push_promise, promise_stream: 5, payload: 'abc' }, 7], - [{ type: :ping, payload: 'blob' * 2 }, 8], - [{ type: :goaway, last_stream: 5, error: 20, payload: 'blob' }, 12], + [{ type: :push_promise, promise_stream: 5, payload: "abc" }, 7], + [{ type: :ping, payload: "blob" * 2 }, 8], + [{ type: :goaway, last_stream: 5, error: 20, payload: "blob" }, 12], [{ type: :window_update, stream: 1, increment: 1024 }, 4], - [{ type: :continuation, stream: 1, payload: 'abc' }, 3] + [{ type: :continuation, stream: 1, payload: "abc" }, 3] ] frames.each do |(frame, size)| bytes = f.generate(frame) - expect(bytes.slice(1, 2).unpack1('n')).to eq size + expect(bytes.slice(1, 2).unpack1("n")).to eq size expect(bytes.getbyte(0)).to eq 0 end end - it 'should parse single frame at a time' do + it "should parse single frame at a time" do frames = [ - { type: :headers, stream: 1, payload: 'headers' }, - { type: :data, stream: 1, flags: [:end_stream], payload: 'abc' } + { type: :headers, stream: 1, payload: "headers" }, + { type: :data, stream: 1, flags: [:end_stream], payload: "abc" } ] buf = f.generate(frames[0]) << f.generate(frames[1]) @@ -473,8 +473,8 @@ expect(f.parse(buf)).to eq frames[1] end - it 'should process full frames only' do - frame = { type: :headers, stream: 1, payload: 'headers' } + it "should process full frames only" do + frame = { type: :headers, stream: 1, payload: "headers" } bytes = f.generate(frame) expect(f.parse(bytes.slice(0...-1))).to be_nil @@ -482,8 +482,8 @@ expect(bytes).to be_empty end - it 'should ignore unknown extension frames' do - frame = { type: :headers, stream: 1, payload: 'headers' } + it "should ignore unknown extension frames" do + frame = { type: :headers, stream: 1, payload: "headers" } bytes = f.generate(frame) bytes = "#{bytes}#{bytes}".b # Two HEADERS frames in bytes bytes.setbyte(3, 42) # Make the first unknown type 42 diff --git a/spec/helper.rb b/spec/helper.rb index 0ada7e3..bdd3113 100644 --- a/spec/helper.rb +++ b/spec/helper.rb @@ -2,8 +2,8 @@ GC.auto_compact = true if GC.respond_to?(:auto_compact=) -if ENV.key?('CI') - require 'simplecov' +if ENV.key?("CI") + require "simplecov" SimpleCov.command_name "#{RUBY_ENGINE}-#{RUBY_VERSION}" SimpleCov.coverage_dir "coverage/#{RUBY_ENGINE}-#{RUBY_VERSION}" end @@ -11,10 +11,10 @@ RSpec.configure(&:disable_monkey_patching!) RSpec::Expectations.configuration.warn_about_potential_false_positives = false -require 'json' +require "json" # rubocop: disable Style/MixinUsage -require 'http/2' +require "http/2" include HTTP2 include HTTP2::Header include HTTP2::Error @@ -36,7 +36,7 @@ def data_frame type: :data, flags: [:end_stream], stream: 1, - payload: 'text' + payload: "text" } end @@ -92,7 +92,7 @@ def ping_frame { stream: 0, type: :ping, - payload: '12345678' + payload: "12345678" } end @@ -101,7 +101,7 @@ def pong_frame stream: 0, type: :ping, flags: [:ack], - payload: '12345678' + payload: "12345678" } end @@ -110,7 +110,7 @@ def goaway_frame type: :goaway, last_stream: 2, error: :no_error, - payload: 'debug' + payload: "debug" } end @@ -125,7 +125,7 @@ def continuation_frame { type: :continuation, flags: [:end_headers], - payload: '-second-block' + payload: "-second-block" } end @@ -134,9 +134,9 @@ def altsvc_frame type: :altsvc, max_age: 1_402_290_402, # 4 port: 8080, # 2 reserved 1 - proto: 'h2-12', # 1 + 5 - host: 'www.example.com', # 1 + 15 - origin: 'www.example.com' # 15 + proto: "h2-12", # 1 + 5 + host: "www.example.com", # 1 + 15 + origin: "www.example.com" # 15 } end @@ -150,19 +150,19 @@ def origin_frame DATA_FRAMES = %w[headers continuation push_promise data].freeze def control_frames - methods.select { |meth| meth.to_s.end_with?('_frame') } - .reject { |meth| DATA_FRAMES.include?(meth.to_s.gsub(/_frame$/, '')) } + methods.select { |meth| meth.to_s.end_with?("_frame") } + .reject { |meth| DATA_FRAMES.include?(meth.to_s.gsub(/_frame$/, "")) } .map { |meth| __send__(meth) } end def frame_types - methods.select { |meth| meth.to_s.end_with?('_frame') } + methods.select { |meth| meth.to_s.end_with?("_frame") } .map { |meth| __send__(meth) } end end def set_stream_id(bytes, id) - scheme = 'CnCCN' + scheme = "CnCCN" head = bytes.slice!(0, 9).unpack(scheme) head[4] = id diff --git a/spec/hpack_test_spec.rb b/spec/hpack_test_spec.rb index ce2c516..083ced1 100644 --- a/spec/hpack_test_spec.rb +++ b/spec/hpack_test_spec.rb @@ -1,11 +1,9 @@ # frozen_string_literal: true -require 'helper' -require 'json' +require "helper" +require "json" RSpec.describe HTTP2::Header do - using HTTP2::RegexpExtensions - folders = %w[ go-hpack haskell-http2-diff @@ -23,26 +21,26 @@ node-http2-hpack ] - context 'Decompressor' do + context "Decompressor" do folders.each do |folder| - next if folder.include?('#') + next if folder.include?("#") path = File.expand_path("hpack-test-case/#{folder}", File.dirname(__FILE__)) next unless Dir.exist?(path) context folder.to_s do Dir.foreach(path) do |file| - next unless file.include?('.json') + next unless file.include?(".json") it "should decode #{file}" do story = JSON.parse(File.read("#{path}/#{file}")) - cases = story['cases'] - table_size = cases[0]['header_table_size'] || 4096 + cases = story["cases"] + table_size = cases[0]["header_table_size"] || 4096 @dc = Decompressor.new(table_size: table_size) cases.each do |c| - wire = [c['wire']].pack('H*').force_encoding(Encoding::BINARY) + wire = [c["wire"]].pack("H*").force_encoding(Encoding::BINARY) @emitted = @dc.decode(HTTP2::Buffer.new(wire)) - headers = c['headers'].flat_map(&:to_a) + headers = c["headers"].flat_map(&:to_a) expect(@emitted).to eq headers end end @@ -51,16 +49,16 @@ end end - context 'Compressor' do + context "Compressor" do %w[ LINEAR NAIVE SHORTER STATIC ].each do |mode| - next if mode.include?('#') + next if mode.include?("#") - ['', 'H'].each do |huffman| + ["", "H"].each do |huffman| encoding_mode = :"#{mode}#{huffman}" encoding_options = HTTP2::Header::EncodingContext.const_get(encoding_mode) [4096, 512].each do |table_size| @@ -68,17 +66,17 @@ options.update(encoding_options) context "with #{mode}#{huffman} mode and table_size #{table_size}" do - path = File.expand_path('hpack-test-case/raw-data', File.dirname(__FILE__)) + path = File.expand_path("hpack-test-case/raw-data", File.dirname(__FILE__)) Dir.foreach(path) do |file| - next unless file.include?('.json') + next unless file.include?(".json") it "should encode #{file}" do story = JSON.parse(File.read("#{path}/#{file}")) - cases = story['cases'] + cases = story["cases"] @cc = Compressor.new(options) @dc = Decompressor.new(options) cases.each do |c| - headers = c['headers'].flat_map(&:to_a) + headers = c["headers"].flat_map(&:to_a) wire = @cc.encode(headers) decoded = @dc.decode(HTTP2::Buffer.new(wire)) expect(decoded).to eq headers diff --git a/spec/huffman_spec.rb b/spec/huffman_spec.rb index ca81e8f..5505d91 100644 --- a/spec/huffman_spec.rb +++ b/spec/huffman_spec.rb @@ -1,38 +1,38 @@ # frozen_string_literal: true -require 'helper' +require "helper" RSpec.describe HTTP2::Header::Huffman do using HTTP2::StringExtensions huffman_examples = [ # plain, encoded - ['www.example.com', 'f1e3c2e5f23a6ba0ab90f4ff'], + ["www.example.com", "f1e3c2e5f23a6ba0ab90f4ff"], %w[no-cache a8eb10649cbf], - ['Mon, 21 Oct 2013 20:13:21 GMT', 'd07abe941054d444a8200595040b8166e082a62d1bff'] + ["Mon, 21 Oct 2013 20:13:21 GMT", "d07abe941054d444a8200595040b8166e082a62d1bff"] ] - context 'encode' do + context "encode" do let(:encoder) { HTTP2::Header::Huffman.new } huffman_examples.each do |plain, encoded| it "should encode #{plain} into #{encoded}" do - expect(encoder.encode(plain).unpack1('H*')).to eq encoded + expect(encoder.encode(plain).unpack1("H*")).to eq encoded end end end - context 'decode' do + context "decode" do let(:encoder) { HTTP2::Header::Huffman.new } huffman_examples.each do |plain, encoded| it "should decode #{encoded} into #{plain}" do - expect(encoder.decode([encoded].pack('H*'))).to eq plain + expect(encoder.decode([encoded].pack("H*"))).to eq plain end end [ - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0', - 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', - 'http://www.craigslist.org/about/sites/', - 'cl_b=AB2BKbsl4hGM7M4nH5PYWghTM5A; cl_def_lang=en; cl_def_hp=shoals', - 'image/png,image/*;q=0.8,*/*;q=0.5', - 'BX=c99r6jp89a7no&b=3&s=q4; localization=en-us%3Bus%3Bus', - 'UTF-8でエンコードした日本語文字列' + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0", + "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", + "http://www.craigslist.org/about/sites/", + "cl_b=AB2BKbsl4hGM7M4nH5PYWghTM5A; cl_def_lang=en; cl_def_hp=shoals", + "image/png,image/*;q=0.8,*/*;q=0.5", + "BX=c99r6jp89a7no&b=3&s=q4; localization=en-us%3Bus%3Bus", + "UTF-8でエンコードした日本語文字列" ].each do |string| it "should encode then decode '#{string}' into the same" do s = string.dup.force_encoding(Encoding::BINARY) @@ -41,30 +41,30 @@ end end - it 'should encode/decode all_possible 2-byte sequences' do + it "should encode/decode all_possible 2-byte sequences" do (2**16).times do |n| - str = [n].pack('V')[0, 2].force_encoding(Encoding::BINARY) + str = [n].pack("V")[0, 2].force_encoding(Encoding::BINARY) expect(encoder.decode(encoder.encode(str))).to eq str end end - it 'should raise when input is shorter than expected' do + it "should raise when input is shorter than expected" do encoded = huffman_examples.first.last - encoded = [encoded].pack('H*') + encoded = [encoded].pack("H*") expect { encoder.decode(encoded[0...-1]) }.to raise_error(/EOS invalid/) end - it 'should raise when input is not padded by 1s' do - encoded = 'f1e3c2e5f23a6ba0ab90f4fe' # NOTE: the fe at end - encoded = [encoded].pack('H*') + it "should raise when input is not padded by 1s" do + encoded = "f1e3c2e5f23a6ba0ab90f4fe" # note the fe at end + encoded = [encoded].pack("H*") expect { encoder.decode(encoded) }.to raise_error(/EOS invalid/) end - it 'should raise when exceedingly padded' do - encoded = 'e7cf9bebe89b6fb16fa9b6ffff' # NOTE: the extra ff - encoded = [encoded].pack('H*') + it "should raise when exceedingly padded" do + encoded = "e7cf9bebe89b6fb16fa9b6ffff" # note the extra ff + encoded = [encoded].pack("H*") expect { encoder.decode(encoded) }.to raise_error(/EOS invalid/) end - it 'should raise when EOS is explicitly encoded' do - encoded = ['1c7fffffffff'].pack('H*') # a b EOS + it "should raise when EOS is explicitly encoded" do + encoded = ["1c7fffffffff"].pack("H*") # a b EOS expect { encoder.decode(encoded) }.to raise_error(/EOS found/) end end diff --git a/spec/server_spec.rb b/spec/server_spec.rb index 15bd527..83a6f43 100644 --- a/spec/server_spec.rb +++ b/spec/server_spec.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true -require 'helper' -require 'shared_examples/connection' +require "helper" +require "shared_examples/connection" RSpec.describe HTTP2::Server do include FrameHelpers - it_behaves_like 'a connection' do + it_behaves_like "a connection" do let(:conn) do srv = Server.new srv << CONNECTION_PREFACE_MAGIC @@ -23,12 +23,12 @@ let(:srv) { Server.new } let(:f) { Framer.new } - context 'initialization and settings' do - it 'should return even stream IDs' do + context "initialization and settings" do + it "should return even stream IDs" do expect(srv.new_stream.id).to be_even end - it 'should emit SETTINGS on new connection' do + it "should emit SETTINGS on new connection" do frames = [] srv.on(:frame) { |recv| frames << recv } srv << CONNECTION_PREFACE_MAGIC @@ -36,7 +36,7 @@ expect(f.parse(frames[0])[:type]).to eq :settings end - it 'should initialize client with custom connection settings' do + it "should initialize client with custom connection settings" do frames = [] srv = Server.new(settings_max_concurrent_streams: 200, @@ -51,13 +51,13 @@ end end - it 'should allow server push' do + it "should allow server push" do client = Client.new client.on(:frame) { |bytes| srv << bytes } srv.on(:stream) do |stream| expect do - stream.promise({ ':method' => 'GET' }) {} + stream.promise({ ":method" => "GET" }) {} end.to_not raise_error end @@ -65,26 +65,26 @@ client.send headers_frame end - context 'should allow upgrade' do + context "should allow upgrade" do let(:settings) { Client.settings_header(settings_frame[:payload]) } - it 'for bodyless responses' do + it "for bodyless responses" do expect(srv.active_stream_count).to eq(0) - srv.upgrade(settings, RESPONSE_HEADERS, '') + srv.upgrade(settings, RESPONSE_HEADERS, "") expect(srv.active_stream_count).to eq(1) end - it 'for responses with body' do + it "for responses with body" do expect(srv.active_stream_count).to eq(0) - srv.upgrade(settings, RESPONSE_HEADERS + [[:content_length, 4]], 'bang') + srv.upgrade(settings, RESPONSE_HEADERS + [[:content_length, 4]], "bang") expect(srv.active_stream_count).to eq(1) end end - it 'should allow to send supported origins' do + it "should allow to send supported origins" do srv.origin_set = %w[https://www.youtube.com] origins = [] client = Client.new @@ -97,8 +97,8 @@ expect(origins).to eq(%w[https://www.youtube.com]) end - context 'connection management' do - it 'should raise error on invalid connection header' do + context "connection management" do + it "should raise error on invalid connection header" do srv = Server.new expect { srv << f.generate(settings_frame) }.to raise_error(HandshakeError) @@ -109,7 +109,7 @@ end.to_not raise_error end - it 'should not raise an error on frame for a closed stream ID' do + it "should not raise an error on frame for a closed stream ID" do srv = Server.new srv << CONNECTION_PREFACE_MAGIC @@ -124,8 +124,8 @@ end end - context 'stream management' do - it 'should initialize stream with HEADERS priority value' do + context "stream management" do + it "should initialize stream with HEADERS priority value" do srv << CONNECTION_PREFACE_MAGIC srv << f.generate(settings_frame) @@ -141,7 +141,7 @@ expect(stream.weight).to eq 20 end - it 'should process connection management frames after GOAWAY' do + it "should process connection management frames after GOAWAY" do srv << CONNECTION_PREFACE_MAGIC srv << f.generate(settings_frame) srv << f.generate(headers_frame) @@ -151,8 +151,8 @@ end end - context 'API' do - it '.goaway should generate GOAWAY frame with last processed stream ID' do + context "API" do + it ".goaway should generate GOAWAY frame with last processed stream ID" do srv << CONNECTION_PREFACE_MAGIC srv << f.generate(settings_frame) srv << f.generate(headers_frame) @@ -161,10 +161,10 @@ expect(frame[:type]).to eq :goaway expect(frame[:last_stream]).to eq 1 expect(frame[:error]).to eq :internal_error - expect(frame[:payload]).to eq 'payload' + expect(frame[:payload]).to eq "payload" end - srv.goaway(:internal_error, 'payload') + srv.goaway(:internal_error, "payload") end end end diff --git a/spec/shared_examples/connection.rb b/spec/shared_examples/connection.rb index 1ee87a5..367a38f 100644 --- a/spec/shared_examples/connection.rb +++ b/spec/shared_examples/connection.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -RSpec.shared_examples 'a connection' do +RSpec.shared_examples "a connection" do let(:conn) { described_class.new } let(:f) { Framer.new } - context 'settings synchronization' do - it 'should reflect incoming settings when SETTINGS is received' do + context "settings synchronization" do + it "should reflect incoming settings when SETTINGS is received" do expect(conn.remote_settings[:settings_header_table_size]).to eq 4096 settings = settings_frame settings[:payload] = [[:settings_header_table_size, 256]] @@ -15,7 +15,7 @@ expect(conn.remote_settings[:settings_header_table_size]).to eq 256 end - it 'should send SETTINGS ACK when SETTINGS is received' do + it "should send SETTINGS ACK when SETTINGS is received" do settings = settings_frame settings[:payload] = [[:settings_header_table_size, 256]] @@ -34,16 +34,16 @@ end end - context 'flow control' do - it 'should initialize to default flow window' do + context "flow control" do + it "should initialize to default flow window" do expect(conn.remote_window).to eq DEFAULT_FLOW_WINDOW end - it 'should update connection and stream windows on SETTINGS' do + it "should update connection and stream windows on SETTINGS" do settings = settings_frame data = data_frame settings[:payload] = [[:settings_initial_window_size, 1024]] - data[:payload] = 'x' * 2048 + data[:payload] = "x" * 2048 stream = conn.new_stream @@ -59,7 +59,7 @@ expect(stream.remote_window).to eq(-1024) end - it 'should initialize streams with window specified by peer' do + it "should initialize streams with window specified by peer" do settings = settings_frame settings[:payload] = [[:settings_initial_window_size, 1024]] @@ -67,7 +67,7 @@ expect(conn.new_stream.remote_window).to eq 1024 end - it 'should observe connection flow control' do + it "should observe connection flow control" do settings = settings_frame data = data_frame settings[:payload] = [[:settings_max_frame_size, 65_535]] @@ -77,11 +77,11 @@ s2 = conn.new_stream s1.send headers_frame - s1.send data.merge(payload: 'x' * 65_000) + s1.send data.merge(payload: "x" * 65_000) expect(conn.remote_window).to eq 535 s2.send headers_frame - s2.send data.merge(payload: 'x' * 635) + s2.send data.merge(payload: "x" * 635) expect(conn.remote_window).to eq 0 expect(conn.buffered_amount).to eq 100 @@ -90,7 +90,7 @@ expect(conn.remote_window).to eq 900 end - it 'should update window when data received is over half of the maximum local window size' do + it "should update window when data received is over half of the maximum local window size" do settings = settings_frame data = data_frame conn = Client.new(settings_initial_window_size: 500) @@ -106,36 +106,36 @@ expect(frame[:stream]).to eq 0 expect(frame[:increment]).to eq 400 end - conn.receive f.generate(data.merge(payload: 'x' * 200, stream: s1.id)) - conn.receive f.generate(data.merge(payload: 'x' * 200, stream: s2.id)) + conn.receive f.generate(data.merge(payload: "x" * 200, stream: s1.id)) + conn.receive f.generate(data.merge(payload: "x" * 200, stream: s2.id)) expect(s1.local_window).to eq 300 expect(s2.local_window).to eq 300 expect(conn.local_window).to eq 500 end end - context 'connection management' do - it 'should respond to PING frames' do + context "connection management" do + it "should respond to PING frames" do conn << f.generate(settings_frame) expect(conn).to receive(:send) do |frame| expect(frame[:type]).to eq :ping expect(frame[:flags]).to eq [:ack] - expect(frame[:payload]).to eq '12345678' + expect(frame[:payload]).to eq "12345678" end conn << f.generate(ping_frame) end - it 'should fire callback on PONG' do + it "should fire callback on PONG" do conn << f.generate(settings_frame) pong = nil - conn.ping('12345678') { |d| pong = d } + conn.ping("12345678") { |d| pong = d } conn << f.generate(pong_frame) - expect(pong).to eq '12345678' + expect(pong).to eq "12345678" end - it 'should fire callback on receipt of GOAWAY' do + it "should fire callback on receipt of GOAWAY" do last_stream, payload, error = nil conn << f.generate(settings_frame) conn.on(:goaway) do |s, e, p| @@ -143,29 +143,29 @@ error = e payload = p end - conn << f.generate(goaway_frame.merge(last_stream: 17, payload: 'test')) + conn << f.generate(goaway_frame.merge(last_stream: 17, payload: "test")) expect(last_stream).to eq 17 expect(error).to eq :no_error - expect(payload).to eq 'test' + expect(payload).to eq "test" expect(conn).to be_closed end - it 'should raise error when opening new stream after sending GOAWAY' do + it "should raise error when opening new stream after sending GOAWAY" do conn.goaway expect(conn).to be_closed expect { conn.new_stream }.to raise_error(ConnectionClosed) end - it 'should raise error when opening new stream after receiving GOAWAY' do + it "should raise error when opening new stream after receiving GOAWAY" do conn << f.generate(settings_frame) conn << f.generate(goaway_frame) expect { conn.new_stream }.to raise_error(ConnectionClosed) end - it 'should not raise error when receiving connection management frames immediately after emitting goaway' do + it "should not raise error when receiving connection management frames immediately after emitting goaway" do conn.goaway expect(conn).to be_closed @@ -173,14 +173,14 @@ expect { conn << f.generate(ping_frame) }.not_to raise_error(ProtocolError) end - it 'should respond with protocol error when receiving goaway' do + it "should respond with protocol error when receiving goaway" do conn.goaway expect(conn).to be_closed expect { conn << f.generate(goaway_frame) }.to raise_error(ProtocolError) end - it 'should raise error on frame for invalid stream ID' do + it "should raise error on frame for invalid stream ID" do conn << f.generate(settings_frame) expect do @@ -188,7 +188,7 @@ end.to raise_error(ProtocolError) end - it 'should allow to change the frame size' do + it "should allow to change the frame size" do buffer = [] conn.on(:frame) do |bytes| buffer << bytes @@ -197,34 +197,34 @@ stream1.send headers_frame # splits big data - expect { stream1.data('a' * 16_385) }.to change { buffer.size }.by(2) + expect { stream1.data("a" * 16_385) }.to change { buffer.size }.by(2) conn << f.generate(settings_frame.merge(payload: [[:settings_max_frame_size, 65_536]])) stream2 = conn.new_stream stream2.send headers_frame - expect { stream2.data('a' * 16_385, end_stream: false) }.to change { buffer.size }.by(1) + expect { stream2.data("a" * 16_385, end_stream: false) }.to change { buffer.size }.by(1) end end - context 'stream management' do - it 'should initialize to default stream limit (100)' do + context "stream management" do + it "should initialize to default stream limit (100)" do expect(conn.local_settings[:settings_max_concurrent_streams]).to eq 100 end - it 'should change stream limit to received SETTINGS value' do + it "should change stream limit to received SETTINGS value" do conn << f.generate(settings_frame) expect(conn.remote_settings[:settings_max_concurrent_streams]).to eq 10 end - it 'should count open streams against stream limit' do + it "should count open streams against stream limit" do s = conn.new_stream expect(conn.active_stream_count).to eq 0 s.receive headers_frame expect(conn.active_stream_count).to eq 1 end - it 'should not count reserved streams against stream limit' do + it "should not count reserved streams against stream limit" do s1 = conn.new_stream s1.receive push_promise_frame expect(conn.active_stream_count).to eq 0 @@ -253,7 +253,7 @@ expect(s3).to be_closed end - it 'should not exceed stream limit set by peer' do + it "should not exceed stream limit set by peer" do conn << f.generate(settings_frame) expect do @@ -266,7 +266,7 @@ expect { conn.new_stream }.to raise_error(StreamLimitExceeded) end - it 'should initialize idle stream on PRIORITY frame' do + it "should initialize idle stream on PRIORITY frame" do conn << f.generate(settings_frame) stream = nil @@ -277,10 +277,10 @@ end end - context 'framing' do + context "framing" do let(:conn) { connected_conn } - it 'should require that split header blocks are a contiguous sequence' do + it "should require that split header blocks are a contiguous sequence" do headers = headers_frame headers[:flags] = [] @@ -290,7 +290,7 @@ end end - it 'should require that split promise blocks are a contiguous sequence' do + it "should require that split promise blocks are a contiguous sequence" do headers = push_promise_frame headers[:flags] = [] @@ -300,13 +300,13 @@ end end - it 'should raise connection error on decode of invalid frame' do + it "should raise connection error on decode of invalid frame" do frame = f.generate(data_frame) # Receiving DATA on unopened stream 1 is an error. # Connection errors emit protocol error frames expect { conn << frame }.to raise_error(ProtocolError) end - it 'should emit encoded frames via on(:frame)' do + it "should emit encoded frames via on(:frame)" do bytes = nil conn.on(:frame) { |d| bytes = d } conn.settings(settings_max_concurrent_streams: 10, @@ -315,23 +315,23 @@ expect(bytes).to eq f.generate(settings_frame) end - it 'should compress stream headers' do + it "should compress stream headers" do conn.on(:frame) do |bytes| - expect(bytes).not_to include('get') - expect(bytes).not_to include('http') - expect(bytes).not_to include('www.example.org') # should be huffman encoded + expect(bytes).not_to include("get") + expect(bytes).not_to include("http") + expect(bytes).not_to include("www.example.org") # should be huffman encoded end stream = conn.new_stream stream.headers({ - ':method' => 'get', - ':scheme' => 'http', - ':authority' => 'www.example.org', - ':path' => '/resource' + ":method" => "get", + ":scheme" => "http", + ":authority" => "www.example.org", + ":path" => "/resource" }) end - it 'should generate CONTINUATION if HEADERS is too long' do + it "should generate CONTINUATION if HEADERS is too long" do headers = [] conn.on(:frame) do |bytes| # bytes[3]: frame's type field @@ -340,11 +340,11 @@ stream = conn.new_stream stream.headers({ - ':method' => 'get', - ':scheme' => 'http', - ':authority' => 'www.example.org', - ':path' => '/resource', - 'custom' => 'q' * 44_000 + ":method" => "get", + ":scheme" => "http", + ":authority" => "www.example.org", + ":path" => "/resource", + "custom" => "q" * 44_000 }, end_stream: true) expect(headers.size).to eq 3 expect(headers[0][:type]).to eq :headers @@ -355,7 +355,7 @@ expect(headers[2][:flags]).to eq [:end_headers] end - it 'should not generate CONTINUATION if HEADERS fits exactly in a frame' do + it "should not generate CONTINUATION if HEADERS fits exactly in a frame" do headers = [] conn.on(:frame) do |bytes| # bytes[3]: frame's type field @@ -364,11 +364,11 @@ stream = conn.new_stream stream.headers({ - ':method' => 'get', - ':scheme' => 'http', - ':authority' => 'www.example.org', - ':path' => '/resource', - 'custom' => 'q' * 18_682 # this number should be updated when Huffman table is changed + ":method" => "get", + ":scheme" => "http", + ":authority" => "www.example.org", + ":path" => "/resource", + "custom" => "q" * 18_682 # this number should be updated when Huffman table is changed }, end_stream: true) expect(headers[0][:length]).to eq conn.remote_settings[:settings_max_frame_size] expect(headers.size).to eq 1 @@ -377,7 +377,7 @@ expect(headers[0][:flags]).to include(:end_stream) end - it 'should not generate CONTINUATION if HEADERS fits exactly in a frame' do + it "should not generate CONTINUATION if HEADERS fits exactly in a frame" do headers = [] conn.on(:frame) do |bytes| # bytes[3]: frame's type field @@ -386,11 +386,11 @@ stream = conn.new_stream stream.headers({ - ':method' => 'get', - ':scheme' => 'http', - ':authority' => 'www.example.org', - ':path' => '/resource', - 'custom' => 'q' * 18_682 # this number should be updated when Huffman table is changed + ":method" => "get", + ":scheme" => "http", + ":authority" => "www.example.org", + ":path" => "/resource", + "custom" => "q" * 18_682 # this number should be updated when Huffman table is changed }, end_stream: true) expect(headers[0][:length]).to eq conn.remote_settings[:settings_max_frame_size] expect(headers.size).to eq 1 @@ -399,7 +399,7 @@ expect(headers[0][:flags]).to include(:end_stream) end - it 'should generate CONTINUATION if HEADERS exceed the max payload by one byte' do + it "should generate CONTINUATION if HEADERS exceed the max payload by one byte" do headers = [] conn.on(:frame) do |bytes| headers << f.parse(bytes) if [1, 5, 9].include?(bytes[3].ord) @@ -407,11 +407,11 @@ stream = conn.new_stream stream.headers({ - ':method' => 'get', - ':scheme' => 'http', - ':authority' => 'www.example.org', - ':path' => '/resource', - 'custom' => 'q' * 18_683 # this number should be updated when Huffman table is changed + ":method" => "get", + ":scheme" => "http", + ":authority" => "www.example.org", + ":path" => "/resource", + "custom" => "q" * 18_683 # this number should be updated when Huffman table is changed }, end_stream: true) expect(headers[0][:length]).to eq conn.remote_settings[:settings_max_frame_size] expect(headers[1][:length]).to eq 1 @@ -422,8 +422,8 @@ expect(headers[1][:flags]).to eq [:end_headers] end end - context 'API' do - it '.settings should emit SETTINGS frames' do + context "API" do + it ".settings should emit SETTINGS frames" do expect(conn).to receive(:send) do |frame| expect(frame[:type]).to eq :settings expect(frame[:payload]).to eq([ @@ -437,16 +437,16 @@ settings_initial_window_size: 0x7fffffff) end - it '.ping should generate PING frames' do + it ".ping should generate PING frames" do expect(conn).to receive(:send) do |frame| expect(frame[:type]).to eq :ping - expect(frame[:payload]).to eq 'somedata' + expect(frame[:payload]).to eq "somedata" end - conn.ping('somedata') + conn.ping("somedata") end - it '.window_update should emit WINDOW_UPDATE frames' do + it ".window_update should emit WINDOW_UPDATE frames" do expect(conn).to receive(:send) do |frame| expect(frame[:type]).to eq :window_update expect(frame[:increment]).to eq 20 diff --git a/spec/stream_spec.rb b/spec/stream_spec.rb index 21bba9c..fd9afb1 100644 --- a/spec/stream_spec.rb +++ b/spec/stream_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'helper' +require "helper" RSpec.describe HTTP2::Stream do include FrameHelpers @@ -8,159 +8,159 @@ let(:client) { Client.new } let(:stream) { client.new_stream } - context 'stream states' do - it 'should initiliaze all streams to IDLE' do + context "stream states" do + it "should initiliaze all streams to IDLE" do expect(stream.state).to eq :idle end - it 'should set custom stream priority' do + it "should set custom stream priority" do stream = client.new_stream(weight: 3, dependency: 2, exclusive: true) expect(stream.weight).to eq 3 end - context 'idle' do - it 'should transition to open on sent HEADERS' do + context "idle" do + it "should transition to open on sent HEADERS" do stream.send headers_frame expect(stream.state).to eq :open end - it 'should transition to open on received HEADERS' do + it "should transition to open on received HEADERS" do stream.receive headers_frame expect(stream.state).to eq :open end - it 'should transition to reserved (local) on sent PUSH_PROMISE' do + it "should transition to reserved (local) on sent PUSH_PROMISE" do stream.send push_promise_frame expect(stream.state).to eq :reserved_local end - it 'should transition to reserved (remote) on received PUSH_PROMISE' do + it "should transition to reserved (remote) on received PUSH_PROMISE" do stream.receive push_promise_frame expect(stream.state).to eq :reserved_remote end - it 'should reprioritize stream on sent PRIORITY' do + it "should reprioritize stream on sent PRIORITY" do expect { stream.send priority_frame }.to_not raise_error expect(stream.weight).to eq 20 end - it 'should reprioritize stream on received PRIORITY' do + it "should reprioritize stream on received PRIORITY" do expect { stream.send priority_frame }.to_not raise_error expect(stream.weight).to eq 20 end end - context 'reserved (local)' do + context "reserved (local)" do before { stream.send push_promise_frame } - it 'should transition on sent PUSH_PROMISE' do + it "should transition on sent PUSH_PROMISE" do expect(stream.state).to eq :reserved_local end - it 'should allow HEADERS to be sent' do + it "should allow HEADERS to be sent" do expect { stream.send headers_frame }.to_not raise_error end - it 'should raise error if sending invalid frames' do + it "should raise error if sending invalid frames" do frame_types.reject { |frame| %i[headers rst_stream].include?(frame[:type]) }.each do |type| expect { stream.dup.send type }.to raise_error InternalError end end - it 'should raise error on receipt of invalid frames' do + it "should raise error on receipt of invalid frames" do what_types = frame_types.reject { |frame| %i[priority window_update rst_stream].include?(frame[:type]) } what_types.each do |type| expect { stream.dup.receive type }.to raise_error InternalError end end - it 'should transition to half closed (remote) on sent HEADERS' do + it "should transition to half closed (remote) on sent HEADERS" do stream.send headers_frame expect(stream.state).to eq :half_closed_remote end - it 'should transition to closed on sent RST_STREAM' do + it "should transition to closed on sent RST_STREAM" do stream.close expect(stream.state).to eq :closed end - it 'should transition to closed on received RST_STREAM' do + it "should transition to closed on received RST_STREAM" do stream.receive rst_stream_frame expect(stream.state).to eq :closed end - it 'should reprioritize stream on PRIORITY' do + it "should reprioritize stream on PRIORITY" do expect { stream.receive priority_frame }.to_not raise_error expect(stream.weight).to eq 20 end - it 'should increment remote_window on received WINDOW_UPDATE' do + it "should increment remote_window on received WINDOW_UPDATE" do expect { stream.receive window_update_frame }.to_not raise_error expect(stream.remote_window).to eq DEFAULT_FLOW_WINDOW + window_update_frame[:increment] end - it 'should not increment connection remote_window on received WINDOW_UPDATE' do + it "should not increment connection remote_window on received WINDOW_UPDATE" do expect { stream.receive window_update_frame }.to_not raise_error expect(client.remote_window).to eq DEFAULT_FLOW_WINDOW end end - context 'reserved (remote)' do + context "reserved (remote)" do before { stream.receive push_promise_frame } - it 'should transition on received PUSH_PROMISE' do + it "should transition on received PUSH_PROMISE" do expect(stream.state).to eq :reserved_remote end - it 'should raise error if sending invalid frames' do + it "should raise error if sending invalid frames" do frame_types.reject { |frame| %i[priority rst_stream window_update].include?(frame[:type]) }.each do |type| expect { stream.dup.send type }.to raise_error InternalError end end - it 'should raise error on receipt of invalid frames' do + it "should raise error on receipt of invalid frames" do frame_types.reject { |frame| %i[headers rst_stream].include?(frame[:type]) }.each do |type| expect { stream.dup.receive type }.to raise_error InternalError end end - it 'should transition to half closed (local) on received HEADERS' do + it "should transition to half closed (local) on received HEADERS" do stream.receive headers_frame expect(stream.state).to eq :half_closed_local end - it 'should transition to closed on sent RST_STREAM' do + it "should transition to closed on sent RST_STREAM" do stream.close expect(stream.state).to eq :closed end - it 'should transition to closed on received RST_STREAM' do + it "should transition to closed on received RST_STREAM" do stream.receive rst_stream_frame expect(stream.state).to eq :closed end - it 'should reprioritize stream on PRIORITY' do + it "should reprioritize stream on PRIORITY" do expect { stream.send priority_frame }.to_not raise_error expect(stream.weight).to eq 20 end - it 'should increment local_window on sent WINDOW_UPDATE' do + it "should increment local_window on sent WINDOW_UPDATE" do expect { stream.send window_update_frame }.to_not raise_error expect(stream.local_window).to eq DEFAULT_FLOW_WINDOW + window_update_frame[:increment] end end - context 'open' do + context "open" do before { stream.receive headers_frame } - it 'should allow any valid frames types to be sent' do + it "should allow any valid frames types to be sent" do (frame_types - [ping_frame, goaway_frame, settings_frame]).each do |type| expect { stream.dup.send type }.to_not raise_error end end - it 'should allow frames of any type to be received' do + it "should allow frames of any type to be received" do frame_types.each do |type| expect { stream.dup.receive type }.to_not raise_error end end - it 'should transition to half closed (local) if sending END_STREAM' do + it "should transition to half closed (local) if sending END_STREAM" do [data_frame, headers_frame].each do |frame| s = stream.dup @@ -169,7 +169,7 @@ end end - it 'should transition to half closed (remote) if receiving END_STREAM' do + it "should transition to half closed (remote) if receiving END_STREAM" do [data_frame, headers_frame].each do |frame| s = stream.dup f = frame.dup @@ -180,7 +180,7 @@ end end - it 'should transition to half closed if remote opened with END_STREAM' do + it "should transition to half closed if remote opened with END_STREAM" do s = client.new_stream hclose = headers_frame hclose[:flags] = [:end_stream] @@ -189,7 +189,7 @@ expect(s.state).to eq :half_closed_remote end - it 'should transition to half closed if local opened with END_STREAM' do + it "should transition to half closed if local opened with END_STREAM" do s = client.new_stream hclose = headers_frame hclose[:flags] = [:end_stream] @@ -198,18 +198,18 @@ expect(s.state).to eq :half_closed_local end - it 'should transition to closed if sending RST_STREAM' do + it "should transition to closed if sending RST_STREAM" do stream.close expect(stream.state).to eq :closed expect(stream).to be_closed end - it 'should transition to closed if receiving RST_STREAM' do + it "should transition to closed if receiving RST_STREAM" do stream.receive rst_stream_frame expect(stream.state).to eq :closed end - it 'should emit :active on open transition' do + it "should emit :active on open transition" do openp = false openr = false sp = client.new_stream @@ -224,7 +224,7 @@ expect(openr).to be_truthy end - it 'should not emit :active on transition from open' do + it "should not emit :active on transition from open" do order = [] stream = client.new_stream @@ -240,7 +240,7 @@ expect(order).to eq %i[active half_close] end - it 'should emit :close on close transition' do + it "should emit :close on close transition" do closep = false closer = false sp = stream.dup @@ -256,7 +256,7 @@ expect(closer).to be_truthy end - it 'should emit :close after frame is processed' do + it "should emit :close after frame is processed" do order = [] stream = client.new_stream @@ -275,33 +275,33 @@ expect(order).to eq %i[active half_close data close] end - it 'should emit :close with reason' do + it "should emit :close with reason" do reason = nil stream.on(:close) { |r| reason = r } stream.receive rst_stream_frame expect(reason).not_to be_nil end - it 'should reprioritize stream on sent PRIORITY' do + it "should reprioritize stream on sent PRIORITY" do expect { stream.send priority_frame }.to_not raise_error expect(stream.weight).to eq 20 end - it 'should reprioritize stream on received PRIORITY' do + it "should reprioritize stream on received PRIORITY" do expect { stream.receive priority_frame }.to_not raise_error expect(stream.weight).to eq 20 end end - context 'half closed (local)' do + context "half closed (local)" do before { stream.send headers_frame.merge(flags: %i[end_headers end_stream]) } - it 'should raise error on attempt to send invalid frames' do + it "should raise error on attempt to send invalid frames" do frame_types.reject { |frame| %i[priority rst_stream window_update].include?(frame[:type]) }.each do |frame| expect { stream.dup.send frame }.to raise_error InternalError end end - it 'should transition to closed on receipt of END_STREAM flag' do + it "should transition to closed on receipt of END_STREAM flag" do [data_frame, headers_frame, continuation_frame].each do |frame| s = stream.dup f = frame.dup @@ -312,42 +312,42 @@ end end - it 'should transition to closed on receipt of RST_STREAM frame' do + it "should transition to closed on receipt of RST_STREAM frame" do stream.receive rst_stream_frame expect(stream.state).to eq :closed end - it 'should transition to closed if RST_STREAM frame is sent' do + it "should transition to closed if RST_STREAM frame is sent" do stream.send rst_stream_frame expect(stream.state).to eq :closed end - it 'should ignore received WINDOW_UPDATE frames' do + it "should ignore received WINDOW_UPDATE frames" do expect { stream.receive window_update_frame }.to_not raise_error expect(stream.state).to eq :half_closed_local end - it 'should ignore received PRIORITY frames' do + it "should ignore received PRIORITY frames" do expect { stream.receive priority_frame }.to_not raise_error expect(stream.state).to eq :half_closed_local end - it 'should reprioritize stream on sent PRIORITY' do + it "should reprioritize stream on sent PRIORITY" do expect { stream.send priority_frame }.to_not raise_error expect(stream.weight).to eq 20 end - it 'should reprioritize stream (and decendants) on received PRIORITY' do + it "should reprioritize stream (and decendants) on received PRIORITY" do expect { stream.receive priority_frame }.to_not raise_error expect(stream.weight).to eq 20 end - it 'should increment local_window on sent WINDOW_UPDATE' do + it "should increment local_window on sent WINDOW_UPDATE" do expect { stream.send window_update_frame }.to_not raise_error expect(stream.local_window).to eq DEFAULT_FLOW_WINDOW + window_update_frame[:increment] end - it 'should emit :half_close event on transition' do + it "should emit :half_close event on transition" do order = [] stream = client.new_stream stream.on(:active) { order << :active } @@ -360,7 +360,7 @@ expect(order).to eq %i[active half_close] end - it 'should emit :close event on transition to closed' do + it "should emit :close event on transition to closed" do closed = false stream.on(:close) { closed = true } stream.receive rst_stream_frame @@ -370,10 +370,10 @@ end end - context 'half closed (remote)' do + context "half closed (remote)" do before { stream.receive headers_frame.merge(flags: %i[end_headers end_stream]) } - it 'should raise STREAM_CLOSED error on reciept of frames' do + it "should raise STREAM_CLOSED error on reciept of frames" do (frame_types - [priority_frame, rst_stream_frame, window_update_frame]).each do |frame| expect do stream.dup.receive frame @@ -381,7 +381,7 @@ end end - it 'should transition to closed if END_STREAM flag is sent' do + it "should transition to closed if END_STREAM flag is sent" do [data_frame, headers_frame].each do |frame| s = stream.dup @@ -391,16 +391,16 @@ end end - it 'should not transition to closed if END_STREAM flag is sent when overflowing window' do - stream.on(:close) { raise 'should not have closed' } + it "should not transition to closed if END_STREAM flag is sent when overflowing window" do + stream.on(:close) { raise "should not have closed" } data = { type: :data, flags: [], stream: stream.id } 4.times do data = data.merge(flags: [:end_stream]) if stream.remote_window < 16_384 - stream.send data.merge(payload: 'x' * 16_384) + stream.send data.merge(payload: "x" * 16_384) end end - it 'should transition to closed when send buffer is emptied' do + it "should transition to closed when send buffer is emptied" do o = Object.new expect(o).to receive(:tap).once stream.on(:close) do @@ -410,42 +410,42 @@ data = { type: :data, flags: [], stream: stream.id } 4.times do data = data.merge(flags: [:end_stream]) if stream.remote_window < 16_384 - stream.send data.merge(payload: 'x' * 16_384) + stream.send data.merge(payload: "x" * 16_384) end client << f.generate(settings_frame) client << Framer.new.generate(type: :window_update, stream: stream.id, increment: 16_384) end - it 'should transition to closed if RST_STREAM is sent' do + it "should transition to closed if RST_STREAM is sent" do stream.close expect(stream.state).to eq :closed end - it 'should transition to closed on reciept of RST_STREAM frame' do + it "should transition to closed on reciept of RST_STREAM frame" do stream.receive rst_stream_frame expect(stream.state).to eq :closed end - it 'should ignore sent WINDOW_UPDATE frames' do + it "should ignore sent WINDOW_UPDATE frames" do expect { stream.send window_update_frame }.to_not raise_error expect(stream.state).to eq :half_closed_remote end - it 'should increment remote_window on received WINDOW_UPDATE' do + it "should increment remote_window on received WINDOW_UPDATE" do expect { stream.receive window_update_frame }.to_not raise_error expect(stream.remote_window).to eq DEFAULT_FLOW_WINDOW + window_update_frame[:increment] end - it 'should reprioritize stream on sent PRIORITY' do + it "should reprioritize stream on sent PRIORITY" do expect { stream.send priority_frame }.to_not raise_error expect(stream.weight).to eq 20 end - it 'should reprioritize stream on received PRIORITY' do + it "should reprioritize stream on received PRIORITY" do expect { stream.receive priority_frame }.to_not raise_error expect(stream.weight).to eq 20 end - it 'should emit :half_close event on transition' do + it "should emit :half_close event on transition" do order = [] stream = client.new_stream stream.on(:active) { order << :active } @@ -458,7 +458,7 @@ expect(order).to eq %i[active half_close] end - it 'should emit :close event on close transition' do + it "should emit :close event on close transition" do closed = false stream.on(:close) { closed = true } stream.close @@ -468,14 +468,14 @@ end end - context 'closed' do - context 'remote closed stream' do + context "closed" do + context "remote closed stream" do before do stream.send headers_frame.merge(flags: %i[end_headers end_stream]) # half closed local stream.receive headers_frame.merge(flags: %i[end_headers end_stream]) # closed by remote end - it 'should raise STREAM_CLOSED on attempt to send frames' do + it "should raise STREAM_CLOSED on attempt to send frames" do (frame_types - [priority_frame, rst_stream_frame]).each do |frame| expect do stream.dup.send frame @@ -483,7 +483,7 @@ end end - it 'should raise STREAM_CLOSED on receipt of frame' do + it "should raise STREAM_CLOSED on receipt of frame" do (frame_types - [priority_frame, rst_stream_frame, window_update_frame]).each do |frame| expect do stream.dup.receive frame @@ -491,38 +491,38 @@ end end - it 'should allow PRIORITY, RST_STREAM to be sent' do + it "should allow PRIORITY, RST_STREAM to be sent" do expect { stream.send priority_frame }.to_not raise_error expect { stream.send rst_stream_frame }.to_not raise_error end - it 'should allow PRIORITY, RST_STREAM to be received' do + it "should allow PRIORITY, RST_STREAM to be received" do expect { stream.receive priority_frame }.to_not raise_error expect { stream.receive rst_stream_frame }.to_not raise_error end - it 'should reprioritize stream on sent PRIORITY' do + it "should reprioritize stream on sent PRIORITY" do expect { stream.send priority_frame }.to_not raise_error expect(stream.weight).to eq 20 end - it 'should reprioritize stream on received PRIORITY' do + it "should reprioritize stream on received PRIORITY" do expect { stream.receive priority_frame }.to_not raise_error expect(stream.weight).to eq 20 end - it 'should ignore received WINDOW_UPDATE frames' do + it "should ignore received WINDOW_UPDATE frames" do expect { stream.receive window_update_frame }.to_not raise_error expect(stream.state).to eq :closed end end - context 'local closed via RST_STREAM frame' do + context "local closed via RST_STREAM frame" do before do stream.send headers_frame # open stream.send rst_stream_frame # closed by local end - it 'should ignore received frames' do + it "should ignore received frames" do control_frames.each do |frame| expect do cb = [] @@ -561,12 +561,12 @@ # TODO: add test cases to ensure on(:priority) emitted after close - context 'flow control' do - it 'should initialize to default flow control window' do + context "flow control" do + it "should initialize to default flow control window" do expect(stream.remote_window).to eq DEFAULT_FLOW_WINDOW end - it 'should update window size on DATA frames only' do + it "should update window size on DATA frames only" do stream.send headers_frame # go to open expect(stream.remote_window).to eq DEFAULT_FLOW_WINDOW @@ -580,7 +580,7 @@ expect(stream.remote_window).to eq DEFAULT_FLOW_WINDOW - data_frame[:payload].bytesize end - it 'should update window size on receipt of WINDOW_UPDATE' do + it "should update window size on receipt of WINDOW_UPDATE" do stream.send headers_frame stream.send data_frame stream.receive window_update_frame @@ -590,7 +590,7 @@ ) end - it 'should observe session flow control' do + it "should observe session flow control" do settings = settings_frame data = data_frame settings[:payload] = [[:settings_initial_window_size, 1000]] @@ -601,10 +601,10 @@ s1 = client.new_stream s1.send headers_frame - s1.send data.merge(payload: 'x' * 900, flags: []) + s1.send data.merge(payload: "x" * 900, flags: []) expect(s1.remote_window).to eq 100 - s1.send data.merge(payload: 'x' * 200) + s1.send data.merge(payload: "x" * 200) expect(s1.remote_window).to eq 0 expect(s1.buffered_amount).to eq 100 @@ -613,7 +613,7 @@ expect(s1.remote_window).to eq 900 end - it 'should not update window when data received is less than half of maximum local window size' do + it "should not update window when data received is less than half of maximum local window size" do data = data_frame datalen = data[:payload].bytesize expect(stream).not_to receive(:send) do |frame| @@ -624,9 +624,9 @@ stream.receive data end - it 'should update window when data received is over half of the maximum local window size' do - data1 = data_frame.merge(payload: 'a' * 16_384, flags: []) - data2 = data_frame.merge(payload: 'a' * 16_384) + it "should update window when data received is over half of the maximum local window size" do + data1 = data_frame.merge(payload: "a" * 16_384, flags: []) + data2 = data_frame.merge(payload: "a" * 16_384) datalen = 16_384 * 2 expect(stream).to receive(:send) do |frame| expect(frame[:type]).to eq :window_update @@ -637,7 +637,7 @@ stream.receive data2 end - it 'should send a DATA frame with size 0 when there is no window, as long as end_stream flag is set' do + it "should send a DATA frame with size 0 when there is no window, as long as end_stream flag is set" do settings = settings_frame data = data_frame settings[:payload] = [[:settings_initial_window_size, 1000]] @@ -648,21 +648,21 @@ s1 = client.new_stream s1.send headers_frame - s1.send data.merge(payload: 'x' * 1000, flags: []) + s1.send data.merge(payload: "x" * 1000, flags: []) # check if window is exhausted expect(s1.remote_window).to be(0) expect(s1.send_buffer).to be_empty - s1.send data.merge(payload: '', flags: [:end_stream]) + s1.send data.merge(payload: "", flags: [:end_stream]) expect(s1.remote_window).to be(0) expect(s1.send_buffer).to be_empty end end - context 'client API' do - it '.reprioritize should emit PRIORITY frame' do + context "client API" do + it ".reprioritize should emit PRIORITY frame" do expect(stream).to receive(:send) do |frame| expect(frame[:type]).to eq :priority expect(frame[:weight]).to eq 30 @@ -671,20 +671,20 @@ stream.reprioritize weight: 30 end - it '.reprioritize should raise error if invoked by server' do + it ".reprioritize should raise error if invoked by server" do srv = Server.new stream = srv.new_stream expect { stream.reprioritize(weight: 10) }.to raise_error(InternalError) end - it '.headers should emit HEADERS frames' do + it ".headers should emit HEADERS frames" do payload = { - ':method' => 'GET', - ':scheme' => 'http', - ':host' => 'www.example.org', - ':path' => '/resource', - 'custom' => 'value' + ":method" => "GET", + ":scheme" => "http", + ":host" => "www.example.org", + ":path" => "/resource", + "custom" => "value" } expect(stream).to receive(:send) do |frame| @@ -696,22 +696,22 @@ stream.headers(payload, end_stream: false, end_headers: true) end - it '.data should emit DATA frames' do + it ".data should emit DATA frames" do expect(stream).to receive(:send) do |frame| expect(frame[:type]).to eq :data - expect(frame[:payload]).to eq 'text' + expect(frame[:payload]).to eq "text" expect(frame[:flags]).to be_empty end - stream.data('text', end_stream: false) + stream.data("text", end_stream: false) expect(stream).to receive(:send) do |frame| expect(frame[:flags]).to eq [:end_stream] end - stream.data('text') + stream.data("text") end - it '.data should split large DATA frames' do - data = 'x' * 16_384 * 2 + it ".data should split large DATA frames" do + data = "x" * 16_384 * 2 want = [ { type: :data, flags: [], length: 16_384 }, @@ -729,8 +729,8 @@ stream.data("#{data}x") end - it '.data should split large multibyte DATA frames' do - data = '🐼' * 16_384 + it ".data should split large multibyte DATA frames" do + data = "🐼" * 16_384 want = [ { type: :data, flags: [], length: 16_384 }, @@ -750,7 +750,7 @@ stream.data("#{data}x") end - it '.cancel should reset stream with cancel error code' do + it ".cancel should reset stream with cancel error code" do expect(stream).to receive(:send) do |frame| expect(frame[:type]).to eq :rst_stream expect(frame[:error]).to eq :cancel @@ -759,7 +759,7 @@ stream.cancel end - it '.refuse should reset stream with refused stream error code' do + it ".refuse should reset stream with refused stream error code" do expect(stream).to receive(:send) do |frame| expect(frame[:type]).to eq :rst_stream expect(frame[:error]).to eq :refused_stream @@ -768,7 +768,7 @@ stream.refuse end - it '.window_update should emit WINDOW_UPDATE frames' do + it ".window_update should emit WINDOW_UPDATE frames" do expect(stream).to receive(:send) do |frame| expect(frame[:type]).to eq :window_update expect(frame[:increment]).to eq 20 @@ -777,7 +777,7 @@ end end - context 'server API' do + context "server API" do let(:srv) { Server.new } let(:frm) { Framer.new } let(:client_stream) { client.new_stream } @@ -785,7 +785,7 @@ client.on(:frame) { |bytes| srv << bytes } end - it 'should emit received headers via on(:headers)' do + it "should emit received headers via on(:headers)" do headers = REQUEST_HEADERS recv = nil srv.on(:stream) do |stream| @@ -796,8 +796,8 @@ expect(recv).to eq headers end - it 'should emit received payload via on(:data)' do - payload = 'some-payload' + it "should emit received payload via on(:data)" do + payload = "some-payload" srv.on(:stream) do |stream| stream.on(:data) do |recv| expect(recv).to eq payload @@ -808,7 +808,7 @@ client_stream.data(payload) end - it 'should emit received priority parameters via on(:priority)' do + it "should emit received priority parameters via on(:priority)" do new_weight = 15 new_dependency = client_stream.id + 2 callback_called = false @@ -826,7 +826,7 @@ expect(callback_called).to be end - context 'push' do + context "push" do before(:each) do srv.on(:frame) { |bytes| client << bytes } srv.on(:stream) do |stream| @@ -836,13 +836,13 @@ client_stream.headers(REQUEST_HEADERS) end - it '.promise should emit server initiated stream' do + it ".promise should emit server initiated stream" do push = nil @server_stream.promise(REQUEST_HEADERS) { |pstream| push = pstream } expect(push.id).to eq 2 end - it '.promise push stream should have parent stream' do + it ".promise push stream should have parent stream" do push = nil @server_stream.promise(REQUEST_HEADERS) { |pstream| push = pstream } @@ -850,8 +850,8 @@ expect(push.parent.id).to eq @server_stream.id end - context 'stream states' do - it 'server: active > half close > close' do + context "stream states" do + it "server: active > half close > close" do order = [] @server_stream.promise(REQUEST_HEADERS) do |push| stream = push @@ -870,7 +870,7 @@ expect(order).to eq %i[reserved active half_close close] end - it 'client: promise_headers > active > headers > .. > data > close' do + it "client: promise_headers > active > headers > .. > data > close" do order = [] headers = [] promise_headers = [] @@ -896,7 +896,7 @@ @server_stream.promise(REQUEST_HEADERS) do |push| push.headers(RESPONSE_HEADERS) - push.data('somedata') + push.data("somedata") end expect(promise_headers).to eq(REQUEST_HEADERS) diff --git a/tasks/generate_huffman_table.rb b/tasks/generate_huffman_table.rb index f1ea1d5..a74b90f 100644 --- a/tasks/generate_huffman_table.rb +++ b/tasks/generate_huffman_table.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -desc 'Generate Huffman precompiled table in huffman_statemachine.rb' +desc "Generate Huffman precompiled table in huffman_statemachine.rb" task :generate_huffman_table do HuffmanTable::Node.generate_state_table end -require_relative '../lib/http/2/header/huffman' +require_relative "../lib/http/2/header/huffman" module HuffmanTable BITS_AT_ONCE = HTTP2::Header::Huffman::BITS_AT_ONCE @@ -68,7 +68,7 @@ def self.generate_machine (1 << BITS_AT_ONCE).times do |input| n = node - emit = String.new + emit = "".b (BITS_AT_ONCE - 1).downto(0) do |i| bit = (input & (1 << i)).zero? ? 0 : 1 n = n.next[bit] @@ -105,7 +105,7 @@ def self.generate_state_table id += 1 end - File.open(File.expand_path('../lib/http/2/header/huffman_statemachine.rb', File.dirname(__FILE__)), 'w') do |f| + File.open(File.expand_path("../lib/http/2/header/huffman_statemachine.rb", File.dirname(__FILE__)), "w") do |f| f.print <<~HEADER # Machine generated Huffman decoder state machine. # DO NOT EDIT THIS FILE. @@ -122,8 +122,8 @@ class Huffman HEADER id.times do |i| n = id_state[i] - f.print ' [' - string = (1 << BITS_AT_ONCE).times.map do |t| + f.print " [" + string = Array.new((1 << 4)) do |t| transition = n.transitions.fetch(t) emit = transition.emit unless emit == EOS @@ -133,7 +133,7 @@ class Huffman emit = bytes.first end "[#{emit.inspect}, #{state_id.fetch(transition.node)}]" - end.join(', ') + end.join(", ") f.print(string) f.print "],\n" end @@ -152,18 +152,16 @@ class << self # Test decoder def self.decode(input) - emit = '' + emit = "" n = root - nibbles = input.unpack('C*').flat_map { |b| [((b & 0xf0) >> 4), b & 0xf] } + nibbles = input.unpack("C*").flat_map { |b| [((b & 0xf0) >> 4), b & 0xf] } until nibbles.empty? nb = nibbles.shift t = n.transitions[nb] emit << t.emit n = t.node end - puts "len = #{emit.size} n.final = #{n.final} nibbles = #{nibbles}" unless n.final && nibbles.all? do |x| - x == 0xf - end + puts "len = #{emit.size} n.final = #{n.final} nibbles = #{nibbles}" unless n.final && nibbles.all?(0xf) emit end end