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

fix: uncaught exception due to second response with digest auth #530

Merged
merged 1 commit into from
Sep 14, 2024

Conversation

damiengrandi
Copy link
Contributor

@damiengrandi damiengrandi commented Aug 30, 2024

In rare cases, an uncaught exception can happen with digest auth because of the re-use of the same variable representing the response.

Demonstration of the issue:
image
Can cause this crash, the exception have not been caught by the try-catch block:
crash

I fixed the issue by consuming the initial response body before sending a new request:
image
no-crash

Another way to fix this could be to use a dedicated variable for each response, but the first response may need to be closed anyway to avoid potential memory issue.
This issue exists in both version 4.2 and 3.22.2 (tested).

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of HTTP responses to ensure that response bodies are fully consumed before reassigning variables, enhancing robustness and preventing potential issues.
  • Chores
    • Minor adjustments to the request handling logic for better resource management.

Copy link

coderabbitai bot commented Aug 30, 2024

Caution

Review failed

The pull request is closed.

Walkthrough

The changes involve a modification to the HttpClient class in src/HttpClient.ts, focusing on the handling of HTTP responses. A new line has been added to ensure that the response body is fully consumed before the response variable is reassigned, enhancing the management of response bodies.

Changes

Files Change Summary
src/HttpClient.ts Added a line to consume the previous response's body using await response.body.arrayBuffer() before reassigning the response variable.

Possibly related PRs

Poem

In the meadow, where bunnies hop,
A change was made, and we won't stop!
With every byte, we read and play,
Ensuring smooth paths for our day.
So here's to the code, so neat and bright,
A happy dance in the moon's soft light! 🐇✨

Tip

OpenAI O1 model for chat
  • We have deployed OpenAI's latest O1 model for chat.
  • OpenAI claims that this model has superior reasoning capabilities than their GPT-4o model.
  • Please share any feedback with us in the discussions post.

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>.
    • 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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @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.

@damiengrandi damiengrandi changed the title Fix uncaught exception due to second response with d Fix uncaught exception due to second response with digest auth Aug 30, 2024
@damiengrandi damiengrandi reopened this Aug 30, 2024
Copy link

pkg-pr-new bot commented Sep 11, 2024

Open in Stackblitz

yarn add https://pkg.pr.new/node-modules/urllib@530.tgz

commit: 93b210a

@@ -562,6 +562,8 @@ export class HttpClient extends EventEmitter {
// FIXME: merge exists cookie header
requestOptions.headers.cookie = response.headers['set-cookie'].join(';');
}
// Ensure the previous response is consumed as we re-use the same variable
await response.body.arrayBuffer();
Copy link
Member

Choose a reason for hiding this comment

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

@damiengrandi Please add an associated test case for this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi, are you asking for an example to reproduce this issue? With an example URL and additional details?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, a test example to reproduce the problem.

Copy link
Contributor Author

@damiengrandi damiengrandi Sep 12, 2024

Choose a reason for hiding this comment

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

In some specific cases where APIs are slow to respond and DigestAuth is used, an uncaught UND_ERR_BODY_TIMEOUT exception can occur. Below is a source code example illustrating this issue:

import { request, RequestOptions } from 'urllib';

try {
  const username = "someUsername";
  const password = "somePassword";
  const deviceHost = "123.456.789.123";
  const deviceWebPort = 80;
  const path = "/axis-cgi/com/ptz.cgi?camera=1&query=position";

  const requestUrl = `http://${deviceHost}:${deviceWebPort}${path}`;

  const requestOptions: RequestOptions = {
    digestAuth: `${username}:${password}`,
    method: 'GET',
    timeout: [1000, 1], // 1ms to force UND_ERR_BODY_TIMEOUT to happen more often for testing purpose
  };

  // This line may provoke an uncaught exception in some cases
  const res = await request(requestUrl, requestOptions);

  const data = res.data?.toString();
  console.log(data);
} catch (error) {
  console.error(error);
}

I’ve observed this issue when querying PTZ position from an AXIS camera. The device can be slow to respond, which makes it a good candidate for testing this issue. You can find more details on the relevant endpoint in AXIS's documentation here: https://www.axis.com/vapix-library/subjects/t10175981/section/t10036011/display?section=t10036011-t10004639

Unfortunately, due to GDPR restrictions, I can't provide a camera with dummy credentials.
However, you may reproduce the issue with other devices that use DigestAuth and are slow to respond to API requests.

Copy link
Contributor Author

@damiengrandi damiengrandi Sep 13, 2024

Choose a reason for hiding this comment

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

I found a way for you to reproduce in local.
Using the following PHP script to simulate slow response during the first DigestAuth request :

<?php

function sendDigestAuthChallenge() {
    $realm = 'Restricted Area';
    $nonce = uniqid();
    header('HTTP/1.1 401 Unauthorized');
    header('WWW-Authenticate: Digest realm="' . $realm . '",qop="auth",nonce="' . $nonce . '",opaque="' . md5($realm) . '"');

    // Simulate slow response time
    for ($i = 0; $i < 10000000; $i++) {
        echo ".";
    }

    echo "Authentication required";
    exit;
}

if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
    sendDigestAuthChallenge();
}
echo "OK";

And using this code to test it from NodeJS:

do {
  try {
    console.log('HERE');
    const host = 'http://localhost/test_digest/test.php';
    const options: RequestOptions = {
      digestAuth: 'root:root',
      method: 'GET',
      timeout: [3000, 1],
    };
    const response = await request(host, options);
    console.log(response.data?.toString());
  } catch (error) {
    console.error(error);
  }

  // Wait 1 sec
  await new Promise((resolve) => setTimeout(resolve, 1000));
} while (true);

I got the crash very quickly:
image

@fengmk2 fengmk2 changed the title Fix uncaught exception due to second response with digest auth fix: uncaught exception due to second response with digest auth Sep 13, 2024
@fengmk2 fengmk2 merged commit 9a7833e into node-modules:master Sep 14, 2024
14 of 17 checks passed
@fengmk2 fengmk2 added the bug Something isn't working label Sep 14, 2024
@fengmk2
Copy link
Member

fengmk2 commented Sep 14, 2024

@damiengrandi Thanks a lot!

fengmk2 pushed a commit that referenced this pull request Sep 14, 2024
[skip ci]

## [4.2.2](v4.2.1...v4.2.2) (2024-09-14)

### Bug Fixes

* uncaught exception due to second response with digest auth ([#530](#530)) ([9a7833e](9a7833e))
fengmk2 pushed a commit that referenced this pull request Sep 14, 2024
fengmk2 added a commit that referenced this pull request Sep 14, 2024
pick from #530

Co-authored-by: Damien Grandi <damien.grandi@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants