httpray 1.0.4 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 46b15f44920b575fdc88fd97c64ddb14d670539a
4
- data.tar.gz: 84676027d29c606d4fba8b9e28c32408899621f8
3
+ metadata.gz: 9797174918b5b0dc9fc4ed93712eb645aea8b623
4
+ data.tar.gz: e45eb180606696eed1a43c08de52aac61ce6f4bd
5
5
  SHA512:
6
- metadata.gz: 9b9b2ec5dea26db03552cefbc7b8449f88534f63ff13d2b734656fbb1403cb5819264326d7b7c117a2d993148bb8517bd370eb2c652ce5b653954e176ef8e438
7
- data.tar.gz: 84d44dfb708a8c4c3c05b46dc74ec0cca0bab673c64c17d021b41c66f0da8de07356dd4a85fa623f635a00c3c63bd80778d8f558bd137384903266f0b8222055
6
+ metadata.gz: 82e916c0ef86af848840745586b7d2083776cf060559a98e3b4acb883f40417d74e43ec72a12d6e2eccf71f13ded205cbc03f5607f9c5e598f6049398a002f29
7
+ data.tar.gz: 0d41608e36c89f91999c1e5f252173e0aaeb555f6c0bc5a4805b0cf2b170d2ff28233f4e25b0cb642e2ea449d213677c02719956de35b917a8c40191d04c275f
@@ -1,3 +1,3 @@
1
1
  module HTTPray
2
- VERSION = "1.0.4".freeze
2
+ VERSION = "1.1.0".freeze
3
3
  end
data/lib/httpray.rb CHANGED
@@ -12,57 +12,96 @@ module HTTPray
12
12
  "Accept" => "*/*"
13
13
  }.freeze
14
14
 
15
- def self.request2!(method, uri, headers = {}, body = nil, timeout = 1, ssl_context = nil)
16
- uri = URI.parse(uri) unless URI === uri
17
- address = Socket.getaddrinfo(uri.host, nil, Socket::AF_INET).first[3]
18
- socket_address = Socket.pack_sockaddr_in(uri.port, address)
19
-
20
- socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
21
- socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
22
-
23
- expire_time = Time.now + timeout
24
- begin
25
- raise Timeout if Time.now > expire_time
26
- socket.connect_nonblock(socket_address)
27
- rescue IO::WaitReadable, IO::WaitWritable
28
- select_timeout = expire_time - Time.now
29
- select_timeout = 0 if select_timeout < 0
30
- IO.select([socket], [socket], [socket], select_timeout)
31
- retry
15
+ class Connection
16
+ def initialize(host, port, timeout = 1, ssl_context = nil)
17
+ @host = host
18
+ @port = port
19
+ @timeout = timeout
20
+ @ssl_context = ssl_context
21
+ @socket = connect
32
22
  end
33
23
 
34
- original_socket = socket
35
- if uri.scheme == "https"
36
- ssl_context ||= OpenSSL::SSL::SSLContext.new
37
- socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
38
- socket.hostname = uri.host
39
- socket.sync_close = true
24
+ # public
25
+
26
+ def socket
27
+ @socket
28
+ end
29
+
30
+ def request!(method, uri, headers = {}, body = nil)
31
+ begin
32
+ IO.select([@socket], [@socket], [@socket], @timeout) if @socket
33
+ rescue; end
34
+ @socket = connect unless @socket && !@socket.closed?
35
+ socket = @socket
36
+ uri = URI.parse(uri) unless URI === uri
37
+
38
+ headers = DEFAULT_HEADERS.merge(headers).merge("Host" => uri.host)
39
+ headers["Content-Length"] = body.bytesize if body
40
+
41
+ socket.write_nonblock "#{method} #{uri.request_uri} HTTP/1.0\r\n"
42
+ headers.each do |header, value|
43
+ socket.write_nonblock "#{header}: #{value}\r\n"
44
+ end
45
+ socket.write_nonblock "\r\n"
46
+ socket.write_nonblock body if body
47
+ socket
48
+ end
49
+
50
+ def request(*args)
51
+ socket = request!(*args)
52
+ yield(socket) if block_given?
53
+ end
54
+
55
+ # private
56
+
57
+ def connect2
58
+ address = Socket.getaddrinfo(@host, nil, Socket::AF_INET).first[3]
59
+ socket_address = Socket.pack_sockaddr_in(@port, address)
60
+
61
+ socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
62
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
63
+
64
+ expire_time = Time.now + @timeout
40
65
  begin
41
66
  raise Timeout if Time.now > expire_time
42
- socket.connect_nonblock
67
+ socket.connect_nonblock(socket_address)
43
68
  rescue IO::WaitReadable, IO::WaitWritable
44
69
  select_timeout = expire_time - Time.now
45
70
  select_timeout = 0 if select_timeout < 0
46
- IO.select([socket.io], [socket.io], [socket.io], select_timeout)
71
+ IO.select([socket], [socket], [socket], select_timeout)
47
72
  retry
48
73
  end
49
- end
50
74
 
