david 0.3.0.pre → 0.3.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +24 -0
- data/Gemfile +11 -7
- data/Gemfile.lock +155 -19
- data/README.md +21 -3
- data/Rakefile +5 -0
- data/TODO.md +73 -0
- data/benchmarks/Gemfile +4 -0
- data/benchmarks/Gemfile.lock +31 -0
- data/benchmarks/rps.rb +20 -0
- data/bin/david +9 -4
- data/config.ru +4 -0
- data/david.gemspec +10 -4
- data/experiments/mcast.rb +37 -0
- data/experiments/structs.rb +45 -0
- data/{test.rb → experiments/test.rb} +0 -0
- data/lib/david.rb +15 -4
- data/lib/david/actor.rb +18 -0
- data/lib/david/garbage_collector.rb +35 -0
- data/lib/david/observe.rb +102 -0
- data/lib/david/rails/action_controller/base.rb +11 -0
- data/lib/david/railties/config.rb +20 -1
- data/lib/david/railties/middleware.rb +18 -6
- data/lib/david/request.rb +80 -0
- data/lib/david/resource_discovery.rb +92 -0
- data/lib/david/resource_discovery_proxy.rb +13 -0
- data/lib/david/server.rb +72 -27
- data/lib/david/server/constants.rb +48 -0
- data/lib/david/server/deduplication.rb +21 -0
- data/lib/david/server/mapping.rb +64 -12
- data/lib/david/server/multicast.rb +54 -0
- data/lib/david/server/options.rb +32 -0
- data/lib/david/server/respond.rb +146 -0
- data/lib/david/server/utility.rb +1 -6
- data/lib/david/show_exceptions.rb +52 -0
- data/lib/david/version.rb +2 -1
- data/lib/rack/handler/david.rb +16 -6
- data/lib/rack/hello_world.rb +23 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +29 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +78 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/assets.rb +8 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/guerilla_rack_handler_spec.rb +16 -0
- data/spec/mapping_spec.rb +56 -0
- data/spec/observe_spec.rb +111 -0
- data/spec/perf/server_perf_spec.rb +15 -9
- data/spec/resource_discovery_spec.rb +65 -0
- data/spec/server_spec.rb +306 -0
- data/spec/spec_helper.rb +43 -1
- data/spec/utility_spec.rb +8 -0
- metadata +195 -38
- data/lib/david/server/response.rb +0 -124
- data/lib/david/well_known.rb +0 -59
@@ -0,0 +1,92 @@
|
|
1
|
+
module David
|
2
|
+
class ResourceDiscovery
|
3
|
+
include Celluloid
|
4
|
+
|
5
|
+
def initialize(app)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
dup._call(env)
|
11
|
+
end
|
12
|
+
|
13
|
+
def _call(env)
|
14
|
+
return @app.call(env) if env['PATH_INFO'] != '/.well-known/core'
|
15
|
+
return [405, {}, []] if env['REQUEST_METHOD'] != 'GET'
|
16
|
+
|
17
|
+
@env = env
|
18
|
+
|
19
|
+
filtered = routes_hash.select { |link| filter(link) }
|
20
|
+
body = filtered.keys.map(&:to_s).join(',')
|
21
|
+
|
22
|
+
# TODO On multicast, do not respond if result set empty.
|
23
|
+
|
24
|
+
[
|
25
|
+
200,
|
26
|
+
{
|
27
|
+
'Content-Type' => 'application/link-format',
|
28
|
+
'Content-Length' => body.bytesize.to_s
|
29
|
+
},
|
30
|
+
[body]
|
31
|
+
]
|
32
|
+
end
|
33
|
+
|
34
|
+
def register(controller, options)
|
35
|
+
name = controller.controller_name
|
36
|
+
default = options.delete(:default)
|
37
|
+
|
38
|
+
routes_hash.each do |link, route|
|
39
|
+
next unless route[:controller] == name
|
40
|
+
|
41
|
+
link.merge!(default) unless default.nil?
|
42
|
+
|
43
|
+
attrs = options[route[:action].to_sym]
|
44
|
+
link.merge!(attrs) unless attrs.nil?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def clean_routes
|
51
|
+
@clean_routes ||= routes
|
52
|
+
.uniq { |r| r[0] }
|
53
|
+
.select { |r| r if include_route?(r) }
|
54
|
+
.each { |r| delete_format!(r) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def delete_format!(route)
|
58
|
+
route[0].gsub!(/\(\.:format\)\z/, '')
|
59
|
+
end
|
60
|
+
|
61
|
+
def filter(link)
|
62
|
+
href = @env['QUERY_STRING'].split('href=').last
|
63
|
+
|
64
|
+
return true if href.blank?
|
65
|
+
|
66
|
+
# TODO If query end in '*', match on prefix.
|
67
|
+
# Otherwise match on whole string.
|
68
|
+
# https://tools.ietf.org/html/rfc6690#section-4.1
|
69
|
+
link.uri =~ Regexp.new(href)
|
70
|
+
end
|
71
|
+
|
72
|
+
def include_route?(route)
|
73
|
+
!(route[0] =~ /\A\/(assets|rails)/)
|
74
|
+
end
|
75
|
+
|
76
|
+
def routes
|
77
|
+
Rails.application.routes.routes.map do |route|
|
78
|
+
[
|
79
|
+
route.path.spec.to_s,
|
80
|
+
route.defaults[:controller],
|
81
|
+
route.defaults[:action]
|
82
|
+
]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def routes_hash
|
87
|
+
@routes_hash ||= Hash[clean_routes.collect { |r|
|
88
|
+
[CoRE::Link.new(r[0]), { controller: r[1], action: r[2] }]
|
89
|
+
}]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/david/server.rb
CHANGED
@@ -1,35 +1,46 @@
|
|
1
|
+
require 'david/server/deduplication'
|
2
|
+
require 'david/server/multicast'
|
1
3
|
require 'david/server/options'
|
2
|
-
require 'david/server/
|
4
|
+
require 'david/server/respond'
|
3
5
|
|
4
6
|
module David
|
5
7
|
class Server
|
6
8
|
include Celluloid::IO
|
7
|
-
include CoAP::
|
9
|
+
include CoAP::Coding
|
8
10
|
|
11
|
+
include Deduplication
|
12
|
+
include Multicast
|
9
13
|
include Options
|
10
|
-
include
|
14
|
+
include Respond
|
11
15
|
|
12
|
-
attr_reader :logger
|
16
|
+
attr_reader :logger, :socket
|
13
17
|
|
14
18
|
finalizer :shutdown
|
15
19
|
|
16
20
|
def initialize(app, options)
|
17
|
-
@
|
18
|
-
@
|
19
|
-
@
|
20
|
-
@
|
21
|
+
@block = choose(:block, options[:Block])
|
22
|
+
@cbor = choose(:cbor, options[:CBOR])
|
23
|
+
@host = choose(:host, options[:Host])
|
24
|
+
@logger = choose(:logger, options[:Log])
|
25
|
+
@mcast = choose(:mcast, options[:Multicast])
|
26
|
+
@observe = choose(:observe, options[:Observe])
|
27
|
+
@port = options[:Port].to_i
|
21
28
|
|
22
|
-
@app
|
29
|
+
@app = app.respond_to?(:new) ? app.new : app
|
30
|
+
|
31
|
+
@default_format = choose(:default_format, options[:DefaultFormat])
|
32
|
+
|
33
|
+
@dedup_cache = {}
|
23
34
|
|
24
35
|
logger.info "David #{David::VERSION} on #{RUBY_DESCRIPTION}"
|
25
36
|
logger.info "Starting on [#{@host}]:#{@port}"
|
26
37
|
|
27
|
-
ipv6 = IPAddr.new(@host).ipv6?
|
28
|
-
af = ipv6 ? ::Socket::AF_INET6 : ::Socket::AF_INET
|
38
|
+
@ipv6 = IPAddr.new(@host).ipv6?
|
39
|
+
af = @ipv6 ? ::Socket::AF_INET6 : ::Socket::AF_INET
|
29
40
|
|
30
|
-
# Actually Celluloid::IO::
|
31
|
-
# (Use celluloid-io from git, 0.15.0 does not support AF_INET6).
|
41
|
+
# Actually Celluloid::IO::UDPSocket.
|
32
42
|
@socket = UDPSocket.new(af)
|
43
|
+
multicast_initialize if @mcast
|
33
44
|
@socket.bind(@host, @port)
|
34
45
|
|
35
46
|
async.run
|
@@ -37,26 +48,60 @@ module David
|
|
37
48
|
|
38
49
|
private
|
39
50
|
|
40
|
-
def
|
41
|
-
|
42
|
-
end
|
51
|
+
def handle_input(*args)
|
52
|
+
data, sender, _, anc = args
|
43
53
|
|
44
|
-
|
45
|
-
|
46
|
-
|
54
|
+
if defined?(JRuby)
|
55
|
+
port, _, host = sender[1..3]
|
56
|
+
else
|
57
|
+
host, port = sender.ip_address, sender.ip_port
|
58
|
+
end
|
59
|
+
|
60
|
+
message = CoAP::Message.parse(data)
|
61
|
+
request = Request.new(host, port, message, anc)
|
47
62
|
|
48
|
-
|
49
|
-
|
50
|
-
request
|
63
|
+
return unless request.con? || request.non?
|
64
|
+
return unless request.valid_method?
|
65
|
+
return if !request.non? && request.multicast?
|
51
66
|
|
52
|
-
logger.info "[#{host}]:#{port}: #{request}"
|
53
|
-
logger.debug
|
67
|
+
logger.info "[#{host}]:#{port}: #{message} (block #{request.block.num})"
|
68
|
+
logger.debug message.inspect
|
54
69
|
|
55
|
-
|
70
|
+
if request.con? && duplicate?(request) #&& !request.idempotent?
|
71
|
+
response, options = cached_response(request)
|
72
|
+
logger.debug "(mid:#{request.mid} duplicate, response cached)"
|
73
|
+
else
|
74
|
+
response, options = respond(request)
|
75
|
+
end
|
56
76
|
|
57
|
-
|
77
|
+
unless response.nil?
|
78
|
+
logger.debug response.inspect
|
58
79
|
|
59
|
-
|
80
|
+
CoAP::Transmission.send(response, host, port,
|
81
|
+
options.merge(socket: @socket))
|
82
|
+
|
83
|
+
request.options = options
|
84
|
+
cache_response(request, response)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def run
|
89
|
+
loop do
|
90
|
+
if defined?(JRuby)
|
91
|
+
async.handle_input(*@socket.recvfrom(1152))
|
92
|
+
else
|
93
|
+
begin
|
94
|
+
async.handle_input(*@socket.to_io.recvmsg_nonblock)
|
95
|
+
rescue ::IO::WaitReadable
|
96
|
+
Celluloid::IO.wait_readable(@socket)
|
97
|
+
retry
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def shutdown
|
104
|
+
@socket.close unless @socket.nil?
|
60
105
|
end
|
61
106
|
end
|
62
107
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module David
|
2
|
+
class Server
|
3
|
+
module Constants
|
4
|
+
# Freeze some WSGI env keys.
|
5
|
+
REMOTE_ADDR = 'REMOTE_ADDR'.freeze
|
6
|
+
REMOTE_PORT = 'REMOTE_PORT'.freeze
|
7
|
+
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
8
|
+
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
9
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
10
|
+
QUERY_STRING = 'QUERY_STRING'.freeze
|
11
|
+
SERVER_NAME = 'SERVER_NAME'.freeze
|
12
|
+
SERVER_PORT = 'SERVER_PORT'.freeze
|
13
|
+
CONTENT_LENGTH = 'CONTENT_LENGTH'.freeze
|
14
|
+
CONTENT_TYPE = 'CONTENT_TYPE'.freeze
|
15
|
+
HTTP_ACCEPT = 'HTTP_ACCEPT'.freeze
|
16
|
+
|
17
|
+
# Freeze some Rack env keys.
|
18
|
+
RACK_VERSION = 'rack.version'.freeze
|
19
|
+
RACK_URL_SCHEME = 'rack.url_scheme'.freeze
|
20
|
+
RACK_INPUT = 'rack.input'.freeze
|
21
|
+
RACK_ERRORS = 'rack.errors'.freeze
|
22
|
+
RACK_MULTITHREAD = 'rack.multithread'.freeze
|
23
|
+
RACK_MULTIPROCESS = 'rack.multiprocess'.freeze
|
24
|
+
RACK_RUN_ONCE = 'rack.run_once'.freeze
|
25
|
+
RACK_LOGGER = 'rack.logger'.freeze
|
26
|
+
|
27
|
+
# Freeze CoAP specific env keys.
|
28
|
+
COAP_VERSION = 'coap.version'.freeze
|
29
|
+
COAP_MULTICAST = 'coap.multicast'.freeze
|
30
|
+
COAP_DTLS = 'coap.dtls'.freeze
|
31
|
+
COAP_DTLS_ID = 'coap.dtls.id'.freeze
|
32
|
+
COAP_DTLS_NOSEC = 'NoSec'.freeze
|
33
|
+
|
34
|
+
# Freeze some Rack env values.
|
35
|
+
EMPTY_STRING = ''.freeze
|
36
|
+
CONTENT_TYPE_JSON = 'application/json'.freeze
|
37
|
+
CONTENT_TYPE_CBOR = 'application/cbor'.freeze
|
38
|
+
RACK_URL_SCHEME_HTTP = 'http'.freeze
|
39
|
+
|
40
|
+
# Freeze HTTP header strings.
|
41
|
+
HTTP_CACHE_CONTROL = 'Cache-Control'.freeze
|
42
|
+
HTTP_CONTENT_LENGTH = 'Content-Length'.freeze
|
43
|
+
HTTP_CONTENT_TYPE = 'Content-Type'.freeze
|
44
|
+
HTTP_ETAG = 'ETag'.freeze
|
45
|
+
HTTP_LOCATION = 'Location'.freeze
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module David
|
2
|
+
module Deduplication
|
3
|
+
def self.included(base)
|
4
|
+
attr_reader :dedup_cache
|
5
|
+
end
|
6
|
+
|
7
|
+
def cache_response(request, response)
|
8
|
+
return if duplicate?(request)
|
9
|
+
@dedup_cache[[request.host, request.mid]] = [response, Time.now.to_i]
|
10
|
+
end
|
11
|
+
|
12
|
+
def cached_response(request)
|
13
|
+
response = @dedup_cache[[request.host, request.mid]]
|
14
|
+
[response[0], response[0].options] if response
|
15
|
+
end
|
16
|
+
|
17
|
+
def duplicate?(request)
|
18
|
+
return !!cached_response(request)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/david/server/mapping.rb
CHANGED
@@ -1,32 +1,84 @@
|
|
1
1
|
module David
|
2
2
|
class Server
|
3
3
|
module Mapping
|
4
|
+
include Constants
|
5
|
+
|
6
|
+
HTTP_TO_COAP_CODES = {
|
7
|
+
200 => 205,
|
8
|
+
202 => 201,
|
9
|
+
203 => 205,
|
10
|
+
204 => 205,
|
11
|
+
304 => 203,
|
12
|
+
407 => 401,
|
13
|
+
408 => 400,
|
14
|
+
409 => 412,
|
15
|
+
410 => 404,
|
16
|
+
411 => 402,
|
17
|
+
414 => 402,
|
18
|
+
505 => 500,
|
19
|
+
506 => 500,
|
20
|
+
511 => 500,
|
21
|
+
}.freeze
|
22
|
+
|
4
23
|
protected
|
24
|
+
|
25
|
+
def accept_to_http(request)
|
26
|
+
if request.accept.nil?
|
27
|
+
@default_format
|
28
|
+
else
|
29
|
+
CoAP::Registry.convert_content_format(request.accept)
|
30
|
+
end
|
31
|
+
end
|
5
32
|
|
6
33
|
def body_to_cbor(body)
|
7
34
|
JSON.parse(body).to_cbor
|
8
35
|
end
|
9
36
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def etag(options, bytes = 8)
|
15
|
-
etag = options['ETag']
|
16
|
-
etag.delete('"').bytes.first(bytes * 2).pack('C*').hex if etag
|
17
|
-
end
|
37
|
+
def code_to_coap(code)
|
38
|
+
if code.is_a?(Float)
|
39
|
+
return [code.to_i, (code * 100 % 100).round]
|
40
|
+
end
|
18
41
|
|
19
|
-
def http_to_coap_code(code)
|
20
42
|
code = code.to_i
|
21
|
-
|
22
|
-
h = {200 => 205}
|
23
|
-
code = h[code] if h[code]
|
43
|
+
code = HTTP_TO_COAP_CODES[code] if HTTP_TO_COAP_CODES[code]
|
24
44
|
|
25
45
|
a = code / 100
|
26
46
|
b = code - (a * 100)
|
27
47
|
|
28
48
|
[a, b]
|
29
49
|
end
|
50
|
+
|
51
|
+
def etag_to_coap(headers, bytes = 8)
|
52
|
+
etag = headers[HTTP_ETAG]
|
53
|
+
|
54
|
+
if etag
|
55
|
+
etag = etag.split('"')
|
56
|
+
etag = etag[1] || etag[0]
|
57
|
+
|
58
|
+
etag
|
59
|
+
.bytes
|
60
|
+
.first(bytes * 2)
|
61
|
+
.pack('C*')
|
62
|
+
.hex
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def location_to_coap(headers)
|
67
|
+
l = headers[HTTP_LOCATION].split('/').reject(&:empty?)
|
68
|
+
return l.empty? ? nil : l
|
69
|
+
rescue NoMethodError
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def max_age_to_coap(headers)
|
74
|
+
headers[HTTP_CACHE_CONTROL][/max-age=([0-9]*)/, 1]
|
75
|
+
rescue NoMethodError
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def method_to_http(method)
|
80
|
+
method.to_s.upcase
|
81
|
+
end
|
30
82
|
end
|
31
83
|
end
|
32
84
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module David
|
2
|
+
class Server
|
3
|
+
# See https://tools.ietf.org/html/rfc7252#section-12.8
|
4
|
+
module Multicast
|
5
|
+
def multicast_initialize
|
6
|
+
@socket.to_io.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1)
|
7
|
+
|
8
|
+
if @ipv6
|
9
|
+
maddrs = ['ff02::fd', 'ff05::fd']
|
10
|
+
maddrs << 'ff02::1' if OS.osx? # OSX needs ff02::1 explicitly joined.
|
11
|
+
maddrs.each { |maddr| multicast_listen_ipv6(maddr) }
|
12
|
+
|
13
|
+
setsockopts_ipv6
|
14
|
+
else
|
15
|
+
maddrs = ['224.0.1.187']
|
16
|
+
multicast_listen_ipv4(maddrs.first)
|
17
|
+
|
18
|
+
setsockopts_ipv4
|
19
|
+
end
|
20
|
+
|
21
|
+
logger.debug "Joined multicast groups: #{maddrs.join(', ')}"
|
22
|
+
rescue Errno::ENODEV, Errno::EADDRNOTAVAIL
|
23
|
+
logger.warn 'Multicast initialization failure: Device not found.'
|
24
|
+
@mcast = false
|
25
|
+
end
|
26
|
+
|
27
|
+
def multicast_listen_ipv4(address)
|
28
|
+
mreq = IPAddr.new(address).hton + IPAddr.new('0.0.0.0').hton
|
29
|
+
@socket.to_io.setsockopt(:IPPROTO_IP, :IP_ADD_MEMBERSHIP, mreq)
|
30
|
+
end
|
31
|
+
|
32
|
+
def multicast_listen_ipv6(address)
|
33
|
+
ifindex = 0
|
34
|
+
|
35
|
+
# http://lists.apple.com/archives/darwin-kernel/2014/Mar/msg00012.html
|
36
|
+
if OS.osx?
|
37
|
+
ifname = Socket.if_up?('en1') ? 'en1' : 'en0'
|
38
|
+
ifindex = Socket.if_nametoindex(ifname)
|
39
|
+
end
|
40
|
+
|
41
|
+
mreq = IPAddr.new(address).hton + [ifindex].pack('i_')
|
42
|
+
@socket.to_io.setsockopt(:IPPROTO_IPV6, :IPV6_JOIN_GROUP, mreq)
|
43
|
+
end
|
44
|
+
|
45
|
+
def setsockopts_ipv4
|
46
|
+
@socket.to_io.setsockopt(:IPPROTO_IP, :IP_PKTINFO, 1)
|
47
|
+
end
|
48
|
+
|
49
|
+
def setsockopts_ipv6
|
50
|
+
@socket.to_io.setsockopt(:IPPROTO_IPV6, :IPV6_RECVPKTINFO, 1)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|