Skip to content

fix(gcp): register nested URL protocol handler in GcfJarLauncher - #1434

Open
akenra wants to merge 1 commit into
spring-cloud:mainfrom
akenra:bugfix/gh-1336
Open

fix(gcp): register nested URL protocol handler in GcfJarLauncher#1434
akenra wants to merge 1 commit into
spring-cloud:mainfrom
akenra:bugfix/gh-1336

Conversation

@akenra

@akenra akenra commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

hi, @olegz!

I decided to try and fix this issue due to popular demand.

GcfJarLauncher's constructor bypasses Launcher.launch() and creates a LaunchedClassLoader directly, so it must register the nested: URL protocol handler itself. The tricky part is that the functions-framework does not load the function JAR on the system classpath - it loads it in a child URLClassLoader - and the JVM resolves java.protocol.handler.pkgs handlers only through the bootstrap/system classloaders. As a result, Handlers.register() alone still leaves the launcher with java.net.MalformedURLException: unknown protocol: nested (reproduced on a real Boot 4.1.0 function JAR, exactly as in gh-1336).

What I did:

  • The constructor now installs a URLStreamHandlerFactory that provides the nested.Handler instance directly, so the handler no longer has to be visible to the system classloader. Handlers.register() is kept as a fallback for the case where some other code has already installed a factory.
  • Reworked the test to reproduce the real deployment topology: it builds a Spring Boot fat JAR, forks a JVM that loads GcfJarLauncher from that JAR through a child URLClassLoader (spring-boot-loader is NOT on the system classpath), and asserts that the launcher actually constructs (GCF_JAR_LAUNCHER_SUCCEEDED), not just that the protocol property is set. The test fails with the Spring Cloud Function on GCP not compatible with Spring Boot 4.x #1336 stack trace on the previous version of this PR and passes with the factory installed.

Fixes gh-1336

@akenra
akenra force-pushed the bugfix/gh-1336 branch 2 times, most recently from 099f18c to 8d82e93 Compare August 2, 2026 11:48

@yurbar yurbar left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unfortunately this doesn't fix Cloud Functions. Handlers.register() only sets java.protocol.handler.pkgs, but java.net.URL resolves the handler class via the bootstrap/system classloader, and the functions-framework loads the function jar in a child URLClassLoader — so nested.Handler is never visible. Reproduced on a real Boot 4.1.0 function jar: still unknown protocol: nested with the jar in a child loader; it only works with the jar on the system classpath, which is what the new test does (-cp classPath) — and the test never asserts GCF_JAR_LAUNCHER_SUCCEEDED, so it passes even when the launcher throws

Handlers.register() only sets java.protocol.handler.pkgs, which the JVM resolves through the bootstrap/system classloaders. The functions-framework loads the deployed function JAR in a child URLClassLoader, so nested.Handler inside the JAR is never visible there and URLs still fail with 'unknown protocol: nested'. Install a URLStreamHandlerFactory that provides the handler directly and rework the test to load the launcher from a fat JAR through a child classloader, asserting the launcher constructs successfully.

Fixes spring-cloudgh-1336

Signed-off-by: akenra <37288280+akenra@users.noreply.github.com>
@akenra

akenra commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

@yurbar thanks for the catch. I reworked the PR:

  • Handlers.register() is no longer the whole fix. The constructor now installs a URLStreamHandlerFactory that returns nested.Handler directly (the class is referenced from the launcher's own classloader, so no system-classpath visibility is needed). Handlers.register() stays as a fallback if another factory is already installed.
  • The test now reproduces the real topology: it builds a Spring Boot fat JAR (loader classes at the root, a stub FunctionInvoker in BOOT-INF/lib), forks a JVM whose classpath has no spring-boot-loader, and loads GcfJarLauncher through a child URLClassLoader - mirroring how the functions-framework loads the deployed JAR. It asserts the launcher constructs (GCF_JAR_LAUNCHER_SUCCEEDED), which was the missing check.
  • Verified locally: the new test fails with the exact Spring Cloud Function on GCP not compatible with Spring Boot 4.x #1336 stack trace (unknown protocol: nested in JarUrlConnection.) on the previous code, and passes with the factory installed.

Could you re-check when you have a moment? Happy to adjust if the reproduction still doesn't match your setup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Spring Cloud Function on GCP not compatible with Spring Boot 4.x

2 participants