Skip to content

Allow backend URLs to be configurable via window variables#11378

Merged
vdonato merged 2 commits intodevelopfrom
vdonato/configurable-backend-urls
May 19, 2025
Merged

Allow backend URLs to be configurable via window variables#11378
vdonato merged 2 commits intodevelopfrom
vdonato/configurable-backend-urls

Conversation

@vdonato
Copy link
Copy Markdown
Collaborator

@vdonato vdonato commented May 15, 2025

Describe your changes

In some new (Snowflake-internal) deployments of Streamlit, we want to be able to configure an
app to load its index.html from one origin but make requests to a Streamlit server at another.
This PR enables this by reading the window.__STREAMLIT_BACKEND_BASE_URL and
window.__STREAMLIT_HOST_CONFIG_BASE_URL variables and sending requests meant
for the Streamlit server to those URLs if the variables are set. Otherwise, we continue to assume
that the server can be reached at window.location.href as usual.

The index.html served by this deployment will then be modified to set these variables in
<script> tags to configure these URLs.

This may also eventually be useful more generally by the Open Source community in some scenarios
where an app developer, for example, wants to serve the static assets of their app from a CDN while
hosting their Streamlit server at a different origin, but we'll need to make improvements to our
documentation + provide some additional tooling before we can consider this broadly supported.

Note that:

  • We also made a small refactor to get rid of the isHttps helper function since it's redundant on top
    of the protocol field that's already set when parsing a URL.
  • When window.__STREAMLIT_BACKEND_BASE_URL is set, we currently expect there to be some weirdness
    that happens with multipage apps and the browser's forward/back buttons, but given this behavior isn't
    exposed unless you're changing Streamlit internals, and since fixing this will require some thought, we punt
    on it for now.

Testing Plan

  • Unit Tests (JS and/or Python)
    • Added JS unit tests
  • Any manual testing needed?
    • We're not quite at the point where we can test this e2e just yet and won't be for a few weeks, but merging this code should still be fine since behavior will be totally unchanged in the regular PyPI distribution of Streamlit.

Contribution License Agreement

By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.

@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io bot commented May 15, 2025

🎉 Snyk checks have passed. No issues have been found so far.

security/snyk check is complete. No issues have been found. (View Details)

license/snyk check is complete. No issues have been found. (View Details)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented May 15, 2025

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-11378/streamlit-1.45.1-py3-none-any.whl
🕹️ Preview app pr-11378.streamlit.app (☁️ Deploy here if not accessible)

