Skip to content
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

feat: impl fetch #542

Merged
merged 2 commits into from
Oct 8, 2024
Merged

feat: impl fetch #542

merged 2 commits into from
Oct 8, 2024

Conversation

killagu
Copy link
Member

@killagu killagu commented Oct 8, 2024

  • keep urllib:request, urllib:response channel message
  • add urllib:fetch:request, urllib:fetch:response channel message

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a FetchFactory class for enhanced HTTP request management with diagnostics.
    • Added a new FetchOpaque type for tracking request metadata.
    • Enhanced HttpClient with new timing and diagnostics capabilities.
  • Bug Fixes

    • Improved error handling for socket-related issues in HttpClient.
  • Documentation

    • Expanded public exports for better usability, including new interfaces and constants.
  • Tests

    • Added unit tests for the new fetch functionality to ensure reliability.

- keep urllib:request, urllib:response channel message
- add urllib:fetch:request, urllib:fetch:response channel message
@killagu killagu requested a review from fengmk2 October 8, 2024 05:28
Copy link

coderabbitai bot commented Oct 8, 2024

Walkthrough

The changes introduce several new functionalities and modifications across multiple files in the codebase. A new FetchOpaqueInterceptor is implemented to manage opaque request data in fetch operations, while the HttpAgent and HttpClient classes are updated for better structure and functionality. New types and interfaces are added to enhance request metadata handling. Additionally, a new FetchFactory class is created for managing HTTP requests with improved diagnostics. The index and utils files are updated to support these changes, and unit tests are added to ensure the functionality of the new fetch capabilities.

Changes

File Change Summary
src/FetchOpaqueInterceptor.ts - Added interfaces: FetchOpaque, OpaqueInterceptorOptions
- Added function: fetchOpaqueInterceptor(opts: OpaqueInterceptorOptions)
src/HttpAgent.ts - Changed HttpAgentOptions from type alias to interface extending Agent.Options
- Updated constructor to use lookupFunction for DNS lookups
src/HttpClient.ts - Exported constant: PROTO_RE
- Added interface: UnidiciTimingInfo
- Exported channels object
- Updated internal method calls for socket information handling
src/Request.ts - Added new type: FetchMeta
- Updated RequestMeta to include optional ctx property
src/fetch.ts - Added class: FetchFactory
- Added several methods for HTTP request management and diagnostics, including fetch, setClientOptions, and getDispatcherPoolStats
- Added interfaces: UrllibRequestInit, FetchDiagnosticsMessage, FetchResponseDiagnosticsMessage
src/index.ts - Enhanced exports including Request, Response, HttpClient, and FetchFactory
- Added new method: curl for handling domain socket connections
src/utils.ts - Added functions: updateSocketInfo, convertHeader for socket management and header conversion
test/fetch.test.ts - Introduced unit tests for the fetch functionality using Vitest, validating request and response diagnostics

Possibly related PRs

  • fix: add export type #540: The changes in src/index.ts include new exports related to HttpClient and HttpAgent, which may interact with the new FetchOpaqueInterceptor functionality introduced in the main PR, particularly in terms of managing HTTP requests and options.

Suggested labels

enhancement

Suggested reviewers

  • fengmk2

Poem

In the warren where code does play,
New fetches hop and dance today.
Opaque data in a swirl,
HTTP magic starts to unfurl.
With tests that leap and bounds that soar,
Our rabbit code is clever, for sure! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

pkg-pr-new bot commented Oct 8, 2024

Open in Stackblitz

yarn add https://pkg.pr.new/node-modules/[email protected]

commit: b1e131f

Copy link

codecov bot commented Oct 8, 2024

Codecov Report

Attention: Patch coverage is 85.11450% with 39 lines in your changes missing coverage. Please review.

Project coverage is 94.70%. Comparing base (1765806) to head (b1e131f).
Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
src/fetch.ts 81.57% 35 Missing ⚠️
src/utils.ts 92.00% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #542      +/-   ##
==========================================
- Coverage   97.47%   94.70%   -2.78%     
==========================================
  Files           7        9       +2     
  Lines         990     1208     +218     
  Branches      260      281      +21     
==========================================
+ Hits          965     1144     +179     
- Misses         21       60      +39     
  Partials        4        4              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@fengmk2 fengmk2 added the enhancement New feature or request label Oct 8, 2024
@fengmk2 fengmk2 merged commit 55a634c into master Oct 8, 2024
18 of 20 checks passed
@fengmk2 fengmk2 deleted the feat/fetch branch October 8, 2024 05:34
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 14

