raptor 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/raptor/http1.rb CHANGED
@@ -21,7 +21,6 @@ module Raptor
21
21
  BODY_BUFFER_THRESHOLD = 256 * 1024
22
22
  CHUNKED_WRITE_THRESHOLD = 512 * 1024
23
23
  FILE_CHUNK_SIZE = 64 * 1024
24
- MAX_CHUNK_OVERHEAD = 16 * 1024
25
24
  READ_BUFFER_SIZE = 64 * 1024
26
25
  RESPONSE_BUFFER_CAPACITY = 4 * 1024
27
26
  KEEPALIVE_READ_TIMEOUT = 0.001
@@ -38,7 +37,6 @@ module Raptor
38
37
  h[status] = "HTTP/1.1 #{status}#{reason ? " #{reason}" : ""}\r\n".freeze
39
38
  end
40
39
 
41
- STATUS_WITH_NO_ENTITY_BODY = [204, 304, *100..199].freeze
42
40
  CONTINUE_RESPONSE = "HTTP/1.1 100 Continue\r\n\r\n"
43
41
  BAD_REQUEST_RESPONSE = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
44
42
  CONTENT_TOO_LARGE_RESPONSE = "HTTP/1.1 413 Content Too Large\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
@@ -48,6 +46,7 @@ module Raptor
48
46
  CONNECTION_KEEPALIVE = "keep-alive"
49
47
  EXPECT_100_CONTINUE = "100-continue"
50
48
  TRANSFER_ENCODING_CHUNKED = "chunked"
49
+ CHUNKED_TERMINATOR = "0\r\n\r\n"
51
50
 
52
51
  HTTP_CONNECTION = "HTTP_CONNECTION"
53
52
  HTTP_EXPECT = "HTTP_EXPECT"
@@ -56,10 +55,6 @@ module Raptor
56
55
  RACK_HIJACKED = "rack.hijacked"
57
56
  RACK_HIJACK_IO = "rack.hijack_io"
58
57
 
