New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(remix): Resolve Remix Request API compatibility issues. #6215
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// Based on Remix's implementation of Fetch API | ||
// https://github.com/remix-run/web-std-io/tree/main/packages/fetch | ||
|
||
import { RemixRequest } from './types'; | ||
|
||
/* | ||
* Symbol extractor utility to be able to access internal fields of Remix requests. | ||
*/ | ||
const getInternalSymbols = ( | ||
request: Record<string, unknown>, | ||
): { | ||
bodyInternalsSymbol: string; | ||
requestInternalsSymbol: string; | ||
} => { | ||
const symbols = Object.getOwnPropertySymbols(request); | ||
return { | ||
bodyInternalsSymbol: symbols.find(symbol => symbol.toString().includes('Body internals')) as any, | ||
requestInternalsSymbol: symbols.find(symbol => symbol.toString().includes('Request internals')) as any, | ||
}; | ||
}; | ||
|
||
/** | ||
* Vendored from: | ||
* https://github.com/remix-run/web-std-io/blob/f715b354c8c5b8edc550c5442dec5712705e25e7/packages/fetch/src/utils/get-search.js#L5 | ||
*/ | ||
export const getSearch = (parsedURL: URL): string => { | ||
if (parsedURL.search) { | ||
return parsedURL.search; | ||
} | ||
|
||
const lastOffset = parsedURL.href.length - 1; | ||
const hash = parsedURL.hash || (parsedURL.href[lastOffset] === '#' ? '#' : ''); | ||
return parsedURL.href[lastOffset - hash.length] === '?' ? '?' : ''; | ||
}; | ||
|
||
/** | ||
* Convert a Request to Node.js http request options. | ||
* The options object to be passed to http.request | ||
* Vendored / modified from: | ||
* https://github.com/remix-run/web-std-io/blob/f715b354c8c5b8edc550c5442dec5712705e25e7/packages/fetch/src/request.js#L259 | ||
*/ | ||
export const normalizeRemixRequest = (request: RemixRequest): Record<string, any> => { | ||
const { requestInternalsSymbol, bodyInternalsSymbol } = getInternalSymbols(request); | ||
|
||
if (!requestInternalsSymbol) { | ||
throw new Error('Could not find request internals symbol'); | ||
} | ||
|
||
const { parsedURL } = request[requestInternalsSymbol]; | ||
const headers = new Headers(request[requestInternalsSymbol].headers); | ||
|
||
// Fetch step 1.3 | ||
if (!headers.has('Accept')) { | ||
headers.set('Accept', '*/*'); | ||
} | ||
|
||
// HTTP-network-or-cache fetch steps 2.4-2.7 | ||
let contentLengthValue = null; | ||
if (request.body === null && /^(post|put)$/i.test(request.method)) { | ||
contentLengthValue = '0'; | ||
} | ||
|
||
if (request.body !== null) { | ||
const totalBytes = request[bodyInternalsSymbol].size; | ||
// Set Content-Length if totalBytes is a number (that is not NaN) | ||
if (typeof totalBytes === 'number' && !Number.isNaN(totalBytes)) { | ||
contentLengthValue = String(totalBytes); | ||
} | ||
} | ||
|
||
if (contentLengthValue) { | ||
headers.set('Content-Length', contentLengthValue); | ||
} | ||
|
||
// HTTP-network-or-cache fetch step 2.11 | ||
if (!headers.has('User-Agent')) { | ||
headers.set('User-Agent', 'node-fetch'); | ||
} | ||
|
||
// HTTP-network-or-cache fetch step 2.15 | ||
if (request.compress && !headers.has('Accept-Encoding')) { | ||
headers.set('Accept-Encoding', 'gzip,deflate,br'); | ||
} | ||
|
||
let { agent } = request; | ||
|
||
if (typeof agent === 'function') { | ||
agent = agent(parsedURL); | ||
} | ||
|
||
if (!headers.has('Connection') && !agent) { | ||
headers.set('Connection', 'close'); | ||
} | ||
|
||
// HTTP-network fetch step 4.2 | ||
// chunked encoding is handled by Node.js | ||
const search = getSearch(parsedURL); | ||
|
||
// Manually spread the URL object instead of spread syntax | ||
const requestOptions = { | ||
path: parsedURL.pathname + search, | ||
pathname: parsedURL.pathname, | ||
hostname: parsedURL.hostname, | ||
protocol: parsedURL.protocol, | ||
port: parsedURL.port, | ||
hash: parsedURL.hash, | ||
search: parsedURL.search, | ||
// @ts-ignore - it does not has a query | ||
query: parsedURL.query, | ||
href: parsedURL.href, | ||
method: request.method, | ||
// @ts-ignore - not sure what this supposed to do | ||
headers: headers[Symbol.for('nodejs.util.inspect.custom')](), | ||
insecureHTTPParser: request.insecureHTTPParser, | ||
agent, | ||
|
||
// [SENTRY] For compatibility with Sentry SDK RequestData parser, adding `originalUrl` property. | ||
originalUrl: parsedURL.href, | ||
}; | ||
|
||
return requestOptions; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this your choice or just copied from their implementation? (Just curious why the spread operator is being avoided.)
Same question with the
@ts-ignore
s below.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neither of the above is a blocker (just my own curiosity), so I'm going to go ahead and merge this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, they are copied from the original implementation. Not sure what's the reason for spread operator there, but it seems they have kept the approach of
node-fetch
.