Skip to content

Commit

Permalink
Make SafeURI 9k compatible by using java.net.URI internally
Browse files Browse the repository at this point in the history
Ruby 2.0's stdlib no longer allows relative IPv6 URLs like: //[::1]
This is a key feature of the Elasticsearch output. See more in
logstash-plugins/logstash-output-elasticsearch#604

The only way to fix this is to use a better URI class which means
introducing a slight incompatibility here. I don't believe that anyone
is using the URI class other than the ES output, and I don't that there
is anyone using any portions of the API here that are no longer
compatible.

Normally we would only make such a change in a major version, but our
hands are tied here. If we are to go with JRuby 9k, which is critical
for support we must use a different, better, library.

java.net.URI is stable and works well, so its probably superior in the
long run anyway.

Fixes elastic#7236
  • Loading branch information
andrewvc committed Jun 2, 2017
1 parent 5378cc9 commit 280722e
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 13 deletions.
13 changes: 13 additions & 0 deletions logstash-core/lib/logstash/instrument/global_metrics.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class GlobalMetrics
class Stats(metric)
@metric = metric
end

def initialize(metric)
@metric = metric

@pipeline_reloads = metric.namespace([:stats, :pipelines])
end


end
141 changes: 130 additions & 11 deletions logstash-core/lib/logstash/util/safe_uri.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,21 @@ class LogStash::Util::SafeURI

extend Forwardable

def_delegators :@uri, :coerce, :query=, :route_from, :port=, :default_port, :select, :normalize!, :absolute?, :registry=, :path, :password, :hostname, :merge, :normalize, :host, :component_ary, :userinfo=, :query, :set_opaque, :+, :merge!, :-, :password=, :parser, :port, :set_host, :set_path, :opaque=, :scheme, :fragment=, :set_query, :set_fragment, :userinfo, :hostname=, :set_port, :path=, :registry, :opaque, :route_to, :set_password, :hierarchical?, :set_user, :set_registry, :set_userinfo, :fragment, :component, :user=, :set_scheme, :absolute, :host=, :relative?, :scheme=, :user

attr_reader :uri

public
def initialize(arg)
@uri = case arg
when String
arg = "//#{arg}" if HOSTNAME_PORT_REGEX.match(arg)
URI.parse(arg)
when URI
java.net.URI.new(arg)
when java.net.URI
arg
when URI
java.net.URI.new(arg.to_s)
else
raise ArgumentError, "Expected a string or URI, got a #{arg.class} creating a URL"
raise ArgumentError, "Expected a string, java.net.URI, or URI, got a #{arg.class} creating a URL"
end
end

Expand All @@ -37,20 +38,138 @@ def inspect
end

def sanitized
return uri unless uri.password # nothing to sanitize here!
return uri unless password # nothing to sanitize here!

safe = uri.clone
safe.password = PASS_PLACEHOLDER
safe
user_info = user ? "#{user}:#{PASS_PLACEHOLDER}" : nil

make_uri(scheme, user_info, host, port, path, query, fragment)
end

def ==(other)
other.is_a?(::LogStash::Util::SafeURI) ? @uri == other.uri : false
end

def clone
cloned_uri = uri.clone
self.class.new(cloned_uri)
# No need to clone the URI, in java its immutable
self.class.new(uri)
end

def update(field, value)
new_scheme = scheme
new_user = user
new_password = password
new_host = host
new_port = port
new_path = path
new_query = query
new_fragment = fragment

case field
when :scheme
new_scheme = value
when :user
new_user = value
when :password
new_password = value
when :host
new_host = value
when :port
new_port = value
when :path
new_path = value
when :query
new_query = value
when :fragment
new_fragment = value
end

user_info = new_user
if new_user && new_password
user_info += ":" + new_password
end

@uri = make_uri(new_scheme, user_info, new_host, new_port, new_path, new_query, new_fragment)
end

def user
if @uri.userInfo
@uri.userInfo.split(":")[0]
end
end

def user=(new_user)
update(:user, new_user)
end

def password
if @uri.userInfo
@uri.userInfo.split(":")[1]
end
end

def password=(new_password)
update(:password, new_password)
end

def hostname
# Alias from the ruby library
host
end

def host=(new_host)
update(:host, new_host)
end

def port
# In java this is an int
uri.port < 1 ? nil : uri.port
end

def port=(new_port)
update(:port, new_port)
end

def path=(new_path)
update(:path, new_path)
end

def query=(new_query)
update(:query, new_query)
end

def fragment=(new_fragment)
update(:fragment, new_fragment)
end

# Same algorithm as Ruby's URI class uses
def normalize!
if path && path == ''
path = '/'
end
if scheme && scheme != scheme.downcase
scheme = self.scheme.downcase
end
if host && host != host.downcase
host = self.host.downcase
end
end

def normalize
d = self.dup
d.normalize!
d
end

def_delegators :@uri, :absolute?, :scheme, :host, :path, :query, :fragment, :userinfo

private

# Jruby doesn't guess the constructor correctly if there are some nil things in place
# hence, this method
def make_uri(scheme, user_info, host, port, path, query, fragment)
# It is lot legal to have a path not starting with a /
prefixed_path = path && path[0] != "/" ? "/#{path}" : path
java.net.URI.new(scheme, user_info, host, port || -1, prefixed_path, query, fragment)
end
end

4 changes: 2 additions & 2 deletions logstash-core/spec/logstash/config/mixin_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@
expect(clone.uri.to_s).to eql(uri_hidden)
end

it "should make the real URI object available under #uri" do
expect(subject.uri.uri).to be_a(::URI)
it "should make the real java.net.URI object available under #uri" do
expect(subject.uri.uri).to be_a(java.net.URI)
end

it "should obfuscate original_params" do
Expand Down

0 comments on commit 280722e

Please sign in to comment.