fast_ci 0.1.2 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +26 -0
- data/.rubocop.yml +3 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +154 -43
- data/LICENSE.txt +1 -1
- data/README.md +5 -1
- data/bin/fastci_brakeman +9 -0
- data/bin/fastci_bundle_audit +55 -0
- data/bin/fastci_rubycritic +10 -0
- data/fast_ci.gemspec +8 -5
- data/lib/fast_ci/brakeman/commandline.rb +12 -0
- data/lib/fast_ci/brakeman.rb +30 -0
- data/lib/fast_ci/configuration.rb +20 -5
- data/lib/fast_ci/dryrun_runner_prepend.rb +28 -0
- data/lib/fast_ci/extract_definitions.rb +44 -0
- data/lib/fast_ci/rspec_dryrun_formatter.rb +117 -0
- data/lib/fast_ci/rspec_formatter.rb +274 -0
- data/lib/fast_ci/rspec_run_formatter.rb +247 -0
- data/lib/fast_ci/ruby_critic/cli/application.rb +51 -0
- data/lib/fast_ci/runner_prepend.rb +107 -0
- data/lib/fast_ci/simple_cov/reporting.rb +39 -0
- data/lib/fast_ci/simple_cov.rb +68 -0
- data/lib/fast_ci/version.rb +1 -1
- data/lib/fast_ci.rb +112 -26
- data/lib/minitest/reporters/fastci_reporter.rb +287 -0
- data/lib/minitest/rubyci_plugin.rb +9 -0
- metadata +86 -9
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubycritic/cli/application'
|
4
|
+
require 'fast_ci'
|
5
|
+
require 'base64'
|
6
|
+
require 'zlib'
|
7
|
+
|
8
|
+
module FastCI
|
9
|
+
module RubyCritic
|
10
|
+
module Cli
|
11
|
+
class Application < ::RubyCritic::Cli::Application
|
12
|
+
def execute
|
13
|
+
events = []
|
14
|
+
events << ['ruby_critic_run'.upcase, {}]
|
15
|
+
status = super
|
16
|
+
|
17
|
+
content = File.read('tmp/rubycritic/report.json')
|
18
|
+
report = JSON.load(content)
|
19
|
+
modules = report['analysed_modules']
|
20
|
+
report['analysed_modules'] = {}
|
21
|
+
modules.each do |mod|
|
22
|
+
report['analysed_modules'][ mod['path'] ] = mod
|
23
|
+
mod['smells'].each do |smell|
|
24
|
+
location = smell['locations'].first
|
25
|
+
start_line = location['line'] - 1
|
26
|
+
end_line = start_line + 3
|
27
|
+
lines = File.readlines(location['path'])[start_line..end_line]
|
28
|
+
location['src'] = lines.join
|
29
|
+
smell['locations'][0] = location
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
compressed_data = ::Base64.strict_encode64(Zlib::Deflate.deflate(report.to_json, 9))
|
34
|
+
events << ['ruby_critic_exit_status'.upcase, ['0', { exitstatus: status, output: '', compressed_data: compressed_data }]]
|
35
|
+
|
36
|
+
if ENV['FSCI_REMOTE_TESTS'] == 'true'
|
37
|
+
json_events = {
|
38
|
+
build_id: FastCI.configuration.orig_build_id,
|
39
|
+
compressed_data: Base64.strict_encode64(Zlib::Deflate.deflate(JSON.fast_generate(events), 9)),
|
40
|
+
}
|
41
|
+
|
42
|
+
FastCI.send_events(json_events)
|
43
|
+
else
|
44
|
+
FastCI.report_ruby_critic(compressed_data, 'passed')
|
45
|
+
end
|
46
|
+
return status
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative "rspec_formatter"
|
3
|
+
require_relative "extract_definitions"
|
4
|
+
|
5
|
+
module FastCI
|
6
|
+
module RunnerPrepend
|
7
|
+
def run_specs(example_groups)
|
8
|
+
@rspec_started_at = Time.now
|
9
|
+
json_events = {
|
10
|
+
build_id: FastCI.configuration.orig_build_id,
|
11
|
+
compressed_data: Base64.strict_encode64(Zlib::Deflate.deflate(JSON.fast_generate([['RSPEC_RUN', { started_at: @rspec_started_at, test_env_number: ENV["TEST_ENV_NUMBER"] }]]), 9)),
|
12
|
+
}
|
13
|
+
FastCI.send_events(json_events)
|
14
|
+
|
15
|
+
examples_count = @world.example_count(example_groups)
|
16
|
+
|
17
|
+
example_groups = example_groups.reduce({}) do |acc, ex_group|
|
18
|
+
if acc[ex_group.file_path]
|
19
|
+
acc[ex_group.file_path] << ex_group
|
20
|
+
else
|
21
|
+
acc[ex_group.file_path] = [ex_group]
|
22
|
+
end
|
23
|
+
acc
|
24
|
+
end
|
25
|
+
|
26
|
+
FastCI.configure { |c| c.run_key = "rspec" }
|
27
|
+
|
28
|
+
FastCI.rspec_ws.on(:enq_request) do
|
29
|
+
example_groups.reduce({}) do |example_group_descriptions, (file, example_groups)|
|
30
|
+
example_groups.each do |example_group|
|
31
|
+
data = FastCI::ExtractDescriptions.new.call(example_group, count: true)
|
32
|
+
|
33
|
+
next if data[:test_count] == 0
|
34
|
+
|
35
|
+
if example_group_descriptions[file]
|
36
|
+
example_group_descriptions[file].merge!(data) do |k, v1, v2|
|
37
|
+
v1 + v2
|
38
|
+
end
|
39
|
+
else
|
40
|
+
example_group_descriptions[file] = data
|
41
|
+
end
|
42
|
+
end
|
43
|
+
example_group_descriptions
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
examples_passed = @configuration.reporter.report(examples_count) do |reporter|
|
48
|
+
@configuration.with_suite_hooks do
|
49
|
+
if examples_count == 0 && @configuration.fail_if_no_examples
|
50
|
+
return @configuration.failure_exit_code
|
51
|
+
end
|
52
|
+
|
53
|
+
formatter = FastCI::RspecFormatter.new(STDOUT)
|
54
|
+
|
55
|
+
reporter.register_listener(formatter, :example_finished)
|
56
|
+
if ENV['FSCI_REMOTE_TESTS'] == 'true'
|
57
|
+
reporter.register_listener(formatter, :start)
|
58
|
+
reporter.register_listener(formatter, :example_group_started)
|
59
|
+
reporter.register_listener(formatter, :example_started)
|
60
|
+
reporter.register_listener(formatter, :example_passed)
|
61
|
+
reporter.register_listener(formatter, :example_failed)
|
62
|
+
reporter.register_listener(formatter, :example_pending)
|
63
|
+
reporter.register_listener(formatter, :example_group_finished)
|
64
|
+
reporter.register_listener(formatter, :close)
|
65
|
+
end
|
66
|
+
|
67
|
+
FastCI.rspec_ws.on(:deq) do |tests|
|
68
|
+
tests.each do |test|
|
69
|
+
file, scoped_id = test.split(":", 2)
|
70
|
+
Thread.current[:fastci_scoped_ids] = scoped_id
|
71
|
+
example_groups[file].each do |file_group|
|
72
|
+
formatter.current_test_key = test
|
73
|
+
|
74
|
+
file_group.run(reporter)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
formatter.dump_and_reset
|
79
|
+
end
|
80
|
+
|
81
|
+
FastCI.rspec_await
|
82
|
+
|
83
|
+
formatter.passed?
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
exit_code(examples_passed)
|
88
|
+
end
|
89
|
+
|
90
|
+
def exit_code(examples_passed=false)
|
91
|
+
if ENV['FSCI_REMOTE_TESTS'] == 'true'
|
92
|
+
run_time = Time.now - (@rspec_started_at || 1.seconds.ago)
|
93
|
+
events = @world.non_example_failure ? [['RSPEC_RUN', { failed_after: run_time, test_env_number: ENV["TEST_ENV_NUMBER"] }]] : [['RSPEC_RUN', { succeed_after: run_time, test_env_number: ENV["TEST_ENV_NUMBER"] }]]
|
94
|
+
json_events = {
|
95
|
+
build_id: FastCI.configuration.orig_build_id,
|
96
|
+
compressed_data: Base64.strict_encode64(Zlib::Deflate.deflate(JSON.fast_generate(events), 9)),
|
97
|
+
}
|
98
|
+
FastCI.send_events(json_events)
|
99
|
+
end
|
100
|
+
|
101
|
+
return @configuration.error_exit_code || @configuration.failure_exit_code if @world.non_example_failure
|
102
|
+
return @configuration.failure_exit_code unless examples_passed
|
103
|
+
|
104
|
+
0
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module FastCI
|
2
|
+
module SimpleCov
|
3
|
+
module Reporting
|
4
|
+
def self.included base
|
5
|
+
base.instance_eval do
|
6
|
+
unless (ENV['FAST_CI_SECRET_KEY'] || '').empty?
|
7
|
+
def write_last_run(result)
|
8
|
+
::SimpleCov::LastRun.write(result:
|
9
|
+
result.coverage_statistics.transform_values do |stats|
|
10
|
+
round_coverage(stats.percent)
|
11
|
+
end)
|
12
|
+
|
13
|
+
source = {}
|
14
|
+
|
15
|
+
result.source_files.each do |source_file|
|
16
|
+
source[source_file.filename.gsub(root, '')] = source_file.src
|
17
|
+
end
|
18
|
+
|
19
|
+
result_json = {}
|
20
|
+
|
21
|
+
result.as_json.each do |command, data|
|
22
|
+
result_json[command] = data
|
23
|
+
data['coverage'].clone.each do |src, file_data|
|
24
|
+
result_json[command]['coverage'].delete(src)
|
25
|
+
|
26
|
+
file_data['src'] = source[src.gsub(root, '')]
|
27
|
+
|
28
|
+
result_json[command]['coverage'][src.gsub(root, '')] = file_data
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
FastCI.report_simplecov(result_json.to_json)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative "simple_cov/reporting"
|
2
|
+
|
3
|
+
module FastCI
|
4
|
+
module SimpleCov
|
5
|
+
if ENV['FSCI_REMOTE_TESTS'] == 'true' && ENV["SIMPLECOV_ACTIVE"] && ENV['DRYRUN'] != 'true'
|
6
|
+
::SimpleCov.send(:at_exit) do
|
7
|
+
::SimpleCov.result.format!
|
8
|
+
|
9
|
+
config = {
|
10
|
+
minimum_coverage: ::SimpleCov.minimum_coverage,
|
11
|
+
maximum_coverage_drop: ::SimpleCov.maximum_coverage_drop,
|
12
|
+
minimum_coverage_by_file: ::SimpleCov.minimum_coverage_by_file,
|
13
|
+
}
|
14
|
+
rspec_runner_index = ENV["TEST_ENV_NUMBER".freeze].to_i
|
15
|
+
events = [['simplecov_config'.upcase, [ rspec_runner_index, config ]]]
|
16
|
+
|
17
|
+
json_events = {
|
18
|
+
build_id: FastCI.configuration.orig_build_id,
|
19
|
+
compressed_data: Base64.strict_encode64(Zlib::Deflate.deflate(JSON.fast_generate(events), 9)),
|
20
|
+
}
|
21
|
+
|
22
|
+
FastCI.send_events(json_events)
|
23
|
+
end
|
24
|
+
|
25
|
+
module PrependSc
|
26
|
+
def start(*args, &block)
|
27
|
+
add_filter "tmp"
|
28
|
+
merge_timeout 3600
|
29
|
+
command_name "RSpec_#{ENV["TEST_ENV_NUMBER".freeze].to_i}"
|
30
|
+
|
31
|
+
if ENV["NO_COVERAGE"]
|
32
|
+
use_merging false
|
33
|
+
return
|
34
|
+
end
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
::SimpleCov.singleton_class.prepend(PrependSc)
|
39
|
+
|
40
|
+
module Scf
|
41
|
+
def format!
|
42
|
+
return if ENV["NO_COVERAGE"]
|
43
|
+
rspec_runner_index = ENV["TEST_ENV_NUMBER".freeze].to_i
|
44
|
+
|
45
|
+
original_result_json = if ENV['CI_PROJECT_DIR'].present?
|
46
|
+
JSON.fast_generate(original_result.transform_keys {|key| key.sub(ENV['CI_PROJECT_DIR'], '/app') })
|
47
|
+
else
|
48
|
+
JSON.fast_generate(original_result)
|
49
|
+
end
|
50
|
+
compressed_data = Base64.strict_encode64(Zlib::Deflate.deflate(original_result_json, 9))
|
51
|
+
events = [['simplecov_result'.upcase, [ rspec_runner_index, compressed_data ]]]
|
52
|
+
|
53
|
+
json_events = {
|
54
|
+
build_id: FastCI.configuration.orig_build_id,
|
55
|
+
compressed_data: Base64.strict_encode64(Zlib::Deflate.deflate(JSON.fast_generate(events), 9)),
|
56
|
+
}
|
57
|
+
|
58
|
+
FastCI.send_events(json_events)
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
::SimpleCov::Result.prepend(Scf)
|
64
|
+
else
|
65
|
+
::SimpleCov.send(:include, FastCI::SimpleCov::Reporting) unless ENV['DRYRUN'] == 'true'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/fast_ci/version.rb
CHANGED
data/lib/fast_ci.rb
CHANGED
@@ -3,10 +3,12 @@
|
|
3
3
|
require_relative "fast_ci/version"
|
4
4
|
require_relative "fast_ci/configuration"
|
5
5
|
require_relative "fast_ci/exceptions"
|
6
|
+
require_relative "minitest/reporters/FastCI_reporter"
|
6
7
|
|
7
8
|
require "async"
|
8
9
|
require "async/http/endpoint"
|
9
10
|
require "async/websocket/client"
|
11
|
+
require "net/http"
|
10
12
|
|
11
13
|
module FastCI
|
12
14
|
class Error < StandardError; end
|
@@ -20,27 +22,86 @@ module FastCI
|
|
20
22
|
yield(configuration)
|
21
23
|
end
|
22
24
|
|
23
|
-
def
|
24
|
-
@
|
25
|
+
def rspec_ws
|
26
|
+
@rspec_ws ||= WebSocket.new('rspec')
|
25
27
|
end
|
26
28
|
|
27
|
-
def
|
28
|
-
|
29
|
+
def minitest_ws
|
30
|
+
@minitest_ws ||= WebSocket.new('minitest')
|
31
|
+
end
|
32
|
+
|
33
|
+
def report_simplecov(results)
|
34
|
+
post_report(report_options('simplecov', results))
|
35
|
+
end
|
36
|
+
|
37
|
+
def report_ruby_critic(compressed_data, status)
|
38
|
+
post_report(report_options('ruby_critic', compressed_data).merge({ status: status }))
|
39
|
+
end
|
40
|
+
|
41
|
+
def report_brakeman(compressed_data, status)
|
42
|
+
post_report(report_options('brakeman', compressed_data).merge({ status: status }))
|
43
|
+
end
|
44
|
+
|
45
|
+
def report_bundler_audit(compressed_data, status)
|
46
|
+
post_report(report_options('bundler_audit', compressed_data).merge({ status: status }))
|
47
|
+
end
|
48
|
+
|
49
|
+
def rspec_await
|
50
|
+
rspec_ws.await
|
51
|
+
end
|
52
|
+
|
53
|
+
def minitest_await
|
54
|
+
minitest_ws.await
|
29
55
|
end
|
30
56
|
|
31
57
|
def debug(msg)
|
32
|
-
puts "\n\e[36mDEBUG: \e[0m #{msg}\n" if ENV["
|
58
|
+
puts "\n\e[36mDEBUG: \e[0m #{msg}\n" if ENV["fast_ci_DEBUG"]
|
59
|
+
end
|
60
|
+
|
61
|
+
def report_options(run_key, content)
|
62
|
+
{
|
63
|
+
build_id: FastCI.configuration.build_id,
|
64
|
+
run_key: run_key,
|
65
|
+
secret_key: FastCI.configuration.secret_key,
|
66
|
+
branch: FastCI.configuration.branch,
|
67
|
+
commit: FastCI.configuration.commit,
|
68
|
+
commit_msg: FastCI.configuration.commit_msg,
|
69
|
+
author: FastCI.configuration.author.to_json,
|
70
|
+
content: content
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def post_report(data)
|
75
|
+
uri = URI("#{FastCI.configuration.fastci_api_url}/api/runs")
|
76
|
+
res = Net::HTTP.post_form(uri, data)
|
77
|
+
end
|
78
|
+
|
79
|
+
def send_events(data)
|
80
|
+
reset_webmock = false
|
81
|
+
if defined?(WebMock)
|
82
|
+
reset_webmock = !WebMock.net_connect_allowed?
|
83
|
+
WebMock.allow_net_connect!
|
84
|
+
end
|
85
|
+
|
86
|
+
uri = URI("#{FastCI.configuration.fastci_main_url}/api/v1/gitlab_events")
|
87
|
+
res = Net::HTTP.post_form(uri, data)
|
88
|
+
|
89
|
+
if reset_webmock
|
90
|
+
WebMock.disable_net_connect!
|
91
|
+
end
|
33
92
|
end
|
34
93
|
end
|
35
94
|
|
36
95
|
class WebSocket
|
37
96
|
attr_reader :node_index
|
97
|
+
attr_accessor :connection, :task, :run_key
|
38
98
|
|
39
99
|
SUPPORTED_EVENTS = %i[enq_request deq].freeze
|
40
100
|
|
41
|
-
def initialize
|
101
|
+
def initialize(run_key)
|
42
102
|
@on = {}
|
43
103
|
@ref = 0
|
104
|
+
@run_key = run_key
|
44
105
|
end
|
45
106
|
|
46
107
|
def on(event, &block)
|
@@ -50,34 +111,49 @@ module FastCI
|
|
50
111
|
@on[event] = block
|
51
112
|
end
|
52
113
|
|
53
|
-
def send_msg(
|
114
|
+
def send_msg(event, payload = {})
|
54
115
|
FastCI.debug("ws#send_msg: #{event} -> #{payload.inspect}")
|
55
116
|
connection.write({ "topic": topic, "event": event, "payload": payload, "ref": ref })
|
56
117
|
connection.flush
|
57
118
|
end
|
58
119
|
|
59
|
-
def
|
60
|
-
before_start_connection
|
120
|
+
def connect_to_ws
|
61
121
|
Async do |task|
|
122
|
+
before_start_connection
|
62
123
|
Async::WebSocket::Client.connect(endpoint) do |connection|
|
63
124
|
after_start_connection
|
64
|
-
|
125
|
+
self.connection = connection
|
126
|
+
self.task = task
|
127
|
+
yield
|
128
|
+
ensure
|
65
129
|
|
130
|
+
leave
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def await(retry_count = 0)
|
136
|
+
connect_to_ws do
|
137
|
+
send_msg("phx_join")
|
138
|
+
|
139
|
+
begin
|
66
140
|
while message = connection.read
|
67
141
|
FastCI.debug("ws#msg_received: #{message.inspect}")
|
68
142
|
|
69
143
|
response = message.dig(:payload, :response)
|
70
144
|
|
71
145
|
case response&.dig(:event) || message[:event]
|
146
|
+
when "phx_error"
|
147
|
+
raise("[FastCI] Unexpected error")
|
72
148
|
when "join"
|
73
|
-
handle_join(
|
149
|
+
handle_join(response)
|
74
150
|
when "deq_request"
|
75
|
-
handle_deq_request(
|
151
|
+
handle_deq_request(response)
|
76
152
|
when "deq"
|
77
153
|
if (tests = response[:tests]).any?
|
78
154
|
result = @on[:deq].call(tests)
|
79
155
|
task.async do
|
80
|
-
send_msg(
|
156
|
+
send_msg("deq", result)
|
81
157
|
end
|
82
158
|
else
|
83
159
|
break
|
@@ -88,15 +164,23 @@ module FastCI
|
|
88
164
|
puts response
|
89
165
|
end
|
90
166
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
167
|
+
rescue => e
|
168
|
+
puts e.message
|
169
|
+
puts e.backtrace.join("\n")
|
170
|
+
task&.stop
|
94
171
|
end
|
95
172
|
end
|
96
173
|
end
|
97
174
|
|
98
175
|
private
|
99
176
|
|
177
|
+
def leave
|
178
|
+
send_msg("leave")
|
179
|
+
connection.close
|
180
|
+
rescue StandardError => e
|
181
|
+
# noop
|
182
|
+
end
|
183
|
+
|
100
184
|
# https://github.com/bblimke/webmock/blob/b709ba22a2949dc3bfac662f3f4da88a21679c2e/lib/webmock/http_lib_adapters/async_http_client_adapter.rb#L8
|
101
185
|
def before_start_connection
|
102
186
|
if defined?(WebMock::HttpLibAdapters::AsyncHttpClientAdapter)
|
@@ -111,18 +195,18 @@ module FastCI
|
|
111
195
|
end
|
112
196
|
end
|
113
197
|
|
114
|
-
def handle_join(
|
198
|
+
def handle_join(response)
|
115
199
|
@node_index = response[:node_index]
|
116
200
|
|
117
201
|
FastCI.debug("NODE_INDEX: #{@node_index}")
|
118
202
|
|
119
|
-
send_msg(
|
203
|
+
send_msg("enq", { tests: @on[:enq_request].call }) if node_index.zero?
|
120
204
|
|
121
|
-
send_msg(
|
205
|
+
send_msg("deq") if response[:state] == "running"
|
122
206
|
end
|
123
207
|
|
124
|
-
def handle_deq_request(
|
125
|
-
send_msg(
|
208
|
+
def handle_deq_request(_response)
|
209
|
+
send_msg("deq")
|
126
210
|
end
|
127
211
|
|
128
212
|
def ref
|
@@ -130,21 +214,23 @@ module FastCI
|
|
130
214
|
end
|
131
215
|
|
132
216
|
def topic
|
133
|
-
"test_orchestrator:#{
|
217
|
+
"test_orchestrator:#{run_key}-#{FastCI.configuration.build_id}"
|
134
218
|
end
|
135
219
|
|
136
220
|
def endpoint
|
137
221
|
params = URI.encode_www_form({
|
138
222
|
build_id: FastCI.configuration.build_id,
|
139
|
-
run_key:
|
223
|
+
run_key: run_key,
|
140
224
|
secret_key: FastCI.configuration.secret_key,
|
225
|
+
branch: FastCI.configuration.branch,
|
141
226
|
commit: FastCI.configuration.commit,
|
142
|
-
|
227
|
+
commit_msg: FastCI.configuration.commit_msg,
|
228
|
+
author: FastCI.configuration.author.to_json
|
143
229
|
})
|
144
230
|
|
145
|
-
url = "
|
231
|
+
url = "wss://#{FastCI.configuration.api_url}/test_orchestrators/socket/websocket?#{params}"
|
146
232
|
|
147
|
-
Async::HTTP::Endpoint.parse(url)
|
233
|
+
Async::HTTP::Endpoint.parse(url, alpn_protocols: Async::HTTP::Protocol::HTTP11.names)
|
148
234
|
end
|
149
235
|
end
|
150
236
|
end
|