🧹 Outside diff range and nitpick comments (7)
test/fetch.test.ts (2)

48-51: LGTM: Fetch-specific assertions are comprehensive.

The assertions for fetch-specific diagnostics messages are well-implemented, covering fetch, response, and timing information.

Consider adding an assertion to check if the timingInfo contains expected properties, such as startTime or endTime, to ensure the timing data is meaningful.

assert(fetchResponseDiagnosticsMessage!.timingInfo.startTime);
assert(fetchResponseDiagnosticsMessage!.timingInfo.endTime);

1-57: Overall, excellent test implementation with room for minor enhancements.

This test file provides a comprehensive check of the fetch functionality, including diagnostics messages and dispatcher pool stats. The structure is solid and follows testing best practices.

To further improve the test:

  1. Consider adding more specific test cases to cover different scenarios (e.g., error cases, different response types).
  2. Add comments to explain the purpose of certain steps, especially for the dispatcher pool stats.
  3. Consider parameterizing the test to run with different configurations of the FetchFactory client options.

Great job on implementing this test suite!

src/HttpAgent.ts (1)

Line range hint 39-66: Well-structured implementation of lookupFunction!

The new implementation of lookupFunction is well-designed and incorporates the necessary logic for DNS lookup and address checking. Great job on handling both string and array address formats for compatibility.

A minor suggestion for improved readability:

Consider extracting the address checking logic into a separate method. This would make the lookupFunction more concise and easier to understand at a glance. For example:

private checkAddressValidity(address: string | { address: string, family: number }[], family: number, hostname: string): Error | null {
  if (typeof address === 'string') {
    if (!this.options.checkAddress(address, family, hostname)) {
      return new IllegalAddressError(hostname, address, family);
    }
  } else if (Array.isArray(address)) {
    for (const addr of address) {
      if (!this.options.checkAddress(addr.address, addr.family, hostname)) {
        return new IllegalAddressError(hostname, addr.address, addr.family);
      }
    }
  }
  return null;
}

Then, you can use this method in the lookupFunction:

const error = this.checkAddressValidity(address, family, hostname);
if (error) return (callback as any)(error, address, family);

This refactoring would improve the overall readability and maintainability of the code.

src/Request.ts (3)

164-164: LGTM! Consider adding JSDoc comment for ctx.

The addition of the optional ctx property to RequestMeta is a good improvement for flexibility. The use of the unknown type is appropriate for a generic context.

Consider adding a JSDoc comment to explain the purpose and potential usage of the ctx property. For example:

/** Optional context information for the request */
ctx?: unknown;

166-169: LGTM! Consider adding JSDoc comments and type imports.

The addition of the FetchMeta type aligns well with the PR objectives for implementing new fetch-related channel messages. The structure is consistent with existing types.

Consider the following improvements:

  1. Add JSDoc comments to explain the purpose of the FetchMeta type and its properties.
  2. Import the Request type explicitly for better code readability.

Example implementation:

import type { Request } from 'undici';

/** Metadata for fetch requests */
export type FetchMeta = {
  /** Unique identifier for the fetch request */
  requestId: number;
  /** The fetch request object */
  request: Request;
};

6-6: Overall, the changes look good and align with the PR objectives.

The addition of the FetchMeta type and the modification of RequestMeta enhance the functionality for handling fetch requests. The changes are consistent with the existing codebase structure and naming conventions.

As the project evolves to include more fetch-related functionality, consider creating a separate file for fetch-specific types if the number of such types increases. This would help maintain a clear separation of concerns and improve code organization.

Also applies to: 165-169

src/HttpClient.ts (1)

69-69: Clarify the Commented-out Property 'ALPNNegotiatedProtocol'

The property ALPNNegotiatedProtocol is commented out within the UnidiciTimingInfo interface. If this property is intended to be part of the interface, consider uncommenting it or providing a comment explaining its current status.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 7c8aff0 and b1e131f.

📒 Files selected for processing (8)
  • src/FetchOpaqueInterceptor.ts (1 hunks)
  • src/HttpAgent.ts (3 hunks)
  • src/HttpClient.ts (5 hunks)
  • src/Request.ts (2 hunks)
  • src/fetch.ts (1 hunks)
  • src/index.ts (2 hunks)
  • src/utils.ts (2 hunks)
  • test/fetch.test.ts (1 hunks)
