httplog 1.0.0 → 1.0.1

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.
@@ -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