59
- ILLEGAL_HEADER_KEY_REGEX = /[\x00-\x20\(\)<>@,;:\\"\/\[\]\?=\{\}\x7F]/
60
- ILLEGAL_HEADER_VALUE_REGEX = /[\x00-\x08\x0A-\x1F]/
61
- CHUNK_SIZE_REGEX = /\A[0-9A-Fa-f]+\z/
62
-
63
58
  # Returns true when an HTTP/1.1 request lacks a valid `Host` header per
64
59
  # RFC 9112 section 3.2, where a valid value is a non-empty single-value
65
60
  # line.
@@ -102,59 +97,6 @@ module Raptor
102
97
  false
103
98
  end
104
99
 
105
- # Decodes a chunked transfer-encoded body buffer.
106
- #
107
- # Returns the decoded bytes and a state symbol: `:complete` when the
108
- # terminating zero-length chunk and trailer section were fully consumed,
109
- # `:too_large` when the decoded size would exceed `max_size`, `:malformed`
110
- # when a chunk-size line is not valid hex or chunk framing overhead exceeds
111
- # `MAX_CHUNK_OVERHEAD`, or `:incomplete` otherwise.
112
- #
113
- # @param buffer [String] the raw body buffer to decode
114
- # @param max_size [Integer, nil] maximum decoded body size, or nil for unlimited
115
- # @return [Array(String, Symbol)] decoded body and completion state
116
- #
117
- # @rbs (String buffer, ?Integer? max_size) -> [String, Symbol]
118
- def self.decode_chunked(buffer, max_size = nil)
119
- decoded = String.new
120
- offset = 0
121
- overhead = 0
122
-
123
- while offset < buffer.bytesize
124
- crlf = buffer.index("\r\n", offset)
125
- return [decoded, :incomplete] unless crlf
126
-
127
- size_line = buffer.byteslice(offset, crlf - offset)
128
- semicolon = size_line.index(";")
129
- size_part = semicolon ? size_line.byteslice(0, semicolon) : size_line
130
- return [decoded, :malformed] unless size_part.match?(CHUNK_SIZE_REGEX)
131
-
132
- chunk_size = size_part.to_i(16)
133
-
134
- if chunk_size.zero?
135
- trailer_offset = crlf + 2
136
- loop do
137
- trailer_crlf = buffer.index("\r\n", trailer_offset)
138
- return [decoded, :incomplete] unless trailer_crlf
139
- return [decoded, :complete] if trailer_crlf == trailer_offset
140
-
141
- trailer_offset = trailer_crlf + 2
142
- end
143
- end
144
-
145
- return [decoded, :too_large] if max_size && (decoded.bytesize + chunk_size) > max_size
146
-
147
- overhead += (crlf - offset) + 4
148
- return [decoded, :malformed] if overhead > (decoded.bytesize + chunk_size + MAX_CHUNK_OVERHEAD)
149
-
150
- offset = crlf + 2
151
- decoded << buffer.byteslice(offset, chunk_size)
152
- offset += chunk_size + 2
153
- end
154
-
155
- [decoded, :incomplete]
156
- end
157
-
158
100
  # Advances an HTTP/1.x request parse from the state hash's buffered
159
101
  # bytes. A complete well-formed request returns with `:complete` set
160
102
  # plus populated `:env` and `:body`; malformed or oversized input flips
@@ -191,7 +133,7 @@ module Raptor
191
133
  if max_body_size && parser.content_length > max_body_size
192
134
  data.merge(env: env, body: nil, parse_data: parse_data, complete: true, too_large: true)
193
135
  elsif parser.chunked?
194
- decoded_body, chunked_state = decode_chunked(body_buffer, max_body_size)
136
+ decoded_body, chunked_state = HttpParser.decode_chunked(body_buffer, max_body_size)
195
137
 
196
138
  case chunked_state
197
139
  when :complete
@@ -479,7 +421,7 @@ module Raptor
479
421
  if parser.chunked?
480
422
  return :incomplete unless decode_chunked
481
423
 
482
- body, chunked_state = Http1.decode_chunked(body, @max_body_size)
424
+ body, chunked_state = HttpParser.decode_chunked(body, @max_body_size)
483
425
  case chunked_state
484
426
  when :complete
485
427
  env.delete(HTTP_TRANSFER_ENCODING)
@@ -581,7 +523,7 @@ module Raptor
581
523
  hijacked = headers.is_a?(Hash) && !!headers[Rack::RACK_HIJACK]
582
524
  streaming = body.respond_to?(:call) && !body.respond_to?(:each)
583
525
  keep_alive = (hijacked || streaming) ? false : keep_alive?(rack_env, request_count)
584
- response_size = response_size(headers, body) unless hijacked
526
+ response_size = response_size(headers, body) if @access_log_io && !hijacked
585
527
  response_started = true
586
528
  write_response(socket, rack_env, status, headers, body, keep_alive: keep_alive)
587
529
  end
@@ -919,11 +861,11 @@ module Raptor
919
861
 
920
862
  response = +"#{HTTP_11} 103 Early Hints\r\n"
921
863
  hints.each do |key, value|
922
- next if illegal_header_key?(key)
864
+ next if HttpParser.illegal_header_key?(key)
923
865
 
924
866
  values = value.is_a?(Array) ? value : [value]
925
867
  values.each do |hint_value|
926
- next if illegal_header_value?(hint_value.to_s)
868
+ next if HttpParser.illegal_header_value?(hint_value.to_s)
927
869
 
928
870
  response << "#{key.downcase}: #{hint_value}\r\n"
929
871
  end
@@ -948,27 +890,29 @@ module Raptor
948
890
  validate_status(status)
949
891
  response_hijack = headers.is_a?(Hash) ? headers.delete(Rack::RACK_HIJACK) : nil
950
892
  headers = normalize_headers(headers)
951
- validate_headers(headers, status)
893
+ no_entity_body = (status >= 100 && status < 200) || status == 204 || status == 304
894
+ validate_headers(headers, status, no_entity_body)
952
895
 
953
896
  headers["connection"] = keep_alive ? CONNECTION_KEEPALIVE : CONNECTION_CLOSE
954
897
 
955
898
  http_version = env[Rack::SERVER_PROTOCOL] == HTTP_11 ? HTTP_11 : HTTP_10
956
- no_body = env[Rack::REQUEST_METHOD] == "HEAD" || STATUS_WITH_NO_ENTITY_BODY.include?(status)
899
+ no_body = env[Rack::REQUEST_METHOD] == "HEAD" || no_entity_body
957
900
 
958
901
  response = build_status_line(http_version, status)
959
902
 
960
- cork_socket(socket)
903
+ corked = !keep_alive
904
+ cork_socket(socket) if corked
961
905
 
962
906
  if response_hijack
963
907
  write_hijacked_response(socket, response, headers, response_hijack)
964
908
  elsif no_body
965
- write_no_body_response(socket, response, headers, status)
909
+ write_no_body_response(socket, response, headers, no_entity_body)
966
910
  else
967
911
  write_full_response(socket, response, headers, body, http_version)
968
912
  end
969
913
  ensure
970
914
  body.close if body.respond_to?(:close)
971
- uncork_socket(socket)
915
+ uncork_socket(socket) if corked
972
916
  socket.flush rescue nil
973
917
  end
974
918
 
@@ -1001,7 +945,7 @@ module Raptor
1001
945
  headers.each do |key, value|
1002
946
  raise TypeError, "header keys must be Strings" unless key.is_a?(String)
1003
947
 
1004
- next if illegal_header_key?(key)
948
+ next if HttpParser.illegal_header_key?(key)
1005
949
 
1006
950
  normalized_key = key.match?(/[A-Z]/) ? key.downcase : key
1007
951
  next if normalized_key.start_with?(RACK_HEADER_PREFIX)
@@ -1017,12 +961,13 @@ module Raptor
1017
961
  #
1018
962
  # @param headers [Hash] normalized response headers
1019
963
  # @param status [Integer] HTTP status code
964
+ # @param no_entity_body [Boolean] whether the status forbids an entity body
1020
965
  # @return [void]
1021
966
  # @raise [ArgumentError] if a forbidden header is present for the status
1022
967
  #
1023
- # @rbs (Hash[String, String | Array[String]] headers, Integer status) -> void
1024
- def validate_headers(headers, status)
1025
- if STATUS_WITH_NO_ENTITY_BODY.include?(status)
968
+ # @rbs (Hash[String, String | Array[String]] headers, Integer status, bool no_entity_body) -> void
969
+ def validate_headers(headers, status, no_entity_body)
970
+ if no_entity_body
1026
971
  raise ArgumentError, "content-type must not be present for status #{status}" if headers.key?(Rack::CONTENT_TYPE)
1027
972
 
1028
973
  raise ArgumentError, "content-length must not be present for status #{status}" if headers.key?(Rack::CONTENT_LENGTH)
@@ -1055,7 +1000,7 @@ module Raptor
1055
1000
  #
1056
1001
  # @rbs (TCPSocket socket, String response, Hash[String, String | Array[String]] headers, ^(TCPSocket) -> void response_hijack) -> void
1057
1002
  def write_hijacked_response(socket, response, headers, response_hijack)
1058
- format_headers(response, headers)
1003
+ HttpParser.format_headers(response, headers)
1059
1004
  response << "\r\n"
1060
1005
  socket_write(socket, response)
1061
1006
  uncork_socket(socket)
@@ -1069,16 +1014,16 @@ module Raptor
1069
1014
  # @param socket [TCPSocket] the client socket
1070
1015
  # @param response [String] the status line accumulated so far
1071
1016
  # @param headers [Hash] normalized response headers
1072
- # @param status [Integer] HTTP status code
1017
+ # @param no_entity_body [Boolean] whether the response status forbids an entity body
1073
1018
  # @return [void]
1074
1019
  #
1075
- # @rbs (TCPSocket socket, String response, Hash[String, String | Array[String]] headers, Integer status) -> void
1076
- def write_no_body_response(socket, response, headers, status)
1077
- unless STATUS_WITH_NO_ENTITY_BODY.include?(status)
1020
+ # @rbs (TCPSocket socket, String response, Hash[String, String | Array[String]] headers, bool no_entity_body) -> void
1021
+ def write_no_body_response(socket, response, headers, no_entity_body)
1022
+ unless no_entity_body
1078
1023
  headers[Rack::CONTENT_LENGTH] = "0" unless headers.key?(Rack::CONTENT_LENGTH) || headers.key?(Rack::TRANSFER_ENCODING)
1079
1024
  end
1080
1025
 
1081
- format_headers(response, headers)
1026
+ HttpParser.format_headers(response, headers)
1082
1027
  response << "\r\n"
1083
1028
  socket_write(socket, response)
1084
1029
  end
@@ -1097,7 +1042,7 @@ module Raptor
1097
1042
  # @rbs (TCPSocket socket, String response, Hash[String, String | Array[String]] headers, untyped body, String http_version) -> void
1098
1043
  def write_full_response(socket, response, headers, body, http_version)
1099
1044
  if body.respond_to?(:call)
1100
- format_headers(response, headers)
1045
+ HttpParser.format_headers(response, headers)
1101
1046
  response << "\r\n"
1102
1047
  socket_write(socket, response)
1103
1048
  uncork_socket(socket)
@@ -1123,7 +1068,7 @@ module Raptor
1123
1068
  headers[Rack::TRANSFER_ENCODING] = TRANSFER_ENCODING_CHUNKED
1124
1069
  end
1125
1070
 
1126
- format_headers(response, headers)
1071
+ HttpParser.format_headers(response, headers)
1127
1072
  response << "\r\n"
1128
1073
 
1129
1074
  if body.respond_to?(:to_path) && (path = body.to_path) && File.readable?(path)
@@ -1172,13 +1117,13 @@ module Raptor
1172
1117
  if use_chunked
1173
1118
  buffer = response
1174
1119
  while (chunk = file.read(FILE_CHUNK_SIZE))
1175
- buffer << chunk.bytesize.to_s(16) << "\r\n" << chunk << "\r\n"
1120
+ HttpParser.chunked_encode(buffer, chunk)
1176
1121
  if buffer.bytesize >= CHUNKED_WRITE_THRESHOLD
1177
1122
  socket_write(socket, buffer)
1178
1123
  buffer = +""
1179
1124
  end
1180
1125
  end
1181
- buffer << "0\r\n\r\n"
1126
+ buffer << CHUNKED_TERMINATOR
1182
1127
  socket_write(socket, buffer)
1183
1128
  elsif content_length && content_length < BODY_BUFFER_THRESHOLD
1184
1129
  response << file.read(content_length)
@@ -1221,7 +1166,8 @@ module Raptor
1221
1166
  raise TypeError, "body must yield String values" unless chunk.is_a?(String)
1222
1167
 
1223
1168
  if use_chunked
1224
- response << chunk.bytesize.to_s(16) << "\r\n" << chunk << "\r\n0\r\n\r\n"
1169
+ HttpParser.chunked_encode(response, chunk)
1170
+ response << CHUNKED_TERMINATOR
1225
1171
  socket_write(socket, response)
1226
1172
  else
1227
1173
  socket_writev(socket, [response, chunk])
@@ -1244,15 +1190,13 @@ module Raptor
1244
1190
  body_array.each do |chunk|
1245
1191
  raise TypeError, "body must yield String values" unless chunk.is_a?(String)
1246
1192
 
1247
- next if chunk.empty?
1248
-
1249
- buffer << chunk.bytesize.to_s(16) << "\r\n" << chunk << "\r\n"
1193
+ HttpParser.chunked_encode(buffer, chunk)
1250
1194
  if buffer.bytesize >= CHUNKED_WRITE_THRESHOLD
1251
1195
  socket_write(socket, buffer)
1252
1196
  buffer = +""
1253
1197
  end
1254
1198
  end
1255
- buffer << "0\r\n\r\n"
1199
+ buffer << CHUNKED_TERMINATOR
1256
1200
  socket_write(socket, buffer)
1257
1201
  else
1258
1202
  body_array.each do |chunk|
@@ -1275,14 +1219,15 @@ module Raptor
1275
1219
  def write_enumerable_body(socket, response, body, use_chunked)
1276
1220
  if use_chunked
1277
1221
  socket_write(socket, response)
1222
+ buffer = +""
1278
1223
  body.each do |chunk|
1279
1224
  raise TypeError, "body must yield String values" unless chunk.is_a?(String)
1280
1225
 
1281
- next if chunk.empty?
1282
-
1283
- socket_write(socket, "#{chunk.bytesize.to_s(16)}\r\n#{chunk}\r\n")
1226
+ buffer.clear
1227
+ HttpParser.chunked_encode(buffer, chunk)
1228
+ socket_write(socket, buffer) unless buffer.empty?
1284
1229
  end
1285
- socket_write(socket, "0\r\n\r\n")
1230
+ socket_write(socket, CHUNKED_TERMINATOR)
1286
1231
  else
1287
1232
  body.each do |chunk|
1288
1233
  raise TypeError, "body must yield String values" unless chunk.is_a?(String)
@@ -1293,72 +1238,6 @@ module Raptor
1293
1238
  end
1294
1239
  end
1295
1240
 
1296
- # Returns true if the header key contains characters illegal in HTTP headers.
1297
- #
1298
- # @param key [String] the header key to check
1299
- # @return [Boolean] true if the key is illegal
1300
- #
1301
- # @rbs (String key) -> bool
1302
- def illegal_header_key?(key)
1303
- key.match?(ILLEGAL_HEADER_KEY_REGEX)
1304
- end
1305
-
1306
- # Returns true if the header value contains characters illegal in HTTP headers.
1307
- #
1308
- # @param value [String] the header value to check
1309
- # @return [Boolean] true if the value is illegal
1310
- #
1311
- # @rbs (String value) -> bool
1312
- def illegal_header_value?(value)
1313
- value.match?(ILLEGAL_HEADER_VALUE_REGEX)
1314
- end
1315
-
1316
- # Appends normalised header lines to `result`. Skips entries with
1317
- # illegal keys or values. Array values are written as separate lines.
1318
- #
1319
- # @param headers [Hash] normalized response headers
1320
- # @return [String] formatted header lines, each ending with CRLF
1321
- #
1322
- # @rbs (String result, Hash[String, String | Array[String]] headers) -> void
1323
- def format_headers(result, headers)
1324
- headers.each do |name, value|
1325
- next if illegal_header_key?(name)
1326
-
1327
- if value.is_a?(Array)
1328
- value.each { |entry| append_header_value(result, name, entry) }
1329
- else
1330
- append_header_value(result, name, value)
1331
- end
1332
- end
1333
- end
1334
-
1335
- # Appends one or more `name: value` header lines to `result`, splitting
1336
- # newline-joined values across separate lines and skipping empty or
1337
- # illegal values.
1338
- #
1339
- # @param result [String] the buffer to append to
1340
- # @param name [String] the header name
1341
- # @param value [Object] the header value (any object responding to `to_s`)
1342
- # @return [void]
1343
- #
1344
- # @rbs (String result, String name, untyped value) -> void
1345
- def append_header_value(result, name, value)
1346
- string_value = value.is_a?(String) ? value : value.to_s
1347
- return if string_value.empty?
1348
-
1349
- if string_value.include?("\n")
1350
- string_value.split("\n").each do |line|
1351
- next if line.empty? || illegal_header_value?(line)
1352
-
1353
- result << name << ": " << line << "\r\n"
1354
- end
1355
- else
1356
- return if illegal_header_value?(string_value)
1357
-
1358
- result << name << ": " << string_value << "\r\n"
1359
- end
1360
- end
1361
-
1362
1241
  # Calls every `rack.response_finished` callback in reverse
1363
1242
  # registration order, rescuing any that raise.
1364
1243
  #
data/lib/raptor/http2.rb CHANGED
@@ -229,8 +229,6 @@ module Raptor
229
229
  MAX_FRAME_SIZE = 16_384
230
230
 
231
231
  SERVER_PROTOCOL = "HTTP/2"
232
- RACK_HEADER_PREFIX = "rack."
233
- HOP_BY_HOP_HEADERS = ["connection", "transfer-encoding", "keep-alive", "upgrade", "proxy-connection"].freeze
234
232
 
235
233
  REQUEST_PSEUDO_HEADERS = [":method", ":scheme", ":path", ":authority"].freeze
236
234
  REQUIRED_REQUEST_PSEUDO_HEADERS = [":method", ":scheme", ":path"].freeze
@@ -766,20 +764,7 @@ module Raptor
766
764
  def write_http2_response(socket, writer, flow_control, stream_id, status, headers, body)
767
765
  parser = Http2Parser.new
768
766
 
769
- header_pairs = [[":status", status.to_s]]
770
- headers.each do |name, value|
771
- lowered = name.downcase
772
- next if lowered.start_with?(RACK_HEADER_PREFIX)
773
- next if HOP_BY_HOP_HEADERS.include?(lowered)
774
-
775
- if value.is_a?(Array)
776
- value.each { |entry| header_pairs << [lowered, entry.to_s] }
777
- else
778
- header_pairs << [lowered, value.to_s]
779
- end
780
- end
781
-
782
- encoded_headers = parser.encode_headers(header_pairs)
767
+ encoded_headers = parser.encode_response_headers(status, headers)
783
768
  body_chunks = []
784
769
  body_bytes = 0
785
770
  body.each do |chunk|
@@ -10,8 +10,8 @@ rescue LoadError
10
10
  end
11
11
 
12
12
  module Raptor
13
- # Routes incoming connections across worker processes to the worker with
14
- # the lowest reactor backlog.
13
+ # Routes incoming connections across worker processes using two-choice
14
+ # load sampling, atomically reserving the selected slot before routing.
15
15
  #
16
16
  # Auto-enabled on Linux when the `libbpf-ruby` gem is installed and the
17
17
  # BPF object file has been compiled. Falls back silently to standard
@@ -47,7 +47,7 @@ module Raptor
47
47
  return false unless supported?
48
48
 
49
49
  @object = LibBPFRuby::Object.new(BPF_OBJECT_PATH)
50
- @program_fd = @object.program_fd("select_least_loaded")
50
+ @program_fd = @object.program_fd("select_less_loaded")
51
51
  @socks_fd = @object.map_fd("socks")
52
52
  @loads_fd = @object.map_fd("loads")
53
53
 
@@ -99,6 +99,7 @@ module Raptor
99
99
  def self.enable_load_reporting(worker_index)
100
100
  @load_key = [worker_index + 1].pack("L")
101
101
  @load_value = String.new("\x00\x00\x00\x00", encoding: Encoding::ASCII_8BIT)
102
+ @accepted_load_value = String.new("\x00\x00\x00\x00", encoding: Encoding::ASCII_8BIT)
102
103
  end
103
104
 
104
105
  # Publishes this worker's current backlog to the BPF map.
@@ -115,6 +116,22 @@ module Raptor
115
116
  LibBPFRuby.map_update(@loads_fd, @load_key, @load_value)
116
117
  end
117
118
 
119
+ # Publishes an accept-side backlog estimate. This uses a separate value
120
+ # buffer because the server and load reporter call into the map from
121
+ # different threads.
122
+ #
123
+ # @param backlog [Integer] the estimated backlog after accepting a socket
124
+ # @return [void]
125
+ #
126
+ # @rbs (Integer backlog) -> void
127
+ def self.update_accepted_load(backlog)
128
+ @accepted_load_value.setbyte(0, backlog & 0xff)
129
+ @accepted_load_value.setbyte(1, (backlog >> 8) & 0xff)
130
+ @accepted_load_value.setbyte(2, (backlog >> 16) & 0xff)
131
+ @accepted_load_value.setbyte(3, (backlog >> 24) & 0xff)
132
+ LibBPFRuby.map_update(@loads_fd, @load_key, @accepted_load_value)
133
+ end
134
+
118
135
  # Marks a worker's slot as unavailable by parking its load at the
119
136
  # sentinel maximum so the BPF program's least-loaded pick skips it.
120
137
  #
data/lib/raptor/server.rb CHANGED
@@ -152,8 +152,9 @@ module Raptor
152
152
  end
153
153
  end
154
154
 
155
- # Accepts a connection from the given listener and dispatches it into
156
- # the HTTP/1.1 or SSL/HTTP-2 pipeline.
155
+ # Accepts a connection from the given listener, immediately publishes
156
+ # the pending work to the BPF map, and dispatches the socket into the
157
+ # HTTP/1.1 or SSL/HTTP-2 pipeline.
157
158
  #
158
159
  # @param listener [TCPServer, UNIXServer, Binder::SslListener] the ready listener
159
160
  # @return [Boolean] true if a connection was accepted, false if the listener had nothing to dispatch
@@ -172,6 +173,8 @@ module Raptor
172
173
  remote_addr = DEFAULT_REMOTE_ADDR
173
174
  end
174
175
 
176
+ ReuseportBPF.update_accepted_load(@reactor.backlog + 1) if @bpf_active
177
+
175
178
  if listener.is_a?(Binder::SslListener)
176
179
  @thread_pool << proc do
177
180
  dispatch_ssl_connection(listener, tcp_client, remote_addr)
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Raptor
5
- VERSION = "0.16.0"
5
+ VERSION = "0.17.0"
6
6
  end
@@ -26,10 +26,11 @@ module Raptor
26
26
  # `[1, HTTP1_RACTOR_COUNT_CAP]`.
27
27
  #
28
28
  # @param worker_count [Integer] the configured worker count
29
+ # @param cores [Integer] the available processor count
29
30
  # @return [Integer]
30
31
  #
31
- # @rbs (Integer worker_count) -> Integer
32
- def self.default_http1_ractor_count: (Integer worker_count) -> Integer
32
+ # @rbs (Integer worker_count, ?cores: Integer) -> Integer
33
+ def self.default_http1_ractor_count: (Integer worker_count, ?cores: Integer) -> Integer
33
34
 
34
35
  # Returns the default number of HTTP/2 pipeline ractors per
35
36
  # worker for the given worker count, rounded from the available
@@ -37,10 +38,11 @@ module Raptor
37
38
  # `[1, HTTP2_RACTOR_COUNT_CAP]`.
38
39
  #
39
40
  # @param worker_count [Integer] the configured worker count
41
+ # @param cores [Integer] the available processor count
40
42
  # @return [Integer]
41
43
  #
42
- # @rbs (Integer worker_count) -> Integer
43
- def self.default_http2_ractor_count: (Integer worker_count) -> Integer
44
+ # @rbs (Integer worker_count, ?cores: Integer) -> Integer
45
+ def self.default_http2_ractor_count: (Integer worker_count, ?cores: Integer) -> Integer
44
46
 
45
47
  @server_port: Integer
46
48