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.
Files changed (208) hide show
  1. swift/__init__.py +29 -50
  2. swift/account/auditor.py +21 -118
  3. swift/account/backend.py +33 -28
  4. swift/account/reaper.py +37 -28
  5. swift/account/replicator.py +22 -0
  6. swift/account/server.py +60 -26
  7. swift/account/utils.py +28 -11
  8. swift-2.23.2.data/scripts/swift-account-audit → swift/cli/account_audit.py +23 -13
  9. swift-2.23.2.data/scripts/swift-config → swift/cli/config.py +2 -2
  10. swift/cli/container_deleter.py +5 -11
  11. swift-2.23.2.data/scripts/swift-dispersion-populate → swift/cli/dispersion_populate.py +8 -7
  12. swift/cli/dispersion_report.py +10 -9
  13. swift-2.23.2.data/scripts/swift-drive-audit → swift/cli/drive_audit.py +63 -21
  14. swift/cli/form_signature.py +3 -7
  15. swift-2.23.2.data/scripts/swift-get-nodes → swift/cli/get_nodes.py +8 -2
  16. swift/cli/info.py +183 -29
  17. swift/cli/manage_shard_ranges.py +708 -37
  18. swift-2.23.2.data/scripts/swift-oldies → swift/cli/oldies.py +25 -14
  19. swift-2.23.2.data/scripts/swift-orphans → swift/cli/orphans.py +7 -3
  20. swift/cli/recon.py +196 -67
  21. swift-2.23.2.data/scripts/swift-recon-cron → swift/cli/recon_cron.py +17 -20
  22. swift-2.23.2.data/scripts/swift-reconciler-enqueue → swift/cli/reconciler_enqueue.py +2 -3
  23. swift/cli/relinker.py +807 -126
  24. swift/cli/reload.py +135 -0
  25. swift/cli/ringbuilder.py +217 -20
  26. swift/cli/ringcomposer.py +0 -1
  27. swift/cli/shard-info.py +4 -3
  28. swift/common/base_storage_server.py +9 -20
  29. swift/common/bufferedhttp.py +48 -74
  30. swift/common/constraints.py +20 -15
  31. swift/common/container_sync_realms.py +9 -11
  32. swift/common/daemon.py +25 -8
  33. swift/common/db.py +198 -127
  34. swift/common/db_auditor.py +168 -0
  35. swift/common/db_replicator.py +95 -55
  36. swift/common/digest.py +141 -0
  37. swift/common/direct_client.py +144 -33
  38. swift/common/error_limiter.py +93 -0
  39. swift/common/exceptions.py +25 -1
  40. swift/common/header_key_dict.py +2 -9
  41. swift/common/http_protocol.py +373 -0
  42. swift/common/internal_client.py +129 -59
  43. swift/common/linkat.py +3 -4
  44. swift/common/manager.py +284 -67
  45. swift/common/memcached.py +396 -147
  46. swift/common/middleware/__init__.py +4 -0
  47. swift/common/middleware/account_quotas.py +211 -46
  48. swift/common/middleware/acl.py +3 -8
  49. swift/common/middleware/backend_ratelimit.py +230 -0
  50. swift/common/middleware/bulk.py +22 -34
  51. swift/common/middleware/catch_errors.py +1 -3
  52. swift/common/middleware/cname_lookup.py +6 -11
  53. swift/common/middleware/container_quotas.py +1 -1
  54. swift/common/middleware/container_sync.py +39 -17
  55. swift/common/middleware/copy.py +12 -0
  56. swift/common/middleware/crossdomain.py +22 -9
  57. swift/common/middleware/crypto/__init__.py +2 -1
  58. swift/common/middleware/crypto/crypto_utils.py +11 -15
  59. swift/common/middleware/crypto/decrypter.py +28 -11
  60. swift/common/middleware/crypto/encrypter.py +12 -17
  61. swift/common/middleware/crypto/keymaster.py +8 -15
  62. swift/common/middleware/crypto/kms_keymaster.py +2 -1
  63. swift/common/middleware/dlo.py +15 -11
  64. swift/common/middleware/domain_remap.py +5 -4
  65. swift/common/middleware/etag_quoter.py +128 -0
  66. swift/common/middleware/formpost.py +73 -70
  67. swift/common/middleware/gatekeeper.py +8 -1
  68. swift/common/middleware/keystoneauth.py +33 -3
  69. swift/common/middleware/list_endpoints.py +4 -4
  70. swift/common/middleware/listing_formats.py +85 -49
  71. swift/common/middleware/memcache.py +4 -81
  72. swift/common/middleware/name_check.py +3 -2
  73. swift/common/middleware/proxy_logging.py +160 -92
  74. swift/common/middleware/ratelimit.py +17 -10
  75. swift/common/middleware/read_only.py +6 -4
  76. swift/common/middleware/recon.py +59 -22
  77. swift/common/middleware/s3api/acl_handlers.py +25 -3
  78. swift/common/middleware/s3api/acl_utils.py +6 -1
  79. swift/common/middleware/s3api/controllers/__init__.py +6 -0
  80. swift/common/middleware/s3api/controllers/acl.py +3 -2
  81. swift/common/middleware/s3api/controllers/bucket.py +242 -137
  82. swift/common/middleware/s3api/controllers/logging.py +2 -2
  83. swift/common/middleware/s3api/controllers/multi_delete.py +43 -20
  84. swift/common/middleware/s3api/controllers/multi_upload.py +219 -133
  85. swift/common/middleware/s3api/controllers/obj.py +112 -8
  86. swift/common/middleware/s3api/controllers/object_lock.py +44 -0
  87. swift/common/middleware/s3api/controllers/s3_acl.py +2 -2
  88. swift/common/middleware/s3api/controllers/tagging.py +57 -0
  89. swift/common/middleware/s3api/controllers/versioning.py +36 -7
  90. swift/common/middleware/s3api/etree.py +22 -9
  91. swift/common/middleware/s3api/exception.py +0 -4
  92. swift/common/middleware/s3api/s3api.py +113 -41
  93. swift/common/middleware/s3api/s3request.py +384 -218
  94. swift/common/middleware/s3api/s3response.py +126 -23
  95. swift/common/middleware/s3api/s3token.py +16 -17
  96. swift/common/middleware/s3api/schema/delete.rng +1 -1
  97. swift/common/middleware/s3api/subresource.py +7 -10
  98. swift/common/middleware/s3api/utils.py +27 -10
  99. swift/common/middleware/slo.py +665 -358
  100. swift/common/middleware/staticweb.py +64 -37
  101. swift/common/middleware/symlink.py +52 -19
  102. swift/common/middleware/tempauth.py +76 -58
  103. swift/common/middleware/tempurl.py +192 -174
  104. swift/common/middleware/versioned_writes/__init__.py +51 -0
  105. swift/common/middleware/{versioned_writes.py → versioned_writes/legacy.py} +27 -26
  106. swift/common/middleware/versioned_writes/object_versioning.py +1482 -0
  107. swift/common/middleware/x_profile/exceptions.py +1 -4
  108. swift/common/middleware/x_profile/html_viewer.py +18 -19
  109. swift/common/middleware/x_profile/profile_model.py +1 -2
  110. swift/common/middleware/xprofile.py +10 -10
  111. swift-2.23.2.data/scripts/swift-container-server → swift/common/recon.py +13 -8
  112. swift/common/registry.py +147 -0
  113. swift/common/request_helpers.py +324 -57
  114. swift/common/ring/builder.py +67 -25
  115. swift/common/ring/composite_builder.py +1 -1
  116. swift/common/ring/ring.py +177 -51
  117. swift/common/ring/utils.py +1 -1
  118. swift/common/splice.py +10 -6
  119. swift/common/statsd_client.py +205 -0
  120. swift/common/storage_policy.py +49 -44
  121. swift/common/swob.py +86 -102
  122. swift/common/{utils.py → utils/__init__.py} +2191 -2762
  123. swift/common/utils/base.py +131 -0
  124. swift/common/utils/config.py +433 -0
  125. swift/common/utils/ipaddrs.py +256 -0
  126. swift/common/utils/libc.py +345 -0
  127. swift/common/utils/logs.py +859 -0
  128. swift/common/utils/timestamp.py +412 -0
  129. swift/common/wsgi.py +555 -536
  130. swift/container/auditor.py +14 -100
  131. swift/container/backend.py +552 -227
  132. swift/container/reconciler.py +126 -37
  133. swift/container/replicator.py +96 -22
  134. swift/container/server.py +397 -176
  135. swift/container/sharder.py +1580 -639
  136. swift/container/sync.py +94 -88
  137. swift/container/updater.py +53 -32
  138. swift/obj/auditor.py +153 -35
  139. swift/obj/diskfile.py +466 -217
  140. swift/obj/expirer.py +406 -124
  141. swift/obj/mem_diskfile.py +7 -4
  142. swift/obj/mem_server.py +1 -0
  143. swift/obj/reconstructor.py +523 -262
  144. swift/obj/replicator.py +249 -188
  145. swift/obj/server.py +213 -122
  146. swift/obj/ssync_receiver.py +145 -85
  147. swift/obj/ssync_sender.py +113 -54
  148. swift/obj/updater.py +653 -139
  149. swift/obj/watchers/__init__.py +0 -0
  150. swift/obj/watchers/dark_data.py +213 -0
  151. swift/proxy/controllers/account.py +11 -11
  152. swift/proxy/controllers/base.py +848 -604
  153. swift/proxy/controllers/container.py +452 -86
  154. swift/proxy/controllers/info.py +3 -2
  155. swift/proxy/controllers/obj.py +1009 -490
  156. swift/proxy/server.py +185 -112
  157. swift-2.35.0.dist-info/AUTHORS +501 -0
  158. swift-2.35.0.dist-info/LICENSE +202 -0
  159. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/METADATA +52 -61
  160. swift-2.35.0.dist-info/RECORD +201 -0
  161. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/WHEEL +1 -1
  162. {swift-2.23.2.dist-info → swift-2.35.0.dist-info}/entry_points.txt +43 -0
  163. swift-2.35.0.dist-info/pbr.json +1 -0
  164. swift/locale/de/LC_MESSAGES/swift.po +0 -1216
  165. swift/locale/en_GB/LC_MESSAGES/swift.po +0 -1207
  166. swift/locale/es/LC_MESSAGES/swift.po +0 -1085
  167. swift/locale/fr/LC_MESSAGES/swift.po +0 -909
  168. swift/locale/it/LC_MESSAGES/swift.po +0 -894
  169. swift/locale/ja/LC_MESSAGES/swift.po +0 -965
  170. swift/locale/ko_KR/LC_MESSAGES/swift.po +0 -964
  171. swift/locale/pt_BR/LC_MESSAGES/swift.po +0 -881
  172. swift/locale/ru/LC_MESSAGES/swift.po +0 -891
  173. swift/locale/tr_TR/LC_MESSAGES/swift.po +0 -832
  174. swift/locale/zh_CN/LC_MESSAGES/swift.po +0 -833
  175. swift/locale/zh_TW/LC_MESSAGES/swift.po +0 -838
  176. swift-2.23.2.data/scripts/swift-account-auditor +0 -23
  177. swift-2.23.2.data/scripts/swift-account-info +0 -51
  178. swift-2.23.2.data/scripts/swift-account-reaper +0 -23
  179. swift-2.23.2.data/scripts/swift-account-replicator +0 -34
  180. swift-2.23.2.data/scripts/swift-account-server +0 -23
  181. swift-2.23.2.data/scripts/swift-container-auditor +0 -23
  182. swift-2.23.2.data/scripts/swift-container-info +0 -51
  183. swift-2.23.2.data/scripts/swift-container-reconciler +0 -21
  184. swift-2.23.2.data/scripts/swift-container-replicator +0 -34
  185. swift-2.23.2.data/scripts/swift-container-sharder +0 -33
  186. swift-2.23.2.data/scripts/swift-container-sync +0 -23
  187. swift-2.23.2.data/scripts/swift-container-updater +0 -23
  188. swift-2.23.2.data/scripts/swift-dispersion-report +0 -24
  189. swift-2.23.2.data/scripts/swift-form-signature +0 -20
  190. swift-2.23.2.data/scripts/swift-init +0 -119
  191. swift-2.23.2.data/scripts/swift-object-auditor +0 -29
  192. swift-2.23.2.data/scripts/swift-object-expirer +0 -33
  193. swift-2.23.2.data/scripts/swift-object-info +0 -60
  194. swift-2.23.2.data/scripts/swift-object-reconstructor +0 -33
  195. swift-2.23.2.data/scripts/swift-object-relinker +0 -41
  196. swift-2.23.2.data/scripts/swift-object-replicator +0 -37
  197. swift-2.23.2.data/scripts/swift-object-server +0 -27
  198. swift-2.23.2.data/scripts/swift-object-updater +0 -23
  199. swift-2.23.2.data/scripts/swift-proxy-server +0 -23
  200. swift-2.23.2.data/scripts/swift-recon +0 -24
  201. swift-2.23.2.data/scripts/swift-ring-builder +0 -24
  202. swift-2.23.2.data/scripts/swift-ring-builder-analyzer +0 -22
  203. swift-2.23.2.data/scripts/swift-ring-composer +0 -22
  204. swift-2.23.2.dist-info/DESCRIPTION.rst +0 -166
  205. swift-2.23.2.dist-info/RECORD +0 -220
  206. swift-2.23.2.dist-info/metadata.json +0 -1
  207. swift-2.23.2.dist-info/pbr.json +0 -1
  208. {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