🧰 Additional context used
🪛 Biome
src/fetch.ts

[error] 59-262: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

🪛 GitHub Check: codecov/patch
src/fetch.ts

[warning] 68-69: src/fetch.ts#L68-L69
Added lines #L68 - L69 were not covered by tests


[warning] 84-91: src/fetch.ts#L84-L91
Added lines #L84 - L91 were not covered by tests


[warning] 93-98: src/fetch.ts#L93-L98
Added lines #L93 - L98 were not covered by tests


[warning] 101-106: src/fetch.ts#L101-L106
Added lines #L101 - L106 were not covered by tests


[warning] 117-118: src/fetch.ts#L117-L118
Added lines #L117 - L118 were not covered by tests


[warning] 227-237: src/fetch.ts#L227-L237
Added lines #L227 - L237 were not covered by tests

src/utils.ts

[warning] 198-201: src/utils.ts#L198-L201
Added lines #L198 - L201 were not covered by tests

🔇 Additional comments (13)
test/fetch.test.ts (4)

1-20: LGTM: Imports and test setup are well-structured.

The import statements and test suite setup are appropriate and follow best practices. The use of beforeAll and afterAll hooks ensures proper setup and teardown of the test server.


41-47: LGTM: Fetch execution and basic assertions are correct.

The fetch execution and initial assertions are well-implemented. They correctly check for the presence of the response and the expected diagnostics messages.


22-39: LGTM: Comprehensive test case setup with a minor clarification needed.

The test case setup is well-structured, covering all relevant diagnostics channels. The use of type assertions for diagnostics messages ensures type safety.

However, could you clarify the purpose of setting up FetchFactory client options with an empty object? Is this intentional for testing default behavior?

✅ Verification successful

LGTM: Comprehensive test case setup with no further issues found.

The test case setup is well-structured, covering all relevant diagnostics channels. The use of type assertions for diagnostics messages ensures type safety.

The use of an empty object for setting FetchFactory client options is confirmed to be intentional for testing default behavior.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify if there are any other occurrences of setClientOptions with non-empty objects
rg --type typescript 'FetchFactory\.setClientOptions\([^{}]' -g '!test/fetch.test.ts'

Length of output: 122


Script:

#!/bin/bash
# Verify if there are any other occurrences of setClientOptions with non-empty objects in TypeScript files
rg 'FetchFactory\.setClientOptions\([^{}]' -g '*.ts' -g '!test/fetch.test.ts'

Length of output: 79


53-55: LGTM: Dispatcher pool stats assertions are present, but could use more context.

The retrieval and assertion of dispatcher pool stats are implemented correctly. However, it would be beneficial to add a comment explaining the purpose of these stats and what specific properties we expect to see in a typical scenario.

Consider adding assertions for specific expected properties in the stats object to make the test more robust.

Add a comment explaining the purpose of dispatcher pool stats and their significance in this test.

src/HttpAgent.ts (3)

11-16: Excellent improvement to HttpAgentOptions!

The change from a type alias to an interface extending Agent.Options is a great improvement. This modification:

  1. Enhances type checking capabilities.
  2. Allows for easier future extensions to the options.
  3. Maintains compatibility with existing Agent.Options.

The new properties (lookup, checkAddress, connect, and allowH2) align well with the PR objectives of implementing new fetch-related capabilities.


67-68: Effective use of spread operator in super call!

The updated super call effectively incorporates the new options structure:

  1. Using ...baseOpts ensures all base options are passed to the superclass.
  2. The connect object correctly includes the new lookupFunction and allowH2 option.

This change aligns well with the new options structure and functionality introduced in this PR.


Line range hint 1-92: Overall excellent implementation of fetch-related enhancements!

The changes in this file successfully implement the new fetch-related capabilities mentioned in the PR objectives. Key improvements include:

  1. Enhanced HttpAgentOptions structure for better type checking and extensibility.
  2. Well-implemented lookupFunction that incorporates DNS lookup and address checking.
  3. Effective use of new options in the HttpAgent constructor.

The code quality is high, with only minor suggestions for improvement in terms of readability. These changes will significantly enhance the urllib module's capabilities for handling fetch operations.

src/utils.ts (1)

5-7: Imports added are appropriate and necessary

The additions of SocketInfo, symbols, and IncomingHttpHeaders imports are appropriate for the new functionalities introduced in the module.

src/HttpClient.ts (5)

40-40: Importing 'updateSocketInfo' Function

