{"html_url": "https://github.com/simonw/datasette/issues/1590#issuecomment-1012661522", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1590", "id": 1012661522, "node_id": "IC_kwDOBm6k_c48W_0S", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-01-14T01:16:08Z", "updated_at": "2022-01-14T01:16:34Z", "author_association": "OWNER", "body": "OK, I'm going to recommend a workaround for this instead. Here's `asgi.py` updated to strip the prefix before passing the request on to Datasette:\r\n```python\r\nimport pathlib\r\nfrom asgi_cors import asgi_cors\r\nfrom channels.routing import URLRouter\r\nfrom django.urls import re_path\r\nfrom datasette.app import Datasette\r\n\r\n\r\ndef rewrite_path(app, prefix_to_strip):\r\n async def rewrite_path_app(scope, receive, send):\r\n if (\r\n scope[\"type\"] == \"http\"\r\n and \"path\" in scope\r\n and scope[\"path\"].startswith(prefix_to_strip)\r\n ):\r\n scope[\"path\"] = scope[\"path\"][len(prefix_to_strip) :]\r\n if \"raw_path\" in scope:\r\n scope[\"raw_path\"] = scope[\"raw_path\"][len(prefix_to_strip) :]\r\n await app(scope, receive, send)\r\n\r\n return rewrite_path_app\r\n\r\n\r\ndatasette_ = Datasette(\r\n files=[\"fixtures.db\"],\r\n settings={\"base_url\": \"/datasettes/\", \"plugins\": {}},\r\n)\r\napplication = URLRouter(\r\n [\r\n re_path(\r\n r\"^datasettes/.*\",\r\n asgi_cors(rewrite_path(datasette_.app(), \"/datasettes\"), allow_all=True),\r\n ),\r\n ]\r\n)\r\n```\r\nThis works on my laptop - please re-open the ticket if it doesn't work for you!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1099723916, "label": "Table+query JSON and CSV links broken when using `base_url` setting"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1590#issuecomment-1012656790", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1590", "id": 1012656790, "node_id": "IC_kwDOBm6k_c48W-qW", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-01-14T01:05:34Z", "updated_at": "2022-01-14T01:05:34Z", "author_association": "OWNER", "body": "I think this prefixed string mechanism is supposed to prevent the `base_url` prefix from being applied twice: https://github.com/simonw/datasette/blob/3664ddd400062123e99500d28b160c7944408c1a/datasette/url_builder.py#L9-L16\r\n\r\nBut with a bit of extra logging all of the inputs to that are NOT prefixed strings:\r\n\r\n```\r\nUrls.path called with: /datasettes/fixtures/compound_three_primary_keys?_sort=content (PrefixedUrlString = False)\r\n returning /datasettes/datasettes/fixtures/compound_three_primary_keys?_sort=content\r\n```\r\nSo it looks like `urls.path(...)` is indeed the code responsible for doubling up that `/datasettes/` prefix.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1099723916, "label": "Table+query JSON and CSV links broken when using `base_url` setting"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1590#issuecomment-1012653966", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1590", "id": 1012653966, "node_id": "IC_kwDOBm6k_c48W9-O", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-01-14T00:59:07Z", "updated_at": "2022-01-14T00:59:07Z", "author_association": "OWNER", "body": "Since this is a special case bug for when using Datasette as a library I wonder if a good fix here would be to support something like this:\r\n```python\r\napplication = URLRouter([\r\n re_path(r\"^datasettes/.*\", asgi_cors(datasette_.app(remove_path_prefix=\"datasettes/\"), allow_all=True)),\r\n])\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1099723916, "label": "Table+query JSON and CSV links broken when using `base_url` setting"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1590#issuecomment-1012583091", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1590", "id": 1012583091, "node_id": "IC_kwDOBm6k_c48Wsqz", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-01-13T22:41:15Z", "updated_at": "2022-01-13T22:41:15Z", "author_association": "OWNER", "body": "Seeing as this area of the code has produced so many bugs in the past, I think part of the fix may be to write comprehensive documentation about how routing works for the internals documentation. Doing so might help me figure this bug out!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1099723916, "label": "Table+query JSON and CSV links broken when using `base_url` setting"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1590#issuecomment-1010559681", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1590", "id": 1010559681, "node_id": "IC_kwDOBm6k_c48O-rB", "user": {"value": 1001306, "label": "eelkevdbos"}, "created_at": "2022-01-12T02:10:20Z", "updated_at": "2022-01-12T02:10:20Z", "author_association": "NONE", "body": "In my example, path matching happens at the application layer (being the Django channels URLRouter). That might be a somewhat exotic solution that would normally be solved by a proxy like Apache or Nginx. However, in my specific use case, this is a \"feature\" enabling me to do simple management of databases and metadata from within a Django admin app instance mapped in that same router.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1099723916, "label": "Table+query JSON and CSV links broken when using `base_url` setting"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1590#issuecomment-1010556333", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1590", "id": 1010556333, "node_id": "IC_kwDOBm6k_c48O92t", "user": {"value": 1001306, "label": "eelkevdbos"}, "created_at": "2022-01-12T02:03:59Z", "updated_at": "2022-01-12T02:03:59Z", "author_association": "NONE", "body": "Thank you for the quick reply! Just a quick observation, I am running this locally without a proxy, whereas your fly example seems to be running behind an apache proxy (if the name is accurate). Can it be that the apache proxy strips the prefix before it passes on the request to the daphne backend?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1099723916, "label": "Table+query JSON and CSV links broken when using `base_url` setting"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1590#issuecomment-1010540923", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1590", "id": 1010540923, "node_id": "IC_kwDOBm6k_c48O6F7", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-01-12T01:33:49Z", "updated_at": "2022-01-12T01:33:49Z", "author_association": "OWNER", "body": "Looking closer at the code quoted above, it doesn't modify `path` or `raw_path` at all - ALL it does is add the `route_path` to the scope.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1099723916, "label": "Table+query JSON and CSV links broken when using `base_url` setting"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1590#issuecomment-1010538188", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1590", "id": 1010538188, "node_id": "IC_kwDOBm6k_c48O5bM", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-01-12T01:28:41Z", "updated_at": "2022-01-12T01:30:43Z", "author_association": "OWNER", "body": "Oh wait! It looks like `route_path` is something I invented there.\r\n\r\nYup, I added it in https://github.com/simonw/datasette/commit/a63412152518581c6a3d4e142b937e27dabdbfdb - commit message says:\r\n\r\n> - new `route_path` key in `request.scope` storing the path that was used for routing with the `base_url` prefix stripped\r\n\r\nSo actually part of the mystery here is: why does the Fly hosted one NOT have that key?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1099723916, "label": "Table+query JSON and CSV links broken when using `base_url` setting"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1590#issuecomment-1010538016", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1590", "id": 1010538016, "node_id": "IC_kwDOBm6k_c48O5Yg", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-01-12T01:28:19Z", "updated_at": "2022-01-12T01:28:19Z", "author_association": "OWNER", "body": "The Daphne one has this key: `'route_path': '/-/asgi-scope',`\r\n\r\nMaybe Datasette's routing code needs to look out for that, if it's available, and use it to reconstruct the requested path?\r\n\r\nThe code in question is here: https://github.com/simonw/datasette/blob/8c401ee0f054de2f568c3a8302c9223555146407/datasette/app.py#L1143-L1149", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1099723916, "label": "Table+query JSON and CSV links broken when using `base_url` setting"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1590#issuecomment-1010537058", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1590", "id": 1010537058, "node_id": "IC_kwDOBm6k_c48O5Ji", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-01-12T01:26:34Z", "updated_at": "2022-01-12T01:26:34Z", "author_association": "OWNER", "body": "I'm using the https://datasette.io/plugins/datasette-debug-asgi plugin to investigate.\r\n\r\nOn my laptop using Daphne I get this: http://127.0.0.1:8032/datasettes/-/asgi-scope\r\n\r\n```\r\n{'actor': None,\r\n 'asgi': {'version': '3.0'},\r\n 'client': ['127.0.0.1', 53767],\r\n 'csrftoken': ._asgi_csrf_decorator..app_wrapped_with_csrf..get_csrftoken at 0x1122aeef0>,\r\n 'headers': [(b'host', b'127.0.0.1:8032'),\r\n (b'user-agent',\r\n b'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko'\r\n b'/20100101 Firefox/95.0'),\r\n (b'accept',\r\n b'text/html,application/xhtml+xml,application/xml;q=0.9,image/'\r\n b'avif,image/webp,*/*;q=0.8'),\r\n (b'accept-language', b'en-US,en;q=0.5'),\r\n (b'accept-encoding', b'gzip, deflate'),\r\n (b'dnt', b'1'),\r\n (b'connection', b'keep-alive'),\r\n (b'cookie', b'_ga=GA1.1.742283954.1628542653'),\r\n (b'upgrade-insecure-requests', b'1'),\r\n (b'sec-fetch-dest', b'document'),\r\n (b'sec-fetch-mode', b'navigate'),\r\n (b'sec-fetch-site', b'none'),\r\n (b'sec-fetch-user', b'?1')],\r\n 'http_version': '1.1',\r\n 'method': 'GET',\r\n 'path': '/datasettes/-/asgi-scope',\r\n 'path_remaining': '',\r\n 'query_string': b'',\r\n 'raw_path': b'/datasettes/-/asgi-scope',\r\n 'root_path': '',\r\n 'route_path': '/-/asgi-scope',\r\n 'scheme': 'http',\r\n 'server': ['127.0.0.1', 8032],\r\n 'type': 'http',\r\n 'url_route': {'kwargs': {}}}\r\n```\r\nOn the demo running on Fly (which I just redeployed with that plugin) I get this: https://datasette-apache-proxy-demo.fly.dev/prefix/-/asgi-scope\r\n\r\n```\r\n{'actor': None,\r\n 'asgi': {'spec_version': '2.1', 'version': '3.0'},\r\n 'client': ('86.109.12.167', 0),\r\n 'csrftoken': ._asgi_csrf_decorator..app_wrapped_with_csrf..get_csrftoken at 0x7f4c0413bca0>,\r\n 'headers': [(b'host', b'datasette-apache-proxy-demo.fly.dev'),\r\n (b'user-agent',\r\n b'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:95.0) Gecko'\r\n b'/20100101 Firefox/95.0'),\r\n (b'accept',\r\n b'text/html,application/xhtml+xml,application/xml;q=0.9,image/'\r\n b'avif,image/webp,*/*;q=0.8'),\r\n (b'accept-language', b'en-US,en;q=0.5'),\r\n (b'accept-encoding', b'gzip, deflate, br'),\r\n (b'dnt', b'1'),\r\n (b'x-request-start', b't=1641950740651658'),\r\n (b'sec-fetch-dest', b'document'),\r\n (b'sec-fetch-mode', b'navigate'),\r\n (b'sec-fetch-site', b'none'),\r\n (b'sec-fetch-user', b'?1'),\r\n (b'fly-client-ip', b'24.5.172.176'),\r\n (b'x-forwarded-for',\r\n b'24.5.172.176, 213.188.193.173, 86.109.12.167'),\r\n (b'fly-forwarded-proto', b'https'),\r\n (b'x-forwarded-proto', b'https'),\r\n (b'fly-forwarded-ssl', b'on'),\r\n (b'x-forwarded-ssl', b'on'),\r\n (b'fly-forwarded-port', b'443'),\r\n (b'x-forwarded-port', b'443'),\r\n (b'fly-region', b'sjc'),\r\n (b'fly-request-id', b'01FS5Y805BX43HM94T8XW610KG'),\r\n (b'via', b'2 fly.io'),\r\n (b'fly-dispatch-start', b't=1641950740683198;instance=87f188a2'),\r\n (b'x-forwarded-host', b'datasette-apache-proxy-demo.fly.dev'),\r\n (b'x-forwarded-server', b'localhost'),\r\n (b'connection', b'Keep-Alive')],\r\n 'http_version': '1.1',\r\n 'method': 'GET',\r\n 'path': '/-/asgi-scope',\r\n 'query_string': b'',\r\n 'raw_path': b'/-/asgi-scope',\r\n 'root_path': '',\r\n 'scheme': 'https',\r\n 'server': ('127.0.0.1', 8001),\r\n 'type': 'http',\r\n 'url_route': {'kwargs': {}}}\r\n```\r\n\r\nThe version that works as ` 'raw_path': b'/-/asgi-scope'` - the version that fails has `'raw_path': b'/datasettes/-/asgi-scope'`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1099723916, "label": "Table+query JSON and CSV links broken when using `base_url` setting"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1590#issuecomment-1010533133", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1590", "id": 1010533133, "node_id": "IC_kwDOBm6k_c48O4MN", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-01-12T01:19:19Z", "updated_at": "2022-01-12T01:19:19Z", "author_association": "OWNER", "body": "Thanks for the steps to reproduce - I have your bug running on my laptop now.\r\n\r\nI've been mostly testing this stuff using the hosted copy of Datasette here, which doesn't exhibit the bug: https://datasette-apache-proxy-demo.fly.dev/prefix/fixtures?sql=select+sqlite_version%28%29\r\n\r\nSomething interesting definitely going on here!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1099723916, "label": "Table+query JSON and CSV links broken when using `base_url` setting"}, "performed_via_github_app": null}