1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
#--
# Author:: Daniel DeLeo (<dan@chef.io>)
# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require "zlib"
require_relative "http_request"
class Chef
class HTTP
# Middleware-ish class for handling compression in HTTP responses.
class Decompressor
class NoopInflater
def inflate(chunk)
chunk
end
alias :handle_chunk :inflate
end
class GzipInflater < Zlib::Inflate
def initialize
super(Zlib::MAX_WBITS + 16)
end
alias :handle_chunk :inflate
end
class DeflateInflater < Zlib::Inflate
def initialize
super
end
alias :handle_chunk :inflate
end
CONTENT_ENCODING = "content-encoding".freeze
GZIP = "gzip".freeze
DEFLATE = "deflate".freeze
IDENTITY = "identity".freeze
def initialize(opts = {})
@disable_gzip = false
handle_options(opts)
end
def handle_request(method, url, headers = {}, data = false)
headers[HTTPRequest::ACCEPT_ENCODING] = HTTPRequest::ENCODING_GZIP_DEFLATE unless gzip_disabled?
[method, url, headers, data]
end
def handle_response(http_response, rest_request, return_value)
# temporary hack, skip processing if return_value is false
# needed to keep conditional get stuff working correctly.
return [http_response, rest_request, return_value] if return_value == false
response_body = decompress_body(http_response)
http_response.body.replace(response_body) if http_response.body.respond_to?(:replace)
[http_response, rest_request, return_value]
end
def handle_stream_complete(http_response, rest_request, return_value)
[http_response, rest_request, return_value]
end
def decompress_body(response)
if gzip_disabled? || response.body.nil?
response.body
else
case response[CONTENT_ENCODING]
when GZIP
Chef::Log.trace "Decompressing gzip response"
Zlib::Inflate.new(Zlib::MAX_WBITS + 16).inflate(response.body)
when DEFLATE
Chef::Log.trace "Decompressing deflate response"
Zlib::Inflate.inflate(response.body)
else
response.body
end
end
end
# This isn't used when this class is used as middleware; it returns an
# object you can use to unzip/inflate a streaming response.
def stream_response_handler(response)
if gzip_disabled?
Chef::Log.trace "disable_gzip is set. \
Not using #{response[CONTENT_ENCODING]} \
and initializing noop stream deflator."
NoopInflater.new
else
case response[CONTENT_ENCODING]
when GZIP
Chef::Log.trace "Initializing gzip stream deflator"
GzipInflater.new
when DEFLATE
Chef::Log.trace "Initializing deflate stream deflator"
DeflateInflater.new
else
Chef::Log.trace "content_encoding = '#{response[CONTENT_ENCODING]}' \
initializing noop stream deflator."
NoopInflater.new
end
end
end
# gzip is disabled using the disable_gzip => true option in the
# constructor. When gzip is disabled, no 'Accept-Encoding' header will be
# set, and the response will not be decompressed, no matter what the
# Content-Encoding header of the response is. The intended use case for
# this is to work around situations where you request +file.tar.gz+, but
# the server responds with a content type of tar and a content encoding of
# gzip, tricking the client into decompressing the response so you end up
# with a tar archive (no gzip) named file.tar.gz
def gzip_disabled?
@disable_gzip
end
private
def handle_options(opts)
opts.each do |name, value|
case name.to_s
when "disable_gzip"
@disable_gzip = value
end
end
end
end
end
end
|