httplog 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,25 @@
1
+ require 'http'
2
+ class HTTPAdapter < HTTPBaseAdapter
3
+ def send_get_request
4
+ client.get(parse_uri.to_s)
5
+ end
6
+
7
+ def send_head_request
8
+ client.head(parse_uri.to_s)
9
+ end
10
+
11
+ def send_post_request
12
+ client.post(parse_uri.to_s, body: @data)
13
+ end
14
+
15
+ def send_post_form_request
16
+ client.post(parse_uri.to_s, form: @params)
17
+ end
18
+
19
+ private
20
+
21
+ def client
22
+ method_name = respond_to?(:with_headers) ? :with_headers : :headers
23
+ ::HTTP.send(method_name, @headers)
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ class HTTPBaseAdapter
2
+ def initialize(host, port, path, headers, data, params, protocol = 'http')
3
+ @host = host
4
+ @port = port
5
+ @path = path
6
+ @headers = headers
7
+ @data = data
8
+ @params = params
9
+ @protocol = protocol
10
+ end
11
+
12
+ def logs_data?
13
+ true
14
+ end
15
+
16
+ def parse_uri
17
+ URI.parse("#{@protocol}://#{@host}:#{@port}#{@path}")
18
+ end
19
+
20
+ def expected_response_body
21
+ "\n<html>"
22
+ end
23
+
24
+ def self.is_libcurl?
25
+ false
26
+ end
27
+
28
+ def self.should_log_headers?
29
+ true
30
+ end
31
+ end
@@ -0,0 +1,14 @@
1
+ require 'httparty'
2
+ class HTTPartyAdapter < HTTPBaseAdapter
3
+ def send_get_request
4
+ HTTParty.get(parse_uri.to_s, headers: @headers)
5
+ end
6
+
7
+ def send_head_request
8
+ HTTParty.head(parse_uri.to_s, headers: @headers)
9
+ end
10
+
11
+ def send_post_request
12
+ HTTParty.post(parse_uri.to_s, body: @data, headers: @headers)
13
+ end
14
+ end
@@ -0,0 +1,25 @@
1
+ class HTTPClientAdapter < HTTPBaseAdapter
2
+ def send_get_request
3
+ ::HTTPClient.get(parse_uri, header: @headers)
4
+ end
5
+
6
+ def send_head_request
7
+ ::HTTPClient.head(parse_uri, header: @headers)
8
+ end
9
+
10
+ def send_post_request
11
+ ::HTTPClient.post(parse_uri, body: @data, header: @headers)
12
+ end
13
+
14
+ def send_post_form_request
15
+ ::HTTPClient.post(parse_uri, body: @params, header: @headers)
16
+ end
17
+
18
+ def send_multipart_post_request
19
+ send_post_form_request
20
+ end
21
+
22
+ def self.response_should_be
23
+ HTTP::Message
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ class NetHTTPAdapter < HTTPBaseAdapter
2
+ def send_get_request
3
+ Net::HTTP.get_response(@host, [@path, @data].compact.join('?'), @port)
4
+ end
5
+
6
+ def send_head_request
7
+ http = Net::HTTP.new(@host, @port)
8
+ http.head(@path, @headers)
9
+ end
10
+
11
+ def send_post_request
12
+ http = Net::HTTP.new(@host, @port)
13
+ resp = http.post(@path, @data)
14
+ end
15
+
16
+ def send_post_form_request
17
+ res = Net::HTTP.post_form(parse_uri, @params)
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ class OpenUriAdapter < HTTPBaseAdapter
2
+ def send_get_request
3
+ open(parse_uri)
4
+ end
5
+
6
+ def expected_response_body
7
+ " (not available yet)"
8
+ end
9
+
10
+ def self.should_log_headers?
11
+ false
12
+ end
13
+
14
+ def logs_data?
15
+ false
16
+ end
17
+ end
@@ -0,0 +1,34 @@
1
+ require "patron"
2
+ class PatronAdapter < HTTPBaseAdapter
3
+ def send_get_request
4
+ session = Patron::Session.new
5
+ session.get(parse_uri.to_s, @headers)
6
+ end
7
+
8
+ def send_head_request
9
+ session = Patron::Session.new
10
+ session.head(parse_uri.to_s, @headers)
11
+ end
12
+
13
+ def send_post_request
14
+ session = Patron::Session.new
15
+ session.post(parse_uri.to_s, @data, @headers)
16
+ end
17
+
18
+ def send_post_form_request
19
+ session = Patron::Session.new
20
+ session.post(parse_uri.to_s, @params, @headers)
21
+ end
22
+
23
+ def send_multipart_post_request
24
+ data = @params.dup
25
+ file = @params.delete('file')
26
+
27
+ session = Patron::Session.new
28
+ session.post_multipart(parse_uri.to_s, data, {file: file.path}, @headers)
29
+ end
30
+
31
+ def self.is_libcurl?
32
+ true
33
+ end
34
+ end
@@ -0,0 +1,27 @@
1
+ require 'excon'
2
+ class TyphoeusAdapter < HTTPBaseAdapter
3
+
4
+ def send_get_request
5
+ Typhoeus.get(parse_uri.to_s, headers: @headers)
6
+ end
7
+
8
+ def send_head_request
9
+ Typhoeus.head(parse_uri.to_s, headers: @headers)
10
+ end
11
+
12
+ def send_post_request
13
+ Typhoeus.post(parse_uri.to_s, body: @data, headers: @headers)
14
+ end
15
+
16
+ def send_post_form_request
17
+ Typhoeus.post(parse_uri.to_s, body: @params, headers: @headers)
18
+ end
19
+
20
+ def send_multipart_post_request
21
+ send_post_form_request
22
+ end
23
+
24
+ def self.is_libcurl?
25
+ true
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ module HttpLog
6
+ describe Configuration do
7
+
8
+ let(:config) { Configuration.new }
9
+
10
+ describe "#compact_log" do
11
+ it "defaults to false" do
12
+ expect(config.compact_log).to eq(false)
13
+ end
14
+ end
15
+
16
+ describe "#compact_log=" do
17
+ it "sets values" do
18
+ config.compact_log = true
19
+ expect(config.compact_log).to eq(true)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,358 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe HttpLog do
6
+
7
+ let(:host) { 'localhost' }
8
+ let(:port) { 9292 }
9
+ let(:path) { "/index.html" }
10
+ let(:headers) { { "accept" => "*/*", "foo" => "bar" } }
11
+ let(:data) { "foo=bar&bar=foo" }
12
+ let(:params) { {'foo' => 'bar:form-data', 'bar' => 'foo'} }
13
+
14
+ ADAPTERS = [
15
+ NetHTTPAdapter,
16
+ OpenUriAdapter,
17
+ HTTPClientAdapter,
18
+ HTTPartyAdapter,
19
+ FaradayAdapter,
20
+ ExconAdapter,
21
+ EthonAdapter,
22
+ # FIXME: typheous does weird things to reponse headers using ethon as the default adapter,
23
+ # even though ethon stand-alone works just fine. Not worth supporting since Typhoeus has its
24
+ # own logging facility.
25
+ #
26
+ # TyphoeusAdapter,
27
+ PatronAdapter,
28
+ HTTPAdapter
29
+ ]
30
+
31
+ ADAPTERS.each do |adapter_class|
32
+ context adapter_class, :adapter => adapter_class.to_s do
33
+ let(:adapter) { adapter_class.new(host, port, path, headers, data, params) }
34
+
35
+ context "with default configuration" do
36
+ connection_test_method = adapter_class.is_libcurl? ? :to_not : :to
37
+
38
+ if adapter_class.method_defined? :send_get_request
39
+ it "should log GET requests" do
40
+ res = adapter.send_get_request
41
+
42
+ expect(log).send(connection_test_method, include(HttpLog::LOG_PREFIX + "Connecting: #{host}:#{port}"))
43
+
44
+ expect(log).to include(HttpLog::LOG_PREFIX + "Sending: GET http://#{host}:#{port}#{path}")
45
+ expect(log).to include(HttpLog::LOG_PREFIX + "Data:")
46
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Header:")
47
+ expect(log).to include(HttpLog::LOG_PREFIX + "Status: 200")
48
+ expect(log).to include(HttpLog::LOG_PREFIX + "Benchmark: ")
49
+ expect(log).to include(HttpLog::LOG_PREFIX + "Response:#{adapter.expected_response_body}")
50
+ expect(log.colorized?).to be_falsey
51
+
52
+ expect(res).to be_a adapter.response if adapter.respond_to? :response
53
+ end
54
+
55
+ context "with gzipped response body" do
56
+ let(:path) { '/index.html.gz' }
57
+ let(:data) { nil }
58
+
59
+ it "decompresses gzipped response body" do
60
+ adapter.send_get_request
61
+ expect(log).to include(HttpLog::LOG_PREFIX + "Response:#{adapter.expected_response_body}")
62
+ end
63
+
64
+ if adapter_class.method_defined? :send_head_request
65
+ it "doesn't try to decompress body for HEAD requests" do
66
+ adapter.send_head_request
67
+ expect(log).to include(HttpLog::LOG_PREFIX + "Response:")
68
+ end
69
+ end
70
+ end
71
+
72
+ context "with UTF-8 response body" do
73
+ let(:path) { '/utf8.html' }
74
+ let(:data) { nil }
75
+
76
+ it "works" do
77
+ adapter.send_get_request
78
+ expect(log).to include(HttpLog::LOG_PREFIX + "Response:#{adapter.expected_response_body}")
79
+ if adapter.logs_data?
80
+ expect(log).to include(" <title>Блог Яндекса</title>")
81
+ end
82
+ end
83
+ end
84
+
85
+ context "with binary response body" do
86
+ let(:path) { '/test.bin' }
87
+ let(:data) { nil }
88
+
89
+ it "doesn't log response" do
90
+ adapter.send_get_request
91
+ expect(log).to include(HttpLog::LOG_PREFIX + "Response: (not showing binary data)")
92
+ end
93
+ end
94
+ end
95
+
96
+ if adapter_class.method_defined? :send_post_request
97
+ it "logs POST requests" do
98
+ res = adapter.send_post_request
99
+
100
+ expect(log).send(connection_test_method, include(HttpLog::LOG_PREFIX + "Connecting: #{host}:#{port}"))
101
+
102
+ expect(log).to include(HttpLog::LOG_PREFIX + "Sending: POST http://#{host}:#{port}#{path}")
103
+ expect(log).to include(HttpLog::LOG_PREFIX + "Data: foo=bar&bar=foo")
104
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Header:")
105
+ expect(log).to include(HttpLog::LOG_PREFIX + "Status: 200")
106
+ expect(log).to include(HttpLog::LOG_PREFIX + "Benchmark: ")
107
+ expect(log).to include(HttpLog::LOG_PREFIX + "Response:#{adapter.expected_response_body}")
108
+ expect(log.colorized?).to be_falsey
109
+
110
+ expect(res).to be_a adapter.response if adapter.respond_to? :response
111
+ end
112
+
113
+ context "with non-UTF request data" do
114
+ let(:data) { "a UTF-8 striñg with an 8BIT-ASCII character: \xC3".freeze }
115
+ it "does not raise and error" do
116
+ expect {adapter.send_post_request }.to_not raise_error
117
+ expect(log).to include(HttpLog::LOG_PREFIX + "Response:")
118
+ end
119
+ end
120
+
121
+ context "with URI encoded non-UTF data" do
122
+ let(:data) { "a UTF-8 striñg with a URI encoded 8BIT-ASCII character: %c3" }
123
+ it "does not raise and error" do
124
+ expect { adapter.send_post_request }.to_not raise_error
125
+ expect(log).to include(HttpLog::LOG_PREFIX + "Response:")
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ context "with custom configuration" do
132
+ context "GET requests" do
133
+
134
+ it "should not log anything unless enabled is set" do
135
+ HttpLog.configure { |c| c.enabled = false }
136
+ adapter.send_get_request
137
+ expect(log).to eq('')
138
+ end
139
+
140
+ it "should log at other levels" do
141
+ HttpLog.configure { |c| c.severity = Logger::Severity::INFO }
142
+ adapter.send_get_request
143
+ expect(log).to include("INFO")
144
+ end
145
+
146
+ it "should log headers if enabled" do
147
+ HttpLog.configure { |c| c.log_headers = true }
148
+ adapter.send_get_request
149
+ # request header
150
+ expect(log.downcase).to include(HttpLog::LOG_PREFIX + "Header: accept: */*".downcase)
151
+ # response header
152
+ expect(log.downcase).to include(HttpLog::LOG_PREFIX + "Header: server: thin".downcase)
153
+ end
154
+
155
+ it "should not log headers if disabled" do
156
+ HttpLog.configure { |c| c.log_headers = false }
157
+ adapter.send_get_request
158
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Header:")
159
+ end
160
+
161
+ it "should log the request if url does not match blacklist pattern" do
162
+ HttpLog.configure { |c| c.url_blacklist_pattern = /example.com/ }
163
+ adapter.send_get_request
164
+ expect(log).to include(HttpLog::LOG_PREFIX + "Sending: GET")
165
+ end
166
+
167
+ it "should log the request if url matches whitelist pattern and not the blacklist pattern" do
168
+ HttpLog.configure { |c| c.url_blacklist_pattern = /example.com/ }
169
+ HttpLog.configure { |c| c.url_whitelist_pattern = /#{host}:#{port}/ }
170
+ adapter.send_get_request
171
+ expect(log).to include(HttpLog::LOG_PREFIX + "Sending: GET")
172
+ end
173
+
174
+ it "should not log the request if url matches blacklist pattern" do
175
+ HttpLog.configure { |c| c.url_blacklist_pattern = /#{host}:#{port}/ }
176
+ adapter.send_get_request
177
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Sending: GET")
178
+ end
179
+
180
+ it "should not log the request if url does not match whitelist pattern" do
181
+ HttpLog.configure { |c| c.url_whitelist_pattern = /example.com/ }
182
+ adapter.send_get_request
183
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Sending: GET")
184
+ end
185
+
186
+ it "should not log the request if url matches blacklist pattern and the whitelist pattern" do
187
+ HttpLog.configure { |c| c.url_blacklist_pattern = /#{host}:#{port}/ }
188
+ HttpLog.configure { |c| c.url_whitelist_pattern = /#{host}:#{port}/ }
189
+ adapter.send_get_request
190
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Sending: GET")
191
+ end
192
+
193
+ it "should not log the request if disabled" do
194
+ HttpLog.configure { |c| c.log_request = false }
195
+ adapter.send_get_request
196
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Sending: GET")
197
+ end
198
+
199
+ it "should not log the connection if disabled" do
200
+ HttpLog.configure { |c| c.log_connect = false }
201
+ adapter.send_get_request
202
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Connecting: #{host}:#{port}")
203
+ end
204
+
205
+ it "should not log data if disabled" do
206
+ HttpLog.configure { |c| c.log_data = false }
207
+ adapter.send_get_request
208
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Data:")
209
+ end
210
+
211
+ it "should colorized output" do
212
+ HttpLog.configure { |c| c.color = :red }
213
+ adapter.send_get_request
214
+ expect(log.colorized?).to be_truthy
215
+ end
216
+
217
+ it "should log with custom string prefix" do
218
+ HttpLog.configure { |c| c.prefix = '[my logger]' }
219
+ adapter.send_get_request
220
+ expect(log).to include("[my logger]")
221
+ expect(log).to_not include(HttpLog::LOG_PREFIX)
222
+ end
223
+
224
+ it "should log with custom lambda prefix" do
225
+ HttpLog.configure { |c| c.prefix = -> { '[custom prefix]' } }
226
+ adapter.send_get_request
227
+ expect(log).to include("[custom prefix]")
228
+ expect(log).to_not include(HttpLog::LOG_PREFIX)
229
+ end
230
+ end
231
+
232
+ context "POST requests" do
233
+ if adapter_class.method_defined? :send_post_request
234
+ it "should not log data if disabled" do
235
+ HttpLog.configure { |c| c.log_data = false }
236
+ adapter.send_post_request
237
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Data:")
238
+ end
239
+
240
+ it "should not log the response if disabled" do
241
+ HttpLog.configure { |c| c.log_response = false }
242
+ adapter.send_post_request
243
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Reponse:")
244
+ end
245
+
246
+ it "should prefix all response lines" do
247
+ HttpLog.configure { |c| c.prefix_response_lines = true }
248
+
249
+ adapter.send_post_request
250
+ expect(log).to include(HttpLog::LOG_PREFIX + "Response:")
251
+ expect(log).to include(HttpLog::LOG_PREFIX + "<html>")
252
+ end
253
+
254
+ it "should prefix all response lines with line numbers" do
255
+ HttpLog.configure { |c| c.prefix_response_lines = true }
256
+ HttpLog.configure { |c| c.prefix_line_numbers = true }
257
+
258
+ adapter.send_post_request
259
+ expect(log).to include(HttpLog::LOG_PREFIX + "Response:")
260
+ expect(log).to include(HttpLog::LOG_PREFIX + "1: <html>")
261
+ end
262
+
263
+ it "should not log the benchmark if disabled" do
264
+ HttpLog.configure { |c| c.log_benchmark = false }
265
+ adapter.send_post_request
266
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Benchmark:")
267
+ end
268
+
269
+ it "should colorized output" do
270
+ HttpLog.configure { |c| c.color = :red }
271
+ adapter.send_post_request
272
+ expect(log.colorized?).to be_truthy
273
+ end
274
+ end
275
+ end
276
+
277
+ context "POST form data requests" do
278
+ if adapter_class.method_defined? :send_post_form_request
279
+ it "should not log data if disabled" do
280
+ HttpLog.configure { |c| c.log_data = false }
281
+ adapter.send_post_form_request
282
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Data:")
283
+ end
284
+
285
+ it "should not log the response if disabled" do
286
+ HttpLog.configure { |c| c.log_response = false }
287
+ adapter.send_post_form_request
288
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Reponse:")
289
+ end
290
+
291
+ it "should not log the benchmark if disabled" do
292
+ HttpLog.configure { |c| c.log_benchmark = false }
293
+ adapter.send_post_form_request
294
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Benchmark:")
295
+ end
296
+ end
297
+ end
298
+
299
+ context "POST multi-part requests (file upload)" do
300
+ let(:upload) { Tempfile.new('http-log') }
301
+ let(:params) { {'foo' => 'bar', 'file' => upload} }
302
+
303
+ if adapter_class.method_defined? :send_multipart_post_request
304
+ it "should not log data if disabled" do
305
+ HttpLog.configure { |c| c.log_data = false }
306
+ adapter.send_multipart_post_request
307
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Data:")
308
+ end
309
+
310
+ it "should not log the response if disabled" do
311
+ HttpLog.configure { |c| c.log_response = false }
312
+ adapter.send_multipart_post_request
313
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Reponse:")
314
+ end
315
+
316
+ it "should not log the benchmark if disabled" do
317
+ HttpLog.configure { |c| c.log_benchmark = false }
318
+ adapter.send_multipart_post_request
319
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Benchmark:")
320
+ end
321
+ end
322
+ end
323
+ end
324
+
325
+ context "with compact config" do
326
+ before(:each) { HttpLog.configure { |c| c.compact_log = true } }
327
+
328
+ it "should log a single line with status and benchmark" do
329
+ adapter.send_get_request
330
+ expect(log).to match /\[httplog\] GET http:\/\/#{host}:#{port}#{path}(\?.*)? completed with status code \d{3} in (\d|\.)+/
331
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Connecting: #{host}:#{port}")
332
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Response:")
333
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Data:")
334
+ expect(log).to_not include(HttpLog::LOG_PREFIX + "Benchmark: ")
335
+ end
336
+ end
337
+
338
+ =begin # NOTE: dropping this in v0.99.0 to support ruby 2.4.0. log4r has been stale for years.
339
+ context "with log4r" do
340
+
341
+ before(:each) do
342
+ require 'log4r'
343
+ require 'log4r/yamlconfigurator'
344
+ require 'log4r/outputter/datefileoutputter'
345
+ log4r_config= YAML.load_file(File.join(File.dirname(__FILE__),"support/log4r.yml"))
346
+ Log4r::YamlConfigurator.decode_yaml( log4r_config['log4r_config'] )
347
+ HttpLog.configure{ |c| c.logger = Log4r::Logger['test'] }
348
+ end
349
+
350
+ it "works" do
351
+ expect { adapter.send_get_request }.to_not raise_error
352
+ end
353
+ end
354
+ =end
355
+ end
356
+ end
357
+
358
+ end