The addition of updateSocketInfo to the import statement ensures that the function is available for use within this file. This change is appropriate.


50-50: Exporting 'PROTO_RE' Constant

Exporting PROTO_RE allows external modules to access this regular expression, enhancing reusability. The export statement is correctly implemented.


161-165: Addition of 'fetchRequest' and 'fetchResponse' Channels

The new channels fetchRequest and fetchResponse enhance the diagnostics capabilities of the HTTP client. The implementation is correct and consistent with existing patterns.


733-733: Consistent Invocation of 'updateSocketInfo' Function

As previously noted, updateSocketInfo is invoked with a different number of arguments compared to line 657. Ensure that this discrepancy is intentional and that the function handles varying argument counts appropriately.


657-657: Verify Consistent Usage of 'updateSocketInfo' Function

At line 657, updateSocketInfo is called with two arguments. However, at line 733, it is called with three arguments. Please verify that the function is intended to handle different numbers of arguments and that this usage is correct.

To confirm the function signature, run the following script:

✅ Verification successful

Consistent Usage Confirmed for updateSocketInfo Function

The updateSocketInfo function is correctly used with both two and three arguments as per its definition.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the definition of 'updateSocketInfo' to verify accepted arguments.

# Expected result: The function should accept both two and three arguments, or usage should be updated for consistency.

# Search for the function definition
rg --type js --type ts --hidden --glob '!*node_modules/*' -A 5 'function updateSocketInfo'

Length of output: 548

Comment on lines +1 to +2
// const { AsyncLocalStorage } = require('node:async_hooks');
import { AsyncLocalStorage } from 'node:async_hooks';
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove commented-out code to improve code clarity

The file contains several blocks of commented-out code (lines 1-2, 6-7, and 20-27) which may no longer be needed. Removing these unused code sections can enhance readability and maintainability.

Also applies to: 6-7, 20-27

Comment on lines +8 to +18
export interface FetchOpaque {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
[symbols.kRequestId]: number;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
[symbols.kRequestStartTime]: number;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
[symbols.kEnableRequestTiming]: number;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid using @ts-ignore by properly typing symbol-keyed properties

In the FetchOpaque interface, @ts-ignore comments are used to suppress TypeScript errors when defining properties with symbol keys. Instead of suppressing these errors, consider properly typing the symbol properties to adhere to TypeScript's type system.

For example, you can declare the symbols as unique symbol types:

declare const kRequestId: unique symbol;
declare const kRequestStartTime: unique symbol;
declare const kEnableRequestTiming: unique symbol;

export interface FetchOpaque {
  [kRequestId]: number;
  [kRequestStartTime]: number;
  [kEnableRequestTiming]: number;
}

This approach allows TypeScript to recognize the symbol keys without suppressing type checks.

}

