httplog 1.0.2 → 1.0.3
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +55 -0
- data/.rubocop_todo.yml +36 -0
- data/.travis.yml +0 -1
- data/Gemfile +3 -1
- data/Gemfile.lock +2 -2
- data/Guardfile +10 -9
- data/README.md +7 -1
- data/Rakefile +7 -5
- data/gemfiles/http2.gemfile +5 -3
- data/gemfiles/http3.gemfile +5 -3
- data/gemfiles/http4.gemfile +5 -3
- data/gemfiles/rack1.gemfile +5 -3
- data/gemfiles/rack2.gemfile +5 -3
- data/httplog.gemspec +29 -28
- data/lib/httplog.rb +11 -9
- data/lib/httplog/adapters/ethon.rb +7 -9
- data/lib/httplog/adapters/excon.rb +9 -11
- data/lib/httplog/adapters/http.rb +9 -10
- data/lib/httplog/adapters/httpclient.rb +7 -4
- data/lib/httplog/adapters/net_http.rb +5 -5
- data/lib/httplog/adapters/patron.rb +4 -2
- data/lib/httplog/configuration.rb +4 -5
- data/lib/httplog/http_log.rb +33 -26
- data/lib/httplog/version.rb +3 -1
- data/spec/adapters/ethon_adapter.rb +6 -4
- data/spec/adapters/excon_adapter.rb +3 -1
- data/spec/adapters/faraday_adapter.rb +3 -1
- data/spec/adapters/http_adapter.rb +2 -0
- data/spec/adapters/http_base_adapter.rb +10 -8
- data/spec/adapters/httparty_adapter.rb +2 -0
- data/spec/adapters/httpclient_adapter.rb +2 -0
- data/spec/adapters/net_http_adapter.rb +5 -5
- data/spec/adapters/open_uri_adapter.rb +4 -2
- data/spec/adapters/patron_adapter.rb +4 -2
- data/spec/adapters/typhoeus_adapter.rb +2 -1
- data/spec/configuration_spec.rb +5 -6
- data/spec/lib/http_client_spec.rb +15 -0
- data/spec/lib/http_log_spec.rb +333 -0
- data/spec/spec_helper.rb +7 -7
- data/spec/support/not_gzipped.html.gz +8 -0
- data/spec/support/test_server.rb +16 -14
- metadata +43 -39
- data/spec/http_log_spec.rb +0 -358
@@ -1,25 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
if defined?(::HTTP::Client) && defined?(::HTTP::Connection)
|
2
|
-
module ::HTTP
|
4
|
+
module ::HTTP # rubocop:disable Style/ClassAndModuleChildren
|
3
5
|
class Client
|
4
|
-
|
5
|
-
#
|
6
6
|
request_method = respond_to?('make_request') ? 'make_request' : 'perform'
|
7
7
|
orig_request_method = "orig_#{request_method}"
|
8
8
|
alias_method(orig_request_method, request_method) unless method_defined?(orig_request_method)
|
9
9
|
|
10
10
|
define_method request_method do |req, options|
|
11
|
-
|
12
11
|
log_enabled = HttpLog.url_approved?(req.uri)
|
13
12
|
|
14
13
|
if log_enabled
|
15
14
|
HttpLog.log_request(req.verb, req.uri)
|
16
15
|
HttpLog.log_headers(req.headers.to_h)
|
17
16
|
|
18
|
-
if defined?(::HTTP::Request::Body)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
body = if defined?(::HTTP::Request::Body)
|
18
|
+
req.body.respond_to?(:source) ? req.body.source : req.body.instance_variable_get(:@body)
|
19
|
+
else
|
20
|
+
req.body
|
21
|
+
end
|
23
22
|
|
24
23
|
HttpLog.log_data(body.to_s)
|
25
24
|
body.rewind if body.respond_to?(:rewind)
|
@@ -43,7 +42,7 @@ if defined?(::HTTP::Client) && defined?(::HTTP::Connection)
|
|
43
42
|
end
|
44
43
|
|
45
44
|
class Connection
|
46
|
-
|
45
|
+
alias orig_initialize initialize unless method_defined?(:orig_initialize)
|
47
46
|
|
48
47
|
def initialize(req, options)
|
49
48
|
HttpLog.log_connection(req.uri.host, req.uri.port) if HttpLog.url_approved?(req.uri)
|
@@ -1,7 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
if defined?(::HTTPClient)
|
2
4
|
class HTTPClient
|
3
5
|
private
|
4
|
-
|
6
|
+
|
7
|
+
alias orig_do_get_block do_get_block
|
5
8
|
|
6
9
|
def do_get_block(req, proxy, conn, &block)
|
7
10
|
log_enabled = HttpLog.url_approved?(req.header.request_uri)
|
@@ -32,11 +35,11 @@ if defined?(::HTTPClient)
|
|
32
35
|
conn.push(res)
|
33
36
|
end
|
34
37
|
|
35
|
-
raise retryable_response
|
38
|
+
raise retryable_response unless retryable_response.nil?
|
36
39
|
end
|
37
40
|
|
38
41
|
class Session
|
39
|
-
|
42
|
+
alias orig_create_socket create_socket
|
40
43
|
|
41
44
|
# up to version 2.6, the method signature is `create_socket(site)`; after that,
|
42
45
|
# it's `create_socket(hort, port)`
|
@@ -53,7 +56,7 @@ if defined?(::HTTPClient)
|
|
53
56
|
if HttpLog.url_approved?("#{host}:#{port}")
|
54
57
|
HttpLog.log_connection(host, port)
|
55
58
|
end
|
56
|
-
orig_create_socket(host,port)
|
59
|
+
orig_create_socket(host, port)
|
57
60
|
end
|
58
61
|
end
|
59
62
|
end
|
@@ -1,10 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Net
|
2
4
|
class HTTP
|
3
|
-
|
4
|
-
|
5
|
+
alias orig_request request unless method_defined?(:orig_request)
|
6
|
+
alias orig_connect connect unless method_defined?(:orig_connect)
|
5
7
|
|
6
8
|
def request(req, body = nil, &block)
|
7
|
-
|
8
9
|
url = "http://#{@address}:#{@port}#{req.path}"
|
9
10
|
|
10
11
|
log_enabled = HttpLog.url_approved?(url)
|
@@ -14,7 +15,7 @@ module Net
|
|
14
15
|
HttpLog.log_headers(req.each_header.collect)
|
15
16
|
# A bit convoluted becase post_form uses form_data= to assign the data, so
|
16
17
|
# in that case req.body will be empty.
|
17
|
-
HttpLog
|
18
|
+
HttpLog.log_data(req.body.nil? || req.body.empty? ? body : req.body) # if req.method == 'POST'
|
18
19
|
end
|
19
20
|
|
20
21
|
bm = Benchmark.realtime do
|
@@ -38,5 +39,4 @@ module Net
|
|
38
39
|
orig_connect
|
39
40
|
end
|
40
41
|
end
|
41
|
-
|
42
42
|
end
|
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
if defined?(Patron)
|
2
4
|
module Patron
|
3
5
|
class Session
|
4
|
-
|
6
|
+
alias orig_request request
|
5
7
|
def request(action_name, url, headers, options = {})
|
6
8
|
log_enabled = HttpLog.url_approved?(url)
|
7
9
|
|
8
10
|
if log_enabled
|
9
11
|
HttpLog.log_request(action_name, url)
|
10
12
|
HttpLog.log_headers(headers)
|
11
|
-
HttpLog.log_data(options[:data])# if action_name == :post
|
13
|
+
HttpLog.log_data(options[:data]) # if action_name == :post
|
12
14
|
end
|
13
15
|
|
14
16
|
bm = Benchmark.realtime do
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HttpLog
|
2
4
|
class Configuration
|
3
5
|
attr_accessor :enabled,
|
@@ -12,7 +14,6 @@ module HttpLog
|
|
12
14
|
:log_status,
|
13
15
|
:log_response,
|
14
16
|
:log_benchmark,
|
15
|
-
:compact_log,
|
16
17
|
:url_whitelist_pattern,
|
17
18
|
:url_blacklist_pattern,
|
18
19
|
:color,
|
@@ -33,7 +34,6 @@ module HttpLog
|
|
33
34
|
@log_status = true
|
34
35
|
@log_response = true
|
35
36
|
@log_benchmark = true
|
36
|
-
@compact_log = false
|
37
37
|
@url_whitelist_pattern = /.*/
|
38
38
|
@url_blacklist_pattern = nil
|
39
39
|
@color = false
|
@@ -44,9 +44,8 @@ module HttpLog
|
|
44
44
|
|
45
45
|
# TODO: remove in 1.0.0
|
46
46
|
def []=(key, value)
|
47
|
-
|
48
|
-
|
47
|
+
warn 'DEPRECATION WARNING: Assignment to HttpLog.options will be removed in version 1.0.0. Please use HttpLog.configure block instead as described here: https://github.com/trusche/httplog#configuration'
|
48
|
+
send("#{key}=", value)
|
49
49
|
end
|
50
|
-
|
51
50
|
end
|
52
51
|
end
|
data/lib/httplog/http_log.rb
CHANGED
@@ -1,21 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'logger'
|
5
|
+
require 'benchmark'
|
6
|
+
require 'colorize'
|
7
|
+
require 'rack'
|
6
8
|
|
7
9
|
module HttpLog
|
8
|
-
LOG_PREFIX =
|
10
|
+
LOG_PREFIX = '[httplog] '.freeze
|
9
11
|
|
10
12
|
class << self
|
11
|
-
|
12
|
-
attr_accessor :configuration
|
13
|
+
attr_writer :configuration
|
13
14
|
|
14
15
|
def configuration
|
15
16
|
@configuration ||= Configuration.new
|
16
17
|
end
|
17
|
-
|
18
|
-
|
18
|
+
alias config configuration
|
19
|
+
alias options configuration # TODO: remove with 1.0.0
|
19
20
|
|
20
21
|
def reset!
|
21
22
|
@configuration = nil
|
@@ -37,7 +38,7 @@ module HttpLog
|
|
37
38
|
|
38
39
|
def log_connection(host, port = nil)
|
39
40
|
return if config.compact_log || !config.log_connect
|
40
|
-
log("Connecting: #{[host, port].compact.join(
|
41
|
+
log("Connecting: #{[host, port].compact.join(':')}")
|
41
42
|
end
|
42
43
|
|
43
44
|
def log_request(method, uri)
|
@@ -47,7 +48,7 @@ module HttpLog
|
|
47
48
|
|
48
49
|
def log_headers(headers = {})
|
49
50
|
return if config.compact_log || !config.log_headers
|
50
|
-
headers.each do |key,value|
|
51
|
+
headers.each do |key, value|
|
51
52
|
log("Header: #{key}: #{value}")
|
52
53
|
end
|
53
54
|
end
|
@@ -63,36 +64,39 @@ module HttpLog
|
|
63
64
|
log("Benchmark: #{seconds.to_f.round(6)} seconds")
|
64
65
|
end
|
65
66
|
|
66
|
-
def log_body(body, encoding = nil, content_type=nil)
|
67
|
+
def log_body(body, encoding = nil, content_type = nil)
|
67
68
|
return if config.compact_log || !config.log_response
|
68
69
|
|
69
70
|
unless text_based?(content_type)
|
70
|
-
log(
|
71
|
+
log('Response: (not showing binary data)')
|
71
72
|
return
|
72
73
|
end
|
73
74
|
|
74
75
|
if body.is_a?(Net::ReadAdapter)
|
75
76
|
# open-uri wraps the response in a Net::ReadAdapter that defers reading
|
76
77
|
# the content, so the reponse body is not available here.
|
77
|
-
log(
|
78
|
+
log('Response: (not available yet)')
|
78
79
|
return
|
79
80
|
end
|
80
81
|
|
81
82
|
if encoding =~ /gzip/ && body && !body.empty?
|
82
|
-
|
83
|
-
|
84
|
-
|
83
|
+
begin
|
84
|
+
sio = StringIO.new(body.to_s)
|
85
|
+
gz = Zlib::GzipReader.new(sio)
|
86
|
+
body = gz.read
|
87
|
+
rescue Zlib::GzipFile::Error => e
|
88
|
+
log("(gzip decompression failed)")
|
89
|
+
end
|
85
90
|
end
|
86
91
|
|
87
92
|
data = utf_encoded(body.to_s, content_type)
|
88
93
|
|
89
94
|
if config.prefix_response_lines
|
90
|
-
log(
|
95
|
+
log('Response:')
|
91
96
|
log_data_lines(data)
|
92
97
|
else
|
93
98
|
log("Response:\n#{data}")
|
94
99
|
end
|
95
|
-
|
96
100
|
end
|
97
101
|
|
98
102
|
def log_data(data)
|
@@ -100,7 +104,7 @@ module HttpLog
|
|
100
104
|
data = utf_encoded(data.to_s.dup)
|
101
105
|
|
102
106
|
if config.prefix_data_lines
|
103
|
-
log(
|
107
|
+
log('Data:')
|
104
108
|
log_data_lines(data)
|
105
109
|
else
|
106
110
|
log("Data: #{data}")
|
@@ -120,10 +124,14 @@ module HttpLog
|
|
120
124
|
|
121
125
|
private
|
122
126
|
|
123
|
-
def utf_encoded(data, content_type=nil)
|
127
|
+
def utf_encoded(data, content_type = nil)
|
124
128
|
charset = content_type.to_s.scan(/; charset=(\S+)/).flatten.first || 'UTF-8'
|
125
|
-
|
126
|
-
|
129
|
+
begin
|
130
|
+
data.force_encoding(charset)
|
131
|
+
rescue StandardError
|
132
|
+
data.force_encoding('UTF-8')
|
133
|
+
end
|
134
|
+
data.encode('UTF-8', invalid: :replace, undef: :replace)
|
127
135
|
end
|
128
136
|
|
129
137
|
def text_based?(content_type)
|
@@ -131,7 +139,7 @@ module HttpLog
|
|
131
139
|
# it will allow application/json and the like without having to resort to more
|
132
140
|
# heavy-handed checks.
|
133
141
|
content_type =~ /^text/ ||
|
134
|
-
|
142
|
+
content_type =~ /^application/ && content_type != 'application/octet-stream'
|
135
143
|
end
|
136
144
|
|
137
145
|
def log_data_lines(data)
|
@@ -151,6 +159,5 @@ module HttpLog
|
|
151
159
|
config.prefix.to_s
|
152
160
|
end
|
153
161
|
end
|
154
|
-
|
155
162
|
end
|
156
163
|
end
|
data/lib/httplog/version.rb
CHANGED
@@ -1,20 +1,22 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ethon'
|
2
4
|
class EthonAdapter < HTTPBaseAdapter
|
3
5
|
def send_get_request
|
4
6
|
easy = Ethon::Easy.new
|
5
|
-
easy.http_request(parse_uri.to_s, :get,
|
7
|
+
easy.http_request(parse_uri.to_s, :get, headers: @headers)
|
6
8
|
easy.perform
|
7
9
|
end
|
8
10
|
|
9
11
|
def send_head_request
|
10
12
|
easy = Ethon::Easy.new
|
11
|
-
easy.http_request(parse_uri.to_s, :head,
|
13
|
+
easy.http_request(parse_uri.to_s, :head, headers: @headers)
|
12
14
|
easy.perform
|
13
15
|
end
|
14
16
|
|
15
17
|
def send_post_request
|
16
18
|
easy = Ethon::Easy.new
|
17
|
-
easy.http_request(parse_uri.to_s, :post,
|
19
|
+
easy.http_request(parse_uri.to_s, :post, headers: @headers, body: @data)
|
18
20
|
easy.perform
|
19
21
|
end
|
20
22
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'faraday'
|
2
4
|
class FaradayAdapter < HTTPBaseAdapter
|
3
5
|
def send_get_request
|
@@ -47,7 +49,7 @@ class FaradayAdapter < HTTPBaseAdapter
|
|
47
49
|
faraday.request :multipart
|
48
50
|
faraday.request :url_encoded
|
49
51
|
|
50
|
-
faraday.adapter
|
52
|
+
faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
|
51
53
|
end
|
52
54
|
end
|
53
55
|
end
|
@@ -1,12 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class HTTPBaseAdapter
|
2
|
-
def initialize(
|
3
|
-
@host
|
4
|
-
@port
|
5
|
-
@path
|
6
|
-
@headers
|
7
|
-
@data
|
8
|
-
@params
|
9
|
-
@protocol = protocol
|
4
|
+
def initialize(options = {})
|
5
|
+
@host = options.fetch(:host, 'localhost')
|
6
|
+
@port = options.fetch(:port, 80)
|
7
|
+
@path = options.fetch(:path, '/')
|
8
|
+
@headers = options.fetch(:headers, {})
|
9
|
+
@data = options.fetch(:data, nil)
|
10
|
+
@params = options.fetch(:params, {})
|
11
|
+
@protocol = options.fetch(:protocol, 'http')
|
10
12
|
end
|
11
13
|
|
12
14
|
def logs_data?
|
@@ -1,19 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class NetHTTPAdapter < HTTPBaseAdapter
|
2
4
|
def send_get_request
|
3
5
|
Net::HTTP.get_response(@host, [@path, @data].compact.join('?'), @port)
|
4
6
|
end
|
5
7
|
|
6
8
|
def send_head_request
|
7
|
-
|
8
|
-
http.head(@path, @headers)
|
9
|
+
Net::HTTP.new(@host, @port).head(@path, @headers)
|
9
10
|
end
|
10
11
|
|
11
12
|
def send_post_request
|
12
|
-
|
13
|
-
resp = http.post(@path, @data)
|
13
|
+
Net::HTTP.new(@host, @port).post(@path, @data)
|
14
14
|
end
|
15
15
|
|
16
16
|
def send_post_form_request
|
17
|
-
|
17
|
+
Net::HTTP.post_form(parse_uri, @params)
|
18
18
|
end
|
19
19
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class OpenUriAdapter < HTTPBaseAdapter
|
2
4
|
def send_get_request
|
3
|
-
open(parse_uri)
|
5
|
+
open(parse_uri) # rubocop:disable Security/Open
|
4
6
|
end
|
5
7
|
|
6
8
|
def expected_response_body
|
7
|
-
|
9
|
+
' (not available yet)'
|
8
10
|
end
|
9
11
|
|
10
12
|
def self.should_log_headers?
|