curb 1.3.5 → 1.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,6 +18,13 @@ class TestCurbCurlEasy < Test::Unit::TestCase
18
18
  assert_equal 200, easy.code
19
19
  end
20
20
 
21
+ def test_perform_with_implicit_multi
22
+ easy = Curl::Easy.new($TEST_URL)
23
+
24
+ assert_nothing_raised { easy.perform }
25
+ assert_match(/^# DO NOT REMOVE THIS COMMENT/, easy.body_str)
26
+ end
27
+
21
28
  def test_curlopt_resolve
22
29
  require 'resolv'
23
30
  uri = URI.parse(TestServlet.url)
@@ -1629,6 +1636,36 @@ class TestCurbCurlEasy < Test::Unit::TestCase
1629
1636
  easy.http_get
1630
1637
  end
1631
1638
 
1639
+ def test_easy_reset_clears_network_policy_allowlists
1640
+ easy = Curl::Easy.new(TestServlet.url)
1641
+ easy.allowed_hosts = ['127.0.0.1']
1642
+ easy.network_policy = :public
1643
+ easy.allowed_cidrs = ['1.1.1.0/24']
1644
+
1645
+ settings = easy.reset
1646
+
1647
+ assert settings.key?(:allowed_hosts)
1648
+ assert settings.key?(:allowed_cidrs)
1649
+ assert_nil easy.allowed_hosts
1650
+ assert_nil easy.allowed_cidrs
1651
+ assert_equal :none, easy.network_policy
1652
+ rescue NotImplementedError
1653
+ omit('network_policy=:public requires CURLOPT_OPENSOCKETFUNCTION support')
1654
+ end
1655
+
1656
+ def test_frozen_easy_close_and_reset_skip_safety_override_cleanup
1657
+ closed_easy = Curl::Easy.new
1658
+ closed_easy.freeze
1659
+
1660
+ assert_nothing_raised { closed_easy.close }
1661
+
1662
+ reset_easy = Curl::Easy.new
1663
+ reset_easy.safe_http!
1664
+ reset_easy.freeze
1665
+
1666
+ assert_nothing_raised { reset_easy.reset }
1667
+ end
1668
+
1632
1669
  def test_last_result_initialization
1633
1670
  # Test for issue #463 - ensure last_result is properly initialized to 0
1634
1671
  easy = Curl::Easy.new
@@ -1780,6 +1817,35 @@ class TestCurbCurlEasy < Test::Unit::TestCase
1780
1817
  end
1781
1818
  end
1782
1819
 
1820
+ def test_close_during_http_post_argument_coercion_is_blocked
1821
+ curl = Curl::Easy.new(TestServlet.url)
1822
+ field = Object.new
1823
+ field.define_singleton_method(:to_s) do
1824
+ curl.close
1825
+ "a=b"
1826
+ end
1827
+
1828
+ error = assert_raise(RuntimeError) { curl.http_post(field) }
1829
+ assert_match(/Cannot close an active curl handle/, error.message)
1830
+
1831
+ assert_nothing_raised { curl.close }
1832
+ end
1833
+
1834
+ def test_close_during_url_coercion_is_blocked
1835
+ curl = Curl::Easy.new
1836
+ url = Object.new
1837
+ url.define_singleton_method(:to_str) do
1838
+ curl.close
1839
+ TestServlet.url
1840
+ end
1841
+
1842
+ curl.url = url
1843
+ error = assert_raise(RuntimeError) { curl.perform }
1844
+ assert_match(/Cannot close an active curl handle/, error.message)
1845
+
1846
+ assert_nothing_raised { curl.close }
1847
+ end
1848
+
1783
1849
  def test_close_in_on_progress_is_blocked
1784
1850
  curl = Curl::Easy.new(TestServlet.url)
1785
1851
  did_raise = false
@@ -1865,6 +1931,16 @@ class TestCurbCurlEasy < Test::Unit::TestCase
1865
1931
  end
1866
1932
  end
1867
1933
 
1934
+ def test_poison
1935
+ res_a = Curl.get("#{TestServlet.url}?a_body")
1936
+ first_a_body = Digest::MD5.hexdigest(res_a.body)
1937
+ res_b = Curl.get("#{TestServlet.url}?b_body")
1938
+
1939
+ b_body = Digest::MD5.hexdigest(res_b.body)
1940
+ a_body = Digest::MD5.hexdigest(res_a.body)
1941
+ assert_not_equal a_body, b_body, "we expected #{a_body} to be #{first_a_body}"
1942
+ end
1943
+
1868
1944
  include TestServerMethods
1869
1945
 
1870
1946
  def setup
@@ -5,8 +5,208 @@ class TestCurbCurlMaxFileSize < Test::Unit::TestCase
5
5
  @easy = Curl::Easy.new
6
6
  end
7
7
 
8
- def test_maxfilesize
8
+ def teardown
9
+ Curl.__send__(:clear_safe!) if Curl.respond_to?(:clear_safe!, true)
10
+ super
11
+ end
12
+
13
+ def test_maxfilesize_setopt
9
14
  @easy.set(Curl::CURLOPT_MAXFILESIZE, 5000000)
15
+ @easy.set(Curl::CURLOPT_MAXFILESIZE_LARGE, 5000000) if Curl.const_defined?(:CURLOPT_MAXFILESIZE_LARGE)
16
+ end
17
+
18
+ def test_max_body_bytes_exact_limit_succeeds
19
+ with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 8\r\n\r\n12345678") do |url|
20
+ @easy.url = url
21
+ @easy.max_body_bytes = 8
22
+ @easy.perform
23
+
24
+ assert_equal '12345678', @easy.body_str
25
+ end
26
+ end
27
+
28
+ def test_max_body_bytes_rejects_large_buffered_response
29
+ with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 64\r\n\r\n#{'x' * 64}") do |url|
30
+ @easy.url = url
31
+ @easy.max_body_bytes = 10
32
+
33
+ assert_body_limit_error do
34
+ @easy.perform
35
+ end
36
+
37
+ assert_operator @easy.body_str.to_s.bytesize, :<=, 10
38
+ end
39
+ end
40
+
41
+ def test_safe_get_accepts_max_body_bytes_as_second_argument
42
+ with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 64\r\n\r\n#{'x' * 64}") do |url|
43
+ assert_body_limit_error do
44
+ Curl.safe_get(url, :max_body_bytes => 10)
45
+ end
46
+ end
47
+ end
48
+
49
+ def test_safe_get_reapplies_max_body_bytes_after_user_block
50
+ with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 64\r\n\r\n#{'x' * 64}") do |url|
51
+ assert_body_limit_error do
52
+ Curl.safe_get(url, {}, :max_body_bytes => 10) do |easy|
53
+ easy.max_body_bytes = nil
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def test_safe_bang_max_body_bytes_applies_to_easy_perform
60
+ with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 64\r\n\r\n#{'x' * 64}") do |url|
61
+ Curl.safe! do |config|
62
+ config.max_body_bytes = 10
63
+ end
64
+
65
+ @easy.url = url
66
+
67
+ assert_body_limit_error do
68
+ @easy.perform
69
+ end
70
+ end
71
+ end
72
+
73
+ def test_safe_bang_reapplies_max_body_bytes_after_shortcut_block
74
+ with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 64\r\n\r\n#{'x' * 64}") do |url|
75
+ Curl.safe! do |config|
76
+ config.max_body_bytes = 10
77
+ end
78
+
79
+ assert_body_limit_error do
80
+ Curl.get(url) do |easy|
81
+ easy.max_body_bytes = nil
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ def test_safe_get_max_body_bytes_tightens_global_cap
88
+ with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 64\r\n\r\n#{'x' * 64}") do |url|
89
+ Curl.safe! do |config|
90
+ config.max_body_bytes = 1_000
91
+ end
92
+
93
+ assert_body_limit_error do
94
+ Curl.safe_get(url, :max_body_bytes => 10)
95
+ end
96
+ end
97
+ end
98
+
99
+ def test_safe_bang_rejects_negative_max_body_bytes
100
+ assert_raise(ArgumentError) do
101
+ Curl.safe! do |config|
102
+ config.max_body_bytes = -1
103
+ end
104
+ end
10
105
  end
11
106
 
107
+ def test_safe_head_does_not_apply_body_cap_to_content_length
108
+ with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 65536\r\n\r\n") do |url|
109
+ easy = Curl.safe_head(url, :max_body_bytes => 1)
110
+
111
+ assert_equal 200, easy.response_code
112
+ assert_equal '', easy.body_str
113
+ end
114
+ end
115
+
116
+ def test_max_body_bytes_rejects_chunked_response_without_content_length
117
+ chunks = ["1234", "5678", "90"]
118
+ response = +"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"
119
+ chunks.each { |chunk| response << "#{chunk.bytesize.to_s(16)}\r\n#{chunk}\r\n" }
120
+ response << "0\r\n\r\n"
121
+
122
+ with_raw_http_response(response) do |url|
123
+ @easy.url = url
124
+ @easy.max_body_bytes = 8
125
+
126
+ assert_body_limit_error do
127
+ @easy.perform
128
+ end
129
+
130
+ assert_operator @easy.body_str.to_s.bytesize, :<=, 8
131
+ end
132
+ end
133
+
134
+ def test_max_body_bytes_limits_on_body_callback
135
+ chunks = ["1234", "5678", "90"]
136
+ response = +"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"
137
+ chunks.each { |chunk| response << "#{chunk.bytesize.to_s(16)}\r\n#{chunk}\r\n" }
138
+ response << "0\r\n\r\n"
139
+ seen = +''
140
+
141
+ with_raw_http_response(response) do |url|
142
+ @easy.url = url
143
+ @easy.max_body_bytes = 8
144
+ @easy.on_body { |data| seen << data; data.bytesize }
145
+
146
+ assert_body_limit_error do
147
+ @easy.perform
148
+ end
149
+
150
+ assert_equal '12345678', seen
151
+ assert_nil @easy.body_str
152
+ end
153
+ end
154
+
155
+ def test_max_body_bytes_zero_clears_limit
156
+ with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\n123456789012") do |url|
157
+ @easy.url = url
158
+ @easy.max_body_bytes = 1
159
+ @easy.max_body_bytes = 0
160
+ @easy.perform
161
+
162
+ assert_nil @easy.max_body_bytes
163
+ assert_equal '123456789012', @easy.body_str
164
+ end
165
+ end
166
+
167
+ def test_max_body_bytes_counter_resets_between_reused_transfers
168
+ @easy.max_body_bytes = 8
169
+
170
+ with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 8\r\n\r\n12345678") do |url|
171
+ @easy.url = url
172
+ @easy.perform
173
+ assert_equal '12345678', @easy.body_str
174
+ end
175
+
176
+ with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 8\r\n\r\nabcdefgh") do |url|
177
+ @easy.url = url
178
+ @easy.perform
179
+ assert_equal 'abcdefgh', @easy.body_str
180
+ end
181
+ end
182
+
183
+ private
184
+
185
+ def assert_body_limit_error
186
+ assert_raise(Curl::Err::FileSizeExceededError) do
187
+ yield
188
+ end
189
+ end
190
+
191
+ def with_raw_http_response(response)
192
+ server = TCPServer.new('127.0.0.1', 0)
193
+ port = server.addr[1]
194
+ thread = Thread.new do
195
+ socket = server.accept
196
+ begin
197
+ socket.readpartial(4096)
198
+ rescue EOFError
199
+ end
200
+ socket.write(response)
201
+ socket.close
202
+ end
203
+
204
+ yield "http://127.0.0.1:#{port}/"
205
+ ensure
206
+ server.close if server && !server.closed?
207
+ if thread
208
+ thread.join(5)
209
+ thread.kill if thread.alive?
210
+ end
211
+ end
12
212
  end
@@ -189,6 +189,23 @@ class TestCurbCurlMulti < Test::Unit::TestCase
189
189
  second_multi.close if defined?(second_multi) && second_multi
190
190
  end
191
191
 
192
+ def test_easy_multi_assignment_detaches_from_old_multi
193
+ multi = Curl::Multi.new
194
+ easy = Curl::Easy.new(TestServlet.url)
195
+
196
+ multi.add(easy)
197
+ easy.multi = nil
198
+
199
+ assert_nil easy.multi
200
+ assert_equal({}, multi.requests)
201
+ assert_nothing_raised { easy.close }
202
+ assert_nothing_raised { multi.close }
203
+
204
+ multi = nil
205
+ ensure
206
+ multi.close if defined?(multi) && multi
207
+ end
208
+
192
209
  def test_close_makes_multi_unusable
193
210
  multi = Curl::Multi.new
194
211
  multi.close
@@ -752,6 +769,23 @@ class TestCurbCurlMulti < Test::Unit::TestCase
752
769
  m.close if m
753
770
  end
754
771
 
772
+ def test_native_safety_signatures_are_pruned_after_perform
773
+ m = Curl::Multi.new
774
+ easy = Curl::Easy.new($TEST_URL)
775
+
776
+ m.add(easy)
777
+ signatures = m.__send__(:__curb_native_safety_signatures)
778
+ assert_equal easy, m.requests[easy.object_id]
779
+ assert signatures.key?(easy.object_id)
780
+
781
+ m.perform
782
+
783
+ assert_equal 0, m.requests.length
784
+ assert_empty signatures
785
+ ensure
786
+ m.close if m
787
+ end
788
+
755
789
  def test_easy_multi_is_cleared_after_perform
756
790
  m = Curl::Multi.new
757
791
  c = Curl::Easy.new($TEST_URL)
@@ -766,6 +800,21 @@ class TestCurbCurlMulti < Test::Unit::TestCase
766
800
  m.close if m
767
801
  end
768
802
 
803
+ def test_frozen_idle_easy_multi_reference_is_cleared_on_close
804
+ multi = Curl::Multi.new
805
+ easy = Curl::Easy.new($TEST_URL)
806
+ easy.freeze
807
+
808
+ easy.multi = multi
809
+
810
+ assert_same multi, easy.multi
811
+ assert_nothing_raised { multi.close }
812
+ assert_nil easy.multi
813
+ multi = nil
814
+ ensure
815
+ multi.close if multi
816
+ end
817
+
769
818
  def test_easy_multi_is_available_during_completion_callbacks
770
819
  multi = Curl::Multi.new
771
820
  easy = Curl::Easy.new($TEST_URL)
@@ -991,6 +1040,193 @@ class TestCurbCurlMulti < Test::Unit::TestCase
991
1040
  end
992
1041
  end
993
1042
 
1043
+ def test_multi_download_refuses_existing_explicit_download_path
1044
+ root_uri = 'http://127.0.0.1:9129/ext/'
1045
+
1046
+ Dir.mktmpdir('curb-download-') do |download_dir|
1047
+ download_path = File.join(download_dir, 'curb_easy.c')
1048
+ File.write(download_path, 'sentinel')
1049
+
1050
+ assert_raise(Curl::DownloadTargetExistsError) do
1051
+ Curl::Multi.download([root_uri + 'curb_easy.c'], {}, {}, [download_path])
1052
+ end
1053
+
1054
+ assert_equal 'sentinel', File.read(download_path)
1055
+ end
1056
+ end
1057
+
1058
+ def test_multi_download_failed_transfer_does_not_publish_file_without_block
1059
+ Dir.mktmpdir('curb-download-') do |download_dir|
1060
+ download_path = File.join(download_dir, 'missing')
1061
+
1062
+ error = assert_raise(Curl::Multi::DownloadError) do
1063
+ Curl::Multi.download(['file:///curb/missing'], {}, {}, [download_path])
1064
+ end
1065
+
1066
+ assert !error.errors.empty?
1067
+ assert !File.exist?(download_path)
1068
+ end
1069
+ end
1070
+
1071
+ def test_multi_download_failed_transfer_does_not_publish_file_or_call_block
1072
+ called = false
1073
+
1074
+ Dir.mktmpdir('curb-download-') do |download_dir|
1075
+ download_path = File.join(download_dir, 'missing')
1076
+
1077
+ error = assert_raise(Curl::Multi::DownloadError) do
1078
+ Curl::Multi.download(['file:///curb/missing'], {}, {}, [download_path]) do |_curl, _path|
1079
+ called = true
1080
+ end
1081
+ end
1082
+
1083
+ assert !error.errors.empty?
1084
+ assert_equal false, called
1085
+ assert !File.exist?(download_path)
1086
+ end
1087
+ end
1088
+
1089
+ def test_multi_download_failed_transfer_closes_io_destination
1090
+ Dir.mktmpdir('curb-download-') do |download_dir|
1091
+ download_path = File.join(download_dir, 'missing')
1092
+ io = File.open(download_path, 'wb')
1093
+
1094
+ error = assert_raise(Curl::Multi::DownloadError) do
1095
+ Curl::Multi.download(['file:///curb/missing'], {}, {}, [io])
1096
+ end
1097
+
1098
+ assert !error.errors.any? { |e| e.is_a?(ArgumentError) }
1099
+ assert io.closed?
1100
+ assert_equal '', File.read(download_path)
1101
+ end
1102
+ end
1103
+
1104
+ def test_multi_download_preserves_body_limit_callback_error
1105
+ with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 64\r\n\r\n#{'x' * 64}") do |url|
1106
+ Dir.mktmpdir('curb-download-') do |download_dir|
1107
+ download_path = File.join(download_dir, 'too-large')
1108
+
1109
+ error = assert_raise(Curl::Err::FileSizeExceededError) do
1110
+ Curl::Multi.download([url], { :max_body_bytes => 10 }, {}, [download_path])
1111
+ end
1112
+
1113
+ assert_match(/Maximum body size exceeded/, error.message)
1114
+ assert !File.exist?(download_path)
1115
+ end
1116
+ end
1117
+ end
1118
+
1119
+ def test_multi_download_preserves_output_write_callback_error
1120
+ writer_error = Class.new(StandardError)
1121
+
1122
+ with_raw_http_response("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata") do |url|
1123
+ Dir.mktmpdir('curb-download-') do |download_dir|
1124
+ writer = File.open(File.join(download_dir, 'writer'), 'wb')
1125
+ writer.define_singleton_method(:write) do |_data|
1126
+ raise writer_error, 'writer failed'
1127
+ end
1128
+
1129
+ error = assert_raise(writer_error) do
1130
+ Curl::Multi.download([url], {}, {}, [writer])
1131
+ end
1132
+
1133
+ assert_equal 'writer failed', error.message
1134
+ assert writer.closed?
1135
+ end
1136
+ end
1137
+ end
1138
+
1139
+ def test_multi_download_safe_directory_rejects_unsafe_names
1140
+ root_uri = 'http://127.0.0.1:9129/ext/'
1141
+
1142
+ Dir.mktmpdir('curb-download-') do |download_dir|
1143
+ ['../escape.c', '.env', 'nested/file', '/tmp/escape.c', "bad\0name"].each do |name|
1144
+ assert_raise(ArgumentError, "expected #{name.inspect} to be rejected") do
1145
+ Curl::Multi.download([root_uri + 'curb_easy.c'], {}, {}, [name], :download_dir => download_dir)
1146
+ end
1147
+ end
1148
+ end
1149
+ end
1150
+
1151
+ def test_multi_download_rejects_duplicate_destinations
1152
+ root_uri = 'http://127.0.0.1:9129/ext/'
1153
+
1154
+ Dir.mktmpdir('curb-download-') do |download_dir|
1155
+ assert_raise(ArgumentError) do
1156
+ Curl::Multi.download(
1157
+ [root_uri + 'curb_easy.c?one=1', root_uri + 'curb_easy.c?two=2'],
1158
+ {},
1159
+ {},
1160
+ nil,
1161
+ :download_dir => download_dir
1162
+ )
1163
+ end
1164
+
1165
+ assert !File.exist?(File.join(download_dir, 'curb_easy.c'))
1166
+ assert_equal [], Dir[File.join(download_dir, '.curb-download-*')]
1167
+ end
1168
+ end
1169
+
1170
+ def test_multi_download_safe_directory_strips_query_from_url_basename
1171
+ root_uri = 'http://127.0.0.1:9129/ext/'
1172
+
1173
+ Dir.mktmpdir('curb-download-') do |download_dir|
1174
+ Curl::Multi.download([root_uri + 'curb_easy.c?cache=1'], {}, {}, nil, :download_dir => download_dir)
1175
+
1176
+ download_path = File.join(download_dir, 'curb_easy.c')
1177
+ assert File.exist?(download_path)
1178
+ assert !File.exist?(File.join(download_dir, 'curb_easy.c?cache=1'))
1179
+ assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(download_path)
1180
+ end
1181
+ end
1182
+
1183
+ def test_multi_download_hash_url_config_uses_hash_url
1184
+ root_uri = 'http://127.0.0.1:9129/ext/'
1185
+
1186
+ Dir.mktmpdir('curb-download-') do |download_dir|
1187
+ download_path = File.join(download_dir, 'curb_easy.c')
1188
+
1189
+ Curl::Multi.download([{ :url => root_uri + 'curb_easy.c', :method => :get }], {}, {}, [download_path]) do |curl, path|
1190
+ assert_equal 200, curl.response_code
1191
+ assert_equal download_path, path
1192
+ end
1193
+
1194
+ assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(download_path)
1195
+ end
1196
+ end
1197
+
1198
+ def test_multi_download_duplicate_urls_use_distinct_explicit_paths
1199
+ url = "file://#{File.expand_path(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c'))}"
1200
+
1201
+ Dir.mktmpdir('curb-download-') do |download_dir|
1202
+ paths = [
1203
+ File.join(download_dir, 'a.c'),
1204
+ File.join(download_dir, 'b.c')
1205
+ ]
1206
+ completed_paths = []
1207
+
1208
+ Curl::Multi.download([url, url], {}, {}, paths) do |_curl, path|
1209
+ completed_paths << path
1210
+ end
1211
+
1212
+ assert_equal paths.sort, completed_paths.sort
1213
+ assert_equal File.read(paths[0]), File.read(paths[1])
1214
+ end
1215
+ end
1216
+
1217
+ def test_multi_download_accepts_indexed_hash_download_paths
1218
+ root_uri = 'http://127.0.0.1:9129/ext/'
1219
+
1220
+ Dir.mktmpdir('curb-download-') do |download_dir|
1221
+ download_path = File.join(download_dir, 'curb_easy.c')
1222
+
1223
+ Curl::Multi.download([root_uri + 'curb_easy.c'], {}, {}, {0 => download_path})
1224
+
1225
+ assert File.exist?(download_path)
1226
+ assert_equal File.read(File.join(File.dirname(__FILE__), '..','ext','curb_easy.c')), File.read(download_path)
1227
+ end
1228
+ end
1229
+
994
1230
  def test_multi_easy_post_01
995
1231
  urls = [
996
1232
  { :url => TestServlet.url + '?q=1', :post_fields => {'field1' => 'value1', 'k' => 'j'}},
@@ -1279,6 +1515,28 @@ class TestCurbCurlMulti < Test::Unit::TestCase
1279
1515
  server_thread.kill if defined?(server_thread) && server_thread&.alive?
1280
1516
  end
1281
1517
 
1518
+ def with_raw_http_response(response)
1519
+ server = TCPServer.new('127.0.0.1', 0)
1520
+ port = server.addr[1]
1521
+ thread = Thread.new do
1522
+ socket = server.accept
1523
+ begin
1524
+ socket.readpartial(4096)
1525
+ rescue EOFError
1526
+ end
1527
+ socket.write(response)
1528
+ socket.close
1529
+ end
1530
+
1531
+ yield "http://127.0.0.1:#{port}/"
1532
+ ensure
1533
+ server.close if defined?(server) && server && !server.closed?
1534
+ if defined?(thread) && thread
1535
+ thread.join(5)
1536
+ thread.kill if thread.alive?
1537
+ end
1538
+ end
1539
+
1282
1540
  include TestServerMethods
1283
1541
 
1284
1542
  def setup