Skip to content

FetchHTTPClient for WASM#154

Merged
xbhatnag merged 9 commits into
mainfrom
fetch-client
May 7, 2026
Merged

FetchHTTPClient for WASM#154
xbhatnag merged 9 commits into
mainfrom
fetch-client

Conversation

@xbhatnag
Copy link
Copy Markdown
Collaborator

@xbhatnag xbhatnag commented May 1, 2026

Motivation

This PR proposes FetchHTTPClient: a HTTP Client implementation for Swift WASM built on top of Javascript's fetch() API.

Modifications

  • Added a barebones FetchHTTPClient implementation of HTTPClient
  • Added an example program Examples/WASMClient that uses FetchHTTPClient to make arbitrary network requests (within the confines of what the browser will allow).
  • Added Examples/WASMClient/README.md to explain how to build and run the example program.

Note: WASM targets only build when the HTTP_API_ENABLE_WASM environment variable is set on the host system.

Feature set

✅ Request and response headers
✅ Streamed response bodies
⚠️ Request bodies (request body is collected upfront prior to the request because some browsers don't support streaming request bodies)
⚠️ URL parsing (swift-http-types doesn't allow scheme-less URLs, which are valid in Javascript, for example: fetch("/index.html"))
❌ Request options (there are many options that exist in JS RequestInit that still need to be exposed)
❌ Trailers (browsers just don't support them)
❌ Swift Task Cancellation (broken because the cancel doesn't propagate to JS fetch())

@xbhatnag xbhatnag requested review from FranzBusch and guoye-zhang May 1, 2026 22:08
@xbhatnag xbhatnag self-assigned this May 1, 2026
@xbhatnag xbhatnag added the 🔨 semver/patch No public API change. label May 1, 2026
@xbhatnag
Copy link
Copy Markdown
Collaborator Author

xbhatnag commented May 1, 2026

large_response_body.mov
post.mov

return false;
}
for i in span.indices {
bytes.append(span[i])
Copy link
Copy Markdown
Collaborator Author

@xbhatnag xbhatnag May 1, 2026

Choose a reason for hiding this comment

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

Online searches suggest I can just do bytes.append(contentsOf: span) but the compiler was not having it, because Span does not implement Sequence?

Still, there's gotta be a better way than adding byte by byte!

Comment thread Sources/FetchHTTPClient/FetchHTTPClient.swift Outdated
Comment thread Package.swift Outdated
Comment thread Sources/FetchHTTPClient/JSImports.swift Outdated
Comment thread Sources/FetchHTTPClient/Buffer.swift Outdated
Comment thread Sources/FetchHTTPClient/FetchHTTPClient.swift Outdated
Comment thread Sources/FetchHTTPClient/FetchHTTPClient.swift
Comment thread Examples/WASMClient/UI.swift Outdated
Comment thread Sources/FetchHTTPClient/FetchHTTPClient.swift Outdated
Comment thread Sources/FetchHTTPClient/FetchHTTPClient.swift Outdated
@guoye-zhang guoye-zhang added 🆕 semver/minor Adds new public API. and removed 🔨 semver/patch No public API change. labels May 6, 2026
Comment thread Package.swift Outdated

import PackageDescription

let enableWASM = Context.environment["SWIFT_ENABLE_WASM_TARGETS"] != nil
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This environment variable now conditionally adds the WASM targets in for compilation and is disabled by default.

Comment thread Sources/FetchHTTPClient/FetchHTTPClient.swift Outdated
@xbhatnag
Copy link
Copy Markdown
Collaborator Author

xbhatnag commented May 7, 2026

I hacked up our Client Conformance Tests to work in WASM with the Test Server hosted out of process. Then I ran the test on Safari + Mac.

These tests failed as expected:

  • EchoInterleave and SpeakInterleave: Not surprising, because we are collecting the request body upfront. Some browsers cannot stream the request body.
  • BasicCookieSetAndUse: Not surprising, because browsers don't reveal the Set-Cookie header with JS/WASM.
  • TrailerRead and TrailerWrite: Not surprising, because browsers don't support trailers at all.

These were the unexpected failures that need addressing:

  • CancelPreBody and CancelPreHeaders: Cancellation is currently broken with fetch(). We need to setup a JS AbortController and use that when the Swift task is cancelled.
  • The browser is modifying the case of the request header names (forcing them to lowercase), which is breaking some assumptions in our conformance tests. To be fair, this is actually an issue with the conformance tests. The tests should be case-insensitive when checking headers.
  • ETag: Have yet to investigate why this is failing. Multiple assertions are coming up false.

Everything else passed 🎉

@xbhatnag xbhatnag merged commit 60f3be3 into main May 7, 2026
20 of 23 checks passed
@xbhatnag xbhatnag deleted the fetch-client branch May 7, 2026 21:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🆕 semver/minor Adds new public API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants