)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1495c7c92d7e3d92a624757597df9d65af5caf12","unresolved":true,"context_lines":[{"line_number":18,"context_line":""},{"line_number":19,"context_line":"Take a cue from eventlet\u0027s Timeout and create a new s3api-specific"},{"line_number":20,"context_line":"BaseException (which does *not* inherit from Exception) to improve"},{"line_number":21,"context_line":"our ability to cut through all the layers."},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"Change-Id: I9924ff3b8d7d246631fe61b916823e028e2c01f2"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":7,"id":"13ed8fc9_ac3edfb1","line":21,"updated":"2024-04-02 22:21:58.000000000","message":"yikes kind of drastic; I guess as long as WE always catch it it\u0027s ok.","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"671906e94dff391767e949fa1be3425c1040b012","unresolved":true,"context_lines":[{"line_number":18,"context_line":""},{"line_number":19,"context_line":"Take a cue from eventlet\u0027s Timeout and create a new s3api-specific"},{"line_number":20,"context_line":"BaseException (which does *not* inherit from Exception) to improve"},{"line_number":21,"context_line":"our ability to cut through all the layers."},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"Change-Id: I9924ff3b8d7d246631fe61b916823e028e2c01f2"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":7,"id":"8233cf72_f9355883","line":21,"in_reply_to":"13ed8fc9_ac3edfb1","updated":"2024-04-03 20:03:16.000000000","message":"FWIW, I thought this up a bit ago when [one of the new eventlet maintainers expressed surprise at `Timeout` inheriting from `BaseException`](https://github.com/eventlet/eventlet/pull/911#discussion_r1477988429) and I explained how it was key to a lot of eventlet\u0027s premise of being able to write (and use!) single-threaded imperative code without thinking much about how eventlet\u0027s monkey-patched everything out from under you.\n\nIDK, maybe it\u0027s too clever by half -- but it seems like a more-or-less reasonable way to bypass third-party code that\u0027s between where we can detect the problem and where we can fully handle it.\n\nIf we\u0027re not sold on it, though, let\u0027s address it *now*, \u0027cause the further we get out in the chain, the more I\u0027m going to need to be able to raise some kind of input error in `read` that will get handled in `s3api`.","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"a57fc01c125294e9dc04ec1103d84cae77a86c83","unresolved":false,"context_lines":[{"line_number":18,"context_line":""},{"line_number":19,"context_line":"Take a cue from eventlet\u0027s Timeout and create a new s3api-specific"},{"line_number":20,"context_line":"BaseException (which does *not* inherit from Exception) to improve"},{"line_number":21,"context_line":"our ability to cut through all the layers."},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"Change-Id: I9924ff3b8d7d246631fe61b916823e028e2c01f2"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":7,"id":"5178e863_9f0a1f0e","line":21,"in_reply_to":"8233cf72_f9355883","updated":"2024-05-07 18:54:55.000000000","message":"Acknowledged","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"}],"/PATCHSET_LEVEL":[{"author":{"_account_id":597,"name":"Pete Zaitcev","email":"zaitcev@kotori.zaitcev.us","username":"zaitcev"},"change_message_id":"943335479bc3b09e7138b08de3a70e00999f6d2a","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"c889a906_8b94541d","updated":"2024-02-15 03:10:42.000000000","message":"I would rather punish people who write \"except Exception:\", but this works too.","commit_id":"1c65512f8cadd8db04a80929590095618f979d1b"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"f82ed1c39e0a87cea3d3462462af1f917dedc9aa","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"36e2b14f_56c08151","updated":"2024-02-15 21:06:46.000000000","message":"OK, a bit of broader context on this, since it seems like both Matt and Pete are on board:\n\nFor the `aws-chunked` patches ([836755](https://review.opendev.org/c/openstack/swift/+/836755) and [908953](https://review.opendev.org/c/openstack/swift/+/908953)) I\u0027m thinking about pushing for a general `InputStreamError` (which `InputChecksumMismatch` would then subclass) with additional subclasses for protocol errors and signature mismatches, and using those instead of the `swob.HTTPForbidden`s that are in the `wsgi.input` read path currently.\n\nI\u0027ll get this patch cleaned up regardless, but if we like that idea, I\u0027ll work on getting those stacked up on top of this and getting rid of the `swob.HTTPForbidden`s.","commit_id":"1c65512f8cadd8db04a80929590095618f979d1b"},{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"02e2aac18562dcf6d2f55936cc15946bb3cf4a36","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"3731863b_9c8c0fac","updated":"2024-02-15 02:30:30.000000000","message":"Yeah I like the idea. Anything catcing Exception between s3api and back, which let\u0027s face it is not completely unexpected, could easily catch and break things.\n\nOnly worry is if we stat using base exceptions like this more we can get into the same boat, but I guess we\u0027d never use an `except BaseException` so probably moot 😊 And so long as we\u0027re always explicit we should be ok.\n\nObviously we need to fix up the pep8 things.","commit_id":"1c65512f8cadd8db04a80929590095618f979d1b"},{"author":{"_account_id":597,"name":"Pete Zaitcev","email":"zaitcev@kotori.zaitcev.us","username":"zaitcev"},"change_message_id":"a100b9a73a8bdc40f759d52a3c6515916479218d","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"c4c5cb0c_0a1445d8","updated":"2024-02-28 01:36:33.000000000","message":"Appears to be a rename and fixes for Matt\u0027s comments.","commit_id":"47958e42185bc44ce97edfaad187729295fe11bf"},{"author":{"_account_id":597,"name":"Pete Zaitcev","email":"zaitcev@kotori.zaitcev.us","username":"zaitcev"},"change_message_id":"247f9f94c1a9939248a208703d4189bc91676191","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":4,"id":"8613c09e_354c9f85","updated":"2024-03-06 21:21:38.000000000","message":"Actually n/m, Tim explained where I went wrong.","commit_id":"ef34027e1817aaae7f1c8d4eab1c2314a7c20cd9"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"38b595ddafda48c5f374fa5d2b8a9f77c31135ac","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":4,"id":"caa83832_c88a74e2","updated":"2024-03-01 21:25:55.000000000","message":"recheck\n\npy2 failures were fixed by https://review.opendev.org/c/openstack/swift/+/910753","commit_id":"ef34027e1817aaae7f1c8d4eab1c2314a7c20cd9"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"a57fc01c125294e9dc04ec1103d84cae77a86c83","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":7,"id":"e4f019d3_25fef8a5","updated":"2024-05-07 18:54:55.000000000","message":"\u003e and raised to where exactly?\n\nI think this is *the* question with this approach.  I assume the existing tests make some effort already to ensure that anywhere we might raies this error we\u0027re catching it and turning it into a valid wsgi response object.\n\nBut because using BaseException is such an unexpected way to achive a goto like symantic I think it\u0027s reasonable as an experiment to understand what the failure mode might look like *if* this pattern failed us.  Maybe eventlet.wsgi alrady has to catch *all* BaseException because of Timeout and it will happily close the connection and move on - if the server were to *die* however I think it might be more reasonable to invest futher.\n\nIt would be nice if this included a functional test so that I could quickly remove the error translation and see what happens if I just re-raise the BaseException out of s3api.","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1495c7c92d7e3d92a624757597df9d65af5caf12","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":7,"id":"a087b286_884ebad9","updated":"2024-04-02 22:21:58.000000000","message":"I\u0027d probably feel a good bit more comfortable if there was an `except SigMismatch` in common.mw.s3api.S3ApiMiddleware.__call__ \"just in case\" - but I\u0027m probably not appreciating thow bad the existing pattern was; a wsgi.input read that can raise an HTTPException - crazy.\n\nBut our solution is to have it raise something even CRAZIER so that we can be sure no one will catch it?  Is that actually what we want - to bypass exception handling?\n\nMaybe we could just set a *flag* and then raise something like an IOError or whatever people calling read on wsgi.input might already be expected to handle correctly - so we can be *sure* that no one could *possibly* actually store the object (we never gave them the last chunk!) - but then on the way out to returning a response; regardless of the http status code/response/error, if we set the \"hash validation failed\" flag, we re-write the response as 422?","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1307a67128dfd5bf69587e343c57ff5e66526bda","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":7,"id":"e17ce7c6_198517e2","updated":"2024-05-15 23:04:42.000000000","message":"Tim, I think I might finally be able to validate this functionally.  I updated my script:\n\nhttps://gist.github.com/clayg/2bf9dd63d55c1864a2aa8bbee062bc97\n\nI\u0027m now running it like:\n\n    vagrant@saio:~$ python /vagrant/.scratch/checksum_s3_upload.py s3test test --break-after-seek-to-begining 2\n    \nIt\u0027s not sending the Content-MD5 anymore:\n\n\tDEBUG:botocore.auth:CanonicalRequest:\n\tPUT\n\t/s3test/test\n\n\thost:saio:8080\n\tx-amz-content-sha256:bb9f8df61474d25e71fa00722318cd387396ca1736605e1248821cc0de3d3af8\n\tx-amz-date:20240515T225728Z\n\n\thost;x-amz-content-sha256;x-amz-date\n\tbb9f8df61474d25e71fa00722318cd387396ca1736605e1248821cc0de3d3af8\n\tDEBUG:botocore.auth:StringToSign:\n\tAWS4-HMAC-SHA256\n\t20240515T225728Z\n\t20240515/us-east-1/s3/aws4_request\n\td3bf260feec29830d21878d13eeee5fbcf5ae194a319aa958adb372edf776ece\n\tDEBUG:botocore.auth:Signature:\n\t116bd2e05f12784f12313024dd7c79a30182ba891803a9ab2049e0a80cca3a8f\n\tDEBUG:botocore.hooks:Event request-created.s3.PutObject: calling handler \u003cfunction signal_transferring at 0x7f5faef90ee0\u003e\n\tDEBUG:botocore.hooks:Event request-created.s3.PutObject: calling handler \u003cfunction add_retry_headers at 0x7f5fafb77a30\u003e\n\tDEBUG:botocore.endpoint:Sending http request: \u003cAWSPreparedRequest stream_output\u003dFalse, method\u003dPUT, url\u003dhttp://saio:8080/s3test/test, headers\u003d{\u0027User-Agent\u0027: b\u0027Boto3/1.29.5 md/Botocore#1.32.5 ua/2.0 os/linux#5.15.0-105-generic md/arch#x86_64 lang/python#3.10.12 md/pyimpl#CPython cfg/retry-mode#legacy Botocore/1.32.5\u0027, \u0027Expect\u0027: b\u0027100-continue\u0027, \u0027X-Amz-Date\u0027: b\u002720240515T225728Z\u0027, \u0027X-Amz-Content-SHA256\u0027: b\u0027bb9f8df61474d25e71fa00722318cd387396ca1736605e1248821cc0de3d3af8\u0027, \u0027Authorization\u0027: b\u0027AWS4-HMAC-SHA256 Credential\u003dtest:tester/20240515/us-east-1/s3/aws4_request, SignedHeaders\u003dhost;x-amz-content-sha256;x-amz-date, Signature\u003d116bd2e05f12784f12313024dd7c79a30182ba891803a9ab2049e0a80cca3a8f\u0027, \u0027amz-sdk-invocation-id\u0027: b\u00277c581f3c-7b88-4dfc-9793-1d2f220ff558\u0027, \u0027amz-sdk-request\u0027: b\u0027attempt\u003d1\u0027, \u0027Content-Length\u0027: \u00274194304\u0027}\u003e\n\nBut on master the error I\u0027m getting (on attempt\u003d1) still isn\u0027t obviously the sha256 mismatch?\n\n\tDEBUG:urllib3.connectionpool:http://saio:8080 \"PUT /s3test/test HTTP/1.1\" 400 None\n\tDEBUG:botocore.parsers:Response headers: {\u0027Content-Type\u0027: \u0027application/xml\u0027, \u0027x-amz-id-2\u0027: \u0027txfb1103833ea3488386843-0066453dd8\u0027, \u0027x-amz-request-id\u0027: \u0027txfb1103833ea3488386843-0066453dd8\u0027, \u0027X-Trans-Id\u0027: \u0027txfb1103833ea3488386843-0066453dd8\u0027, \u0027X-Openstack-Request-Id\u0027: \u0027txfb1103833ea3488386843-0066453dd8\u0027, \u0027Date\u0027: \u0027Wed, 15 May 2024 22:57:28 GMT\u0027, \u0027Transfer-Encoding\u0027: \u0027chunked\u0027}\n\tDEBUG:botocore.parsers:Response body:\n\tb\"\u003c?xml version\u003d\u00271.0\u0027 encoding\u003d\u0027UTF-8\u0027?\u003e\\n\u003cError\u003e\u003cCode\u003eBadDigest\u003c/Code\u003e\u003cMessage\u003eThe Content-MD5 you specified did not match what we received.\u003c/Message\u003e\u003cRequestId\u003etxfb1103833ea3488386843-0066453dd8\u003c/RequestId\u003e\u003c/Error\u003e\"\n\nBut after I restart my proxies I see:\n\n\tDEBUG:urllib3.connectionpool:http://saio:8080 \"PUT /s3test/test HTTP/1.1\" 400 None\n\tDEBUG:botocore.parsers:Response headers: {\u0027Content-Type\u0027: \u0027application/xml\u0027, \u0027x-amz-id-2\u0027: \u0027tx063c40738b024b31881db-0066453f2f\u0027, \u0027x-amz-request-id\u0027: \u0027tx063c40738b024b31881db-0066453f2f\u0027, \u0027X-Trans-Id\u0027: \u0027tx063c40738b024b31881db-0066453f2f\u0027, \u0027X-Openstack-Request-Id\u0027: \u0027tx063c40738b024b31881db-0066453f2f\u0027, \u0027Date\u0027: \u0027Wed, 15 May 2024 23:03:11 GMT\u0027, \u0027Transfer-Encoding\u0027: \u0027chunked\u0027}\n\tDEBUG:botocore.parsers:Response body:\n\tb\"\u003c?xml version\u003d\u00271.0\u0027 encoding\u003d\u0027UTF-8\u0027?\u003e\\n\u003cError\u003e\u003cCode\u003eBadDigest\u003c/Code\u003e\u003cMessage\u003eThe X-Amz-Content-SHA56 you specified did not match what we received.\u003c/Message\u003e\u003cRequestId\u003etx063c40738b024b31881db-0066453f2f\u003c/RequestId\u003e\u003c/Error\u003e\"\n\nso that\u0027s... gooder, right?!","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"2436664b3df9ab5b4672902bfc390506d1212e6f","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":7,"id":"9c683d7d_4c25d5a0","updated":"2024-05-08 15:05:13.000000000","message":"on master today, using a script like:\n\nhttps://gist.github.com/clayg/2bf9dd63d55c1864a2aa8bbee062bc97\n\nafter a few retries all returning 400 I get an exception from boto like:\n\n```\nArguments: (ClientError(\u0027An error occurred (BadDigest) when calling the PutObject operation (reached max retries: 4): The Content-MD5 you specified did not match what we received.\u0027),)\n```\n\n... which makes sense given I artificially \"broke\" the content coming out of my fileobj to make it not match the client headers (which I guess don\u0027t get re-calculated)\n\nwith this patch that works exactly the same!\n\nbut i\u0027m seeing the *object* servers rejecting the uploads, and the proxy server is just passing the response through - so I\u0027m not sure I\"m actually hitting/triggering the sha-256 checksum error - maybe more just normaly md5/etag checksum error?\n\nMaybe if I turned on encryption?","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1334069a426bbd7ff30cddf335568305932ae937","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":7,"id":"d862f1ca_3643c89f","updated":"2024-05-16 16:17:19.000000000","message":"so when I \"broke\" this code to \"leak\" one of these new BaseExceptions my client recieved a nice clean 500 - thanks catch_errors!\n\nI think using a specific non-http exception is an improvement!\n\nIt\u0027s hard to think through all the ways an intermediary middleware might have screwed this up.  I think they would be likely to catch an HTTPException, but perhaps unlikely (?) to mis-translate 422?  If we\u0027d raised a non-http exception (any *more specific* exception?) there\u0027s a \"reasonable chance\" (?) it might accidently get caught with `except Exception:` (where maybe an HTTPException previously would be re-raised?) e.g.\n\n```\nexcept HTTPException:\n    raise\nexcept Exception:\n    raise HTTPServerError()\n```\n\nSo maybe BaseException is \"more likely\" to bubble out?  Unless they were using a base `except:` - in which case we might still get a 500 instead of 400/422.\n\nBut maybe a bare except is \"least likely\" (and mostly only in the domain of catch_errors?).  Anyway, I think this is an improvement over what we have?\n\nI think some additional testing would be a nice-to-have, but since this is an existing feature the lack of tests isn\u0027t a blocker for me - but I\u0027d like to see them added:\n\n919881: test: more test for s3api v4 checksum | https://review.opendev.org/c/openstack/swift/+/919881\n\nI\u0027m less sure what to do about automated functional testing, since most of the signature stuff is handled by the sdk it\u0027s not *that* easy to make requests that get this stuff wrong (short of *actual* bit-flips in transmission!).  Maybe this script will provide inspiration for future work if we need it:\n\nhttps://gist.github.com/clayg/2bf9dd63d55c1864a2aa8bbee062bc97","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1334069a426bbd7ff30cddf335568305932ae937","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":7,"id":"9e6e8926_9f53d611","in_reply_to":"010c0cfd_7ca5ba55","updated":"2024-05-16 16:17:19.000000000","message":"this is *fantastic*!!! CatchErrors has a *bare except* - we could freaking raise a SystemExit and we\u0027ll still return a 500.  Thanks for confirming!\n\nWhen I \"broke\" this code to \"leak\" one of these BaseExceptions it got caught in the exact same spot on my client recieved a clean 500:\n\n        DEBUG:urllib3.connectionpool:http://saio:8080 \"PUT /s3test/test HTTP/1.1\" 500 17\n        DEBUG:botocore.parsers:Response headers: {\u0027Content-Type\u0027: \u0027text/plain\u0027, \u0027Content-Length\u0027: \u002717\u0027, \u0027X-Trans-Id\u0027: \u0027tx78937e5902d144c9850c5-0066461c7a\u0027, \u0027X-Openstack-Request-Id\u0027: \u0027tx78937e5902d144c9850c5-0066461c7a\u0027, \u0027Date\u0027: \u0027Thu, 16 May 2024 14:47:23 GMT\u0027}\n        DEBUG:botocore.parsers:Response body:\n        b\u0027An error occurred\u0027\n        DEBUG:botocore.parsers:Exception caught when parsing error response body:\n        Traceback (most recent call last):\n          File \"/usr/local/lib/python3.10/dist-packages/botocore/parsers.py\", line 505, in _parse_xml_string_to_dom\n            parser.feed(xml_string)\n        xml.etree.ElementTree.ParseError: syntax error: line 1, column 0\n\n        During handling of the above exception, another exception occurred:\n\n        Traceback (most recent call last):\n          File \"/usr/local/lib/python3.10/dist-packages/botocore/parsers.py\", line 1065, in _do_error_parse\n            return self._parse_error_from_body(response)\n          File \"/usr/local/lib/python3.10/dist-packages/botocore/parsers.py\", line 1089, in _parse_error_from_body\n            root \u003d self._parse_xml_string_to_dom(xml_contents)\n          File \"/usr/local/lib/python3.10/dist-packages/botocore/parsers.py\", line 508, in _parse_xml_string_to_dom\n            raise ResponseParserError(\n        botocore.parsers.ResponseParserError: Unable to parse response (syntax error: line 1, column 0), invalid XML received. Further retries may succeed:\n        b\u0027An error occurred\u0027\n\nproxy logs loooked similar to your test:\n\n        May 16 14:43:01 saio proxy-server: Error: An error occurred: \n        Traceback (most recent call last):\n          File \"/vagrant/swift/swift/common/middleware/catch_errors.py\", line 75, in handle_request\n            resp \u003d self._app_call(env)\n          File \"/vagrant/swift/swift/common/wsgi.py\", line 1134, in _app_call\n            resp \u003d self.app(env, self._start_response)\n          File \"/vagrant/swift/swift/common/middleware/gatekeeper.py\", line 129, in __call__\n            return self.app(env, gatekeeper_response)\n          File \"/vagrant/swift/swift/common/middleware/healthcheck.py\", line 52, in __call__\n            return self.app(env, start_response)\n          File \"/vagrant/swift/swift/common/middleware/proxy_logging.py\", line 470, in __call__\n            iterable \u003d self.app(env, my_start_response)\n          File \"/vagrant/swift/swift/common/middleware/domain_remap.py\", line 199, in __call__\n            return self.app(env, start_response)\n          File \"/vagrant/swift/swift/common/middleware/memcache.py\", line 32, in __call__\n            return self.app(env, start_response)\n          File \"/vagrant/swift/swift/common/middleware/listing_formats.py\", line 160, in __call__\n            return self.app(env, start_response)\n          File \"/vagrant/.scratch/swift-nvratelimit/nvratelimit.py\", line 941, in __call__\n            app_iter \u003d reiterate(self.app(env, nvratelimit_start_response))\n          File \"/vagrant/swift/swift/common/middleware/etag_quoter.py\", line 65, in __call__\n            return self.app(env, start_response)\n          File \"/vagrant/swift/swift/common/swob.py\", line 1580, in _wsgify\n            return func(*new_args)(env, start_response)\n          File \"/vagrant/swift/swift/common/swob.py\", line 1580, in _wsgify\n            return func(*new_args)(env, start_response)\n          File \"/vagrant/swift/swift/common/middleware/tempurl.py\", line 554, in __call__\n            return self.app(env, start_response)\n          File \"/vagrant/swift/swift/common/middleware/s3api/s3api.py\", line 349, in __call__\n            resp \u003d self.handle_request(req)\n          File \"/vagrant/swift/swift/common/middleware/s3api/s3api.py\", line 389, in handle_request\n            res \u003d handler(req)\n          File \"/vagrant/swift/swift/common/middleware/s3api/controllers/obj.py\", line 176, in PUT\n            resp \u003d req.get_response(self.app)\n          File \"/vagrant/swift/swift/common/middleware/s3api/s3request.py\", line 1701, in get_response\n            return self.get_acl_response(app, method, container, obj,\n          File \"/vagrant/swift/swift/common/middleware/s3api/s3request.py\", line 1677, in get_acl_response\n            resp \u003d self._get_response(\n          File \"/vagrant/swift/swift/common/middleware/s3api/s3request.py\", line 1419, in _get_response\n            sw_resp \u003d sw_req.get_response(app)\n          File \"/vagrant/swift/swift/common/swob.py\", line 1165, in get_response\n            status, headers, app_iter \u003d self.call_application(application)\n          File \"/vagrant/swift/swift/common/swob.py\", line 1149, in call_application\n            app_iter \u003d application(self.environ, start_response)\n          File \"/vagrant/swift/swift/common/middleware/s3api/s3api.py\", line 182, in __call__\n            return self.app(env, start_response)\n          File \"/vagrant/swift/swift/common/middleware/tempauth.py\", line 288, in __call__\n            return self.app(env, start_response)\n          File \"/vagrant/swift/swift/common/middleware/copy.py\", line 253, in __call__\n            return self.app(env, start_response)\n          File \"/vagrant/swift/swift/common/middleware/ratelimit.py\", line 322, in __call__\n            return self.app(env, start_response)\n          File \"/vagrant/swift/swift/common/middleware/slo.py\", line 1919, in __call__\n            return self.app(env, start_response)\n          File \"/vagrant/swift/swift/common/middleware/dlo.py\", line 447, in __call__\n            return self.app(env, start_response)\n          File \"/vagrant/swift/swift/common/middleware/versioned_writes/legacy.py\", line 866, in __call__\n            return self.object_request(\n          File \"/vagrant/swift/swift/common/middleware/versioned_writes/object_versioning.py\", line 1480, in __call__\n            return self.object_request(\n          File \"/vagrant/swift/swift/common/middleware/symlink.py\", line 753, in __call__\n            return context.handle_object(req, start_response)\n          File \"/vagrant/swift/swift/common/middleware/symlink.py\", line 713, in handle_object\n            resp \u003d self._app_call(req.environ)\n          File \"/vagrant/swift/swift/common/wsgi.py\", line 1134, in _app_call\n            resp \u003d self.app(env, self._start_response)\n          File \"/vagrant/swift/swift/common/middleware/crypto/keymaster.py\", line 316, in __call__\n            return km_context.handle_request(req, start_response)\n          File \"/vagrant/swift/swift/common/middleware/crypto/keymaster.py\", line 200, in handle_request\n            resp \u003d self._app_call(req.environ)\n          File \"/vagrant/swift/swift/common/wsgi.py\", line 1134, in _app_call\n            resp \u003d self.app(env, self._start_response)\n          File \"/vagrant/swift/swift/common/middleware/crypto/decrypter.py\", line 467, in __call__\n            return self.app(env, start_response)\n          File \"/vagrant/swift/swift/common/middleware/crypto/encrypter.py\", line 369, in __call__\n            return self.app(env, start_response)\n          File \"/vagrant/swift/swift/common/middleware/proxy_logging.py\", line 470, in __call__\n            iterable \u003d self.app(env, my_start_response)\n          File \"/vagrant/swift/swift/proxy/server.py\", line 489, in __call__\n            return self.handle_request(req)(env, start_response)\n          File \"/vagrant/swift/swift/proxy/server.py\", line 598, in handle_request\n            return handler(req)\n          File \"/vagrant/swift/swift/proxy/controllers/base.py\", line 375, in wrapped\n            return func(*a, **kw)\n          File \"/vagrant/swift/swift/proxy/controllers/obj.py\", line 899, in PUT\n            resp \u003d self._store_object(\n          File \"/vagrant/swift/swift/proxy/controllers/obj.py\", line 1094, in _store_object\n            self._transfer_data(req, data_source, putters, nodes)\n          File \"/vagrant/swift/swift/proxy/controllers/obj.py\", line 1020, in _transfer_data\n            chunk \u003d next(data_source)\n          File \"/vagrant/swift/swift/common/utils/__init__.py\", line 3709, in __next__\n            return self._get_next_item()\n          File \"/vagrant/swift/swift/common/utils/__init__.py\", line 6525, in _get_next_item\n            return super(CooperativeIterator, self)._get_next_item()\n          File \"/vagrant/swift/swift/common/utils/__init__.py\", line 3705, in _get_next_item\n            return next(self.wrapped_iter)\n          File \"/vagrant/swift/swift/proxy/controllers/obj.py\", line 882, in reader\n            return req.environ[\u0027wsgi.input\u0027].read(\n          File \"/vagrant/swift/swift/common/utils/__init__.py\", line 3778, in read\n            chunk \u003d self.wsgi_input.read(*args, **kwargs)\n          File \"/vagrant/swift/swift/common/middleware/s3api/s3request.py\", line 155, in read\n            raise S3InputSHA256Mismatch(\n        swift.common.middleware.s3api.s3request.S3InputSHA256Mismatch: The X-Amz-Content-SHA56 you specified did not match what we received. (txn: txe9c57ad69978446e879a3-0066461b75) (client_ip: 127.0.0.1)\n\nWTG CatchErrors!\n\nhttps://github.com/NVIDIA/swift/blob/master/test/unit/common/middleware/test_catch_errors.py#L23","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"671906e94dff391767e949fa1be3425c1040b012","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":7,"id":"bfa9db32_55ca142d","in_reply_to":"a087b286_884ebad9","updated":"2024-04-03 20:03:16.000000000","message":"\u003e Maybe we could just set a *flag*\n\nI thought you *didn\u0027t* much care for me stuffing extra crap in the WSGI env 😄\n\nBiggest trouble I see is ensuring that the env that the input sees (and sets its flag in) is the same env that we\u0027re looking at later; we certainly do things that make that not-obvious: https://github.com/openstack/swift/blob/2.33.0/swift/common/middleware/s3api/s3request.py#L1094 -- and it further complicates (or even completely prevents) anyone downstream of us being able to detect that this is what happened!\n\n\u003e raise something like an IOError or whatever people calling read on wsgi.input might already be expected to handle correctly\n\nWhat would \"handle correctly\" even mean in that context? I guess... make sure things get closed down properly and return some kind of 4xx? Which one, why? And *then* guess what -- it doesn\u0027t matter anyway!","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"85c8077e629c81ec01cb4fc38d82866b414d9f6e","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":7,"id":"010c0cfd_7ca5ba55","in_reply_to":"e4f019d3_25fef8a5","updated":"2024-05-07 20:56:04.000000000","message":"\u003e what happens if I just re-raise the BaseException out of s3api\n\nWell that\u0027s easy enough to test:\n```\ndiff --git a/swift/common/middleware/s3api/s3api.py b/swift/common/middleware/s3api/s3api.py\nindex bd0f0c01e..56812eee8 100644\n--- a/swift/common/middleware/s3api/s3api.py\n+++ b/swift/common/middleware/s3api/s3api.py\n@@ -315,6 +315,7 @@ class S3ApiMiddleware(object):\n         return False\n \n     def __call__(self, env, start_response):\n+        raise BaseException\n         origin \u003d env.get(\u0027HTTP_ORIGIN\u0027)\n         if self.conf.cors_preflight_allow_origin and \\\n                 self.is_s3_cors_preflight(env):\n```\nGets me a client response like\n```\n$ curl -v http://saio/info\n* Host saio:80 was resolved.\n* IPv6: (none)\n* IPv4: 127.0.2.1\n*   Trying 127.0.2.1:80...\n* Connected to saio (127.0.2.1) port 80\n\u003e GET /info HTTP/1.1\n\u003e Host: saio\n\u003e User-Agent: curl/8.5.0\n\u003e Accept: */*\n\u003e \n\u003c HTTP/1.1 500 Internal Error\n\u003c Content-Type: text/plain\n\u003c Content-Length: 17\n\u003c X-Trans-Id: txc6f13a8f87d54ee7a6bf1-00663a9482\n\u003c X-Openstack-Request-Id: txc6f13a8f87d54ee7a6bf1-00663a9482\n\u003c Date: Tue, 07 May 2024 20:52:18 GMT\n\u003c \n* Connection #0 to host saio left intact\nAn error occurred\n```\nand logs like\n```\n$ tail /var/log/syslog | sed -e \u0027s/#012/\\n/g\u0027\n2024-05-07T20:52:16.278166+00:00 saio proxy-server: Adding required filter listing_formats to pipeline at position 5\n2024-05-07T20:52:16.278781+00:00 saio proxy-server: Adding required filter gatekeeper to pipeline at position 1\n2024-05-07T20:52:16.279386+00:00 saio proxy-server: Pipeline was modified. New pipeline is \"catch_errors gatekeeper healthcheck proxy-logging domain_remap cache listing_formats etag-quoter container_sync bulk tempurl s3api tempauth copy ratelimit slo dlo versioned_writes symlink keymaster encryption subrequest-logging proxy-server\".\n2024-05-07T20:52:16.279909+00:00 saio proxy-server: The following digest algorithms are allowed by default but deprecated: sha1. Support will be disabled by default in a future release, and later removed entirely.\n2024-05-07T20:52:16.303216+00:00 saio proxy-server: Use tempauth middleware.\n2024-05-07T20:52:16.321742+00:00 saio proxy-server: STDERR: (375028) wsgi starting up on http://0.0.0.0:8080\n2024-05-07T20:52:18.476426+00:00 saio proxy-server: STDERR: (375028) accepted (\u0027127.0.0.1\u0027, 45254)\n2024-05-07T20:52:18.477146+00:00 saio kernel: audit: type\u003d1400 audit(1715115138.474:19749): apparmor\u003d\"DENIED\" operation\u003d\"sendmsg\" class\u003d\"file\" info\u003d\"Failed name lookup - disconnected path\" error\u003d-13 profile\u003d\"rsyslogd\" name\u003d\"var/lib/haproxy/dev/log\" pid\u003d16669 comm\u003d\"haproxy\" requested_mask\u003d\"r\" denied_mask\u003d\"r\" fsuid\u003d0 ouid\u003d0\n2024-05-07T20:52:18.483843+00:00 saio proxy-server: Error: An error occurred: \nTraceback (most recent call last):\n  File \"/vagrant/swift/swift/common/middleware/catch_errors.py\", line 75, in handle_request\n    resp \u003d self._app_call(env)\n           ^^^^^^^^^^^^^^^^^^^\n  File \"/vagrant/swift/swift/common/wsgi.py\", line 1137, in _app_call\n    resp \u003d self.app(env, self._start_response)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/vagrant/swift/swift/common/middleware/gatekeeper.py\", line 129, in __call__\n    return self.app(env, gatekeeper_response)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/vagrant/swift/swift/common/middleware/healthcheck.py\", line 52, in __call__\n    return self.app(env, start_response)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/vagrant/swift/swift/common/middleware/proxy_logging.py\", line 471, in __call__\n    iterable \u003d self.app(env, my_start_response)\n               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/vagrant/swift/swift/common/middleware/domain_remap.py\", line 199, in __call__\n    return self.app(env, start_response)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/vagrant/swift/swift/common/middleware/memcache.py\", line 32, in __call__\n    return self.app(env, start_response)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/vagrant/swift/swift/common/middleware/listing_formats.py\", line 157, in __call__\n    return self.app(env, start_response)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/vagrant/swift/swift/common/middleware/etag_quoter.py\", line 65, in __call__\n    return self.app(env, start_response)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/vagrant/swift/swift/common/swob.py\", line 1580, in _wsgify\n    return func(*new_args)(env, start_response)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/vagrant/swift/swift/common/swob.py\", line 1580, in _wsgify\n    return func(*new_args)(env, start_response)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/vagrant/swift/swift/common/middleware/tempurl.py\", line 554, in __call__\n    return self.app(env, start_response)\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/vagrant/swift/swift/common/middleware/s3api/s3api.py\", line 318, in __call__\n    raise BaseException\nBaseException (txn: txc6f13a8f87d54ee7a6bf1-00663a9482)\n2024-05-07T20:52:18.484401+00:00 saio proxy-server: STDERR: 127.0.0.1 - - [07/May/2024 20:52:18] \"GET /info HTTP/1.1\" 500 239 0.006405 (txn: txc6f13a8f87d54ee7a6bf1-00663a9482)\n```","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"}],"swift/common/middleware/s3api/s3request.py":[{"author":{"_account_id":597,"name":"Pete Zaitcev","email":"zaitcev@kotori.zaitcev.us","username":"zaitcev"},"change_message_id":"839863d67fb5caade7dbbdadf4ebd612c48c59d7","unresolved":true,"context_lines":[{"line_number":870,"context_line":"            try:"},{"line_number":871,"context_line":"                body \u003d self.body_file.read(max_length)"},{"line_number":872,"context_line":"            except S3InputSHA256Mismatch as err:"},{"line_number":873,"context_line":"                raise BadDigest(err.args[0])"},{"line_number":874,"context_line":"        else:"},{"line_number":875,"context_line":"            # No (or zero) Content-Length provided, and not chunked transfer;"},{"line_number":876,"context_line":"            # no body. Assume zero-length, and enforce a required body below."}],"source_content_type":"text/x-python","patch_set":4,"id":"64c8976c_1ed0dd83","line":873,"updated":"2024-03-01 14:36:10.000000000","message":"Curious why args[0]? Why not str()?\n\nDid you make sure it actually worksed? The code is just this:\n\nclass S3InputSHA256Mismatch(BaseException):\n\nAnd I tried this:\n\n    Python 3.12.1 (main, Dec 18 2023, 00:00:00) [GCC 13.2.1 20231205 (Red Hat 13.2.1-6)] on linux\n    Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n    \u003e\u003e\u003e e \u003d BaseException(\u0027mesg\u0027)\n    \u003e\u003e\u003e e\n    BaseException(\u0027mesg\u0027)\n    \u003e\u003e\u003e str(e)\n    \u0027mesg\u0027\n    \u003e\u003e\u003e e.arg[0]\n    Traceback (most recent call last):\n      File \"\u003cstdin\u003e\", line 1, in \u003cmodule\u003e\n    AttributeError: \u0027BaseException\u0027 object has no attribute \u0027arg\u0027. Did you mean: \u0027args\u0027?\n    \u003e\u003e\u003e \n\nSorry, but this looks dubious.","commit_id":"ef34027e1817aaae7f1c8d4eab1c2314a7c20cd9"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"38b595ddafda48c5f374fa5d2b8a9f77c31135ac","unresolved":true,"context_lines":[{"line_number":870,"context_line":"            try:"},{"line_number":871,"context_line":"                body \u003d self.body_file.read(max_length)"},{"line_number":872,"context_line":"            except S3InputSHA256Mismatch as err:"},{"line_number":873,"context_line":"                raise BadDigest(err.args[0])"},{"line_number":874,"context_line":"        else:"},{"line_number":875,"context_line":"            # No (or zero) Content-Length provided, and not chunked transfer;"},{"line_number":876,"context_line":"            # no body. Assume zero-length, and enforce a required body below."}],"source_content_type":"text/x-python","patch_set":4,"id":"bb30d8d1_b5da5b5b","line":873,"in_reply_to":"64c8976c_1ed0dd83","updated":"2024-03-01 21:25:55.000000000","message":"\u003e Curious why args[0]?\n\nMainly out of habit, after getting burned on `err.msg` during the py3 transition ;-)\n\n\u003e Did you make sure it actually worksed?\n\nHuh. I could have sworn I came upon this because of some test failure, but applying\n```\ndiff --git a/swift/common/middleware/s3api/s3request.py b/swift/common/middleware/s3api/s3request.py\nindex cc954201f..fd2a6fe21 100644\n--- a/swift/common/middleware/s3api/s3request.py\n+++ b/swift/common/middleware/s3api/s3request.py\n@@ -151,9 +151,7 @@ class HashingInput(object):\n                 self._hasher.hexdigest() !\u003d self._expected):\n             self.close()\n             # Since we don\u0027t return the last chunk, the PUT never completes\n-            raise S3InputSHA256Mismatch(\n-                \u0027The X-Amz-Content-SHA56 you specified did not match \u0027\n-                \u0027what we received.\u0027)\n+            raise S3InputSHA256Mismatch(\u0027foobar\u0027)\n         return chunk\n \n     def close(self):\n```\ndidn\u0027t pop any failures. Maybe I was just annoyed at needing to repeat the string in two places.\n\nFWIW, we\u0027ve got a bunch of unit tests that\u0027ll pop if I turn it into a `NameError`:\n```\nFAILED test/unit/common/middleware/s3api/test_multi_upload.py::TestS3ApiMultiUpload::test_bucket_upload_part_v4_bad_hash - AssertionError: \u0027500 Internal Server Error\u0027 !\u003d \u0027400 Bad Request\u0027\nFAILED test/unit/common/middleware/s3api/test_multi_upload.py::TestS3ApiMultiUpload::test_object_multipart_upload_invalid_sha256 - AssertionError: \u0027400 Bad Request\u0027 !\u003d \u0027500 Internal Server Error\u0027\nFAILED test/unit/common/middleware/s3api/test_multi_upload.py::TestS3ApiMultiUploadNonUTC::test_bucket_upload_part_v4_bad_hash - AssertionError: \u0027500 Internal Server Error\u0027 !\u003d \u0027400 Bad Request\u0027\nFAILED test/unit/common/middleware/s3api/test_multi_upload.py::TestS3ApiMultiUploadNonUTC::test_object_multipart_upload_invalid_sha256 - AssertionError: \u0027400 Bad Request\u0027 !\u003d \u0027500 Internal Server Error\u0027\nFAILED test/unit/common/middleware/s3api/test_obj.py::TestS3ApiObj::test_object_PUT_v4_bad_hash - AssertionError: \u0027500\u0027 !\u003d \u0027400\u0027\nFAILED test/unit/common/middleware/s3api/test_obj.py::TestS3ApiObjNonUTC::test_object_PUT_v4_bad_hash - AssertionError: \u0027500\u0027 !\u003d \u0027400\u0027\nFAILED test/unit/common/middleware/s3api/test_obj.py::TestS3ApiObjAcl::test_object_PUT_v4_bad_hash - AssertionError: \u0027500\u0027 !\u003d \u0027400\u0027\nFAILED test/unit/common/middleware/s3api/test_obj.py::TestS3ApiObjNonUTCAcl::test_object_PUT_v4_bad_hash - AssertionError: \u0027500\u0027 !\u003d \u0027400\u0027\nFAILED test/unit/common/middleware/s3api/test_s3request.py::TestHashingInput::test_bad_hash - NameError: name \u0027asdf\u0027 is not defined\nFAILED test/unit/common/middleware/s3api/test_s3request.py::TestHashingInput::test_empty_bad_hash - NameError: name \u0027asdf\u0027 is not defined\nFAILED test/unit/common/middleware/s3api/test_s3request.py::TestHashingInput::test_too_long - NameError: name \u0027asdf\u0027 is not defined\nFAILED test/unit/common/middleware/s3api/test_s3request.py::TestHashingInput::test_too_short - NameError: name \u0027asdf\u0027 is not defined\n```\n\nSo, yeah, it should work fine:\n```\n\u003e\u003e\u003e e\u003dBaseException(\u0027mesg\u0027)\n\u003e\u003e\u003e e.args[0]\n\u0027mesg\u0027\n\u003e\u003e\u003e str(e)\n\u0027mesg\u0027\n```\nSix of one, half-dozen of the other.","commit_id":"ef34027e1817aaae7f1c8d4eab1c2314a7c20cd9"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1334069a426bbd7ff30cddf335568305932ae937","unresolved":false,"context_lines":[{"line_number":870,"context_line":"            try:"},{"line_number":871,"context_line":"                body \u003d self.body_file.read(max_length)"},{"line_number":872,"context_line":"            except S3InputSHA256Mismatch as err:"},{"line_number":873,"context_line":"                raise BadDigest(err.args[0])"},{"line_number":874,"context_line":"        else:"},{"line_number":875,"context_line":"            # No (or zero) Content-Length provided, and not chunked transfer;"},{"line_number":876,"context_line":"            # no body. Assume zero-length, and enforce a required body below."}],"source_content_type":"text/x-python","patch_set":4,"id":"6e439774_abf40ffe","line":873,"in_reply_to":"bb30d8d1_b5da5b5b","updated":"2024-05-16 16:17:19.000000000","message":"Acknowledged","commit_id":"ef34027e1817aaae7f1c8d4eab1c2314a7c20cd9"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1495c7c92d7e3d92a624757597df9d65af5caf12","unresolved":true,"context_lines":[{"line_number":141,"context_line":"                self._hasher.hexdigest() !\u003d self._expected):"},{"line_number":142,"context_line":"            self.close()"},{"line_number":143,"context_line":"            # Since we don\u0027t return the last chunk, the PUT never completes"},{"line_number":144,"context_line":"            raise swob.HTTPUnprocessableEntity("},{"line_number":145,"context_line":"                \u0027The X-Amz-Content-SHA56 you specified did not match \u0027"},{"line_number":146,"context_line":"                \u0027what we received.\u0027)"},{"line_number":147,"context_line":"        return chunk"}],"source_content_type":"text/x-python","patch_set":7,"id":"2c2dad03_b938410f","side":"PARENT","line":144,"updated":"2024-04-02 22:21:58.000000000","message":"wait so we just wrap wsgi.input - so anyone reading the request input could have had a swob.HTTPException raised?\n\n... so this was kind of existing gross.  But we only ever raise the exception before returning the \"last\" chunk.\n\nI feel like the way the proxy raises 422 on md5 checksum mis-match for EC is a lot more obvious:\n\nhttps://github.com/NVIDIA/swift/blob/master/swift/proxy/controllers/obj.py#L3196-L3198","commit_id":"0e5aeb50457345801ad70aed0c367e1f9820e113"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"671906e94dff391767e949fa1be3425c1040b012","unresolved":true,"context_lines":[{"line_number":141,"context_line":"                self._hasher.hexdigest() !\u003d self._expected):"},{"line_number":142,"context_line":"            self.close()"},{"line_number":143,"context_line":"            # Since we don\u0027t return the last chunk, the PUT never completes"},{"line_number":144,"context_line":"            raise swob.HTTPUnprocessableEntity("},{"line_number":145,"context_line":"                \u0027The X-Amz-Content-SHA56 you specified did not match \u0027"},{"line_number":146,"context_line":"                \u0027what we received.\u0027)"},{"line_number":147,"context_line":"        return chunk"}],"source_content_type":"text/x-python","patch_set":7,"id":"6e8e46e8_5780d457","side":"PARENT","line":144,"in_reply_to":"2c2dad03_b938410f","updated":"2024-04-03 20:03:16.000000000","message":"The difference is the proxy is a WSGI *app* instead of a *filter* -- it\u0027s wholly responsible for reading the input, not trying to wrap it up and pass it down a pipeline.\n\nThe only alternative I see to us raising *something* is to buffer the whole input and check the digest before continuing down the pipeline, and that\u0027s a non-starter for memory reasons.","commit_id":"0e5aeb50457345801ad70aed0c367e1f9820e113"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"a57fc01c125294e9dc04ec1103d84cae77a86c83","unresolved":false,"context_lines":[{"line_number":141,"context_line":"                self._hasher.hexdigest() !\u003d self._expected):"},{"line_number":142,"context_line":"            self.close()"},{"line_number":143,"context_line":"            # Since we don\u0027t return the last chunk, the PUT never completes"},{"line_number":144,"context_line":"            raise swob.HTTPUnprocessableEntity("},{"line_number":145,"context_line":"                \u0027The X-Amz-Content-SHA56 you specified did not match \u0027"},{"line_number":146,"context_line":"                \u0027what we received.\u0027)"},{"line_number":147,"context_line":"        return chunk"}],"source_content_type":"text/x-python","patch_set":7,"id":"dac9ff79_690cf3e8","side":"PARENT","line":144,"in_reply_to":"6e8e46e8_5780d457","updated":"2024-05-07 18:54:55.000000000","message":"Acknowledged","commit_id":"0e5aeb50457345801ad70aed0c367e1f9820e113"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1334069a426bbd7ff30cddf335568305932ae937","unresolved":true,"context_lines":[{"line_number":910,"context_line":"            # Limit the read similar to how SLO handles manifests"},{"line_number":911,"context_line":"            try:"},{"line_number":912,"context_line":"                body \u003d self.body_file.read(max_length)"},{"line_number":913,"context_line":"            except swob.HTTPException as err:"},{"line_number":914,"context_line":"                if err.status_int \u003d\u003d HTTP_UNPROCESSABLE_ENTITY:"},{"line_number":915,"context_line":"                    # Special case for HashingInput check"},{"line_number":916,"context_line":"                    raise BadDigest("}],"source_content_type":"text/x-python","patch_set":7,"id":"f39405b5_5d8fe0ab","side":"PARENT","line":913,"updated":"2024-05-16 16:17:19.000000000","message":"I think this was untested, so that that\u0027s kind of a pre-existing issue and the new code seems correct:\n\nhttps://review.opendev.org/c/openstack/swift/+/919881","commit_id":"0e5aeb50457345801ad70aed0c367e1f9820e113"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1495c7c92d7e3d92a624757597df9d65af5caf12","unresolved":true,"context_lines":[{"line_number":154,"context_line":"            # Since we don\u0027t return the last chunk, the PUT never completes"},{"line_number":155,"context_line":"            raise S3InputSHA256Mismatch("},{"line_number":156,"context_line":"                \u0027The X-Amz-Content-SHA56 you specified did not match \u0027"},{"line_number":157,"context_line":"                \u0027what we received.\u0027)"},{"line_number":158,"context_line":"        return chunk"},{"line_number":159,"context_line":""},{"line_number":160,"context_line":"    def close(self):"}],"source_content_type":"text/x-python","patch_set":7,"id":"9591829f_861cfac0","line":157,"updated":"2024-04-02 22:21:58.000000000","message":"ok, and now anyone trying to `except Exception` while reading will have their error handling skipped ... and raised to where exactly?","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"a57fc01c125294e9dc04ec1103d84cae77a86c83","unresolved":false,"context_lines":[{"line_number":154,"context_line":"            # Since we don\u0027t return the last chunk, the PUT never completes"},{"line_number":155,"context_line":"            raise S3InputSHA256Mismatch("},{"line_number":156,"context_line":"                \u0027The X-Amz-Content-SHA56 you specified did not match \u0027"},{"line_number":157,"context_line":"                \u0027what we received.\u0027)"},{"line_number":158,"context_line":"        return chunk"},{"line_number":159,"context_line":""},{"line_number":160,"context_line":"    def close(self):"}],"source_content_type":"text/x-python","patch_set":7,"id":"c8bdbc81_c6383b5f","line":157,"in_reply_to":"9591829f_861cfac0","updated":"2024-05-07 18:54:55.000000000","message":"Acknowledged","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1334069a426bbd7ff30cddf335568305932ae937","unresolved":true,"context_lines":[{"line_number":918,"context_line":"            raise MalformedXML()"},{"line_number":919,"context_line":""},{"line_number":920,"context_line":"        if te or ml:"},{"line_number":921,"context_line":"            # Limit the read similar to how SLO handles manifests"},{"line_number":922,"context_line":"            try:"},{"line_number":923,"context_line":"                body \u003d self.body_file.read(max_length)"},{"line_number":924,"context_line":"            except S3InputSHA256Mismatch as err:"}],"source_content_type":"text/x-python","patch_set":7,"id":"03d5ecd2_3c34f0d6","line":921,"updated":"2024-05-16 16:17:19.000000000","message":"swift/test/unit/common/middleware/s3api/test_bucket.py::TestS3ApiBucketNoACL::test_bucket_PUT_with_mixed_case_location might get us into this block with a v4 signature!","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1495c7c92d7e3d92a624757597df9d65af5caf12","unresolved":true,"context_lines":[{"line_number":1416,"context_line":""},{"line_number":1417,"context_line":"        try:"},{"line_number":1418,"context_line":"            sw_resp \u003d sw_req.get_response(app)"},{"line_number":1419,"context_line":"        except S3InputSHA256Mismatch as err:"},{"line_number":1420,"context_line":"            # hopefully by now any modifications to the path (e.g. tenant to"},{"line_number":1421,"context_line":"            # account translation) will have been made by auth middleware"},{"line_number":1422,"context_line":"            self.environ[\u0027s3api.backend_path\u0027] \u003d sw_req.environ[\u0027PATH_INFO\u0027]"}],"source_content_type":"text/x-python","patch_set":7,"id":"395cb4f0_7c7bf3bd","line":1419,"updated":"2024-04-02 22:21:58.000000000","message":"we would have converted the req.environ[\u0027wsgi.input\u0027] to a HashingInput way back in `_canonical_request` - is this realistically the *only* path through which a req might have someone try to read it\u0027s wsgi.input?\n\nI\u0027m thinking of the signature validation failure where we had requests show up at the proxy-app with a funky looking path; is it possible a HashingInput could also escape this error handling?","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"85c8077e629c81ec01cb4fc38d82866b414d9f6e","unresolved":true,"context_lines":[{"line_number":1416,"context_line":""},{"line_number":1417,"context_line":"        try:"},{"line_number":1418,"context_line":"            sw_resp \u003d sw_req.get_response(app)"},{"line_number":1419,"context_line":"        except S3InputSHA256Mismatch as err:"},{"line_number":1420,"context_line":"            # hopefully by now any modifications to the path (e.g. tenant to"},{"line_number":1421,"context_line":"            # account translation) will have been made by auth middleware"},{"line_number":1422,"context_line":"            self.environ[\u0027s3api.backend_path\u0027] \u003d sw_req.environ[\u0027PATH_INFO\u0027]"}],"source_content_type":"text/x-python","patch_set":7,"id":"998d30ce_8c30eaf7","line":1419,"in_reply_to":"36d6f2ab_2868d958","updated":"2024-05-07 20:56:04.000000000","message":"\u003e if it *were* to escape eventlet would do something reasonable anyway\n\nNot eventlet, but [`catch_errors`](https://github.com/openstack/swift/blob/2.33.0/swift/common/middleware/catch_errors.py#L76)\n\nIf `catch_errors` *didn\u0027t* handle it, it\u0027d bubble all the way out to the hub, killing the connection-handling greenthread and potentially leaving the client with an empty reply -- but the pool linked in some cleanup, so it knows the thing died and can spawn another. The core accept loop is unfazed.","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"671906e94dff391767e949fa1be3425c1040b012","unresolved":true,"context_lines":[{"line_number":1416,"context_line":""},{"line_number":1417,"context_line":"        try:"},{"line_number":1418,"context_line":"            sw_resp \u003d sw_req.get_response(app)"},{"line_number":1419,"context_line":"        except S3InputSHA256Mismatch as err:"},{"line_number":1420,"context_line":"            # hopefully by now any modifications to the path (e.g. tenant to"},{"line_number":1421,"context_line":"            # account translation) will have been made by auth middleware"},{"line_number":1422,"context_line":"            self.environ[\u0027s3api.backend_path\u0027] \u003d sw_req.environ[\u0027PATH_INFO\u0027]"}],"source_content_type":"text/x-python","patch_set":7,"id":"5b71c659_e0640a7f","line":1419,"in_reply_to":"395cb4f0_7c7bf3bd","updated":"2024-04-03 20:03:16.000000000","message":"There are only two ways that input gets read: either we read it in `s3api` (in which case we\u0027re using the `xml` helper above), or we pass it down the pipeline and it gets read there. There\u0027s a really strong preference in `s3api` to say things like `resp \u003d req.get_response(self.app, ...)` which ultimately routes us through here.\n\nI think I maybe agree it\u0027d be nice if we had better boundaries such that it\u0027d be obvious that all cases are covered; some filter like\n```\ndef __call__(self, env, start_response):\n    self.maybe_wrap_input(env)\n    try:\n        return reiterate(self.app(env, start_response))\n    except S3InputSHA256Mismatch:\n        return BadDigest(...)(env, start_response)\n```\nbut I also think\n\n* that\u0027s going to require more refactoring than you might be expecting (in no small part because of things like the environ-munging going on in `_canonical_request`) and\n* we\u0027re going to find ourselves needing to think more about pipeline-ordering problems (which we\u0027ve historically kinda sucked at)","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"a57fc01c125294e9dc04ec1103d84cae77a86c83","unresolved":true,"context_lines":[{"line_number":1416,"context_line":""},{"line_number":1417,"context_line":"        try:"},{"line_number":1418,"context_line":"            sw_resp \u003d sw_req.get_response(app)"},{"line_number":1419,"context_line":"        except S3InputSHA256Mismatch as err:"},{"line_number":1420,"context_line":"            # hopefully by now any modifications to the path (e.g. tenant to"},{"line_number":1421,"context_line":"            # account translation) will have been made by auth middleware"},{"line_number":1422,"context_line":"            self.environ[\u0027s3api.backend_path\u0027] \u003d sw_req.environ[\u0027PATH_INFO\u0027]"}],"source_content_type":"text/x-python","patch_set":7,"id":"36d6f2ab_2868d958","line":1419,"in_reply_to":"5b71c659_e0640a7f","updated":"2024-05-07 18:54:55.000000000","message":"yes someting like that would be sufficient for me to think by-passinging downstream error handling with a BaseException is safe, and safety seems like one of the minimum pre-requisites for \"reasonable\"\n\nI\u0027m not sure I catch your point about the caveats - but I probably would if I tried it myself; it\u0027s also possible the it\u0027s somewhat moot in the near term *if* it\u0027s currently the case that\n\na) no such path for the InputMismatch to escape the current error handling exists\nb) if it *were* to escape eventlet would do something reasonable anyway\n\nin which case maybe the refactoring is sort of YAGNI - I\u0027ll plan on spending some more time with this patch to try and convnice myself one way or another.","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1334069a426bbd7ff30cddf335568305932ae937","unresolved":false,"context_lines":[{"line_number":1416,"context_line":""},{"line_number":1417,"context_line":"        try:"},{"line_number":1418,"context_line":"            sw_resp \u003d sw_req.get_response(app)"},{"line_number":1419,"context_line":"        except S3InputSHA256Mismatch as err:"},{"line_number":1420,"context_line":"            # hopefully by now any modifications to the path (e.g. tenant to"},{"line_number":1421,"context_line":"            # account translation) will have been made by auth middleware"},{"line_number":1422,"context_line":"            self.environ[\u0027s3api.backend_path\u0027] \u003d sw_req.environ[\u0027PATH_INFO\u0027]"}],"source_content_type":"text/x-python","patch_set":7,"id":"05eeb20c_4a0e47a5","line":1419,"in_reply_to":"998d30ce_8c30eaf7","updated":"2024-05-16 16:17:19.000000000","message":"bare except is the boss!  Thanks for the link!\n\nand even if catch errors weren\u0027t the bees knees, eventlet has each request in a spawn\u0027d coro, so those are allowed to blow up pretty gnarly - also gtk.","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"1334069a426bbd7ff30cddf335568305932ae937","unresolved":true,"context_lines":[{"line_number":1420,"context_line":"            # hopefully by now any modifications to the path (e.g. tenant to"},{"line_number":1421,"context_line":"            # account translation) will have been made by auth middleware"},{"line_number":1422,"context_line":"            self.environ[\u0027s3api.backend_path\u0027] \u003d sw_req.environ[\u0027PATH_INFO\u0027]"},{"line_number":1423,"context_line":"            raise BadDigest(err.args[0])"},{"line_number":1424,"context_line":"        else:"},{"line_number":1425,"context_line":"            # reuse account"},{"line_number":1426,"context_line":"            _, self.account, _ \u003d split_path(sw_resp.environ[\u0027PATH_INFO\u0027],"}],"source_content_type":"text/x-python","patch_set":7,"id":"cdb04919_5db60466","line":1423,"updated":"2024-05-16 16:17:19.000000000","message":"this is definately tested by the new test, it even says \"X-Amz-Content-SHA56\" in the response body!\n\nWhen testing functionally I can see my object-servers log a 499\n\n```\nvagrant@saio:~$ grep tx51d9077a60d9477aaea5d-00664625b0 /var/log/syslog\nMay 16 15:26:40 saio proxy-server: User: test:tester uses token s3 (trans_id tx51d9077a60d9477aaea5d-00664625b0)\nMay 16 15:26:40 saio proxy-server: User test:tester has admin authorizing. (txn: tx51d9077a60d9477aaea5d-00664625b0) (client_ip: 127.0.0.1)\nMay 16 15:26:40 saio proxy-server: Calling S3Api Middleware (txn: tx51d9077a60d9477aaea5d-00664625b0) (client_ip: 127.0.0.1)\nMay 16 15:26:40 saio proxy-server: checking permission: s3test  HEAD {\u0027Host\u0027: \u0027saio:8080\u0027, \u0027Accept-Encoding\u0027: \u0027identity\u0027, \u0027User-Agent\u0027: \u0027Boto3/1.29.5 md/Botocore#1.32.5 ua/2.0 os/linux#5.15.0-105-generic md/arch#x86_64 lang/python#3.10.12 md/pyimpl#CPython cfg/retry-mode#legacy Botocore/1.32.5\u0027, \u0027Content-Md5\u0027: \u0027OmtrusTwBlZI32zx4PzqdA\u003d\u003d\u0027, \u0027Expect\u0027: \u0027100-continue\u0027, \u0027X-Amz-Date\u0027: \u002720240516T152640Z\u0027, \u0027X-Amz-Content-Sha256\u0027: \u0027bb9f8df61474d25e71fa00722318cd387396ca1736605e1248821cc0de3d3af8\u0027, \u0027Authorization\u0027: \u0027AWS4-HMAC-SHA256 Credential\u003dtest:tester/20240516/us-east-1/s3/aws4_request, SignedHeaders\u003dcontent-md5;host;x-amz-content-sha256;x-amz-date, Signature\u003d0c7f4b9d64fa63c98b571ac9e1712edbc1efbea046999df734b482aed68fc249\u0027, \u0027Amz-Sdk-Invocation-Id\u0027: \u00271f7450b2-d0e9-478f-aca1-9a4e51ddfc96\u0027, \u0027Amz-Sdk-Request\u0027: \u0027attempt\u003d1\u0027, \u0027Etag\u0027: \u00273a6b6bbac4f0065648df6cf1e0fcea74\u0027, \u0027X-Timestamp\u0027: \u00271715873200.95881\u0027, \u0027Content-Length\u0027: \u00274194304\u0027, \u0027Content-Type\u0027: \u0027binary/octet-stream\u0027} (txn: tx51d9077a60d9477aaea5d-00664625b0) (client_ip: 127.0.0.1)\nMay 16 15:26:40 saio container-6031: 127.0.0.1 - - [16/May/2024:15:26:40 +0000] \"HEAD /sdb3/5/AUTH_test/s3test\" 204 - \"HEAD http://saio:8080/v1/AUTH_test/s3test\" \"tx51d9077a60d9477aaea5d-00664625b0\" \"proxy-server 118773\" 0.0017 \"-\" 117353 0\nMay 16 15:26:40 saio container-6031: STDERR: 127.0.0.1 - - [16/May/2024 15:26:40] \"HEAD /sdb3/5/AUTH_test/s3test HTTP/1.1\" 204 684 0.002213 (txn: tx51d9077a60d9477aaea5d-00664625b0)\nMay 16 15:26:40 saio proxy-server: 127.0.0.1 127.0.0.1 16/May/2024/15/26/40 HEAD /v1/AUTH_test/s3test HTTP/1.0 204 - Boto3/1.29.5%20md/Botocore%231.32.5%20ua/2.0%20os/linux%235.15.0-105-generic%20md/arch%23x86_64%20lang/python%233.10.12%20md/pyimpl%23CPython%20cfg/retry-mode%23legacy%20Botocore/1.32.5 - - - 3a6b6bbac4f0065648df6cf1e0fcea74 tx51d9077a60d9477aaea5d-00664625b0 - 0.0061 S3 - 1715873200.959355354 1715873200.965503454 0\nMay 16 15:26:41 saio object-6040: 127.0.0.1 - - [16/May/2024:15:26:41 +0000] \"PUT /sdb4/58/AUTH_test/s3test/bad-sha-test2\" 499 89 \"PUT http://saio:8080/v1/AUTH_test/s3test/bad-sha-test2\" \"tx51d9077a60d9477aaea5d-00664625b0\" \"proxy-server 118773\" 0.3177 \"-\" 117350 0\nMay 16 15:26:41 saio object-6040: STDERR: 127.0.0.1 - - [16/May/2024 15:26:41] \"PUT /sdb4/58/AUTH_test/s3test/bad-sha-test2 HTTP/1.1\" 499 239 0.318985 (txn: tx51d9077a60d9477aaea5d-00664625b0)\nMay 16 15:26:41 saio object-6030: 127.0.0.1 - - [16/May/2024:15:26:41 +0000] \"PUT /sdb3/58/AUTH_test/s3test/bad-sha-test2\" 499 89 \"PUT http://saio:8080/v1/AUTH_test/s3test/bad-sha-test2\" \"tx51d9077a60d9477aaea5d-00664625b0\" \"proxy-server 118773\" 0.3167 \"-\" 117352 0\nMay 16 15:26:41 saio object-6030: STDERR: 127.0.0.1 - - [16/May/2024 15:26:41] \"PUT /sdb3/58/AUTH_test/s3test/bad-sha-test2 HTTP/1.1\" 499 239 0.317783 (txn: tx51d9077a60d9477aaea5d-00664625b0)\nMay 16 15:26:41 saio object-6010: 127.0.0.1 - - [16/May/2024:15:26:41 +0000] \"PUT /sdb1/58/AUTH_test/s3test/bad-sha-test2\" 499 89 \"PUT http://saio:8080/v1/AUTH_test/s3test/bad-sha-test2\" \"tx51d9077a60d9477aaea5d-00664625b0\" \"proxy-server 118773\" 0.3207 \"-\" 117351 0\nMay 16 15:26:41 saio object-6010: STDERR: 127.0.0.1 - - [16/May/2024 15:26:41] \"PUT /sdb1/58/AUTH_test/s3test/bad-sha-test2 HTTP/1.1\" 499 239 0.321867 (txn: tx51d9077a60d9477aaea5d-00664625b0)\nMay 16 15:26:41 saio proxy-server: 127.0.0.1 127.0.0.1 16/May/2024/15/26/41 PUT /s3test/bad-sha-test2 HTTP/1.0 400 - Boto3/1.29.5%20md/Botocore%231.32.5%20ua/2.0%20os/linux%235.15.0-105-generic%20md/arch%23x86_64%20lang/python%233.10.12%20md/pyimpl%23CPython%20cfg/retry-mode%23legacy%20Botocore/1.32.5 - 4194304 221 3a6b6bbac4f0065648df6cf1e0fcea74 tx51d9077a60d9477aaea5d-00664625b0 - 0.3483 - - 1715873200.953065395 1715873201.301405907 -\nMay 16 15:26:41 saio proxy-server: STDERR: 127.0.0.1 - - [16/May/2024 15:26:41] \"PUT /s3test/bad-sha-test2 HTTP/1.1\" 400 566 0.354641 (txn: tx51d9077a60d9477aaea5d-00664625b0)\n```\n\n... and of course no object is created!  ;)","commit_id":"f5e422eb7858f789066a20b7718a316ea951659f"}],"test/unit/common/middleware/s3api/test_obj.py":[{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"02e2aac18562dcf6d2f55936cc15946bb3cf4a36","unresolved":true,"context_lines":[{"line_number":643,"context_line":"            except Exception:"},{"line_number":644,"context_line":"                self.logger.exception(\u0027uh oh\u0027)"},{"line_number":645,"context_line":"                start_response(\u0027599 Uh Oh\u0027, [])"},{"line_number":646,"context_line":"                return [b\u0027\u0027]"},{"line_number":647,"context_line":""},{"line_number":648,"context_line":"        self.s3api.app \u003d error_catching_app"},{"line_number":649,"context_line":""}],"source_content_type":"text/x-python","patch_set":1,"id":"39e8ea68_a7d34bad","line":646,"updated":"2024-02-15 02:30:30.000000000","message":"Oh nice, have an app the catches an exception and does something else, yet the mismatch from s3api wins!","commit_id":"1c65512f8cadd8db04a80929590095618f979d1b"}],"test/unit/common/middleware/s3api/test_s3request.py":[{"author":{"_account_id":7233,"name":"Matthew Oliver","email":"matt@oliver.net.au","username":"mattoliverau"},"change_message_id":"02e2aac18562dcf6d2f55936cc15946bb3cf4a36","unresolved":true,"context_lines":[{"line_number":1275,"context_line":"        with self.assertRaises(InputChecksumMismatch) as raised:"},{"line_number":1276,"context_line":"            wrapped.read(3)"},{"line_number":1277,"context_line":"        self.assertIsInstance(raised.exception, BaseException)"},{"line_number":1278,"context_line":"        # won\u0027t get caught be most things in a pipeline"},{"line_number":1279,"context_line":"        self.assertNotIsInstance(raised.exception, Exception)"},{"line_number":1280,"context_line":"        # the error causes us to close the input"},{"line_number":1281,"context_line":"        self.assertTrue(wrapped._input.closed)"}],"source_content_type":"text/x-python","patch_set":1,"id":"e4cb890b_b1ed9558","line":1278,"range":{"start_line":1278,"start_character":27,"end_line":1278,"end_character":29},"updated":"2024-02-15 02:30:30.000000000","message":"by","commit_id":"1c65512f8cadd8db04a80929590095618f979d1b"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"38b595ddafda48c5f374fa5d2b8a9f77c31135ac","unresolved":false,"context_lines":[{"line_number":1275,"context_line":"        with self.assertRaises(InputChecksumMismatch) as raised:"},{"line_number":1276,"context_line":"            wrapped.read(3)"},{"line_number":1277,"context_line":"        self.assertIsInstance(raised.exception, BaseException)"},{"line_number":1278,"context_line":"        # won\u0027t get caught be most things in a pipeline"},{"line_number":1279,"context_line":"        self.assertNotIsInstance(raised.exception, Exception)"},{"line_number":1280,"context_line":"        # the error causes us to close the input"},{"line_number":1281,"context_line":"        self.assertTrue(wrapped._input.closed)"}],"source_content_type":"text/x-python","patch_set":1,"id":"2b6d5008_78eb66f0","line":1278,"range":{"start_line":1278,"start_character":27,"end_line":1278,"end_character":29},"in_reply_to":"e4cb890b_b1ed9558","updated":"2024-03-01 21:25:55.000000000","message":"Done","commit_id":"1c65512f8cadd8db04a80929590095618f979d1b"}]}
