uri 0.10.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of uri might be problematic. Click here for more details.

@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'generic'
4
+
5
+ module URI
6
+
7
+ #
8
+ # The "file" URI is defined by RFC8089.
9
+ #
10
+ class File < Generic
11
+ # A Default port of nil for URI::File.
12
+ DEFAULT_PORT = nil
13
+
14
+ #
15
+ # An Array of the available components for URI::File.
16
+ #
17
+ COMPONENT = [
18
+ :scheme,
19
+ :host,
20
+ :path
21
+ ].freeze
22
+
23
+ #
24
+ # == Description
25
+ #
26
+ # Creates a new URI::File object from components, with syntax checking.
27
+ #
28
+ # The components accepted are +host+ and +path+.
29
+ #
30
+ # The components should be provided either as an Array, or as a Hash
31
+ # with keys formed by preceding the component names with a colon.
32
+ #
33
+ # If an Array is used, the components must be passed in the
34
+ # order <code>[host, path]</code>.
35
+ #
36
+ # Examples:
37
+ #
38
+ # require 'uri'
39
+ #
40
+ # uri1 = URI::File.build(['host.example.com', '/path/file.zip'])
41
+ # uri1.to_s # => "file://host.example.com/path/file.zip"
42
+ #
43
+ # uri2 = URI::File.build({:host => 'host.example.com',
44
+ # :path => '/ruby/src'})
45
+ # uri2.to_s # => "file://host.example.com/ruby/src"
46
+ #
47
+ def self.build(args)
48
+ tmp = Util::make_components_hash(self, args)
49
+ super(tmp)
50
+ end
51
+
52
+ # Protected setter for the host component +v+.
53
+ #
54
+ # See also URI::Generic.host=.
55
+ #
56
+ def set_host(v)
57
+ v = "" if v.nil? || v == "localhost"
58
+ @host = v
59
+ end
60
+
61
+ # do nothing
62
+ def set_port(v)
63
+ end
64
+
65
+ # raise InvalidURIError
66
+ def check_userinfo(user)
67
+ raise URI::InvalidURIError, "can not set userinfo for file URI"
68
+ end
69
+
70
+ # raise InvalidURIError
71
+ def check_user(user)
72
+ raise URI::InvalidURIError, "can not set user for file URI"
73
+ end
74
+
75
+ # raise InvalidURIError
76
+ def check_password(user)
77
+ raise URI::InvalidURIError, "can not set password for file URI"
78
+ end
79
+
80
+ # do nothing
81
+ def set_userinfo(v)
82
+ end
83
+
84
+ # do nothing
85
+ def set_user(v)
86
+ end
87
+
88
+ # do nothing
89
+ def set_password(v)
90
+ end
91
+ end
92
+
93
+ @@schemes['FILE'] = File
94
+ end
@@ -0,0 +1,267 @@
1
+ # frozen_string_literal: false
2
+ # = uri/ftp.rb
3
+ #
4
+ # Author:: Akira Yamada <akira@ruby-lang.org>
5
+ # License:: You can redistribute it and/or modify it under the same term as Ruby.
6
+ # Revision:: $Id$
7
+ #
8
+ # See URI for general documentation
9
+ #
10
+
11
+ require_relative 'generic'
12
+
13
+ module URI
14
+
15
+ #
16
+ # FTP URI syntax is defined by RFC1738 section 3.2.
17
+ #
18
+ # This class will be redesigned because of difference of implementations;
19
+ # the structure of its path. draft-hoffman-ftp-uri-04 is a draft but it
20
+ # is a good summary about the de facto spec.
21
+ # http://tools.ietf.org/html/draft-hoffman-ftp-uri-04
22
+ #
23
+ class FTP < Generic
24
+ # A Default port of 21 for URI::FTP.
25
+ DEFAULT_PORT = 21
26
+
27
+ #
28
+ # An Array of the available components for URI::FTP.
29
+ #
30
+ COMPONENT = [
31
+ :scheme,
32
+ :userinfo, :host, :port,
33
+ :path, :typecode
34
+ ].freeze
35
+
36
+ #
37
+ # Typecode is "a", "i", or "d".
38
+ #
39
+ # * "a" indicates a text file (the FTP command was ASCII)
40
+ # * "i" indicates a binary file (FTP command IMAGE)
41
+ # * "d" indicates the contents of a directory should be displayed
42
+ #
43
+ TYPECODE = ['a', 'i', 'd'].freeze
44
+
45
+ # Typecode prefix ";type=".
46
+ TYPECODE_PREFIX = ';type='.freeze
47
+
48
+ def self.new2(user, password, host, port, path,
49
+ typecode = nil, arg_check = true) # :nodoc:
50
+ # Do not use this method! Not tested. [Bug #7301]
51
+ # This methods remains just for compatibility,
52
+ # Keep it undocumented until the active maintainer is assigned.
53
+ typecode = nil if typecode.size == 0
54
+ if typecode && !TYPECODE.include?(typecode)
55
+ raise ArgumentError,
56
+ "bad typecode is specified: #{typecode}"
57
+ end
58
+
59
+ # do escape
60
+
61
+ self.new('ftp',
62
+ [user, password],
63
+ host, port, nil,
64
+ typecode ? path + TYPECODE_PREFIX + typecode : path,
65
+ nil, nil, nil, arg_check)
66
+ end
67
+
68
+ #
69
+ # == Description
70
+ #
71
+ # Creates a new URI::FTP object from components, with syntax checking.
72
+ #
73
+ # The components accepted are +userinfo+, +host+, +port+, +path+, and
74
+ # +typecode+.
75
+ #
76
+ # The components should be provided either as an Array, or as a Hash
77
+ # with keys formed by preceding the component names with a colon.
78
+ #
79
+ # If an Array is used, the components must be passed in the
80
+ # order <code>[userinfo, host, port, path, typecode]</code>.
81
+ #
82
+ # If the path supplied is absolute, it will be escaped in order to
83
+ # make it absolute in the URI.
84
+ #
85
+ # Examples:
86
+ #
87
+ # require 'uri'
88
+ #
89
+ # uri1 = URI::FTP.build(['user:password', 'ftp.example.com', nil,
90
+ # '/path/file.zip', 'i'])
91
+ # uri1.to_s # => "ftp://user:password@ftp.example.com/%2Fpath/file.zip;type=i"
92
+ #
93
+ # uri2 = URI::FTP.build({:host => 'ftp.example.com',
94
+ # :path => 'ruby/src'})
95
+ # uri2.to_s # => "ftp://ftp.example.com/ruby/src"
96
+ #
97
+ def self.build(args)
98
+
99
+ # Fix the incoming path to be generic URL syntax
100
+ # FTP path -> URL path
101
+ # foo/bar /foo/bar
102
+ # /foo/bar /%2Ffoo/bar
103
+ #
104
+ if args.kind_of?(Array)
105
+ args[3] = '/' + args[3].sub(/^\//, '%2F')
106
+ else
107
+ args[:path] = '/' + args[:path].sub(/^\//, '%2F')
108
+ end
109
+
110
+ tmp = Util::make_components_hash(self, args)
111
+
112
+ if tmp[:typecode]
113
+ if tmp[:typecode].size == 1
114
+ tmp[:typecode] = TYPECODE_PREFIX + tmp[:typecode]
115
+ end
116
+ tmp[:path] << tmp[:typecode]
117
+ end
118
+
119
+ return super(tmp)
120
+ end
121
+
122
+ #
123
+ # == Description
124
+ #
125
+ # Creates a new URI::FTP object from generic URL components with no
126
+ # syntax checking.
127
+ #
128
+ # Unlike build(), this method does not escape the path component as
129
+ # required by RFC1738; instead it is treated as per RFC2396.
130
+ #
131
+ # Arguments are +scheme+, +userinfo+, +host+, +port+, +registry+, +path+,
132
+ # +opaque+, +query+, and +fragment+, in that order.
133
+ #
134
+ def initialize(scheme,
135
+ userinfo, host, port, registry,
136
+ path, opaque,
137
+ query,
138
+ fragment,
139
+ parser = nil,
140
+ arg_check = false)
141
+ raise InvalidURIError unless path
142
+ path = path.sub(/^\//,'')
143
+ path.sub!(/^%2F/,'/')
144
+ super(scheme, userinfo, host, port, registry, path, opaque,
145
+ query, fragment, parser, arg_check)
146
+ @typecode = nil
147
+ if tmp = @path.index(TYPECODE_PREFIX)
148
+ typecode = @path[tmp + TYPECODE_PREFIX.size..-1]
149
+ @path = @path[0..tmp - 1]
150
+
151
+ if arg_check
152
+ self.typecode = typecode
153
+ else
154
+ self.set_typecode(typecode)
155
+ end
156
+ end
157
+ end
158
+
159
+ # typecode accessor.
160
+ #
161
+ # See URI::FTP::COMPONENT.
162
+ attr_reader :typecode
163
+
164
+ # Validates typecode +v+,
165
+ # returns +true+ or +false+.
166
+ #
167
+ def check_typecode(v)
168
+ if TYPECODE.include?(v)
169
+ return true
170
+ else
171
+ raise InvalidComponentError,
172
+ "bad typecode(expected #{TYPECODE.join(', ')}): #{v}"
173
+ end
174
+ end
175
+ private :check_typecode
176
+
177
+ # Private setter for the typecode +v+.
178
+ #
179
+ # See also URI::FTP.typecode=.
180
+ #
181
+ def set_typecode(v)
182
+ @typecode = v
183
+ end
184
+ protected :set_typecode
185
+
186
+ #
187
+ # == Args
188
+ #
189
+ # +v+::
190
+ # String
191
+ #
192
+ # == Description
193
+ #
194
+ # Public setter for the typecode +v+
195
+ # (with validation).
196
+ #
197
+ # See also URI::FTP.check_typecode.
198
+ #
199
+ # == Usage
200
+ #
201
+ # require 'uri'
202
+ #
203
+ # uri = URI.parse("ftp://john@ftp.example.com/my_file.img")
204
+ # #=> #<URI::FTP ftp://john@ftp.example.com/my_file.img>
205
+ # uri.typecode = "i"
206
+ # uri
207
+ # #=> #<URI::FTP ftp://john@ftp.example.com/my_file.img;type=i>
208
+ #
209
+ def typecode=(typecode)
210
+ check_typecode(typecode)
211
+ set_typecode(typecode)
212
+ typecode
213
+ end
214
+
215
+ def merge(oth) # :nodoc:
216
+ tmp = super(oth)
217
+ if self != tmp
218
+ tmp.set_typecode(oth.typecode)
219
+ end
220
+
221
+ return tmp
222
+ end
223
+
224
+ # Returns the path from an FTP URI.
225
+ #
226
+ # RFC 1738 specifically states that the path for an FTP URI does not
227
+ # include the / which separates the URI path from the URI host. Example:
228
+ #
229
+ # <code>ftp://ftp.example.com/pub/ruby</code>
230
+ #
231
+ # The above URI indicates that the client should connect to
232
+ # ftp.example.com then cd to pub/ruby from the initial login directory.
233
+ #
234
+ # If you want to cd to an absolute directory, you must include an
235
+ # escaped / (%2F) in the path. Example:
236
+ #
237
+ # <code>ftp://ftp.example.com/%2Fpub/ruby</code>
238
+ #
239
+ # This method will then return "/pub/ruby".
240
+ #
241
+ def path
242
+ return @path.sub(/^\//,'').sub(/^%2F/,'/')
243
+ end
244
+
245
+ # Private setter for the path of the URI::FTP.
246
+ def set_path(v)
247
+ super("/" + v.sub(/^\//, "%2F"))
248
+ end
249
+ protected :set_path
250
+
251
+ # Returns a String representation of the URI::FTP.
252
+ def to_s
253
+ save_path = nil
254
+ if @typecode
255
+ save_path = @path
256
+ @path = @path + TYPECODE_PREFIX + @typecode
257
+ end
258
+ str = super
259
+ if @typecode
260
+ @path = save_path
261
+ end
262
+
263
+ return str
264
+ end
265
+ end
266
+ @@schemes['FTP'] = FTP
267
+ end
@@ -0,0 +1,1568 @@
1
+ # frozen_string_literal: true
2
+
3
+ # = uri/generic.rb
4
+ #
5
+ # Author:: Akira Yamada <akira@ruby-lang.org>
6
+ # License:: You can redistribute it and/or modify it under the same term as Ruby.
7
+ # Revision:: $Id$
8
+ #
9
+ # See URI for general documentation
10
+ #
11
+
12
+ require_relative 'common'
13
+ autoload :IPSocket, 'socket'
14
+ autoload :IPAddr, 'ipaddr'
15
+
16
+ module URI
17
+
18
+ #
19
+ # Base class for all URI classes.
20
+ # Implements generic URI syntax as per RFC 2396.
21
+ #
22
+ class Generic
23
+ include URI
24
+
25
+ #
26
+ # A Default port of nil for URI::Generic.
27
+ #
28
+ DEFAULT_PORT = nil
29
+
30
+ #
31
+ # Returns default port.
32
+ #
33
+ def self.default_port
34
+ self::DEFAULT_PORT
35
+ end
36
+
37
+ #
38
+ # Returns default port.
39
+ #
40
+ def default_port
41
+ self.class.default_port
42
+ end
43
+
44
+ #
45
+ # An Array of the available components for URI::Generic.
46
+ #
47
+ COMPONENT = [
48
+ :scheme,
49
+ :userinfo, :host, :port, :registry,
50
+ :path, :opaque,
51
+ :query,
52
+ :fragment
53
+ ].freeze
54
+
55
+ #
56
+ # Components of the URI in the order.
57
+ #
58
+ def self.component
59
+ self::COMPONENT
60
+ end
61
+
62
+ USE_REGISTRY = false # :nodoc:
63
+
64
+ def self.use_registry # :nodoc:
65
+ self::USE_REGISTRY
66
+ end
67
+
68
+ #
69
+ # == Synopsis
70
+ #
71
+ # See ::new.
72
+ #
73
+ # == Description
74
+ #
75
+ # At first, tries to create a new URI::Generic instance using
76
+ # URI::Generic::build. But, if exception URI::InvalidComponentError is raised,
77
+ # then it does URI::Escape.escape all URI components and tries again.
78
+ #
79
+ def self.build2(args)
80
+ begin
81
+ return self.build(args)
82
+ rescue InvalidComponentError
83
+ if args.kind_of?(Array)
84
+ return self.build(args.collect{|x|
85
+ if x.is_a?(String)
86
+ DEFAULT_PARSER.escape(x)
87
+ else
88
+ x
89
+ end
90
+ })
91
+ elsif args.kind_of?(Hash)
92
+ tmp = {}
93
+ args.each do |key, value|
94
+ tmp[key] = if value
95
+ DEFAULT_PARSER.escape(value)
96
+ else
97
+ value
98
+ end
99
+ end
100
+ return self.build(tmp)
101
+ end
102
+ end
103
+ end
104
+
105
+ #
106
+ # == Synopsis
107
+ #
108
+ # See ::new.
109
+ #
110
+ # == Description
111
+ #
112
+ # Creates a new URI::Generic instance from components of URI::Generic
113
+ # with check. Components are: scheme, userinfo, host, port, registry, path,
114
+ # opaque, query, and fragment. You can provide arguments either by an Array or a Hash.
115
+ # See ::new for hash keys to use or for order of array items.
116
+ #
117
+ def self.build(args)
118
+ if args.kind_of?(Array) &&
119
+ args.size == ::URI::Generic::COMPONENT.size
120
+ tmp = args.dup
121
+ elsif args.kind_of?(Hash)
122
+ tmp = ::URI::Generic::COMPONENT.collect do |c|
123
+ if args.include?(c)
124
+ args[c]
125
+ else
126
+ nil
127
+ end
128
+ end
129
+ else
130
+ component = self.class.component rescue ::URI::Generic::COMPONENT
131
+ raise ArgumentError,
132
+ "expected Array of or Hash of components of #{self.class} (#{component.join(', ')})"
133
+ end
134
+
135
+ tmp << nil
136
+ tmp << true
137
+ return self.new(*tmp)
138
+ end
139
+
140
+ #
141
+ # == Args
142
+ #
143
+ # +scheme+::
144
+ # Protocol scheme, i.e. 'http','ftp','mailto' and so on.
145
+ # +userinfo+::
146
+ # User name and password, i.e. 'sdmitry:bla'.
147
+ # +host+::
148
+ # Server host name.
149
+ # +port+::
150
+ # Server port.
151
+ # +registry+::
152
+ # Registry of naming authorities.
153
+ # +path+::
154
+ # Path on server.
155
+ # +opaque+::
156
+ # Opaque part.
157
+ # +query+::
158
+ # Query data.
159
+ # +fragment+::
160
+ # Part of the URI after '#' character.
161
+ # +parser+::
162
+ # Parser for internal use [URI::DEFAULT_PARSER by default].
163
+ # +arg_check+::
164
+ # Check arguments [false by default].
165
+ #
166
+ # == Description
167
+ #
168
+ # Creates a new URI::Generic instance from ``generic'' components without check.
169
+ #
170
+ def initialize(scheme,
171
+ userinfo, host, port, registry,
172
+ path, opaque,
173
+ query,
174
+ fragment,
175
+ parser = DEFAULT_PARSER,
176
+ arg_check = false)
177
+ @scheme = nil
178
+ @user = nil
179
+ @password = nil
180
+ @host = nil
181
+ @port = nil
182
+ @path = nil
183
+ @query = nil
184
+ @opaque = nil
185
+ @fragment = nil
186
+ @parser = parser == DEFAULT_PARSER ? nil : parser
187
+
188
+ if arg_check
189
+ self.scheme = scheme
190
+ self.userinfo = userinfo
191
+ self.hostname = host
192
+ self.port = port
193
+ self.path = path
194
+ self.query = query
195
+ self.opaque = opaque
196
+ self.fragment = fragment
197
+ else
198
+ self.set_scheme(scheme)
199
+ self.set_userinfo(userinfo)
200
+ self.set_host(host)
201
+ self.set_port(port)
202
+ self.set_path(path)
203
+ self.query = query
204
+ self.set_opaque(opaque)
205
+ self.fragment=(fragment)
206
+ end
207
+ if registry
208
+ raise InvalidURIError,
209
+ "the scheme #{@scheme} does not accept registry part: #{registry} (or bad hostname?)"
210
+ end
211
+
212
+ @scheme&.freeze
213
+ self.set_path('') if !@path && !@opaque # (see RFC2396 Section 5.2)
214
+ self.set_port(self.default_port) if self.default_port && !@port
215
+ end
216
+
217
+ #
218
+ # Returns the scheme component of the URI.
219
+ #
220
+ # URI("http://foo/bar/baz").scheme #=> "http"
221
+ #
222
+ attr_reader :scheme
223
+
224
+ # Returns the host component of the URI.
225
+ #
226
+ # URI("http://foo/bar/baz").host #=> "foo"
227
+ #
228
+ # It returns nil if no host component exists.
229
+ #
230
+ # URI("mailto:foo@example.org").host #=> nil
231
+ #
232
+ # The component does not contain the port number.
233
+ #
234
+ # URI("http://foo:8080/bar/baz").host #=> "foo"
235
+ #
236
+ # Since IPv6 addresses are wrapped with brackets in URIs,
237
+ # this method returns IPv6 addresses wrapped with brackets.
238
+ # This form is not appropriate to pass to socket methods such as TCPSocket.open.
239
+ # If unwrapped host names are required, use the #hostname method.
240
+ #
241
+ # URI("http://[::1]/bar/baz").host #=> "[::1]"
242
+ # URI("http://[::1]/bar/baz").hostname #=> "::1"
243
+ #
244
+ attr_reader :host
245
+
246
+ # Returns the port component of the URI.
247
+ #
248
+ # URI("http://foo/bar/baz").port #=> 80
249
+ # URI("http://foo:8080/bar/baz").port #=> 8080
250
+ #
251
+ attr_reader :port
252
+
253
+ def registry # :nodoc:
254
+ nil
255
+ end
256
+
257
+ # Returns the path component of the URI.
258
+ #
259
+ # URI("http://foo/bar/baz").path #=> "/bar/baz"
260
+ #
261
+ attr_reader :path
262
+
263
+ # Returns the query component of the URI.
264
+ #
265
+ # URI("http://foo/bar/baz?search=FooBar").query #=> "search=FooBar"
266
+ #
267
+ attr_reader :query
268
+
269
+ # Returns the opaque part of the URI.
270
+ #
271
+ # URI("mailto:foo@example.org").opaque #=> "foo@example.org"
272
+ # URI("http://foo/bar/baz").opaque #=> nil
273
+ #
274
+ # The portion of the path that does not make use of the slash '/'.
275
+ # The path typically refers to an absolute path or an opaque part.
276
+ # (See RFC2396 Section 3 and 5.2.)
277
+ #
278
+ attr_reader :opaque
279
+
280
+ # Returns the fragment component of the URI.
281
+ #
282
+ # URI("http://foo/bar/baz?search=FooBar#ponies").fragment #=> "ponies"
283
+ #
284
+ attr_reader :fragment
285
+
286
+ # Returns the parser to be used.
287
+ #
288
+ # Unless a URI::Parser is defined, DEFAULT_PARSER is used.
289
+ #
290
+ def parser
291
+ if !defined?(@parser) || !@parser
292
+ DEFAULT_PARSER
293
+ else
294
+ @parser || DEFAULT_PARSER
295
+ end
296
+ end
297
+
298
+ # Replaces self by other URI object.
299
+ #
300
+ def replace!(oth)
301
+ if self.class != oth.class
302
+ raise ArgumentError, "expected #{self.class} object"
303
+ end
304
+
305
+ component.each do |c|
306
+ self.__send__("#{c}=", oth.__send__(c))
307
+ end
308
+ end
309
+ private :replace!
310
+
311
+ #
312
+ # Components of the URI in the order.
313
+ #
314
+ def component
315
+ self.class.component
316
+ end
317
+
318
+ #
319
+ # Checks the scheme +v+ component against the URI::Parser Regexp for :SCHEME.
320
+ #
321
+ def check_scheme(v)
322
+ if v && parser.regexp[:SCHEME] !~ v
323
+ raise InvalidComponentError,
324
+ "bad component(expected scheme component): #{v}"
325
+ end
326
+
327
+ return true
328
+ end
329
+ private :check_scheme
330
+
331
+ # Protected setter for the scheme component +v+.
332
+ #
333
+ # See also URI::Generic.scheme=.
334
+ #
335
+ def set_scheme(v)
336
+ @scheme = v&.downcase
337
+ end
338
+ protected :set_scheme
339
+
340
+ #
341
+ # == Args
342
+ #
343
+ # +v+::
344
+ # String
345
+ #
346
+ # == Description
347
+ #
348
+ # Public setter for the scheme component +v+
349
+ # (with validation).
350
+ #
351
+ # See also URI::Generic.check_scheme.
352
+ #
353
+ # == Usage
354
+ #
355
+ # require 'uri'
356
+ #
357
+ # uri = URI.parse("http://my.example.com")
358
+ # uri.scheme = "https"
359
+ # uri.to_s #=> "https://my.example.com"
360
+ #
361
+ def scheme=(v)
362
+ check_scheme(v)
363
+ set_scheme(v)
364
+ v
365
+ end
366
+
367
+ #
368
+ # Checks the +user+ and +password+.
369
+ #
370
+ # If +password+ is not provided, then +user+ is
371
+ # split, using URI::Generic.split_userinfo, to
372
+ # pull +user+ and +password.
373
+ #
374
+ # See also URI::Generic.check_user, URI::Generic.check_password.
375
+ #
376
+ def check_userinfo(user, password = nil)
377
+ if !password
378
+ user, password = split_userinfo(user)
379
+ end
380
+ check_user(user)
381
+ check_password(password, user)
382
+
383
+ return true
384
+ end
385
+ private :check_userinfo
386
+
387
+ #
388
+ # Checks the user +v+ component for RFC2396 compliance
389
+ # and against the URI::Parser Regexp for :USERINFO.
390
+ #
391
+ # Can not have a registry or opaque component defined,
392
+ # with a user component defined.
393
+ #
394
+ def check_user(v)
395
+ if @opaque
396
+ raise InvalidURIError,
397
+ "can not set user with opaque"
398
+ end
399
+
400
+ return v unless v
401
+
402
+ if parser.regexp[:USERINFO] !~ v
403
+ raise InvalidComponentError,
404
+ "bad component(expected userinfo component or user component): #{v}"
405
+ end
406
+
407
+ return true
408
+ end
409
+ private :check_user
410
+
411
+ #
412
+ # Checks the password +v+ component for RFC2396 compliance
413
+ # and against the URI::Parser Regexp for :USERINFO.
414
+ #
415
+ # Can not have a registry or opaque component defined,
416
+ # with a user component defined.
417
+ #
418
+ def check_password(v, user = @user)
419
+ if @opaque
420
+ raise InvalidURIError,
421
+ "can not set password with opaque"
422
+ end
423
+ return v unless v
424
+
425
+ if !user
426
+ raise InvalidURIError,
427
+ "password component depends user component"
428
+ end
429
+
430
+ if parser.regexp[:USERINFO] !~ v
431
+ raise InvalidComponentError,
432
+ "bad password component"
433
+ end
434
+
435
+ return true
436
+ end
437
+ private :check_password
438
+
439
+ #
440
+ # Sets userinfo, argument is string like 'name:pass'.
441
+ #
442
+ def userinfo=(userinfo)
443
+ if userinfo.nil?
444
+ return nil
445
+ end
446
+ check_userinfo(*userinfo)
447
+ set_userinfo(*userinfo)
448
+ # returns userinfo
449
+ end
450
+
451
+ #
452
+ # == Args
453
+ #
454
+ # +v+::
455
+ # String
456
+ #
457
+ # == Description
458
+ #
459
+ # Public setter for the +user+ component
460
+ # (with validation).
461
+ #
462
+ # See also URI::Generic.check_user.
463
+ #
464
+ # == Usage
465
+ #
466
+ # require 'uri'
467
+ #
468
+ # uri = URI.parse("http://john:S3nsit1ve@my.example.com")
469
+ # uri.user = "sam"
470
+ # uri.to_s #=> "http://sam:V3ry_S3nsit1ve@my.example.com"
471
+ #
472
+ def user=(user)
473
+ check_user(user)
474
+ set_user(user)
475
+ # returns user
476
+ end
477
+
478
+ #
479
+ # == Args
480
+ #
481
+ # +v+::
482
+ # String
483
+ #
484
+ # == Description
485
+ #
486
+ # Public setter for the +password+ component
487
+ # (with validation).
488
+ #
489
+ # See also URI::Generic.check_password.
490
+ #
491
+ # == Usage
492
+ #
493
+ # require 'uri'
494
+ #
495
+ # uri = URI.parse("http://john:S3nsit1ve@my.example.com")
496
+ # uri.password = "V3ry_S3nsit1ve"
497
+ # uri.to_s #=> "http://john:V3ry_S3nsit1ve@my.example.com"
498
+ #
499
+ def password=(password)
500
+ check_password(password)
501
+ set_password(password)
502
+ # returns password
503
+ end
504
+
505
+ # Protected setter for the +user+ component, and +password+ if available
506
+ # (with validation).
507
+ #
508
+ # See also URI::Generic.userinfo=.
509
+ #
510
+ def set_userinfo(user, password = nil)
511
+ unless password
512
+ user, password = split_userinfo(user)
513
+ end
514
+ @user = user
515
+ @password = password if password
516
+
517
+ [@user, @password]
518
+ end
519
+ protected :set_userinfo
520
+
521
+ # Protected setter for the user component +v+.
522
+ #
523
+ # See also URI::Generic.user=.
524
+ #
525
+ def set_user(v)
526
+ set_userinfo(v, @password)
527
+ v
528
+ end
529
+ protected :set_user
530
+
531
+ # Protected setter for the password component +v+.
532
+ #
533
+ # See also URI::Generic.password=.
534
+ #
535
+ def set_password(v)
536
+ @password = v
537
+ # returns v
538
+ end
539
+ protected :set_password
540
+
541
+ # Returns the userinfo +ui+ as <code>[user, password]</code>
542
+ # if properly formatted as 'user:password'.
543
+ def split_userinfo(ui)
544
+ return nil, nil unless ui
545
+ user, password = ui.split(':', 2)
546
+
547
+ return user, password
548
+ end
549
+ private :split_userinfo
550
+
551
+ # Escapes 'user:password' +v+ based on RFC 1738 section 3.1.
552
+ def escape_userpass(v)
553
+ parser.escape(v, /[@:\/]/o) # RFC 1738 section 3.1 #/
554
+ end
555
+ private :escape_userpass
556
+
557
+ # Returns the userinfo, either as 'user' or 'user:password'.
558
+ def userinfo
559
+ if @user.nil?
560
+ nil
561
+ elsif @password.nil?
562
+ @user
563
+ else
564
+ @user + ':' + @password
565
+ end
566
+ end
567
+
568
+ # Returns the user component.
569
+ def user
570
+ @user
571
+ end
572
+
573
+ # Returns the password component.
574
+ def password
575
+ @password
576
+ end
577
+
578
+ #
579
+ # Checks the host +v+ component for RFC2396 compliance
580
+ # and against the URI::Parser Regexp for :HOST.
581
+ #
582
+ # Can not have a registry or opaque component defined,
583
+ # with a host component defined.
584
+ #
585
+ def check_host(v)
586
+ return v unless v
587
+
588
+ if @opaque
589
+ raise InvalidURIError,
590
+ "can not set host with registry or opaque"
591
+ elsif parser.regexp[:HOST] !~ v
592
+ raise InvalidComponentError,
593
+ "bad component(expected host component): #{v}"
594
+ end
595
+
596
+ return true
597
+ end
598
+ private :check_host
599
+
600
+ # Protected setter for the host component +v+.
601
+ #
602
+ # See also URI::Generic.host=.
603
+ #
604
+ def set_host(v)
605
+ @host = v
606
+ end
607
+ protected :set_host
608
+
609
+ #
610
+ # == Args
611
+ #
612
+ # +v+::
613
+ # String
614
+ #
615
+ # == Description
616
+ #
617
+ # Public setter for the host component +v+
618
+ # (with validation).
619
+ #
620
+ # See also URI::Generic.check_host.
621
+ #
622
+ # == Usage
623
+ #
624
+ # require 'uri'
625
+ #
626
+ # uri = URI.parse("http://my.example.com")
627
+ # uri.host = "foo.com"
628
+ # uri.to_s #=> "http://foo.com"
629
+ #
630
+ def host=(v)
631
+ check_host(v)
632
+ set_host(v)
633
+ v
634
+ end
635
+
636
+ # Extract the host part of the URI and unwrap brackets for IPv6 addresses.
637
+ #
638
+ # This method is the same as URI::Generic#host except
639
+ # brackets for IPv6 (and future IP) addresses are removed.
640
+ #
641
+ # uri = URI("http://[::1]/bar")
642
+ # uri.hostname #=> "::1"
643
+ # uri.host #=> "[::1]"
644
+ #
645
+ def hostname
646
+ v = self.host
647
+ /\A\[(.*)\]\z/ =~ v ? $1 : v
648
+ end
649
+
650
+ # Sets the host part of the URI as the argument with brackets for IPv6 addresses.
651
+ #
652
+ # This method is the same as URI::Generic#host= except
653
+ # the argument can be a bare IPv6 address.
654
+ #
655
+ # uri = URI("http://foo/bar")
656
+ # uri.hostname = "::1"
657
+ # uri.to_s #=> "http://[::1]/bar"
658
+ #
659
+ # If the argument seems to be an IPv6 address,
660
+ # it is wrapped with brackets.
661
+ #
662
+ def hostname=(v)
663
+ v = "[#{v}]" if /\A\[.*\]\z/ !~ v && /:/ =~ v
664
+ self.host = v
665
+ end
666
+
667
+ #
668
+ # Checks the port +v+ component for RFC2396 compliance
669
+ # and against the URI::Parser Regexp for :PORT.
670
+ #
671
+ # Can not have a registry or opaque component defined,
672
+ # with a port component defined.
673
+ #
674
+ def check_port(v)
675
+ return v unless v
676
+
677
+ if @opaque
678
+ raise InvalidURIError,
679
+ "can not set port with registry or opaque"
680
+ elsif !v.kind_of?(Integer) && parser.regexp[:PORT] !~ v
681
+ raise InvalidComponentError,
682
+ "bad component(expected port component): #{v.inspect}"
683
+ end
684
+
685
+ return true
686
+ end
687
+ private :check_port
688
+
689
+ # Protected setter for the port component +v+.
690
+ #
691
+ # See also URI::Generic.port=.
692
+ #
693
+ def set_port(v)
694
+ v = v.empty? ? nil : v.to_i unless !v || v.kind_of?(Integer)
695
+ @port = v
696
+ end
697
+ protected :set_port
698
+
699
+ #
700
+ # == Args
701
+ #
702
+ # +v+::
703
+ # String
704
+ #
705
+ # == Description
706
+ #
707
+ # Public setter for the port component +v+
708
+ # (with validation).
709
+ #
710
+ # See also URI::Generic.check_port.
711
+ #
712
+ # == Usage
713
+ #
714
+ # require 'uri'
715
+ #
716
+ # uri = URI.parse("http://my.example.com")
717
+ # uri.port = 8080
718
+ # uri.to_s #=> "http://my.example.com:8080"
719
+ #
720
+ def port=(v)
721
+ check_port(v)
722
+ set_port(v)
723
+ port
724
+ end
725
+
726
+ def check_registry(v) # :nodoc:
727
+ raise InvalidURIError, "can not set registry"
728
+ end
729
+ private :check_registry
730
+
731
+ def set_registry(v) #:nodoc:
732
+ raise InvalidURIError, "can not set registry"
733
+ end
734
+ protected :set_registry
735
+
736
+ def registry=(v)
737
+ raise InvalidURIError, "can not set registry"
738
+ end
739
+
740
+ #
741
+ # Checks the path +v+ component for RFC2396 compliance
742
+ # and against the URI::Parser Regexp
743
+ # for :ABS_PATH and :REL_PATH.
744
+ #
745
+ # Can not have a opaque component defined,
746
+ # with a path component defined.
747
+ #
748
+ def check_path(v)
749
+ # raise if both hier and opaque are not nil, because:
750
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
751
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
752
+ if v && @opaque
753
+ raise InvalidURIError,
754
+ "path conflicts with opaque"
755
+ end
756
+
757
+ # If scheme is ftp, path may be relative.
758
+ # See RFC 1738 section 3.2.2, and RFC 2396.
759
+ if @scheme && @scheme != "ftp"
760
+ if v && v != '' && parser.regexp[:ABS_PATH] !~ v
761
+ raise InvalidComponentError,
762
+ "bad component(expected absolute path component): #{v}"
763
+ end
764
+ else
765
+ if v && v != '' && parser.regexp[:ABS_PATH] !~ v &&
766
+ parser.regexp[:REL_PATH] !~ v
767
+ raise InvalidComponentError,
768
+ "bad component(expected relative path component): #{v}"
769
+ end
770
+ end
771
+
772
+ return true
773
+ end
774
+ private :check_path
775
+
776
+ # Protected setter for the path component +v+.
777
+ #
778
+ # See also URI::Generic.path=.
779
+ #
780
+ def set_path(v)
781
+ @path = v
782
+ end
783
+ protected :set_path
784
+
785
+ #
786
+ # == Args
787
+ #
788
+ # +v+::
789
+ # String
790
+ #
791
+ # == Description
792
+ #
793
+ # Public setter for the path component +v+
794
+ # (with validation).
795
+ #
796
+ # See also URI::Generic.check_path.
797
+ #
798
+ # == Usage
799
+ #
800
+ # require 'uri'
801
+ #
802
+ # uri = URI.parse("http://my.example.com/pub/files")
803
+ # uri.path = "/faq/"
804
+ # uri.to_s #=> "http://my.example.com/faq/"
805
+ #
806
+ def path=(v)
807
+ check_path(v)
808
+ set_path(v)
809
+ v
810
+ end
811
+
812
+ #
813
+ # == Args
814
+ #
815
+ # +v+::
816
+ # String
817
+ #
818
+ # == Description
819
+ #
820
+ # Public setter for the query component +v+.
821
+ #
822
+ # == Usage
823
+ #
824
+ # require 'uri'
825
+ #
826
+ # uri = URI.parse("http://my.example.com/?id=25")
827
+ # uri.query = "id=1"
828
+ # uri.to_s #=> "http://my.example.com/?id=1"
829
+ #
830
+ def query=(v)
831
+ return @query = nil unless v
832
+ raise InvalidURIError, "query conflicts with opaque" if @opaque
833
+
834
+ x = v.to_str
835
+ v = x.dup if x.equal? v
836
+ v.encode!(Encoding::UTF_8) rescue nil
837
+ v.delete!("\t\r\n")
838
+ v.force_encoding(Encoding::ASCII_8BIT)
839
+ raise InvalidURIError, "invalid percent escape: #{$1}" if /(%\H\H)/n.match(v)
840
+ v.gsub!(/(?!%\h\h|[!$-&(-;=?-_a-~])./n.freeze){'%%%02X' % $&.ord}
841
+ v.force_encoding(Encoding::US_ASCII)
842
+ @query = v
843
+ end
844
+
845
+ #
846
+ # Checks the opaque +v+ component for RFC2396 compliance and
847
+ # against the URI::Parser Regexp for :OPAQUE.
848
+ #
849
+ # Can not have a host, port, user, or path component defined,
850
+ # with an opaque component defined.
851
+ #
852
+ def check_opaque(v)
853
+ return v unless v
854
+
855
+ # raise if both hier and opaque are not nil, because:
856
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
857
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
858
+ if @host || @port || @user || @path # userinfo = @user + ':' + @password
859
+ raise InvalidURIError,
860
+ "can not set opaque with host, port, userinfo or path"
861
+ elsif v && parser.regexp[:OPAQUE] !~ v
862
+ raise InvalidComponentError,
863
+ "bad component(expected opaque component): #{v}"
864
+ end
865
+
866
+ return true
867
+ end
868
+ private :check_opaque
869
+
870
+ # Protected setter for the opaque component +v+.
871
+ #
872
+ # See also URI::Generic.opaque=.
873
+ #
874
+ def set_opaque(v)
875
+ @opaque = v
876
+ end
877
+ protected :set_opaque
878
+
879
+ #
880
+ # == Args
881
+ #
882
+ # +v+::
883
+ # String
884
+ #
885
+ # == Description
886
+ #
887
+ # Public setter for the opaque component +v+
888
+ # (with validation).
889
+ #
890
+ # See also URI::Generic.check_opaque.
891
+ #
892
+ def opaque=(v)
893
+ check_opaque(v)
894
+ set_opaque(v)
895
+ v
896
+ end
897
+
898
+ #
899
+ # Checks the fragment +v+ component against the URI::Parser Regexp for :FRAGMENT.
900
+ #
901
+ #
902
+ # == Args
903
+ #
904
+ # +v+::
905
+ # String
906
+ #
907
+ # == Description
908
+ #
909
+ # Public setter for the fragment component +v+
910
+ # (with validation).
911
+ #
912
+ # == Usage
913
+ #
914
+ # require 'uri'
915
+ #
916
+ # uri = URI.parse("http://my.example.com/?id=25#time=1305212049")
917
+ # uri.fragment = "time=1305212086"
918
+ # uri.to_s #=> "http://my.example.com/?id=25#time=1305212086"
919
+ #
920
+ def fragment=(v)
921
+ return @fragment = nil unless v
922
+
923
+ x = v.to_str
924
+ v = x.dup if x.equal? v
925
+ v.encode!(Encoding::UTF_8) rescue nil
926
+ v.delete!("\t\r\n")
927
+ v.force_encoding(Encoding::ASCII_8BIT)
928
+ v.gsub!(/(?!%\h\h|[!-~])./n){'%%%02X' % $&.ord}
929
+ v.force_encoding(Encoding::US_ASCII)
930
+ @fragment = v
931
+ end
932
+
933
+ #
934
+ # Returns true if URI is hierarchical.
935
+ #
936
+ # == Description
937
+ #
938
+ # URI has components listed in order of decreasing significance from left to right,
939
+ # see RFC3986 https://tools.ietf.org/html/rfc3986 1.2.3.
940
+ #
941
+ # == Usage
942
+ #
943
+ # require 'uri'
944
+ #
945
+ # uri = URI.parse("http://my.example.com/")
946
+ # uri.hierarchical?
947
+ # #=> true
948
+ # uri = URI.parse("mailto:joe@example.com")
949
+ # uri.hierarchical?
950
+ # #=> false
951
+ #
952
+ def hierarchical?
953
+ if @path
954
+ true
955
+ else
956
+ false
957
+ end
958
+ end
959
+
960
+ #
961
+ # Returns true if URI has a scheme (e.g. http:// or https://) specified.
962
+ #
963
+ def absolute?
964
+ if @scheme
965
+ true
966
+ else
967
+ false
968
+ end
969
+ end
970
+ alias absolute absolute?
971
+
972
+ #
973
+ # Returns true if URI does not have a scheme (e.g. http:// or https://) specified.
974
+ #
975
+ def relative?
976
+ !absolute?
977
+ end
978
+
979
+ #
980
+ # Returns an Array of the path split on '/'.
981
+ #
982
+ def split_path(path)
983
+ path.split("/", -1)
984
+ end
985
+ private :split_path
986
+
987
+ #
988
+ # Merges a base path +base+, with relative path +rel+,
989
+ # returns a modified base path.
990
+ #
991
+ def merge_path(base, rel)
992
+
993
+ # RFC2396, Section 5.2, 5)
994
+ # RFC2396, Section 5.2, 6)
995
+ base_path = split_path(base)
996
+ rel_path = split_path(rel)
997
+
998
+ # RFC2396, Section 5.2, 6), a)
999
+ base_path << '' if base_path.last == '..'
1000
+ while i = base_path.index('..')
1001
+ base_path.slice!(i - 1, 2)
1002
+ end
1003
+
1004
+ if (first = rel_path.first) and first.empty?
1005
+ base_path.clear
1006
+ rel_path.shift
1007
+ end
1008
+
1009
+ # RFC2396, Section 5.2, 6), c)
1010
+ # RFC2396, Section 5.2, 6), d)
1011
+ rel_path.push('') if rel_path.last == '.' || rel_path.last == '..'
1012
+ rel_path.delete('.')
1013
+
1014
+ # RFC2396, Section 5.2, 6), e)
1015
+ tmp = []
1016
+ rel_path.each do |x|
1017
+ if x == '..' &&
1018
+ !(tmp.empty? || tmp.last == '..')
1019
+ tmp.pop
1020
+ else
1021
+ tmp << x
1022
+ end
1023
+ end
1024
+
1025
+ add_trailer_slash = !tmp.empty?
1026
+ if base_path.empty?
1027
+ base_path = [''] # keep '/' for root directory
1028
+ elsif add_trailer_slash
1029
+ base_path.pop
1030
+ end
1031
+ while x = tmp.shift
1032
+ if x == '..'
1033
+ # RFC2396, Section 4
1034
+ # a .. or . in an absolute path has no special meaning
1035
+ base_path.pop if base_path.size > 1
1036
+ else
1037
+ # if x == '..'
1038
+ # valid absolute (but abnormal) path "/../..."
1039
+ # else
1040
+ # valid absolute path
1041
+ # end
1042
+ base_path << x
1043
+ tmp.each {|t| base_path << t}
1044
+ add_trailer_slash = false
1045
+ break
1046
+ end
1047
+ end
1048
+ base_path.push('') if add_trailer_slash
1049
+
1050
+ return base_path.join('/')
1051
+ end
1052
+ private :merge_path
1053
+
1054
+ #
1055
+ # == Args
1056
+ #
1057
+ # +oth+::
1058
+ # URI or String
1059
+ #
1060
+ # == Description
1061
+ #
1062
+ # Destructive form of #merge.
1063
+ #
1064
+ # == Usage
1065
+ #
1066
+ # require 'uri'
1067
+ #
1068
+ # uri = URI.parse("http://my.example.com")
1069
+ # uri.merge!("/main.rbx?page=1")
1070
+ # uri.to_s # => "http://my.example.com/main.rbx?page=1"
1071
+ #
1072
+ def merge!(oth)
1073
+ t = merge(oth)
1074
+ if self == t
1075
+ nil
1076
+ else
1077
+ replace!(t)
1078
+ self
1079
+ end
1080
+ end
1081
+
1082
+ #
1083
+ # == Args
1084
+ #
1085
+ # +oth+::
1086
+ # URI or String
1087
+ #
1088
+ # == Description
1089
+ #
1090
+ # Merges two URIs.
1091
+ #
1092
+ # == Usage
1093
+ #
1094
+ # require 'uri'
1095
+ #
1096
+ # uri = URI.parse("http://my.example.com")
1097
+ # uri.merge("/main.rbx?page=1")
1098
+ # # => "http://my.example.com/main.rbx?page=1"
1099
+ #
1100
+ def merge(oth)
1101
+ rel = parser.send(:convert_to_uri, oth)
1102
+
1103
+ if rel.absolute?
1104
+ #raise BadURIError, "both URI are absolute" if absolute?
1105
+ # hmm... should return oth for usability?
1106
+ return rel
1107
+ end
1108
+
1109
+ unless self.absolute?
1110
+ raise BadURIError, "both URI are relative"
1111
+ end
1112
+
1113
+ base = self.dup
1114
+
1115
+ authority = rel.userinfo || rel.host || rel.port
1116
+
1117
+ # RFC2396, Section 5.2, 2)
1118
+ if (rel.path.nil? || rel.path.empty?) && !authority && !rel.query
1119
+ base.fragment=(rel.fragment) if rel.fragment
1120
+ return base
1121
+ end
1122
+
1123
+ base.query = nil
1124
+ base.fragment=(nil)
1125
+
1126
+ # RFC2396, Section 5.2, 4)
1127
+ if !authority
1128
+ base.set_path(merge_path(base.path, rel.path)) if base.path && rel.path
1129
+ else
1130
+ # RFC2396, Section 5.2, 4)
1131
+ base.set_path(rel.path) if rel.path
1132
+ end
1133
+
1134
+ # RFC2396, Section 5.2, 7)
1135
+ base.set_userinfo(rel.userinfo) if rel.userinfo
1136
+ base.set_host(rel.host) if rel.host
1137
+ base.set_port(rel.port) if rel.port
1138
+ base.query = rel.query if rel.query
1139
+ base.fragment=(rel.fragment) if rel.fragment
1140
+
1141
+ return base
1142
+ end # merge
1143
+ alias + merge
1144
+
1145
+ # :stopdoc:
1146
+ def route_from_path(src, dst)
1147
+ case dst
1148
+ when src
1149
+ # RFC2396, Section 4.2
1150
+ return ''
1151
+ when %r{(?:\A|/)\.\.?(?:/|\z)}
1152
+ # dst has abnormal absolute path,
1153
+ # like "/./", "/../", "/x/../", ...
1154
+ return dst.dup
1155
+ end
1156
+
1157
+ src_path = src.scan(%r{[^/]*/})
1158
+ dst_path = dst.scan(%r{[^/]*/?})
1159
+
1160
+ # discard same parts
1161
+ while !dst_path.empty? && dst_path.first == src_path.first
1162
+ src_path.shift
1163
+ dst_path.shift
1164
+ end
1165
+
1166
+ tmp = dst_path.join
1167
+
1168
+ # calculate
1169
+ if src_path.empty?
1170
+ if tmp.empty?
1171
+ return './'
1172
+ elsif dst_path.first.include?(':') # (see RFC2396 Section 5)
1173
+ return './' + tmp
1174
+ else
1175
+ return tmp
1176
+ end
1177
+ end
1178
+
1179
+ return '../' * src_path.size + tmp
1180
+ end
1181
+ private :route_from_path
1182
+ # :startdoc:
1183
+
1184
+ # :stopdoc:
1185
+ def route_from0(oth)
1186
+ oth = parser.send(:convert_to_uri, oth)
1187
+ if self.relative?
1188
+ raise BadURIError,
1189
+ "relative URI: #{self}"
1190
+ end
1191
+ if oth.relative?
1192
+ raise BadURIError,
1193
+ "relative URI: #{oth}"
1194
+ end
1195
+
1196
+ if self.scheme != oth.scheme
1197
+ return self, self.dup
1198
+ end
1199
+ rel = URI::Generic.new(nil, # it is relative URI
1200
+ self.userinfo, self.host, self.port,
1201
+ nil, self.path, self.opaque,
1202
+ self.query, self.fragment, parser)
1203
+
1204
+ if rel.userinfo != oth.userinfo ||
1205
+ rel.host.to_s.downcase != oth.host.to_s.downcase ||
1206
+ rel.port != oth.port
1207
+
1208
+ if self.userinfo.nil? && self.host.nil?
1209
+ return self, self.dup
1210
+ end
1211
+
1212
+ rel.set_port(nil) if rel.port == oth.default_port
1213
+ return rel, rel
1214
+ end
1215
+ rel.set_userinfo(nil)
1216
+ rel.set_host(nil)
1217
+ rel.set_port(nil)
1218
+
1219
+ if rel.path && rel.path == oth.path
1220
+ rel.set_path('')
1221
+ rel.query = nil if rel.query == oth.query
1222
+ return rel, rel
1223
+ elsif rel.opaque && rel.opaque == oth.opaque
1224
+ rel.set_opaque('')
1225
+ rel.query = nil if rel.query == oth.query
1226
+ return rel, rel
1227
+ end
1228
+
1229
+ # you can modify `rel', but can not `oth'.
1230
+ return oth, rel
1231
+ end
1232
+ private :route_from0
1233
+ # :startdoc:
1234
+
1235
+ #
1236
+ # == Args
1237
+ #
1238
+ # +oth+::
1239
+ # URI or String
1240
+ #
1241
+ # == Description
1242
+ #
1243
+ # Calculates relative path from oth to self.
1244
+ #
1245
+ # == Usage
1246
+ #
1247
+ # require 'uri'
1248
+ #
1249
+ # uri = URI.parse('http://my.example.com/main.rbx?page=1')
1250
+ # uri.route_from('http://my.example.com')
1251
+ # #=> #<URI::Generic /main.rbx?page=1>
1252
+ #
1253
+ def route_from(oth)
1254
+ # you can modify `rel', but can not `oth'.
1255
+ begin
1256
+ oth, rel = route_from0(oth)
1257
+ rescue
1258
+ raise $!.class, $!.message
1259
+ end
1260
+ if oth == rel
1261
+ return rel
1262
+ end
1263
+
1264
+ rel.set_path(route_from_path(oth.path, self.path))
1265
+ if rel.path == './' && self.query
1266
+ # "./?foo" -> "?foo"
1267
+ rel.set_path('')
1268
+ end
1269
+
1270
+ return rel
1271
+ end
1272
+
1273
+ alias - route_from
1274
+
1275
+ #
1276
+ # == Args
1277
+ #
1278
+ # +oth+::
1279
+ # URI or String
1280
+ #
1281
+ # == Description
1282
+ #
1283
+ # Calculates relative path to oth from self.
1284
+ #
1285
+ # == Usage
1286
+ #
1287
+ # require 'uri'
1288
+ #
1289
+ # uri = URI.parse('http://my.example.com')
1290
+ # uri.route_to('http://my.example.com/main.rbx?page=1')
1291
+ # #=> #<URI::Generic /main.rbx?page=1>
1292
+ #
1293
+ def route_to(oth)
1294
+ parser.send(:convert_to_uri, oth).route_from(self)
1295
+ end
1296
+
1297
+ #
1298
+ # Returns normalized URI.
1299
+ #
1300
+ # require 'uri'
1301
+ #
1302
+ # URI("HTTP://my.EXAMPLE.com").normalize
1303
+ # #=> #<URI::HTTP http://my.example.com/>
1304
+ #
1305
+ # Normalization here means:
1306
+ #
1307
+ # * scheme and host are converted to lowercase,
1308
+ # * an empty path component is set to "/".
1309
+ #
1310
+ def normalize
1311
+ uri = dup
1312
+ uri.normalize!
1313
+ uri
1314
+ end
1315
+
1316
+ #
1317
+ # Destructive version of #normalize.
1318
+ #
1319
+ def normalize!
1320
+ if path&.empty?
1321
+ set_path('/')
1322
+ end
1323
+ if scheme && scheme != scheme.downcase
1324
+ set_scheme(self.scheme.downcase)
1325
+ end
1326
+ if host && host != host.downcase
1327
+ set_host(self.host.downcase)
1328
+ end
1329
+ end
1330
+
1331
+ #
1332
+ # Constructs String from URI.
1333
+ #
1334
+ def to_s
1335
+ str = ''.dup
1336
+ if @scheme
1337
+ str << @scheme
1338
+ str << ':'
1339
+ end
1340
+
1341
+ if @opaque
1342
+ str << @opaque
1343
+ else
1344
+ if @host || %w[file postgres].include?(@scheme)
1345
+ str << '//'
1346
+ end
1347
+ if self.userinfo
1348
+ str << self.userinfo
1349
+ str << '@'
1350
+ end
1351
+ if @host
1352
+ str << @host
1353
+ end
1354
+ if @port && @port != self.default_port
1355
+ str << ':'
1356
+ str << @port.to_s
1357
+ end
1358
+ str << @path
1359
+ if @query
1360
+ str << '?'
1361
+ str << @query
1362
+ end
1363
+ end
1364
+ if @fragment
1365
+ str << '#'
1366
+ str << @fragment
1367
+ end
1368
+ str
1369
+ end
1370
+
1371
+ #
1372
+ # Compares two URIs.
1373
+ #
1374
+ def ==(oth)
1375
+ if self.class == oth.class
1376
+ self.normalize.component_ary == oth.normalize.component_ary
1377
+ else
1378
+ false
1379
+ end
1380
+ end
1381
+
1382
+ def hash
1383
+ self.component_ary.hash
1384
+ end
1385
+
1386
+ def eql?(oth)
1387
+ self.class == oth.class &&
1388
+ parser == oth.parser &&
1389
+ self.component_ary.eql?(oth.component_ary)
1390
+ end
1391
+
1392
+ =begin
1393
+
1394
+ --- URI::Generic#===(oth)
1395
+
1396
+ =end
1397
+ # def ===(oth)
1398
+ # raise NotImplementedError
1399
+ # end
1400
+
1401
+ =begin
1402
+ =end
1403
+
1404
+
1405
+ # Returns an Array of the components defined from the COMPONENT Array.
1406
+ def component_ary
1407
+ component.collect do |x|
1408
+ self.send(x)
1409
+ end
1410
+ end
1411
+ protected :component_ary
1412
+
1413
+ # == Args
1414
+ #
1415
+ # +components+::
1416
+ # Multiple Symbol arguments defined in URI::HTTP.
1417
+ #
1418
+ # == Description
1419
+ #
1420
+ # Selects specified components from URI.
1421
+ #
1422
+ # == Usage
1423
+ #
1424
+ # require 'uri'
1425
+ #
1426
+ # uri = URI.parse('http://myuser:mypass@my.example.com/test.rbx')
1427
+ # uri.select(:userinfo, :host, :path)
1428
+ # # => ["myuser:mypass", "my.example.com", "/test.rbx"]
1429
+ #
1430
+ def select(*components)
1431
+ components.collect do |c|
1432
+ if component.include?(c)
1433
+ self.send(c)
1434
+ else
1435
+ raise ArgumentError,
1436
+ "expected of components of #{self.class} (#{self.class.component.join(', ')})"
1437
+ end
1438
+ end
1439
+ end
1440
+
1441
+ def inspect
1442
+ "#<#{self.class} #{self}>"
1443
+ end
1444
+
1445
+ #
1446
+ # == Args
1447
+ #
1448
+ # +v+::
1449
+ # URI or String
1450
+ #
1451
+ # == Description
1452
+ #
1453
+ # Attempts to parse other URI +oth+,
1454
+ # returns [parsed_oth, self].
1455
+ #
1456
+ # == Usage
1457
+ #
1458
+ # require 'uri'
1459
+ #
1460
+ # uri = URI.parse("http://my.example.com")
1461
+ # uri.coerce("http://foo.com")
1462
+ # #=> [#<URI::HTTP http://foo.com>, #<URI::HTTP http://my.example.com>]
1463
+ #
1464
+ def coerce(oth)
1465
+ case oth
1466
+ when String
1467
+ oth = parser.parse(oth)
1468
+ else
1469
+ super
1470
+ end
1471
+
1472
+ return oth, self
1473
+ end
1474
+
1475
+ # Returns a proxy URI.
1476
+ # The proxy URI is obtained from environment variables such as http_proxy,
1477
+ # ftp_proxy, no_proxy, etc.
1478
+ # If there is no proper proxy, nil is returned.
1479
+ #
1480
+ # If the optional parameter +env+ is specified, it is used instead of ENV.
1481
+ #
1482
+ # Note that capitalized variables (HTTP_PROXY, FTP_PROXY, NO_PROXY, etc.)
1483
+ # are examined, too.
1484
+ #
1485
+ # But http_proxy and HTTP_PROXY is treated specially under CGI environment.
1486
+ # It's because HTTP_PROXY may be set by Proxy: header.
1487
+ # So HTTP_PROXY is not used.
1488
+ # http_proxy is not used too if the variable is case insensitive.
1489
+ # CGI_HTTP_PROXY can be used instead.
1490
+ def find_proxy(env=ENV)
1491
+ raise BadURIError, "relative URI: #{self}" if self.relative?
1492
+ name = self.scheme.downcase + '_proxy'
1493
+ proxy_uri = nil
1494
+ if name == 'http_proxy' && env.include?('REQUEST_METHOD') # CGI?
1495
+ # HTTP_PROXY conflicts with *_proxy for proxy settings and
1496
+ # HTTP_* for header information in CGI.
1497
+ # So it should be careful to use it.
1498
+ pairs = env.reject {|k, v| /\Ahttp_proxy\z/i !~ k }
1499
+ case pairs.length
1500
+ when 0 # no proxy setting anyway.
1501
+ proxy_uri = nil
1502
+ when 1
1503
+ k, _ = pairs.shift
1504
+ if k == 'http_proxy' && env[k.upcase] == nil
1505
+ # http_proxy is safe to use because ENV is case sensitive.
1506
+ proxy_uri = env[name]
1507
+ else
1508
+ proxy_uri = nil
1509
+ end
1510
+ else # http_proxy is safe to use because ENV is case sensitive.
1511
+ proxy_uri = env.to_hash[name]
1512
+ end
1513
+ if !proxy_uri
1514
+ # Use CGI_HTTP_PROXY. cf. libwww-perl.
1515
+ proxy_uri = env["CGI_#{name.upcase}"]
1516
+ end
1517
+ elsif name == 'http_proxy'
1518
+ unless proxy_uri = env[name]
1519
+ if proxy_uri = env[name.upcase]
1520
+ warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.', uplevel: 1
1521
+ end
1522
+ end
1523
+ else
1524
+ proxy_uri = env[name] || env[name.upcase]
1525
+ end
1526
+
1527
+ if proxy_uri.nil? || proxy_uri.empty?
1528
+ return nil
1529
+ end
1530
+
1531
+ if self.hostname
1532
+ begin
1533
+ addr = IPSocket.getaddress(self.hostname)
1534
+ return nil if /\A127\.|\A::1\z/ =~ addr
1535
+ rescue SocketError
1536
+ end
1537
+ end
1538
+
1539
+ name = 'no_proxy'
1540
+ if no_proxy = env[name] || env[name.upcase]
1541
+ return nil unless URI::Generic.use_proxy?(self.hostname, addr, self.port, no_proxy)
1542
+ end
1543
+ URI.parse(proxy_uri)
1544
+ end
1545
+
1546
+ def self.use_proxy?(hostname, addr, port, no_proxy) # :nodoc:
1547
+ hostname = hostname.downcase
1548
+ dothostname = ".#{hostname}"
1549
+ no_proxy.scan(/([^:,\s]+)(?::(\d+))?/) {|p_host, p_port|
1550
+ if !p_port || port == p_port.to_i
1551
+ if p_host.start_with?('.')
1552
+ return false if hostname.end_with?(p_host.downcase)
1553
+ else
1554
+ return false if dothostname.end_with?(".#{p_host.downcase}")
1555
+ end
1556
+ if addr
1557
+ begin
1558
+ return false if IPAddr.new(p_host).include?(addr)
1559
+ rescue IPAddr::InvalidAddressError
1560
+ next
1561
+ end
1562
+ end
1563
+ end
1564
+ }
1565
+ true
1566
+ end
1567
+ end
1568
+ end