swift 2.23.2__py3-none-any.whl → 2.35.0__py3-none-any.whl
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.
- swift/__init__.py +29 -50
- swift/account/auditor.py +21 -118
- swift/account/backend.py +33 -28
- swift/account/reaper.py +37 -28
- swift/account/replicator.py +22 -0
- swift/account/server.py +60 -26
- swift/account/utils.py +28 -11
- swift-2.23.2.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
- swift-2.23.2.data/scripts/swift-config → swift/cli/config.py +2 -2
- swift/cli/container_deleter.py +5 -11
- swift-2.23.2.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
- swift/cli/dispersion_report.py +10 -9
- swift-2.23.2.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
- swift/cli/form_signature.py +3 -7
- swift-2.23.2.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
- swift/cli/info.py +183 -29
- swift/cli/manage_shard_ranges.py +708 -37
- swift-2.23.2.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
- swift-2.23.2.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
- swift/cli/recon.py +196 -67
- swift-2.23.2.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
- swift-2.23.2.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
- swift/cli/relinker.py +807 -126
- swift/cli/reload.py +135 -0
- swift/cli/ringbuilder.py +217 -20
- swift/cli/ringcomposer.py +0 -1
- swift/cli/shard-info.py +4 -3
- swift/common/base_storage_server.py +9 -20
- swift/common/bufferedhttp.py +48 -74
- swift/common/constraints.py +20 -15
- swift/common/container_sync_realms.py +9 -11
- swift/common/daemon.py +25 -8
- swift/common/db.py +198 -127
- swift/common/db_auditor.py +168 -0
- swift/common/db_replicator.py +95 -55
- swift/common/digest.py +141 -0
- swift/common/direct_client.py +144 -33
- swift/common/error_limiter.py +93 -0
- swift/common/exceptions.py +25 -1
- swift/common/header_key_dict.py +2 -9
- swift/common/http_protocol.py +373 -0
- swift/common/internal_client.py +129 -59
- swift/common/linkat.py +3 -4
- swift/common/manager.py +284 -67
- swift/common/memcached.py +396 -147
- swift/common/middleware/__init__.py +4 -0
- swift/common/middleware/account_quotas.py +211 -46
- swift/common/middleware/acl.py +3 -8
- swift/common/middleware/backend_ratelimit.py +230 -0
- swift/common/middleware/bulk.py +22 -34
- swift/common/middleware/catch_errors.py +1 -3
- swift/common/middleware/cname_lookup.py +6 -11
- swift/common/middleware/container_quotas.py +1 -1
- swift/common/middleware/container_sync.py +39 -17
- swift/common/middleware/copy.py +12 -0
- swift/common/middleware/crossdomain.py +22 -9
- swift/common/middleware/crypto/__init__.py +2 -1
- swift/common/middleware/crypto/crypto_utils.py +11 -15
- swift/common/middleware/crypto/decrypter.py +28 -11
- swift/common/middleware/crypto/encrypter.py +12 -17
- swift/common/middleware/crypto/keymaster.py +8 -15
- swift/common/middleware/crypto/kms_keymaster.py +2 -1
- swift/common/middleware/dlo.py +15 -11
- swift/common/middleware/domain_remap.py +5 -4
- swift/common/middleware/etag_quoter.py +128 -0
- swift/common/middleware/formpost.py +73 -70
- swift/common/middleware/gatekeeper.py +8 -1
- swift/common/middleware/keystoneauth.py +33 -3
- swift/common/middleware/list_endpoints.py +4 -4
- swift/common/middleware/listing_formats.py +85 -49
- swift/common/middleware/memcache.py +4 -81
- swift/common/middleware/name_check.py +3 -2
- swift/common/middleware/proxy_logging.py +160 -92
- swift/common/middleware/ratelimit.py +17 -10
- swift/common/middleware/read_only.py +6 -4
- swift/common/middleware/recon.py +59 -22
- swift/common/middleware/s3api/acl_handlers.py +25 -3
- swift/common/middleware/s3api/acl_utils.py +6 -1
- swift/common/middleware/s3api/controllers/__init__.py +6 -0
- swift/common/middleware/s3api/controllers/acl.py +3 -2
- swift/common/middleware/s3api/controllers/bucket.py +242 -137
- swift/common/middleware/s3api/controllers/logging.py +2 -2
- swift/common/middleware/s3api/controllers/multi_delete.py +43 -20
- swift/common/middleware/s3api/controllers/multi_upload.py +219 -133
- swift/common/middleware/s3api/controllers/obj.py +112 -8
- swift/common/middleware/s3api/controllers/object_lock.py +44 -0
- swift/common/middleware/s3api/controllers/s3_acl.py +2 -2
- swift/common/middleware/s3api/controllers/tagging.py +57 -0
- swift/common/middleware/s3api/controllers/versioning.py +36 -7
- swift/common/middleware/s3api/etree.py +22 -9
- swift/common/middleware/s3api/exception.py +0 -4
- swift/common/middleware/s3api/s3api.py +113 -41
- swift/common/middleware/s3api/s3request.py +384 -218
- swift/common/middleware/s3api/s3response.py +126 -23
- swift/common/middleware/s3api/s3token.py +16 -17
- swift/common/middleware/s3api/schema/delete.rng +1 -1
- swift/common/middleware/s3api/subresource.py +7 -10
- swift/common/middleware/s3api/utils.py +27 -10
- swift/common/middleware/slo.py +665 -358
- swift/common/middleware/staticweb.py +64 -37
- swift/common/middleware/symlink.py +52 -19
- swift/common/middleware/tempauth.py +76 -58
- swift/common/middleware/tempurl.py +192 -174
- swift/common/middleware/versioned_writes/__init__.py +51 -0
- swift/common/middleware/{versioned_writes.py → versioned_writes/legacy.py} +27 -26
- swift/common/middleware/versioned_writes/object_versioning.py +1482 -0
- swift/common/middleware/x_profile/exceptions.py +1 -4
- swift/common/middleware/x_profile/html_viewer.py +18 -19
- swift/common/middleware/x_profile/profile_model.py +1 -2
- swift/common/middleware/xprofile.py +10 -10
- swift-2.23.2.data/scripts/swift-container-server → swift/common/recon.py +13 -8
- swift/common/registry.py +147 -0
- swift/common/request_helpers.py +324 -57
- swift/common/ring/builder.py +67 -25
- swift/common/ring/composite_builder.py +1 -1
- swift/common/ring/ring.py +177 -51
- swift/common/ring/utils.py +1 -1
- swift/common/splice.py +10 -6
- swift/common/statsd_client.py +205 -0
- swift/common/storage_policy.py +49 -44
- swift/common/swob.py +86 -102
- swift/common/{utils.py → utils/__init__.py} +2191 -2762
- swift/common/utils/base.py +131 -0
- swift/common/utils/config.py +433 -0
- swift/common/utils/ipaddrs.py +256 -0
- swift/common/utils/libc.py +345 -0
- swift/common/utils/logs.py +859 -0
- swift/common/utils/timestamp.py +412 -0
- swift/common/wsgi.py +555 -536
- swift/container/auditor.py +14 -100
- swift/container/backend.py +552 -227
- swift/container/reconciler.py +126 -37
- swift/container/replicator.py +96 -22
- swift/container/server.py +397 -176
- swift/container/sharder.py +1580 -639
- swift/container/sync.py +94 -88
- swift/container/updater.py +53 -32
- swift/obj/auditor.py +153 -35
- swift/obj/diskfile.py +466 -217
- swift/obj/expirer.py +406 -124
- swift/obj/mem_diskfile.py +7 -4
- swift/obj/mem_server.py +1 -0
- swift/obj/reconstructor.py +523 -262
- swift/obj/replicator.py +249 -188
- swift/obj/server.py +213 -122
- swift/obj/ssync_receiver.py +145 -85
- swift/obj/ssync_sender.py +113 -54
- swift/obj/updater.py +653 -139
- swift/obj/watchers/__init__.py +0 -0
- swift/obj/watchers/dark_data.py +213 -0
- swift/proxy/controllers/account.py +11 -11
- swift/proxy/controllers/base.py +848 -604
- swift/proxy/controllers/container.py +452 -86
- swift/proxy/controllers/info.py +3 -2
- swift/proxy/controllers/obj.py +1009 -490
- swift/proxy/server.py +185 -112
- swift-2.35.0.dist-info/AUTHORS +501 -0
- swift-2.35.0.dist-info/LICENSE +202 -0
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/METADATA +52 -61
- swift-2.35.0.dist-info/RECORD +201 -0
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/entry_points.txt +43 -0
- swift-2.35.0.dist-info/pbr.json +1 -0
- swift/locale/de/LC_MESSAGES/swift.po +0 -1216
- swift/locale/en_GB/LC_MESSAGES/swift.po +0 -1207
- swift/locale/es/LC_MESSAGES/swift.po +0 -1085
- swift/locale/fr/LC_MESSAGES/swift.po +0 -909
- swift/locale/it/LC_MESSAGES/swift.po +0 -894
- swift/locale/ja/LC_MESSAGES/swift.po +0 -965
- swift/locale/ko_KR/LC_MESSAGES/swift.po +0 -964
- swift/locale/pt_BR/LC_MESSAGES/swift.po +0 -881
- swift/locale/ru/LC_MESSAGES/swift.po +0 -891
- swift/locale/tr_TR/LC_MESSAGES/swift.po +0 -832
- swift/locale/zh_CN/LC_MESSAGES/swift.po +0 -833
- swift/locale/zh_TW/LC_MESSAGES/swift.po +0 -838
- swift-2.23.2.data/scripts/swift-account-auditor +0 -23
- swift-2.23.2.data/scripts/swift-account-info +0 -51
- swift-2.23.2.data/scripts/swift-account-reaper +0 -23
- swift-2.23.2.data/scripts/swift-account-replicator +0 -34
- swift-2.23.2.data/scripts/swift-account-server +0 -23
- swift-2.23.2.data/scripts/swift-container-auditor +0 -23
- swift-2.23.2.data/scripts/swift-container-info +0 -51
- swift-2.23.2.data/scripts/swift-container-reconciler +0 -21
- swift-2.23.2.data/scripts/swift-container-replicator +0 -34
- swift-2.23.2.data/scripts/swift-container-sharder +0 -33
- swift-2.23.2.data/scripts/swift-container-sync +0 -23
- swift-2.23.2.data/scripts/swift-container-updater +0 -23
- swift-2.23.2.data/scripts/swift-dispersion-report +0 -24
- swift-2.23.2.data/scripts/swift-form-signature +0 -20
- swift-2.23.2.data/scripts/swift-init +0 -119
- swift-2.23.2.data/scripts/swift-object-auditor +0 -29
- swift-2.23.2.data/scripts/swift-object-expirer +0 -33
- swift-2.23.2.data/scripts/swift-object-info +0 -60
- swift-2.23.2.data/scripts/swift-object-reconstructor +0 -33
- swift-2.23.2.data/scripts/swift-object-relinker +0 -41
- swift-2.23.2.data/scripts/swift-object-replicator +0 -37
- swift-2.23.2.data/scripts/swift-object-server +0 -27
- swift-2.23.2.data/scripts/swift-object-updater +0 -23
- swift-2.23.2.data/scripts/swift-proxy-server +0 -23
- swift-2.23.2.data/scripts/swift-recon +0 -24
- swift-2.23.2.data/scripts/swift-ring-builder +0 -24
- swift-2.23.2.data/scripts/swift-ring-builder-analyzer +0 -22
- swift-2.23.2.data/scripts/swift-ring-composer +0 -22
- swift-2.23.2.dist-info/DESCRIPTION.rst +0 -166
- swift-2.23.2.dist-info/RECORD +0 -220
- swift-2.23.2.dist-info/metadata.json +0 -1
- swift-2.23.2.dist-info/pbr.json +0 -1
- {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,373 @@
|
|
1
|
+
# Copyright (c) 2010-2022 OpenStack Foundation
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
12
|
+
# implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
from eventlet import wsgi, websocket
|
17
|
+
|
18
|
+
from swift.common.utils import generate_trans_id
|
19
|
+
from swift.common.http import HTTP_NO_CONTENT, HTTP_RESET_CONTENT, \
|
20
|
+
HTTP_NOT_MODIFIED
|
21
|
+
|
22
|
+
from eventlet.green.http import client as http_client
|
23
|
+
from html import escape
|
24
|
+
|
25
|
+
|
26
|
+
class SwiftHttpProtocol(wsgi.HttpProtocol):
|
27
|
+
default_request_version = "HTTP/1.0"
|
28
|
+
reject_bad_requests = False
|
29
|
+
|
30
|
+
def __init__(self, *args, **kwargs):
|
31
|
+
# See https://github.com/eventlet/eventlet/pull/590
|
32
|
+
self.pre_shutdown_bugfix_eventlet = not getattr(
|
33
|
+
websocket.WebSocketWSGI, '_WSGI_APP_ALWAYS_IDLE', None)
|
34
|
+
super().__init__(*args, **kwargs)
|
35
|
+
|
36
|
+
def log_request(self, *a):
|
37
|
+
"""
|
38
|
+
Turn off logging requests by the underlying WSGI software.
|
39
|
+
"""
|
40
|
+
pass
|
41
|
+
|
42
|
+
def log_message(self, f, *a):
|
43
|
+
"""
|
44
|
+
Redirect logging other messages by the underlying WSGI software.
|
45
|
+
"""
|
46
|
+
logger = getattr(self.server.app, 'logger', None)
|
47
|
+
if logger:
|
48
|
+
logger.error('ERROR WSGI: ' + f, *a)
|
49
|
+
else:
|
50
|
+
# eventlet<=0.17.4 doesn't have an error method, and in newer
|
51
|
+
# versions the output from error is same as info anyway
|
52
|
+
self.server.log.info('ERROR WSGI: ' + f, *a)
|
53
|
+
|
54
|
+
class MessageClass(wsgi.HttpProtocol.MessageClass):
|
55
|
+
"""Subclass to see when the client didn't provide a Content-Type"""
|
56
|
+
def get_default_type(self):
|
57
|
+
"""If the client didn't provide a content type, leave it blank."""
|
58
|
+
return ''
|
59
|
+
|
60
|
+
def parse_request(self):
|
61
|
+
"""Parse a request (inlined from cpython@7e293984).
|
62
|
+
|
63
|
+
The request should be stored in self.raw_requestline; the results
|
64
|
+
are in self.command, self.path, self.request_version and
|
65
|
+
self.headers.
|
66
|
+
|
67
|
+
Return True for success, False for failure; on failure, any relevant
|
68
|
+
error response has already been sent back.
|
69
|
+
|
70
|
+
"""
|
71
|
+
self.command = None # set in case of error on the first line
|
72
|
+
self.request_version = version = self.default_request_version
|
73
|
+
self.close_connection = True
|
74
|
+
requestline = self.raw_requestline.decode('iso-8859-1')
|
75
|
+
requestline = requestline.rstrip('\r\n')
|
76
|
+
self.requestline = requestline
|
77
|
+
# Split off \x20 explicitly (see https://bugs.python.org/issue33973)
|
78
|
+
words = requestline.split(' ')
|
79
|
+
if len(words) == 0:
|
80
|
+
return False
|
81
|
+
|
82
|
+
if len(words) >= 3: # Enough to determine protocol version
|
83
|
+
version = words[-1]
|
84
|
+
try:
|
85
|
+
if not version.startswith('HTTP/'):
|
86
|
+
raise ValueError
|
87
|
+
base_version_number = version.split('/', 1)[1]
|
88
|
+
version_number = base_version_number.split(".")
|
89
|
+
# RFC 2145 section 3.1 says there can be only one "." and
|
90
|
+
# - major and minor numbers MUST be treated as
|
91
|
+
# separate integers;
|
92
|
+
# - HTTP/2.4 is a lower version than HTTP/2.13, which in
|
93
|
+
# turn is lower than HTTP/12.3;
|
94
|
+
# - Leading zeros MUST be ignored by recipients.
|
95
|
+
if len(version_number) != 2:
|
96
|
+
raise ValueError
|
97
|
+
version_number = int(version_number[0]), int(version_number[1])
|
98
|
+
except (ValueError, IndexError):
|
99
|
+
self.send_error(
|
100
|
+
400,
|
101
|
+
"Bad request version (%r)" % version)
|
102
|
+
return False
|
103
|
+
if version_number >= (1, 1) and \
|
104
|
+
self.protocol_version >= "HTTP/1.1":
|
105
|
+
self.close_connection = False
|
106
|
+
if version_number >= (2, 0):
|
107
|
+
self.send_error(
|
108
|
+
505,
|
109
|
+
"Invalid HTTP version (%s)" % base_version_number)
|
110
|
+
return False
|
111
|
+
self.request_version = version
|
112
|
+
|
113
|
+
if not 2 <= len(words) <= 3:
|
114
|
+
self.send_error(
|
115
|
+
400,
|
116
|
+
"Bad request syntax (%r)" % requestline)
|
117
|
+
return False
|
118
|
+
command, path = words[:2]
|
119
|
+
if len(words) == 2:
|
120
|
+
self.close_connection = True
|
121
|
+
if command != 'GET':
|
122
|
+
self.send_error(
|
123
|
+
400,
|
124
|
+
"Bad HTTP/0.9 request type (%r)" % command)
|
125
|
+
return False
|
126
|
+
|
127
|
+
if path.startswith(('http://', 'https://')):
|
128
|
+
host, sep, rest = path.partition('//')[2].partition('/')
|
129
|
+
if sep:
|
130
|
+
path = '/' + rest
|
131
|
+
|
132
|
+
self.command, self.path = command, path
|
133
|
+
|
134
|
+
# Examine the headers and look for a Connection directive.
|
135
|
+
try:
|
136
|
+
self.headers = http_client.parse_headers(
|
137
|
+
self.rfile,
|
138
|
+
_class=self.MessageClass)
|
139
|
+
except http_client.LineTooLong as err:
|
140
|
+
self.send_error(
|
141
|
+
431,
|
142
|
+
"Line too long",
|
143
|
+
str(err))
|
144
|
+
return False
|
145
|
+
except http_client.HTTPException as err:
|
146
|
+
self.send_error(
|
147
|
+
431,
|
148
|
+
"Too many headers",
|
149
|
+
str(err)
|
150
|
+
)
|
151
|
+
return False
|
152
|
+
|
153
|
+
conntype = self.headers.get('Connection', "")
|
154
|
+
if conntype.lower() == 'close':
|
155
|
+
self.close_connection = True
|
156
|
+
elif (conntype.lower() == 'keep-alive' and
|
157
|
+
self.protocol_version >= "HTTP/1.1"):
|
158
|
+
self.close_connection = False
|
159
|
+
# Examine the headers and look for an Expect directive
|
160
|
+
expect = self.headers.get('Expect', "")
|
161
|
+
if (expect.lower() == "100-continue" and
|
162
|
+
self.protocol_version >= "HTTP/1.1" and
|
163
|
+
self.request_version >= "HTTP/1.1"):
|
164
|
+
if not self.handle_expect_100():
|
165
|
+
return False
|
166
|
+
return True
|
167
|
+
|
168
|
+
def get_environ(self, *args, **kwargs):
|
169
|
+
environ = wsgi.HttpProtocol.get_environ(self, *args, **kwargs)
|
170
|
+
header_payload = self.headers.get_payload()
|
171
|
+
if isinstance(header_payload, list) and len(header_payload) == 1:
|
172
|
+
header_payload = header_payload[0].get_payload()
|
173
|
+
if header_payload:
|
174
|
+
# This shouldn't be here. We must've bumped up against
|
175
|
+
# https://bugs.python.org/issue37093
|
176
|
+
headers_raw = list(environ['headers_raw'])
|
177
|
+
for line in header_payload.rstrip('\r\n').split('\n'):
|
178
|
+
if ':' not in line or line[:1] in ' \t':
|
179
|
+
# Well, we're no more broken than we were before...
|
180
|
+
# Should we support line folding?
|
181
|
+
# Should we 400 a bad header line?
|
182
|
+
break
|
183
|
+
header, value = line.split(':', 1)
|
184
|
+
value = value.strip(' \t\n\r')
|
185
|
+
# NB: Eventlet looks at the headers obj to figure out
|
186
|
+
# whether the client said the connection should close;
|
187
|
+
# see https://github.com/eventlet/eventlet/blob/v0.25.0/
|
188
|
+
# eventlet/wsgi.py#L504
|
189
|
+
self.headers.add_header(header, value)
|
190
|
+
headers_raw.append((header, value))
|
191
|
+
wsgi_key = 'HTTP_' + header.replace('-', '_').encode(
|
192
|
+
'latin1').upper().decode('latin1')
|
193
|
+
if wsgi_key in ('HTTP_CONTENT_LENGTH',
|
194
|
+
'HTTP_CONTENT_TYPE'):
|
195
|
+
wsgi_key = wsgi_key[5:]
|
196
|
+
environ[wsgi_key] = value
|
197
|
+
environ['headers_raw'] = tuple(headers_raw)
|
198
|
+
# Since we parsed some more headers, check to see if they
|
199
|
+
# change how our wsgi.input should behave
|
200
|
+
te = environ.get('HTTP_TRANSFER_ENCODING', '').lower()
|
201
|
+
if te.rsplit(',', 1)[-1].strip() == 'chunked':
|
202
|
+
environ['wsgi.input'].chunked_input = True
|
203
|
+
else:
|
204
|
+
length = environ.get('CONTENT_LENGTH')
|
205
|
+
if length:
|
206
|
+
length = int(length)
|
207
|
+
environ['wsgi.input'].content_length = length
|
208
|
+
if environ.get('HTTP_EXPECT', '').lower() == '100-continue':
|
209
|
+
environ['wsgi.input'].wfile = self.wfile
|
210
|
+
environ['wsgi.input'].wfile_line = \
|
211
|
+
b'HTTP/1.1 100 Continue\r\n'
|
212
|
+
return environ
|
213
|
+
|
214
|
+
def _read_request_line(self):
|
215
|
+
got = super()._read_request_line()
|
216
|
+
# See https://github.com/eventlet/eventlet/pull/590
|
217
|
+
if self.pre_shutdown_bugfix_eventlet:
|
218
|
+
self.conn_state[2] = wsgi.STATE_REQUEST
|
219
|
+
return got
|
220
|
+
|
221
|
+
def handle_one_request(self):
|
222
|
+
got = super().handle_one_request()
|
223
|
+
# See https://github.com/eventlet/eventlet/pull/590
|
224
|
+
if self.pre_shutdown_bugfix_eventlet:
|
225
|
+
if self.conn_state[2] != wsgi.STATE_CLOSE:
|
226
|
+
self.conn_state[2] = wsgi.STATE_IDLE
|
227
|
+
return got
|
228
|
+
|
229
|
+
def send_error(self, code, message=None, explain=None):
|
230
|
+
"""Send and log an error reply, we are overriding the cpython parent
|
231
|
+
class method, so we can have logger generate txn_id's for error
|
232
|
+
response from wsgi since we are at the edge of the proxy server.
|
233
|
+
This sends an error response (so it must be called before any output
|
234
|
+
has been generated), logs the error, and finally sends a piece of HTML
|
235
|
+
explaining the error to the user.
|
236
|
+
|
237
|
+
:param code: an HTTP error code
|
238
|
+
3 digits
|
239
|
+
:param message: a simple optional 1 line reason phrase.
|
240
|
+
*( HTAB / SP / VCHAR / %x80-FF )
|
241
|
+
defaults to short entry matching the response code
|
242
|
+
:param explain: a detailed message defaults to the long entry
|
243
|
+
matching the response code.
|
244
|
+
"""
|
245
|
+
|
246
|
+
try:
|
247
|
+
shortmsg, longmsg = self.responses[code]
|
248
|
+
except KeyError:
|
249
|
+
shortmsg, longmsg = '???', '???'
|
250
|
+
if message is None:
|
251
|
+
message = shortmsg
|
252
|
+
if explain is None:
|
253
|
+
explain = longmsg
|
254
|
+
|
255
|
+
try:
|
256
|
+
# assume we have a LogAdapter
|
257
|
+
txn_id = self.server.app.logger.txn_id # just in case it was set
|
258
|
+
except AttributeError:
|
259
|
+
# turns out we don't have a LogAdapter, so go direct
|
260
|
+
txn_id = generate_trans_id('')
|
261
|
+
self.log_error("code %d, message %s, (txn: %s)", code,
|
262
|
+
message, txn_id)
|
263
|
+
else:
|
264
|
+
# we do have a LogAdapter, but likely not yet a txn_id
|
265
|
+
txn_id = txn_id or generate_trans_id('')
|
266
|
+
self.server.app.logger.txn_id = txn_id
|
267
|
+
self.log_error("code %d, message %s", code, message)
|
268
|
+
self.send_response(code, message)
|
269
|
+
self.send_header('Connection', 'close')
|
270
|
+
|
271
|
+
# Message body is omitted for cases described in:
|
272
|
+
# - RFC7230: 3.3. 1xx, 204(No Content), 304(Not Modified)
|
273
|
+
# - RFC7231: 6.3.6. 205(Reset Content)
|
274
|
+
body = None
|
275
|
+
exclude_status = (HTTP_NO_CONTENT,
|
276
|
+
HTTP_RESET_CONTENT,
|
277
|
+
HTTP_NOT_MODIFIED)
|
278
|
+
if (code >= 200 and
|
279
|
+
code not in exclude_status):
|
280
|
+
# HTML encode to prevent Cross Site Scripting attacks
|
281
|
+
# (see bug https://bugs.python.org/issue1100201)
|
282
|
+
content = (self.error_message_format % {
|
283
|
+
'code': code,
|
284
|
+
'message': escape(message, quote=False),
|
285
|
+
'explain': escape(explain, quote=False)
|
286
|
+
})
|
287
|
+
body = content.encode('UTF-8', 'replace')
|
288
|
+
self.send_header("Content-Type", self.error_content_type)
|
289
|
+
self.send_header('Content-Length', str(len(body)))
|
290
|
+
self.send_header('X-Trans-Id', txn_id)
|
291
|
+
self.send_header('X-Openstack-Request-Id', txn_id)
|
292
|
+
self.end_headers()
|
293
|
+
|
294
|
+
if self.command != 'HEAD' and body:
|
295
|
+
self.wfile.write(body)
|
296
|
+
|
297
|
+
|
298
|
+
class SwiftHttpProxiedProtocol(SwiftHttpProtocol):
|
299
|
+
"""
|
300
|
+
Protocol object that speaks HTTP, including multiple requests, but with
|
301
|
+
a single PROXY line as the very first thing coming in over the socket.
|
302
|
+
This is so we can learn what the client's IP address is when Swift is
|
303
|
+
behind a TLS terminator, like hitch, that does not understand HTTP and
|
304
|
+
so cannot add X-Forwarded-For or other similar headers.
|
305
|
+
|
306
|
+
See http://www.haproxy.org/download/1.7/doc/proxy-protocol.txt for
|
307
|
+
protocol details.
|
308
|
+
"""
|
309
|
+
def __init__(self, *a, **kw):
|
310
|
+
self.proxy_address = None
|
311
|
+
SwiftHttpProtocol.__init__(self, *a, **kw)
|
312
|
+
|
313
|
+
def handle_error(self, connection_line):
|
314
|
+
connection_line = connection_line.decode('latin-1')
|
315
|
+
|
316
|
+
# No further processing will proceed on this connection under any
|
317
|
+
# circumstances. We always send the request into the superclass to
|
318
|
+
# handle any cleanup - this ensures that the request will not be
|
319
|
+
# processed.
|
320
|
+
self.rfile.close()
|
321
|
+
# We don't really have any confidence that an HTTP Error will be
|
322
|
+
# processable by the client as our transmission broken down between
|
323
|
+
# ourselves and our gateway proxy before processing the client
|
324
|
+
# protocol request. Hopefully the operator will know what to do!
|
325
|
+
msg = 'Invalid PROXY line %r' % connection_line
|
326
|
+
# Even assuming HTTP we don't even known what version of HTTP the
|
327
|
+
# client is sending? This entire endeavor seems questionable.
|
328
|
+
self.request_version = self.default_request_version
|
329
|
+
# appease http.server
|
330
|
+
self.command = 'PROXY'
|
331
|
+
self.send_error(400, msg)
|
332
|
+
|
333
|
+
def handle(self):
|
334
|
+
"""Handle multiple requests if necessary."""
|
335
|
+
# ensure the opening line for the connection is a valid PROXY protcol
|
336
|
+
# line; this is the only IO we do on this connection before any
|
337
|
+
# additional wrapping further pollutes the raw socket.
|
338
|
+
connection_line = self.rfile.readline(self.server.url_length_limit)
|
339
|
+
|
340
|
+
if not connection_line.startswith(b'PROXY '):
|
341
|
+
return self.handle_error(connection_line)
|
342
|
+
|
343
|
+
proxy_parts = connection_line.strip(b'\r\n').split(b' ')
|
344
|
+
if proxy_parts[1].startswith(b'UNKNOWN'):
|
345
|
+
# "UNKNOWN", in PROXY protocol version 1, means "not
|
346
|
+
# TCP4 or TCP6". This includes completely legitimate
|
347
|
+
# things like QUIC or Unix domain sockets. The PROXY
|
348
|
+
# protocol (section 2.1) states that the receiver
|
349
|
+
# (that's us) MUST ignore anything after "UNKNOWN" and
|
350
|
+
# before the CRLF, essentially discarding the first
|
351
|
+
# line.
|
352
|
+
pass
|
353
|
+
elif proxy_parts[1] in (b'TCP4', b'TCP6') and len(proxy_parts) == 6:
|
354
|
+
self.client_address = (
|
355
|
+
proxy_parts[2].decode('latin-1'),
|
356
|
+
proxy_parts[4].decode('latin-1'))
|
357
|
+
self.proxy_address = (
|
358
|
+
proxy_parts[3].decode('latin-1'),
|
359
|
+
proxy_parts[5].decode('latin-1'))
|
360
|
+
else:
|
361
|
+
self.handle_error(connection_line)
|
362
|
+
|
363
|
+
return SwiftHttpProtocol.handle(self)
|
364
|
+
|
365
|
+
def get_environ(self, *args, **kwargs):
|
366
|
+
environ = SwiftHttpProtocol.get_environ(self, *args, **kwargs)
|
367
|
+
if self.proxy_address:
|
368
|
+
environ['SERVER_ADDR'] = self.proxy_address[0]
|
369
|
+
environ['SERVER_PORT'] = self.proxy_address[1]
|
370
|
+
if self.proxy_address[1] == '443':
|
371
|
+
environ['wsgi.url_scheme'] = 'https'
|
372
|
+
environ['HTTPS'] = 'on'
|
373
|
+
return environ
|