httplog 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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?
|