rom-ldap 0.2.2
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 +7 -0
- data/CHANGELOG.md +251 -0
- data/CONTRIBUTING.md +18 -0
- data/README.md +172 -0
- data/TODO.md +33 -0
- data/config/responses.yml +328 -0
- data/lib/dry/monitor/ldap/colorizers/default.rb +17 -0
- data/lib/dry/monitor/ldap/colorizers/rouge.rb +31 -0
- data/lib/dry/monitor/ldap/logger.rb +58 -0
- data/lib/rom-ldap.rb +1 -0
- data/lib/rom/ldap.rb +22 -0
- data/lib/rom/ldap/alias.rb +30 -0
- data/lib/rom/ldap/associations.rb +6 -0
- data/lib/rom/ldap/associations/core.rb +23 -0
- data/lib/rom/ldap/associations/many_to_many.rb +18 -0
- data/lib/rom/ldap/associations/many_to_one.rb +22 -0
- data/lib/rom/ldap/associations/one_to_many.rb +32 -0
- data/lib/rom/ldap/associations/one_to_one.rb +19 -0
- data/lib/rom/ldap/associations/self_ref.rb +35 -0
- data/lib/rom/ldap/attribute.rb +327 -0
- data/lib/rom/ldap/client.rb +185 -0
- data/lib/rom/ldap/client/authentication.rb +118 -0
- data/lib/rom/ldap/client/operations.rb +233 -0
- data/lib/rom/ldap/commands.rb +6 -0
- data/lib/rom/ldap/commands/create.rb +41 -0
- data/lib/rom/ldap/commands/delete.rb +17 -0
- data/lib/rom/ldap/commands/update.rb +35 -0
- data/lib/rom/ldap/constants.rb +193 -0
- data/lib/rom/ldap/dataset.rb +286 -0
- data/lib/rom/ldap/dataset/conversion.rb +62 -0
- data/lib/rom/ldap/dataset/dsl.rb +299 -0
- data/lib/rom/ldap/dataset/persistence.rb +44 -0
- data/lib/rom/ldap/directory.rb +126 -0
- data/lib/rom/ldap/directory/capabilities.rb +71 -0
- data/lib/rom/ldap/directory/entry.rb +200 -0
- data/lib/rom/ldap/directory/env.rb +155 -0
- data/lib/rom/ldap/directory/operations.rb +282 -0
- data/lib/rom/ldap/directory/password.rb +122 -0
- data/lib/rom/ldap/directory/root.rb +187 -0
- data/lib/rom/ldap/directory/tokenization.rb +66 -0
- data/lib/rom/ldap/directory/transactions.rb +31 -0
- data/lib/rom/ldap/directory/vendors/active_directory.rb +129 -0
- data/lib/rom/ldap/directory/vendors/apache_ds.rb +27 -0
- data/lib/rom/ldap/directory/vendors/e_directory.rb +16 -0
- data/lib/rom/ldap/directory/vendors/open_directory.rb +12 -0
- data/lib/rom/ldap/directory/vendors/open_dj.rb +25 -0
- data/lib/rom/ldap/directory/vendors/open_ldap.rb +35 -0
- data/lib/rom/ldap/directory/vendors/three_eight_nine.rb +16 -0
- data/lib/rom/ldap/directory/vendors/unknown.rb +22 -0
- data/lib/rom/ldap/dsl.rb +76 -0
- data/lib/rom/ldap/errors.rb +47 -0
- data/lib/rom/ldap/expression.rb +77 -0
- data/lib/rom/ldap/expression_encoder.rb +174 -0
- data/lib/rom/ldap/extensions.rb +50 -0
- data/lib/rom/ldap/extensions/active_support_notifications.rb +26 -0
- data/lib/rom/ldap/extensions/compatibility.rb +11 -0
- data/lib/rom/ldap/extensions/dsml.rb +165 -0
- data/lib/rom/ldap/extensions/msgpack.rb +23 -0
- data/lib/rom/ldap/extensions/optimised_json.rb +25 -0
- data/lib/rom/ldap/extensions/rails_log_subscriber.rb +38 -0
- data/lib/rom/ldap/formatter.rb +26 -0
- data/lib/rom/ldap/functions.rb +207 -0
- data/lib/rom/ldap/gateway.rb +145 -0
- data/lib/rom/ldap/ldif.rb +74 -0
- data/lib/rom/ldap/ldif/exporter.rb +77 -0
- data/lib/rom/ldap/ldif/importer.rb +95 -0
- data/lib/rom/ldap/mapper_compiler.rb +19 -0
- data/lib/rom/ldap/matchers.rb +69 -0
- data/lib/rom/ldap/message_queue.rb +7 -0
- data/lib/rom/ldap/oid.rb +101 -0
- data/lib/rom/ldap/parsers/abstract_syntax.rb +91 -0
- data/lib/rom/ldap/parsers/attribute.rb +290 -0
- data/lib/rom/ldap/parsers/filter_syntax.rb +133 -0
- data/lib/rom/ldap/pdu.rb +285 -0
- data/lib/rom/ldap/plugin/pagination.rb +145 -0
- data/lib/rom/ldap/plugins.rb +7 -0
- data/lib/rom/ldap/projection_dsl.rb +38 -0
- data/lib/rom/ldap/relation.rb +135 -0
- data/lib/rom/ldap/relation/exporting.rb +72 -0
- data/lib/rom/ldap/relation/reading.rb +461 -0
- data/lib/rom/ldap/relation/writing.rb +64 -0
- data/lib/rom/ldap/responses.rb +17 -0
- data/lib/rom/ldap/restriction_dsl.rb +45 -0
- data/lib/rom/ldap/schema.rb +123 -0
- data/lib/rom/ldap/schema/attributes_inferrer.rb +59 -0
- data/lib/rom/ldap/schema/dsl.rb +13 -0
- data/lib/rom/ldap/schema/inferrer.rb +50 -0
- data/lib/rom/ldap/schema/type_builder.rb +133 -0
- data/lib/rom/ldap/scope.rb +19 -0
- data/lib/rom/ldap/search_request.rb +249 -0
- data/lib/rom/ldap/socket.rb +210 -0
- data/lib/rom/ldap/tasks/ldap.rake +103 -0
- data/lib/rom/ldap/tasks/ldif.rake +80 -0
- data/lib/rom/ldap/transaction.rb +29 -0
- data/lib/rom/ldap/type_map.rb +88 -0
- data/lib/rom/ldap/types.rb +158 -0
- data/lib/rom/ldap/version.rb +17 -0
- data/lib/rom/plugins/relation/ldap/active_directory.rb +182 -0
- data/lib/rom/plugins/relation/ldap/auto_restrictions.rb +69 -0
- data/lib/rom/plugins/relation/ldap/e_directory.rb +27 -0
- data/lib/rom/plugins/relation/ldap/instrumentation.rb +35 -0
- data/lib/rouge/lexers/ldap.rb +72 -0
- data/lib/rouge/themes/ldap.rb +49 -0
- metadata +231 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ROM
|
|
4
|
+
module LDAP
|
|
5
|
+
#
|
|
6
|
+
# Search Scope
|
|
7
|
+
#
|
|
8
|
+
# @see https://ldapwiki.com/wiki/LDAP%20Search%20Scopes
|
|
9
|
+
#
|
|
10
|
+
# Constrained to the entry named by baseObject.
|
|
11
|
+
SCOPE_BASE = 0 # "base"
|
|
12
|
+
# Constrained to the immediate subordinates of the entry named by baseObject.
|
|
13
|
+
SCOPE_ONE = 1 # "one"
|
|
14
|
+
# Constrained to the entry named by baseObject and to all its subordinates.
|
|
15
|
+
SCOPE_SUB = 2 # "sub"
|
|
16
|
+
|
|
17
|
+
SCOPES = [SCOPE_BASE, SCOPE_ONE, SCOPE_SUB].freeze
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
using ::BER
|
|
4
|
+
|
|
5
|
+
require 'rom/ldap/types'
|
|
6
|
+
require 'rom/ldap/expression'
|
|
7
|
+
|
|
8
|
+
module ROM
|
|
9
|
+
module LDAP
|
|
10
|
+
# The Search request is defined as follows:
|
|
11
|
+
#
|
|
12
|
+
# SearchRequest ::= [APPLICATION 3] SEQUENCE {
|
|
13
|
+
# baseObject LDAPDN,
|
|
14
|
+
# scope ENUMERATED {
|
|
15
|
+
# baseObject (0),
|
|
16
|
+
# singleLevel (1),
|
|
17
|
+
# wholeSubtree (2),
|
|
18
|
+
# ... },
|
|
19
|
+
# derefAliases ENUMERATED {
|
|
20
|
+
# neverDerefAliases (0),
|
|
21
|
+
# derefInSearching (1),
|
|
22
|
+
# derefFindingBaseObj (2),
|
|
23
|
+
# derefAlways (3) },
|
|
24
|
+
# sizeLimit INTEGER (0 .. maxInt),
|
|
25
|
+
# timeLimit INTEGER (0 .. maxInt),
|
|
26
|
+
# typesOnly BOOLEAN,
|
|
27
|
+
# filter Filter,
|
|
28
|
+
# attributes AttributeSelection }
|
|
29
|
+
#
|
|
30
|
+
# AttributeSelection ::= SEQUENCE OF selector LDAPString
|
|
31
|
+
# -- The LDAPString is constrained to
|
|
32
|
+
# -- <attributeSelector> in Section 4.5.1.8
|
|
33
|
+
#
|
|
34
|
+
# Filter ::= CHOICE {
|
|
35
|
+
# and [0] SET SIZE (1..MAX) OF filter Filter,
|
|
36
|
+
# or [1] SET SIZE (1..MAX) OF filter Filter,
|
|
37
|
+
# not [2] Filter,
|
|
38
|
+
# equalityMatch [3] AttributeValueAssertion,
|
|
39
|
+
# substrings [4] SubstringFilter,
|
|
40
|
+
# greaterOrEqual [5] AttributeValueAssertion,
|
|
41
|
+
# lessOrEqual [6] AttributeValueAssertion,
|
|
42
|
+
# present [7] AttributeDescription,
|
|
43
|
+
# approxMatch [8] AttributeValueAssertion,
|
|
44
|
+
# extensibleMatch [9] MatchingRuleAssertion,
|
|
45
|
+
# ... }
|
|
46
|
+
#
|
|
47
|
+
# SubstringFilter ::= SEQUENCE {
|
|
48
|
+
# type AttributeDescription,
|
|
49
|
+
# substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE {
|
|
50
|
+
# initial [0] AssertionValue, -- can occur at most once
|
|
51
|
+
# any [1] AssertionValue,
|
|
52
|
+
# final [2] AssertionValue } -- can occur at most once
|
|
53
|
+
# }
|
|
54
|
+
#
|
|
55
|
+
# MatchingRuleAssertion ::= SEQUENCE {
|
|
56
|
+
# matchingRule [1] MatchingRuleId OPTIONAL,
|
|
57
|
+
# type [2] AttributeDescription OPTIONAL,
|
|
58
|
+
# matchValue [3] AssertionValue,
|
|
59
|
+
# dnAttributes [4] BOOLEAN DEFAULT FALSE }
|
|
60
|
+
#
|
|
61
|
+
#
|
|
62
|
+
#
|
|
63
|
+
#
|
|
64
|
+
# @see https://tools.ietf.org/html/rfc4511#section-4.5.1
|
|
65
|
+
#
|
|
66
|
+
# @api private
|
|
67
|
+
class SearchRequest
|
|
68
|
+
|
|
69
|
+
extend Initializer
|
|
70
|
+
|
|
71
|
+
# @see https://tools.ietf.org/html/rfc4511#section-4.5.1.7
|
|
72
|
+
#
|
|
73
|
+
# @!attribute [r] expression
|
|
74
|
+
# @return [Expression] Required.
|
|
75
|
+
option :expression, type: Types.Instance(Expression)
|
|
76
|
+
|
|
77
|
+
# @see https://tools.ietf.org/html/rfc4511#section-4.5.1.1
|
|
78
|
+
#
|
|
79
|
+
# @!attribute [r] base
|
|
80
|
+
# @return [String] Set to Relation default_base:
|
|
81
|
+
option :base, proc(&:to_s), type: Types::DN, default: -> { EMPTY_STRING }
|
|
82
|
+
|
|
83
|
+
# Defaults to only searching entries under base object.
|
|
84
|
+
#
|
|
85
|
+
# @see https://tools.ietf.org/html/rfc4511#section-4.5.1.2
|
|
86
|
+
#
|
|
87
|
+
# @!attribute [r] scope
|
|
88
|
+
# @return [Integer]
|
|
89
|
+
option :scope, type: Types::Scope, default: -> { SCOPE_SUB }
|
|
90
|
+
|
|
91
|
+
# Defaults to dereferencing both when searching and when locating the search base.
|
|
92
|
+
#
|
|
93
|
+
# @see https://tools.ietf.org/html/rfc4511#section-4.5.1.3
|
|
94
|
+
#
|
|
95
|
+
# @!attribute [r] deref
|
|
96
|
+
# @return [Integer]
|
|
97
|
+
option :deref, type: Types::Deref, default: -> { DEREF_ALWAYS }
|
|
98
|
+
|
|
99
|
+
# Defaults to all attributes '*' but not operational.
|
|
100
|
+
#
|
|
101
|
+
# @see https://tools.ietf.org/html/rfc4511#section-4.5.1.8
|
|
102
|
+
#
|
|
103
|
+
# @!attribute [r] attributes
|
|
104
|
+
# @return [Array<String>] Restrict attributes returned.
|
|
105
|
+
option :attributes, type: Types::Strings, default: -> { [WILDCARD] }
|
|
106
|
+
|
|
107
|
+
# @see https://tools.ietf.org/html/rfc4511#section-4.5.1.6
|
|
108
|
+
#
|
|
109
|
+
# @!attribute [r] attributes_only
|
|
110
|
+
# @return [Boolean] Do not include values.
|
|
111
|
+
option :attributes_only, type: Types::Strict::Bool, default: -> { false }
|
|
112
|
+
|
|
113
|
+
# @!attribute [r] reverse
|
|
114
|
+
# @return [Boolean]
|
|
115
|
+
option :reverse, type: Types::Strict::Bool, default: -> { false }
|
|
116
|
+
|
|
117
|
+
# Defaults to not paging results
|
|
118
|
+
#
|
|
119
|
+
# Adds :paged_result control to the request.
|
|
120
|
+
#
|
|
121
|
+
# @!attribute [r] paged
|
|
122
|
+
# @return [Boolean]
|
|
123
|
+
option :paged, type: Types::Strict::Bool, default: -> { false }
|
|
124
|
+
|
|
125
|
+
# ads-maxTimeLimit: 15000
|
|
126
|
+
# Defaults to zero, no timeout.
|
|
127
|
+
#
|
|
128
|
+
# @see https://tools.ietf.org/html/rfc4511#section-4.5.1.5
|
|
129
|
+
#
|
|
130
|
+
# @!attribute [r] timeout
|
|
131
|
+
# @return [Integer]
|
|
132
|
+
option :timeout, type: Types::Strict::Integer, default: -> { 0 }
|
|
133
|
+
|
|
134
|
+
# ads-maxSizeLimit: 200
|
|
135
|
+
#
|
|
136
|
+
# @see https://tools.ietf.org/html/rfc4511#section-4.5.1.4
|
|
137
|
+
#
|
|
138
|
+
# @!attribute [r] max
|
|
139
|
+
# @return [Integer]
|
|
140
|
+
option :max, type: Types::Strict::Integer, optional: true
|
|
141
|
+
|
|
142
|
+
# @!attribute [r] sort
|
|
143
|
+
# @return [Array<String>]
|
|
144
|
+
option :sorted, type: Types::Strict::Array, optional: true
|
|
145
|
+
|
|
146
|
+
# Search request components.
|
|
147
|
+
#
|
|
148
|
+
# @return [Array]
|
|
149
|
+
def parts
|
|
150
|
+
[
|
|
151
|
+
base.to_ber, # 4.5.1.1. SearchRequest.baseObject
|
|
152
|
+
scope.to_ber_enumerated, # 4.5.1.2. SearchRequest.scope
|
|
153
|
+
deref.to_ber_enumerated, # 4.5.1.3. SearchRequest.derefAliases
|
|
154
|
+
limit.to_ber, # 4.5.1.4. SearchRequest.sizeLimit
|
|
155
|
+
timeout.to_ber, # 4.5.1.5. SearchRequest.timeLimit
|
|
156
|
+
attributes_only.to_ber, # 4.5.1.6. SearchRequest.typesOnly
|
|
157
|
+
expression.to_ber, # 4.5.1.7. SearchRequest.filter
|
|
158
|
+
ber_attrs.to_ber_sequence # 4.5.1.8. SearchRequest.attributes
|
|
159
|
+
]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
#
|
|
163
|
+
# Controls sent by clients are termed 'request controls', and those
|
|
164
|
+
# sent by servers are termed 'response controls'.
|
|
165
|
+
#
|
|
166
|
+
# Controls ::= SEQUENCE OF control Control
|
|
167
|
+
#
|
|
168
|
+
# Control ::= SEQUENCE {
|
|
169
|
+
# controlType LDAPOID,
|
|
170
|
+
# criticality BOOLEAN DEFAULT FALSE,
|
|
171
|
+
# controlValue OCTET STRING OPTIONAL }
|
|
172
|
+
#
|
|
173
|
+
#
|
|
174
|
+
# @see https://tools.ietf.org/html/rfc4511#section-4.1.11
|
|
175
|
+
#
|
|
176
|
+
# @return [Array]
|
|
177
|
+
def controls
|
|
178
|
+
ctrls = []
|
|
179
|
+
ctrls << build_controls(:paged_results, cookie) if paged
|
|
180
|
+
ctrls << build_controls(:sort_request, sort_rules) if sorted
|
|
181
|
+
ctrls.empty? ? nil : ctrls.to_ber_contextspecific(0)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
private
|
|
185
|
+
|
|
186
|
+
# Set test server to only serve 200 at a time to check paging
|
|
187
|
+
#
|
|
188
|
+
# Limit to no more than 126 entries.
|
|
189
|
+
#
|
|
190
|
+
# @return [Integer]
|
|
191
|
+
#
|
|
192
|
+
# @api private
|
|
193
|
+
def limit
|
|
194
|
+
(0..126).cover?(max) ? max : 0
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# @return [Array]
|
|
198
|
+
#
|
|
199
|
+
# @api private
|
|
200
|
+
def ber_attrs
|
|
201
|
+
Array(attributes).map { |attr| attr.to_s.to_ber }
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# @see https://tools.ietf.org/html/rfc2696
|
|
205
|
+
#
|
|
206
|
+
# @return [Array]
|
|
207
|
+
#
|
|
208
|
+
# @api private
|
|
209
|
+
def cookie
|
|
210
|
+
[126.to_ber, EMPTY_STRING.to_ber]
|
|
211
|
+
# [99.to_ber, EMPTY_STRING.to_ber]
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Control ::= SEQUENCE {
|
|
215
|
+
# controlType LDAPOID,
|
|
216
|
+
# criticality BOOLEAN DEFAULT FALSE,
|
|
217
|
+
# controlValue OCTET STRING OPTIONAL }
|
|
218
|
+
#
|
|
219
|
+
# @return [String] LDAP 'control'
|
|
220
|
+
#
|
|
221
|
+
# @see PDU#result_controls
|
|
222
|
+
#
|
|
223
|
+
def build_controls(type, payload)
|
|
224
|
+
[
|
|
225
|
+
OID[type].to_ber,
|
|
226
|
+
false.to_ber,
|
|
227
|
+
payload.to_ber_sequence.to_s.to_ber
|
|
228
|
+
].to_ber_sequence
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Only uses attribute names because not all vendors have fully implemented SSS.
|
|
232
|
+
#
|
|
233
|
+
# SortKeyList ::= SEQUENCE OF SEQUENCE {
|
|
234
|
+
# attributeType AttributeDescription,
|
|
235
|
+
# orderingRule [0] MatchingRuleId OPTIONAL,
|
|
236
|
+
# reverseOrder [1] BOOLEAN DEFAULT FALSE }
|
|
237
|
+
#
|
|
238
|
+
#
|
|
239
|
+
# @see https://tools.ietf.org/html/rfc2891
|
|
240
|
+
# @see https://docs.ldap.com/ldap-sdk/docs/javadoc/com/unboundid/ldap/sdk/controls/ServerSideSortRequestControl.html
|
|
241
|
+
#
|
|
242
|
+
# @api private
|
|
243
|
+
def sort_rules
|
|
244
|
+
sorted.map { |attr| [attr.to_ber].to_ber_sequence }
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'socket'
|
|
4
|
+
require 'rom/initializer'
|
|
5
|
+
|
|
6
|
+
module GetbyteForSSLSocket
|
|
7
|
+
def getbyte
|
|
8
|
+
byte = getc
|
|
9
|
+
return nil if byte.nil?
|
|
10
|
+
|
|
11
|
+
byte.ord
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module FixSSLSocketSyncClose
|
|
16
|
+
def close
|
|
17
|
+
super
|
|
18
|
+
io.close
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
module ROM
|
|
23
|
+
module LDAP
|
|
24
|
+
#
|
|
25
|
+
# Builds either TCP or UNIX.
|
|
26
|
+
#
|
|
27
|
+
# @api private
|
|
28
|
+
class Socket
|
|
29
|
+
|
|
30
|
+
extend Initializer
|
|
31
|
+
|
|
32
|
+
# Unix socket absolute file path
|
|
33
|
+
#
|
|
34
|
+
# @!attribute [r] path
|
|
35
|
+
# @return [String]
|
|
36
|
+
option :path, type: Types::Strict::String, reader: :private, optional: true
|
|
37
|
+
|
|
38
|
+
# Domain name or IP for TCP connection
|
|
39
|
+
#
|
|
40
|
+
# @!attribute [r] host
|
|
41
|
+
# @return [String]
|
|
42
|
+
option :host, type: Types::Strict::String, reader: :private, optional: true
|
|
43
|
+
|
|
44
|
+
# TCP port
|
|
45
|
+
#
|
|
46
|
+
# @!attribute [r] host
|
|
47
|
+
# @return [Integer]
|
|
48
|
+
option :port, type: Types::Strict::Integer, reader: :private, optional: true
|
|
49
|
+
|
|
50
|
+
# OpenSSL
|
|
51
|
+
# OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
|
|
52
|
+
option :ssl, type: Types::Strict::Hash, reader: :private, optional: true
|
|
53
|
+
|
|
54
|
+
# Config
|
|
55
|
+
# option :timeout, type: Types::Strict::Integer, reader: :private, default: -> { 10 }
|
|
56
|
+
|
|
57
|
+
option :read_timeout, type: Types::Strict::Integer, reader: :private, default: -> { 20 }
|
|
58
|
+
|
|
59
|
+
# Timeout limit in seconds when writing to the socket
|
|
60
|
+
#
|
|
61
|
+
# @!attribute [r] write_timeout
|
|
62
|
+
# @return [Integer] default: 10
|
|
63
|
+
option :write_timeout, type: Types::Strict::Integer, reader: :private, default: -> { 10 }
|
|
64
|
+
|
|
65
|
+
# Retry limit when establishing connection
|
|
66
|
+
#
|
|
67
|
+
# @!attribute [r] retry_count
|
|
68
|
+
# @return [Integer] default: 3
|
|
69
|
+
option :retry_count, type: Types::Strict::Integer, reader: :private, default: -> { 3 }
|
|
70
|
+
|
|
71
|
+
#
|
|
72
|
+
#
|
|
73
|
+
# @!attribute [r] retry_count
|
|
74
|
+
# @return [Boolean] default: true
|
|
75
|
+
option :keep_alive, type: Types::Strict::Bool, reader: :private, default: -> { true }
|
|
76
|
+
|
|
77
|
+
#
|
|
78
|
+
#
|
|
79
|
+
# @!attribute [r] buffered
|
|
80
|
+
# @return [Boolean] default: true
|
|
81
|
+
option :buffered, type: Types::Strict::Bool, reader: :private, default: -> { true }
|
|
82
|
+
|
|
83
|
+
# @return [::Socket, ::OpenSSL::SSL::SSLSocket]
|
|
84
|
+
#
|
|
85
|
+
def call
|
|
86
|
+
socket.do_not_reverse_lookup = true
|
|
87
|
+
socket.sync = buffered
|
|
88
|
+
socket.setsockopt(:SOCKET, :KEEPALIVE, keep_alive)
|
|
89
|
+
socket.setsockopt(:TCP, :NODELAY, buffered) unless path
|
|
90
|
+
|
|
91
|
+
connect!
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
# @return [::Socket]
|
|
97
|
+
#
|
|
98
|
+
# @raise [ConnectionError]
|
|
99
|
+
#
|
|
100
|
+
def connect!
|
|
101
|
+
@counter ||= 1
|
|
102
|
+
|
|
103
|
+
if ssl
|
|
104
|
+
require 'openssl'
|
|
105
|
+
|
|
106
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
|
107
|
+
ctx.set_params(ssl)
|
|
108
|
+
@socket = OpenSSL::SSL::SSLSocket.new(socket, ctx)
|
|
109
|
+
# socket.extend(GetbyteForSSLSocket) unless socket.respond_to?(:getbyte)
|
|
110
|
+
# socket.extend(FixSSLSocketSyncClose)
|
|
111
|
+
socket.connect_nonblock
|
|
112
|
+
else
|
|
113
|
+
socket.connect_nonblock(sockaddr)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
socket
|
|
117
|
+
rescue Errno::EISCONN
|
|
118
|
+
socket
|
|
119
|
+
rescue IO::WaitWritable
|
|
120
|
+
# OpenSSL::SSL::SSLErrorWaitWritable
|
|
121
|
+
if writable?
|
|
122
|
+
connect!
|
|
123
|
+
else
|
|
124
|
+
disconnect!
|
|
125
|
+
raise ConnectionError, "Connection write timeout - #{host}:#{port}"
|
|
126
|
+
end
|
|
127
|
+
rescue IO::WaitReadable
|
|
128
|
+
if readable?
|
|
129
|
+
@counter += 1
|
|
130
|
+
retry unless @counter > retry_count
|
|
131
|
+
else
|
|
132
|
+
raise ConnectionError, "Connection read timeout - #{host}:#{port}"
|
|
133
|
+
end
|
|
134
|
+
rescue Errno::EADDRNOTAVAIL
|
|
135
|
+
raise ConnectionError, "Host or port is invalid - #{host}:#{port}"
|
|
136
|
+
rescue SocketError
|
|
137
|
+
raise ConnectionError, "Host could not be resolved - #{host}:#{port}"
|
|
138
|
+
rescue Errno::ENOENT
|
|
139
|
+
raise ConnectionError, "Path to unix socket is invalid - #{path}"
|
|
140
|
+
rescue Errno::EHOSTDOWN
|
|
141
|
+
raise ConnectionError, "Server is down - #{host}:#{port}"
|
|
142
|
+
rescue Errno::ECONNREFUSED
|
|
143
|
+
raise ConnectionError, "Connection refused - #{host}:#{port}"
|
|
144
|
+
rescue Errno::EAFNOSUPPORT
|
|
145
|
+
raise ConnectionError, "Connection is not supported - #{host}:#{port}"
|
|
146
|
+
rescue
|
|
147
|
+
disconnect!
|
|
148
|
+
raise ConnectionError, "Connection failed - #{host}:#{port}"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
#
|
|
152
|
+
#
|
|
153
|
+
# @return [::Socket]
|
|
154
|
+
#
|
|
155
|
+
def socket
|
|
156
|
+
@socket ||= ::Socket.new((path ? :UNIX : :INET), :STREAM)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# @return [NilClass]
|
|
160
|
+
#
|
|
161
|
+
def disconnect!
|
|
162
|
+
socket.close
|
|
163
|
+
@socket = nil
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# IPV4 or UNIX socket address
|
|
167
|
+
#
|
|
168
|
+
# @return [String] ASCII-8BIT
|
|
169
|
+
#
|
|
170
|
+
# @api private
|
|
171
|
+
def sockaddr
|
|
172
|
+
if addrinfo.unix?
|
|
173
|
+
::Socket.pack_sockaddr_un(addrinfo.unix_path)
|
|
174
|
+
|
|
175
|
+
elsif addrinfo.ipv4?
|
|
176
|
+
::Socket.pack_sockaddr_in(addrinfo.ip_port, addrinfo.ip_address)
|
|
177
|
+
|
|
178
|
+
elsif addrinfo.ipv6_loopback?
|
|
179
|
+
::Socket.pack_sockaddr_in(addrinfo.ip_port, '127.0.0.1')
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Performs DNS lookup
|
|
184
|
+
#
|
|
185
|
+
# @return [Addrinfo]
|
|
186
|
+
#
|
|
187
|
+
# @api private
|
|
188
|
+
def addrinfo
|
|
189
|
+
return Addrinfo.unix(path) if path
|
|
190
|
+
|
|
191
|
+
Addrinfo.tcp(host, port)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# @return [TrueClass,FalseClass]
|
|
195
|
+
#
|
|
196
|
+
# @api private
|
|
197
|
+
def writable?
|
|
198
|
+
!IO.select(nil, [socket], nil, write_timeout).nil?
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# @return [TrueClass,FalseClass]
|
|
202
|
+
#
|
|
203
|
+
# @api private
|
|
204
|
+
def readable?
|
|
205
|
+
!IO.select([socket], nil, nil, read_timeout).nil?
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|