diff --git a/lib/httpx/io.rb b/lib/httpx/io.rb index c3e72d61..8823c933 100644 --- a/lib/httpx/io.rb +++ b/lib/httpx/io.rb @@ -250,8 +250,10 @@ module HTTPX end end module IO + require "httpx/io/unix" extend Registry register "tcp", TCP register "ssl", SSL + register "unix", HTTPX::UNIX end end diff --git a/lib/httpx/io/unix.rb b/lib/httpx/io/unix.rb new file mode 100644 index 00000000..0d84deae --- /dev/null +++ b/lib/httpx/io/unix.rb @@ -0,0 +1,55 @@ +require "forwardable" + +module HTTPX + class UNIX < TCP + extend Forwardable + + def_delegator :@uri, :port, :scheme + + def initialize(uri, options) + @uri = uri + @state = :idle + @options = Options.new(options) + @path = @options.transport_options[:path] + @fallback_protocol = @options.fallback_protocol + if @options.io + @io = case @options.io + when Hash + @options.io[@path] + else + @options.io + end + unless @io.nil? + @keep_open = true + @state = :connected + end + end + @io ||= build_socket + end + + def hostname + @uri.host + end + + def connect + return unless closed? + begin + if @io.closed? + transition(:idle) + @io = build_socket + end + @io.connect_nonblock(Socket.sockaddr_un(@path)) + rescue Errno::EISCONN + end + transition(:connected) + rescue Errno::EINPROGRESS, + Errno::EALREADY, + ::IO::WaitReadable + end + private + + def build_socket + Socket.new(Socket::PF_UNIX, :STREAM, 0) + end + end +end \ No newline at end of file diff --git a/test/io/unix_test.rb b/test/io/unix_test.rb new file mode 100644 index 00000000..9b8a82fe --- /dev/null +++ b/test/io/unix_test.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require "tempfile" +require_relative "../test_helper" + +class UnixTest < Minitest::Test + include HTTPX + + def test_unix_client + on_unix_server do |path| + client = Client.new(transport: "unix", transport_options: { path: path}) + response = client.get("http://unix.com/ping") + assert response.status == 200, "unexpected code (#{response.status})" + assert response.to_s == "pong", "unexpected body (#{response.to_s})" + response.close + client.close + end + end + + private + + RESPONSE_HEADER = (<<-HTTP).lines.map.map(&:chomp).join("\r\n") << ("\r\n" * 2) +HTTP/1.1 200 OK +Date: Mon, 27 Jul 2009 12:28:53 GMT +Content-Length: 4 +Content-Type: text/plain +Connection: close + HTTP + + def on_unix_server + mutex = Mutex.new + resource = ConditionVariable.new + path = File.join(Dir.tmpdir, "httpx-unix.sock") + server = UNIXServer.new(path) + begin + th = Thread.start do + mutex.synchronize do + resource.signal + end + socket = server.accept + socket.readpartial(4096) # drain the socket for the request + socket.write(RESPONSE_HEADER) + socket.write("pong") + socket.close + end + mutex.synchronize do + resource.wait(mutex) + end + yield server.path + ensure + server.close + File.unlink(path) + th.terminate + end + end +end \ No newline at end of file