export function fetchOpaqueInterceptor(opts: OpaqueInterceptorOptions) {
const opaqueLocalStorage = opts?.opaqueLocalStorage;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove unnecessary optional chaining for opaqueLocalStorage

Since opaqueLocalStorage is a required property in OpaqueInterceptorOptions, the optional chaining opts?.opaqueLocalStorage in line 33 and opaqueLocalStorage?.getStore() in line 36 is unnecessary. Removing the optional chaining simplifies the code and ensures that undefined values are properly handled elsewhere if needed.

Apply this change:

- const opaqueLocalStorage = opts?.opaqueLocalStorage;
+ const opaqueLocalStorage = opts.opaqueLocalStorage;

...

- const opaque = opaqueLocalStorage?.getStore();
+ const opaque = opaqueLocalStorage.getStore();

Also applies to: 36-36

export function fetchOpaqueInterceptor(opts: OpaqueInterceptorOptions) {
const opaqueLocalStorage = opts?.opaqueLocalStorage;
return (dispatch: Dispatcher['dispatch']): Dispatcher['dispatch'] => {
return function redirectInterceptor(opts: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandlers) {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Rename the interceptor function for clarity

The function returned is named redirectInterceptor, which may not accurately reflect its purpose in this context. Consider renaming the function to opaqueInterceptor to better convey its role in handling opaque data.

Comment on lines +35 to +38
return function redirectInterceptor(opts: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandlers) {
const opaque = opaqueLocalStorage?.getStore();
(handler as any).opaque = opaque;
return dispatch(opts, handler);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid casting handler to any; extend handler types to include opaque property

Casting handler to any in line 37 ((handler as any).opaque = opaque;) bypasses type safety and can introduce potential bugs. Instead, extend the Dispatcher.DispatchHandlers interface to include the opaque property, ensuring type safety and clarity.

Define a new interface:

interface OpaqueDispatchHandlers extends Dispatcher.DispatchHandlers {
  opaque?: FetchOpaque;
}

Update the function signature:

return function opaqueInterceptor(
  opts: Dispatcher.DispatchOptions,
  handler: OpaqueDispatchHandlers
) {
  const opaque = opaqueLocalStorage.getStore();
  handler.opaque = opaque;
  return dispatch(opts, handler);
};

This approach maintains type integrity and avoids the pitfalls of using any.

Comment on lines +13 to +18
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import undiciSymbols from 'undici/lib/core/symbols.js';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import undiciFetchSymbols from 'undici/lib/web/fetch/symbols.js';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid using @ts-ignore and importing from internal modules

Importing internal modules from undici, such as 'undici/lib/core/symbols.js' and 'undici/lib/web/fetch/symbols.js', is discouraged because internal modules are not part of the public API and may change without notice, leading to potential breaking changes.

Additionally, using @ts-ignore suppresses TypeScript errors, which can hide potential issues in the code.

Consider accessing the required functionality through the official public API or requesting the library maintainers to expose the necessary symbols.

Comment on lines +59 to +262
[symbols.kEnableRequestTiming]: !!(init.timing ?? true),
[symbols.kRequestTiming]: timing,
// [symbols.kRequestOriginalOpaque]: originalOpaque,
};
const reqMeta: RequestMeta = {
requestId,
url: request.url,
args: {
method: request.method as HttpMethod,
type: request.method as HttpMethod,
data: request.body,
headers: convertHeader(request.headers),
},
retries: 0,
};
const fetchMeta: FetchMeta = {
requestId,
request,
};
const socketInfo: SocketInfo = {
id: 0,
localAddress: '',
localPort: 0,
remoteAddress: '',
remotePort: 0,
remoteFamily: '',
bytesWritten: 0,
bytesRead: 0,
handledRequests: 0,
handledResponses: 0,
};
channels.request.publish({
request: reqMeta,
} as RequestDiagnosticsMessage);
channels.fetchRequest.publish({
fetch: fetchMeta,
} as FetchDiagnosticsMessage);

let res: Response;
// keep urllib createCallbackResponse style
const resHeaders: IncomingHttpHeaders = {};
const urllibResponse = {
status: -1,
statusCode: -1,
statusText: '',
statusMessage: '',
headers: resHeaders,
size: 0,
aborted: false,
rt: 0,
keepAliveSocket: true,
requestUrls: [
request.url,
],
timing,
socket: socketInfo,
retries: 0,
socketErrorRetries: 0,
} as any as RawResponseWithMeta;
try {
await FetchFactory.#opaqueLocalStorage.run(internalOpaque, async () => {
res = await UndiciFetch(input, init);
});
} catch (e: any) {
channels.response.publish({
fetch: fetchMeta,
error: e,
} as FetchResponseDiagnosticsMessage);
channels.fetchResponse.publish({
request: reqMeta,
response: urllibResponse,
error: e,
} as ResponseDiagnosticsMessage);
throw e;
}

// get unidici internal response
const state = Reflect.get(res!, undiciFetchSymbols.kState) as Dispatcher.ResponseData;
updateSocketInfo(socketInfo, internalOpaque /* , rawError */);

urllibResponse.headers = convertHeader(res!.headers);
urllibResponse.status = urllibResponse.statusCode = res!.status;
urllibResponse!.statusMessage = res!.statusText;
if (urllibResponse.headers['content-length']) {
urllibResponse.size = parseInt(urllibResponse.headers['content-length']);
}
urllibResponse.rt = performanceTime(requestStartTime);

channels.fetchResponse.publish({
fetch: fetchMeta,
timingInfo: (state as any).timingInfo,
response: res!,
} as FetchResponseDiagnosticsMessage);
channels.response.publish({
request: reqMeta,
response: urllibResponse,
} as ResponseDiagnosticsMessage);
return res!;
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor FetchFactory class to use plain functions instead of static methods

The FetchFactory class contains only static methods and properties. According to best practices, it's preferable to use modules or namespaces when you don't need to instantiate a class. Classes with only static members can add unnecessary complexity and may confuse other developers about the intended use.

Consider refactoring the code by exporting plain functions and constants without wrapping them in a class.

🧰 Tools
🪛 Biome

[error] 59-262: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

🪛 GitHub Check: codecov/patch

[warning] 68-69: src/fetch.ts#L68-L69
Added lines #L68 - L69 were not covered by tests


[warning] 84-91: src/fetch.ts#L84-L91
Added lines #L84 - L91 were not covered by tests


[warning] 93-98: src/fetch.ts#L93-L98
Added lines #L93 - L98 were not covered by tests


[warning] 101-106: src/fetch.ts#L101-L106
Added lines #L101 - L106 were not covered by tests


[warning] 117-118: src/fetch.ts#L117-L118
Added lines #L117 - L118 were not covered by tests


[warning] 227-237: src/fetch.ts#L227-L237
Added lines #L227 - L237 were not covered by tests

Comment on lines +227 to +237
channels.response.publish({
fetch: fetchMeta,
error: e,
} as FetchResponseDiagnosticsMessage);
channels.fetchResponse.publish({
request: reqMeta,
response: urllibResponse,
error: e,
} as ResponseDiagnosticsMessage);
throw e;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add unit tests for error handling in the fetch method

The error handling block in the fetch method is crucial for managing exceptions during the fetch operation and publishing diagnostic messages. Currently, this logic is not covered by unit tests.

Adding tests that simulate errors during the fetch process will ensure that exceptions are handled correctly and that diagnostic messages are published as expected, enhancing the robustness of the code.

Would you like assistance in creating unit tests for these error scenarios?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 227-237: src/fetch.ts#L227-L237
Added lines #L227 - L237 were not covered by tests

Comment on lines +240 to +241
const state = Reflect.get(res!, undiciFetchSymbols.kState) as Dispatcher.ResponseData;
updateSocketInfo(socketInfo, internalOpaque /* , rawError */);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid accessing internal properties of external libraries

Using Reflect.get to access internal properties like undiciFetchSymbols.kState can lead to fragile code because these internal implementations may change without warning in future library updates.

Consider using the public API of the undici library to achieve the desired functionality or reaching out to the library maintainers to expose the necessary properties officially.

Comment on lines +52 to +71
export interface UnidiciTimingInfo {
startTime: number;
redirectStartTime: number;
redirectEndTime: number;
postRedirectStartTime: number;
finalServiceWorkerStartTime: number;
finalNetworkResponseStartTime: number;
finalNetworkRequestStartTime: number;
endTime: number;
encodedBodySize: number;
decodedBodySize: number;
finalConnectionTimingInfo: {
domainLookupStartTime: number;
domainLookupEndTime: number;
connectionStartTime: number;
connectionEndTime: number;
secureConnectionStartTime: number;
// ALPNNegotiatedProtocol: undefined
};
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct Typo in Interface Name to 'UndiciTimingInfo'

The interface name UnidiciTimingInfo seems to have a typo. It should be UndiciTimingInfo to match the 'undici' module name.

Apply this diff to correct the interface name:

-export interface UnidiciTimingInfo {
+export interface UndiciTimingInfo {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface UnidiciTimingInfo {
startTime: number;
redirectStartTime: number;
redirectEndTime: number;
postRedirectStartTime: number;
finalServiceWorkerStartTime: number;
finalNetworkResponseStartTime: number;
finalNetworkRequestStartTime: number;
endTime: number;
encodedBodySize: number;
decodedBodySize: number;
finalConnectionTimingInfo: {
domainLookupStartTime: number;
domainLookupEndTime: number;
connectionStartTime: number;
connectionEndTime: number;
secureConnectionStartTime: number;
// ALPNNegotiatedProtocol: undefined
};
}
export interface UndiciTimingInfo {
startTime: number;
redirectStartTime: number;
redirectEndTime: number;
postRedirectStartTime: number;
finalServiceWorkerStartTime: number;
finalNetworkResponseStartTime: number;
finalNetworkRequestStartTime: number;
endTime: number;
encodedBodySize: number;
decodedBodySize: number;
finalConnectionTimingInfo: {
domainLookupStartTime: number;
domainLookupEndTime: number;
connectionStartTime: number;
connectionEndTime: number;
secureConnectionStartTime: number;
// ALPNNegotiatedProtocol: undefined
};
}

fengmk2 pushed a commit that referenced this pull request Oct 8, 2024
[skip ci]

## [4.4.0](v4.3.1...v4.4.0) (2024-10-08)

### Features

* impl fetch ([#542](#542)) ([55a634c](55a634c))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants