diff options
-rw-r--r-- | config.yml.example | 4 | ||||
-rw-r--r-- | lib/gitlab_net.rb | 7 | ||||
-rw-r--r-- | lib/httpunix.rb | 54 | ||||
-rw-r--r-- | spec/httpunix_spec.rb | 55 |
4 files changed, 118 insertions, 2 deletions
diff --git a/config.yml.example b/config.yml.example index 43d6e85..e7ecc01 100644 --- a/config.yml.example +++ b/config.yml.example @@ -10,7 +10,9 @@ user: git # Default: http://localhost:8080/ # You only have to change the default if you have configured Unicorn # to listen on a custom port, or if you have configured Unicorn to -# only listen on a Unix domain socket. +# only listen on a Unix domain socket. For Unix domain sockets use +# "http+unix://<urlquoted-path-to-socket>/", e.g. +# "http+unix://%2Fpath%2Fto%2Fsocket/" gitlab_url: "http://localhost:8080/" # See installation.md#using-https for additional HTTPS configuration details. diff --git a/lib/gitlab_net.rb b/lib/gitlab_net.rb index 8eb63ae..6f47938 100644 --- a/lib/gitlab_net.rb +++ b/lib/gitlab_net.rb @@ -5,6 +5,7 @@ require 'json' require_relative 'gitlab_config' require_relative 'gitlab_logger' require_relative 'gitlab_access' +require_relative 'httpunix' class GitlabNet class ApiUnreachableError < StandardError; end @@ -63,7 +64,11 @@ class GitlabNet end def http_client_for(uri) - http = Net::HTTP.new(uri.host, uri.port) + if uri.is_a?(URI::HTTPUNIX) + http = Net::HTTPUNIX.new(uri.hostname) + else + http = Net::HTTP.new(uri.host, uri.port) + end if uri.is_a?(URI::HTTPS) http.use_ssl = true diff --git a/lib/httpunix.rb b/lib/httpunix.rb new file mode 100644 index 0000000..12787ee --- /dev/null +++ b/lib/httpunix.rb @@ -0,0 +1,54 @@ +# support for http+unix://... connection scheme +# +# The URI scheme has the same structure as the similar one for python requests. See: +# http://fixall.online/theres-no-need-to-reinvent-the-wheelhttpsgithubcommsabramorequests-unixsocketurl/241810/ +# https://github.com/msabramo/requests-unixsocket + +require 'uri' +require 'net/http' + +module URI + class HTTPUNIX < HTTP + def hostname + # decode %XX from path to file + v = self.host + URI.decode(v) + end + + # port is not allowed in URI + DEFAULT_PORT = nil + def set_port(v) + return v unless v + raise InvalidURIError, "http+unix:// cannot contain port" + end + end + @@schemes['HTTP+UNIX'] = HTTPUNIX +end + +# Based on: +# - http://stackoverflow.com/questions/15637226/ruby-1-9-3-simple-get-request-to-unicorn-through-socket +# - Net::HTTP::connect +module Net + class HTTPUNIX < HTTP + def initialize(socketpath, port=nil) + super(socketpath, port) + @port = nil # HTTP will set it to default - override back -> set DEFAULT_PORT + end + + # override to prevent ":<port>" being appended to HTTP_HOST + def addr_port + address + end + + def connect + D "opening connection to #{address} ..." + s = UNIXSocket.new(address) + D "opened" + @socket = BufferedIO.new(s) + @socket.read_timeout = @read_timeout + @socket.continue_timeout = @continue_timeout + @socket.debug_output = @debug_output + on_connect + end + end +end diff --git a/spec/httpunix_spec.rb b/spec/httpunix_spec.rb new file mode 100644 index 0000000..cd2ede9 --- /dev/null +++ b/spec/httpunix_spec.rb @@ -0,0 +1,55 @@ +require_relative 'spec_helper' +require_relative '../lib/httpunix' +require 'webrick' + +describe URI::HTTPUNIX do + describe :parse do + uri = URI::parse('http+unix://%2Fpath%2Fto%2Fsocket/img.jpg') + subject { uri } + + it { should be_an_instance_of(URI::HTTPUNIX) } + its(:scheme) { should eq('http+unix') } + its(:hostname) { should eq('/path/to/socket') } + its(:path) { should eq('/img.jpg') } + end +end + + +# like WEBrick::HTTPServer, but listens on UNIX socket +class HTTPUNIXServer < WEBrick::HTTPServer + def listen(address, port) + socket = Socket.unix_server_socket(address) + socket.autoclose = false + server = UNIXServer.for_fd(socket.fileno) + socket.close + @listeners << server + end +end + +def tmp_socket_path + File.join(ROOT_PATH, 'tmp', 'socket') +end + +describe Net::HTTPUNIX do + # "hello world" over unix socket server in background thread + FileUtils.mkdir_p(File.dirname(tmp_socket_path)) + server = HTTPUNIXServer.new(:BindAddress => tmp_socket_path) + server.mount_proc '/' do |req, resp| + resp.body = "Hello World (at #{req.path})" + end + Thread.start { server.start } + + it "talks via HTTP ok" do + VCR.turned_off do + begin + WebMock.allow_net_connect! + http = Net::HTTPUNIX.new(tmp_socket_path) + expect(http.get('/').body).to eq('Hello World (at /)') + expect(http.get('/path').body).to eq('Hello World (at /path)') + + ensure + WebMock.disable_net_connect! + end + end + end +end |