51
- headers = DEFAULT_HEADERS.merge(headers).merge("Host" => uri.host)
52
- headers["Content-Length"] = body.bytesize if body
75
+ original_socket = socket
76
+ if @ssl_context
77
+ socket = OpenSSL::SSL::SSLSocket.new(socket, @ssl_context)
78
+ socket.hostname = @host
79
+ socket.sync_close = true
80
+ begin
81
+ raise Timeout if Time.now > expire_time
82
+ socket.connect_nonblock
83
+ rescue IO::WaitReadable, IO::WaitWritable
84
+ select_timeout = expire_time - Time.now
85
+ select_timeout = 0 if select_timeout < 0
86
+ IO.select([socket.io], [socket.io], [socket.io], select_timeout)
87
+ retry
88
+ end
89
+ end
90
+ return socket, original_socket
91
+ end
53
92
 
54
- socket.write_nonblock "#{method} #{uri.request_uri} HTTP/1.0\r\n"
55
- headers.each do |header, value|
56
- socket.write_nonblock "#{header}: #{value}\r\n"
93
+ def connect
94
+ socket, _ = connect2
95
+ socket
57
96
  end
58
- socket.write_nonblock "\r\n"
59
- socket.write_nonblock body if body
60
- return socket, original_socket
61
97
  end
62
98
 
63
- def self.request!(*args)
64
- socket, _ = request2!(*args)
65
- socket
99
+ def self.request!(method, uri, headers = {}, body = nil, timeout = 1, ssl_context = nil)
100
+ uri = URI.parse(uri) unless URI === uri
101
+ ssl_context = nil
102
+ ssl_context = OpenSSL::SSL::SSLContext.new if uri.scheme == "https"
103
+ ark = Connection.new(uri.host, uri.port, timeout, ssl_context)
104
+ ark.request!(method, uri, headers, body)
66
105
  end
67
106
 
68
107
  def self.request(*args)
data/test/httpray_test.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  require 'minitest/autorun'
2
2
  require 'lib/httpray'
3
+ require 'benchmark'
4
+ require 'net/http'
3
5
 
4
- class HTTPrayTest < MiniTest::Unit::TestCase
6
+ class HTTPrayTest < MiniTest::Test
5
7
  def test_request_timesout_with_short_timeout
6
8
  assert_raises HTTPray::Timeout do
7
9
  HTTPray.request("GET", "httppppp://httpbin.org/status/200", {}, nil, 0)
@@ -42,9 +44,10 @@ class HTTPrayTest < MiniTest::Unit::TestCase
42
44
  end
43
45
  end
44
46
  def test_original_socket_closed_with_ssl
45
- socket, original_socket = HTTPray.request2!(
46
- "GET",
47
- "https://httpbin.org/delay/10")
47
+ uri = URI.parse("https://httpbin.org/get")
48
+ ark = HTTPray::Connection.new(uri.host, uri.port, 1, OpenSSL::SSL::SSLContext.new)
49
+ ark.socket.close
50
+ socket, original_socket = ark.connect2
48
51
  refute_same socket, original_socket
49
52
  refute socket.closed?
50
53
  refute original_socket.closed?
@@ -53,9 +56,10 @@ class HTTPrayTest < MiniTest::Unit::TestCase
53
56
  assert original_socket.closed?
54
57
  end
55
58
  def test_original_socket_closed_without_ssl
56
- socket, original_socket = HTTPray.request2!(
57
- "GET",
58
- "http://httpbin.org/delay/10")
59
+ uri = URI.parse("http://httpbin.org/get")
60
+ ark = HTTPray::Connection.new(uri.host, uri.port, 1, nil)
61
+ ark.socket.close
62
+ socket, original_socket = ark.connect2
59
63
  assert_same socket, original_socket
60
64
  refute socket.closed?
61
65
  refute original_socket.closed?
@@ -63,4 +67,36 @@ class HTTPrayTest < MiniTest::Unit::TestCase
63
67
  assert socket.closed?
64
68
  assert original_socket.closed?
65
69
  end
70
+ def test_faster_than_net_http
71
+ uri = URI.parse("http://httpbin.org/delay/1")
72
+ net_http_time = Benchmark.realtime do
73
+ 2.times { Net::HTTP.get(uri) }
74
+ end
75
+ httpray_time = Benchmark.realtime do
76
+ 2.times { HTTPray.request("GET", uri) }
77
+ end
78
+ assert httpray_time < net_http_time
79
+ end
80
+ def test_persistent_connection_faster_than_ephemeral
81
+ uri = URI.parse("http://httpbin.org/delay/1")
82
+ persistent_time = Benchmark.realtime do
83
+ ark = HTTPray::Connection.new(uri.host, uri.port)
84
+ 3.times { ark.request("GET", uri) }
85
+ end
86
+ ephemeral_time = Benchmark.realtime do
87
+ 3.times { HTTPray.request("GET", uri) }
88
+ end
89
+ assert persistent_time < ephemeral_time
90
+ end
91
+ def test_reconnects_on_request_if_necessary
92
+ uri = URI.parse("http://httpbin.org/get")
93
+ ark = HTTPray::Connection.new(uri.host, uri.port, 1, nil)
94
+ original_socket = ark.socket
95
+ ark.request("GET", uri)
96
+ assert_same original_socket, ark.socket
97
+ ark.socket.close
98
+ assert original_socket.closed?
99
+ ark.request("GET", uri)
100
+ refute_same original_socket, ark.socket
101
+ end
66
102
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: httpray
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - G Gordon Worley III
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-14 00:00:00.000000000 Z
11
+ date: 2017-07-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Fire-and-forget HTTP requests for Ruby
14
14
  email: