)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"7f60417523ea9cafd499f9f1c907dc2c72484611","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"f2910b8d_3f013602","updated":"2025-03-19 16:11:20.000000000","message":"I cherry-picked this onto my minimal reproducer on master https://review.opendev.org/c/openstack/swift/+/945004 and it seemed to fix things. \n\nIn tcpdump I can see the Content-Length and Connection: close being sent with the 400 responses.\n\nIt\u0027s a shame that the solution needs to be so convoluted, with three modules participating getting a header sent, and concerns me that it\u0027s hard to understand *how* things are working. But kudos for figuring it out.","commit_id":"4e5ad467b2fc19f32fc641bda24891e5e93533ca"}],"swift/common/middleware/s3api/s3response.py":[{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"8606d58d7527959ebf2288e32ac42d6d35aba6c4","unresolved":true,"context_lines":[{"line_number":244,"context_line":"        swob.HTTPException.__init__("},{"line_number":245,"context_line":"            self, status\u003dkwargs.pop(\u0027status\u0027, self._status),"},{"line_number":246,"context_line":"            content_type\u003d\u0027application/xml\u0027, *args,"},{"line_number":247,"context_line":"            **kwargs)"},{"line_number":248,"context_line":"        self.headers \u003d HeaderKeyDict(self.headers)"},{"line_number":249,"context_line":""},{"line_number":250,"context_line":"    @property"}],"source_content_type":"text/x-python","patch_set":1,"id":"820c90d0_c5ee7b51","line":247,"updated":"2025-03-18 23:48:33.000000000","message":"We can\u0027t just say `body\u003dself._make_body()` here?","commit_id":"83af430dc624c37910c2288edbd8712db6e198a4"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"7f60417523ea9cafd499f9f1c907dc2c72484611","unresolved":true,"context_lines":[{"line_number":244,"context_line":"        swob.HTTPException.__init__("},{"line_number":245,"context_line":"            self, status\u003dkwargs.pop(\u0027status\u0027, self._status),"},{"line_number":246,"context_line":"            content_type\u003d\u0027application/xml\u0027, *args,"},{"line_number":247,"context_line":"            **kwargs)"},{"line_number":248,"context_line":"        self.headers \u003d HeaderKeyDict(self.headers)"},{"line_number":249,"context_line":""},{"line_number":250,"context_line":"    @property"}],"source_content_type":"text/x-python","patch_set":1,"id":"878a52df_3bc1ae0b","line":247,"in_reply_to":"354f274a_5945ea27","updated":"2025-03-19 16:11:20.000000000","message":"plus, the body *must* be lazy-evaluated because it wants to dig \u0027swift.trans_id\u0027 out of the environ, so we can\u0027t even just set the body right after the call to ``__init__`` when we do have an environ.\n\nBut you may well wonder *how* trans_id gets into the Response.environ since we don\u0027t pass an env to the ``swob.Response__init__`` from ``ErrorResponse.__init__``, so the environ is initialized as an empty dict,\n\nand a good while later after digging around the code, you notice that the Response.environ is set, again, in the ``Response.__call__``, overriding whatever *may* have been passed to ``__init__``, and it is set to be the Request.environ, which *does* have \u0027swift.trans_id\u0027, and only *then* do we iterate over ``_body_iter``....\n\n...which begs the question \"why does the ``__init__`` accept an environ?\"\n\n...and why do we make it so hard for ourselves to understand and maintain code? 😭","commit_id":"83af430dc624c37910c2288edbd8712db6e198a4"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"48267f30023916b7abea02e8530636dad8b9bda6","unresolved":true,"context_lines":[{"line_number":244,"context_line":"        swob.HTTPException.__init__("},{"line_number":245,"context_line":"            self, status\u003dkwargs.pop(\u0027status\u0027, self._status),"},{"line_number":246,"context_line":"            content_type\u003d\u0027application/xml\u0027, *args,"},{"line_number":247,"context_line":"            **kwargs)"},{"line_number":248,"context_line":"        self.headers \u003d HeaderKeyDict(self.headers)"},{"line_number":249,"context_line":""},{"line_number":250,"context_line":"    @property"}],"source_content_type":"text/x-python","patch_set":1,"id":"354f274a_5945ea27","line":247,"in_reply_to":"820c90d0_c5ee7b51","updated":"2025-03-19 01:59:25.000000000","message":"right, you get an attribute error on self.environ.","commit_id":"83af430dc624c37910c2288edbd8712db6e198a4"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"48267f30023916b7abea02e8530636dad8b9bda6","unresolved":true,"context_lines":[{"line_number":261,"context_line":"        return \u0027.\u0027.join([str(self.status_int), self.summary])"},{"line_number":262,"context_line":""},{"line_number":263,"context_line":"    def _response_iter(self, app_iter, body):"},{"line_number":264,"context_line":"        # this is the only hook after swob sets self.environ"},{"line_number":265,"context_line":"        self.body \u003d self._make_body()"},{"line_number":266,"context_line":"        return super()._response_iter(app_iter, self.body)"},{"line_number":267,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"3402400a_a7740ba0","line":264,"updated":"2025-03-19 01:59:25.000000000","message":"I tried to call that out.","commit_id":"0d0fdfbec0b0c35a5a883e7bcdb1f49cca51e5b4"}],"test/unit/common/middleware/s3api/test_s3response.py":[{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"9f59f550551cdfe7d9d9de93e77abb178ad4c53e","unresolved":false,"context_lines":[{"line_number":144,"context_line":"            b\"\u003cRequestId\u003efake-trans-id\u003c/RequestId\u003e\""},{"line_number":145,"context_line":"            b\"\u003c/Error\u003e\","},{"line_number":146,"context_line":"            resp.body)"},{"line_number":147,"context_line":"        self.assertEqual(146, int(resp.headers[\u0027Content-Length\u0027]))"},{"line_number":148,"context_line":""},{"line_number":149,"context_line":""},{"line_number":150,"context_line":"if __name__ \u003d\u003d \u0027__main__\u0027:"}],"source_content_type":"text/x-python","patch_set":5,"id":"e4bc329c_fdd7e92f","line":147,"range":{"start_line":147,"start_character":25,"end_line":147,"end_character":28},"updated":"2025-04-21 19:13:54.000000000","message":"Might\u0027ve just gone for `len(resp.body)` since we just asserted on the contents, but sure.","commit_id":"1ca073ce1dac2d0e3554399fc8e8a9e0af5e4c06"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"4bf84584cb693407552238637122d8bf85647dcc","unresolved":false,"context_lines":[{"line_number":144,"context_line":"            b\"\u003cRequestId\u003efake-trans-id\u003c/RequestId\u003e\""},{"line_number":145,"context_line":"            b\"\u003c/Error\u003e\","},{"line_number":146,"context_line":"            resp.body)"},{"line_number":147,"context_line":"        self.assertEqual(146, int(resp.headers[\u0027Content-Length\u0027]))"},{"line_number":148,"context_line":""},{"line_number":149,"context_line":""},{"line_number":150,"context_line":"if __name__ \u003d\u003d \u0027__main__\u0027:"}],"source_content_type":"text/x-python","patch_set":5,"id":"d89fe5b2_1b6cf3e5","line":147,"range":{"start_line":147,"start_character":25,"end_line":147,"end_character":28},"in_reply_to":"e4bc329c_fdd7e92f","updated":"2025-04-21 19:36:40.000000000","message":"wouldn\u0027t that have passed with the old code, tho?  I think I was trying to show that `Content-Length` was making it\u0027s way into the headers.\n\nwith fix reverted:\n\n```\n    def test_error_response_trans_id(self):\n        req \u003d Request.blank(\u0027/bucket/object\u0027)\n        err \u003d DummyErrorResponse(msg\u003d\u0027my-msg\u0027, reason\u003d\u0027my reason\u0027)\n        app \u003d CatchErrorMiddleware(err, {})\n        with unittest.mock.patch(\n                \u0027swift.common.middleware.catch_errors.generate_trans_id\u0027,\n                return_value\u003d\u0027fake-trans-id\u0027):\n            resp \u003d req.get_response(app)\n        self.assertIn(\u0027swift.trans_id\u0027, req.environ)\n        self.assertEqual(418, resp.status_int)\n        self.assertIn(\u0027X-Trans-Id\u0027, resp.headers)\n        self.assertEqual(\n            b\"\u003c?xml version\u003d\u00271.0\u0027 encoding\u003d\u0027UTF-8\u0027?\u003e\\n\"\n            b\"\u003cError\u003e\"\n            b\"\u003cCode\u003eDummyErrorResponse\u003c/Code\u003e\"\n            b\"\u003cMessage\u003emy-msg\u003c/Message\u003e\"\n            b\"\u003cRequestId\u003efake-trans-id\u003c/RequestId\u003e\"\n            b\"\u003c/Error\u003e\",\n            resp.body)\n\u003e       self.assertEqual(146, int(resp.headers[\u0027Content-Length\u0027]))\nE       TypeError: int() argument must be a string, a bytes-like object or a real number, not \u0027NoneType\u0027\n```","commit_id":"1ca073ce1dac2d0e3554399fc8e8a9e0af5e4c06"}]}