@vdonato vdonato added security-assessment-completed impact:internal PR changes only affect internal code change:feature PR contains new feature or enhancement implementation labels May 15, 2025
@vdonato vdonato force-pushed the vdonato/configurable-backend-urls branch from 24d1431 to 8cc01b7 Compare May 15, 2025 21:51
@vdonato vdonato force-pushed the vdonato/configurable-backend-urls branch 3 times, most recently from 92dddcb to 0b1ee90 Compare May 15, 2025 23:41
// weird-looking triple `replace()`.
return decodeURIComponent(
document.location.pathname
pathname
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using document.location.pathname here seems to have been an accident as we pass document.location.pathname to this helper function where it's used as the pathname variable but then proceed to never use the variable.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh :( I think we might want to add a linting rule to catch unused parameters. I added it as tech debt here (cc @sfc-gh-bnisco)

// rerun the current page.
pageScriptHash = currentPageScriptHash
} else if (window.__STREAMLIT_BACKEND_URL) {
// We currently don't support navigating directly to a subpage of a
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it unreasonable to leave the subpage still tied to the index.html and the backend url does not change based on the page being viewed? Or does that break down in some way?

cc @sfc-gh-kmcgrady

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kmcgrady is OOO. I think this should work fine, but not 100% sure. Might need some manual validation.

Copy link
Copy Markdown
Collaborator Author

@vdonato vdonato May 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's some trickiness here so would prefer to just leave this behavior explicitly unsupported for now. I think we'll need to do some work on both the OSS and SiS vNext ends to fully support this behavior, and until then this behavior not working probably won't be a big deal.

The main issue is that I think that the document.location here will effectively be hidden from the user since they generally won't see the URL of the Streamlit app embedded in an iframe without doing something like poking around in developer tools. If we want to support being able to navigate directly to a subpage of a multipage app in the vNext world, then we'd need some way of getting the path of the page that we're navigating to from the Snowsight URL (which is assuming where this info would live) into the path used in the iframe.

At this point, things start to break down because the way that we currently know which parts of the URL are part of the base path and which part is the page name is based on what piece of document.location.href we've determined to be the base path, and setting a different base path messes with this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is supported today - Snowsight supports deep linking into a sub-page directly and we should continue to do so for vNext. You're right though, this wouldn't work OOTB given how we iteratively guess the base URL by guessing host-config. I don't have something clever to suggest as a workaround other than being able to define a static base path which is not a great option. We can leave undefined for now but would like to fast follow.

@sfc-gh-lmasuch sfc-gh-lmasuch requested a review from Copilot May 19, 2025 15:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enables configurable backend URLs by reading new window variables and refactors URL construction logic accordingly. Key changes include:

  • Removing the redundant isHttps() helper and using the URL.protocol property instead.
  • Adding support for window.__STREAMLIT_BACKEND_URL and window.__STREAMLIT_HOST_CONFIG_BACKEND_URL so that static assets and server requests can target different origins.
  • Updating tests and related utilities to support the new URL configuration flows.

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
frontend/utils/src/uri/index.ts Refactored buildHttpUri to use protocol from URL and removed the isHttps() helper.
frontend/utils/src/uri/index.test.ts Updated tests to pass the protocol property instead of relying on isHttps().
frontend/lib/src/util/utils.ts Modified the extraction of pathname using a passed pathname instead of document.location.
frontend/connection/src/utils.ts Refactored base URI functions to support optional configurable backend URL in window.
frontend/connection/src/utils.test.ts Updated tests to reflect changes made to URL building functions and added tests for getBaseUriParts.
frontend/connection/src/types.ts Declared new global window variables for backend configuration.
frontend/connection/src/WebsocketConnection.test.tsx Updated test mocks to include protocol field for URI objects.
frontend/connection/src/DoInitPings.tsx Updated host config URI construction to read from window.__STREAMLIT_HOST_CONFIG_BACKEND_URL.
frontend/connection/src/DefaultStreamlitEndpoints.test.ts Updated mock server URI definitions to include the protocol property.
frontend/app/src/App.tsx Added conditionals and TODO comments to handle cases when window.__STREAMLIT_BACKEND_URL is set.
Comments suppressed due to low confidence (1)

frontend/connection/src/DoInitPings.tsx:130

  • Consider adding validation or error handling when parsing window.__STREAMLIT_HOST_CONFIG_BACKEND_URL. An invalid URL from this global variable could break the host configuration URI construction.
const hostConfigServerUriParts = window.__STREAMLIT_HOST_CONFIG_BACKEND_URL ? getBaseUriParts(window.__STREAMLIT_HOST_CONFIG_BACKEND_URL) : uriParts

Copy link
Copy Markdown
Collaborator

@lukasmasuch lukasmasuch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍 Added a couple of nits above.

@vdonato vdonato force-pushed the vdonato/configurable-backend-urls branch from 0b1ee90 to a81841d Compare May 19, 2025 20:50
@vdonato vdonato enabled auto-merge (squash) May 19, 2025 21:03
@vdonato vdonato merged commit 99728bc into develop May 19, 2025
38 of 39 checks passed
@vdonato vdonato deleted the vdonato/configurable-backend-urls branch May 19, 2025 21:47
vdonato added a commit that referenced this pull request Jun 7, 2025
## Describe your changes

In #11378, we left some TODOs to come back and fix page URL handling for multipage
apps in the world where we configure the client to hit a different origin for the Streamlit
server from the one that we fetched our `index.html` from. This PR addresses these issues
by defining a new `MAIN_PAGE_BASE_URL` field in the `__streamlit` object which lets us
explicitly specify what the base URL for the document (that is, the URL of the main page of
the app) should be.

Note that this change will also make it possible to fix the annoying issue where initial
healthcheck requests hit a 404 when navigating directly to a subpage of a multipage
app in the pure OSS world, but we don't do so in this PR as it'll also require us to change
how our static asset handler works to no longer serve a truly static `index.html` but
dynamically set some pieces of it, which would take a nontrivial amount of additional
effort.

## Testing Plan

- Unit Tests (JS and/or Python)
   - Adjusted JS unit tests

---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this
project are made under the Apache 2.0 license.

---------

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
Co-authored-by: Copilot <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:feature PR contains new feature or enhancement implementation impact:internal PR changes only affect internal code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants