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

[@hono/vite-dev-server] Hostname/IP does not match certificate's altnames: Host: localhost. is not in the cert's altnames: DNS:httpbin.org, DNS:*.httpbin.org #99

Closed
Code-Hex opened this issue Feb 24, 2024 · 5 comments

Comments

@Code-Hex
Copy link

Code-Hex commented Feb 24, 2024

related: #24

Reproduce code:

import { Hono } from 'hono'

const app = new Hono()

app.get('/httpbin/*', async (c) => {
  const url = new URL(c.req.url);
  return await fetch(
    `https://httpbin.org${url.pathname.replace('/httpbin', '')}`,
    c.req.raw,
  );
});

export default app

Started-up vite server as 8787 and executed npx local-ssl-proxy --key localhost-key.pem --cert localhost.pem --source 8788 --target 8787, Request to https://localhost:8788/httpbin/get via browser. I used mkcert to make localhost certification.

It works on Cloudflare Workers and wrangler dev, but when passing through vite-dev-server, it produces the following error and stops working.

cause: Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match certificate's altnames: Host: localhost. is not in the cert's altnames: DNS:httpbin.org, DNS:*.httpbin.org
      at new NodeError (node:internal/errors:405:5)
      at Object.checkServerIdentity (node:tls:337:12)
      at TLSSocket.onConnectSecure (node:_tls_wrap:1637:27)
      at TLSSocket.emit (node:events:514:28)
      at TLSSocket.emit (node:domain:489:12)
      at TLSSocket._finishInit (node:_tls_wrap:1038:8)
      at ssl.onhandshakedone (node:_tls_wrap:824:12)
      at TLSWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
    reason: "Host: localhost. is not in the cert's altnames: DNS:httpbin.org, DNS:*.httpbin.org",
    host: 'localhost',
    cert: {
      subject: [Object: null prototype],
      issuer: [Object: null prototype],
      subjectaltname: 'DNS:httpbin.org, DNS:*.httpbin.org',
...

Workaround

By rewriting the header's host as follows, it starts working. It seems there is a difference in the behavior of fetch between vite-dev-server and wrangler dev.

app.get('/httpbin/*', async (c) => {
  const url = new URL(c.req.url);
  return await fetch(
    `https://httpbin.org${url.pathname.replace('/httpbin', '')}`,
    {
      headers: {
        ...c.req.raw.headers,
        host: 'httpbin.org', // add this
      },
    },
  );
});
@yusukebe
Copy link
Member

Hi @Code-Hex

Which version of @hono/vite-dev-server are you using?

@Code-Hex
Copy link
Author

Code-Hex commented Feb 24, 2024

I'm using 0.8.0. I am also investigating.

I'm sorry, I made a mistake in the order of describing the issues in the ticket. Actually, the error occurs due to the following problem.

The error in the title of this issue occurs when the following code is commented out.

* If the response is not instance of `Response`, throw it so that it can be handled
* by our custom errorHandler and passed through to Vite
*/
if (!(response instanceof Response)) {
throw response
}

There are two issues. The one I initially wrote and the Internal server error: Unknown error: [object Response] issue. The issue I wrote initially occurs after resolving the Unknown error: [object Response]' problem.

I also suspect that the fetch client being called within app.fetch is the cause. The object returned by this client is a Response object, but it is not captured by the instanceof part, resulting in an error. I added lines:

if (!(response instanceof Response)) {
    console.log(new Response(null, 200), response)
    throw response;
}

and below is the output it generates.

_Response [Response] {} Response {
  [Symbol(realm)]: null,
  [Symbol(state)]: {
    aborted: false,
    rangeRequested: false,
    timingAllowPassed: true,
    requestIncludesCredentials: true,
    type: 'default',
    status: 200,
    timingInfo: {
      startTime: 3204.850082397461,
      redirectStartTime: 0,
      redirectEndTime: 0,
      postRedirectStartTime: 3204.850082397461,
      finalServiceWorkerStartTime: 0,
      finalNetworkResponseStartTime: 0,
      finalNetworkRequestStartTime: 0,
      endTime: 0,
      encodedBodySize: 435,
      decodedBodySize: 435,
      finalConnectionTimingInfo: null
    },
    cacheState: '',
    statusText: 'OK',
    headersList: HeadersList {
      cookies: null,
      [Symbol(headers map)]: [Map],
      [Symbol(headers map sorted)]: null
    },
    urlList: [ URL {} ],
    body: { stream: undefined }
  },
  [Symbol(headers)]: HeadersList {
    cookies: null,
    [Symbol(headers map)]: Map(7) {
      'date' => [Object],
      'content-type' => [Object],
      'content-length' => [Object],
      'connection' => [Object],
      'server' => [Object],
      'access-control-allow-origin' => [Object],
      'access-control-allow-credentials' => [Object]
    },
    [Symbol(headers map sorted)]: null
  }
}
Response {
  [Symbol(realm)]: null,
  [Symbol(state)]: {
    aborted: false,
    rangeRequested: false,
    timingAllowPassed: true,
    requestIncludesCredentials: true,
    type: 'default',
    status: 200,
    timingInfo: {
      startTime: 3204.850082397461,
      redirectStartTime: 0,
      redirectEndTime: 0,
      postRedirectStartTime: 3204.850082397461,
      finalServiceWorkerStartTime: 0,
      finalNetworkResponseStartTime: 0,
      finalNetworkRequestStartTime: 0,
      endTime: 0,
      encodedBodySize: 435,
      decodedBodySize: 435,
      finalConnectionTimingInfo: null
    },
    cacheState: '',
    statusText: 'OK',
    headersList: HeadersList {
      cookies: null,
      [Symbol(headers map)]: [Map],
      [Symbol(headers map sorted)]: null
    },
    urlList: [ URL {} ],
    body: { stream: undefined }
  },
  [Symbol(headers)]: HeadersList {
    cookies: null,
    [Symbol(headers map)]: Map(7) {
      'date' => [Object],
      'content-type' => [Object],
      'content-length' => [Object],
      'connection' => [Object],
      'server' => [Object],
      'access-control-allow-origin' => [Object],
      'access-control-allow-credentials' => [Object]
    },
    [Symbol(headers map sorted)]: null
  }
}

I think this is similar issue: nodejs/undici#2358

I confirmed this code works fine! (but there seems to be a better way 😇 )

if (!isResponse(response)) {
    throw response;
}

function isResponse(v: any): v is Response {
    return ["Response", "_Response"].includes(response.constructor.name)
}

@yusukebe
Copy link
Member

Hi @Code-Hex

Thanks for investigating!

I confirmed this code works fine! (but there seems to be a better way 😇 )

I think it's not bad. But I'll find other good ways.

@yusukebe
Copy link
Member

Hi @Code-Hex

Does this issue still happen? I've tried it with the latest @hono/vite-dev-server, v0.11.0, it works well. Can you try it again?

@Code-Hex
Copy link
Author

@yusukebe Thanks for your confirmation!
I have not been able to confirm this, but if the issue comes up again, I will open it and close it for now!

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

No branches or pull requests

2 participants