Websocket pass-through crashes worker script

dash-crypto
#1

I’m having trouble setting up a web socket pass-through on workers.

I tried the minimal case on production on a new domain:

addEventListener('fetch', (event) => {
event.respondWith(fetch('http://ws-origin/', event.request)
  .catch((err) => new Response(err.toString()))
});

When I run curl with the websocket headers, I get an error 1101, which, as stated in the documentation, means that the fetch call probably threw an error that I’m not able to catch.

This post suggests that this should work. Is there something I’m missing?

#2

CF Workers doesn’t support websocket.

#3

@Xaq the post I linked, posted by @KentonVarda says otherwise.

#4

Do you know any example code of such WebSocket pass-through?

#5

The .catch() would only catch asynchronous exceptions. If fetch() throws a synchronous exception, your code won’t catch it. You need to wrap it in a try/catch block, like:

addEventListener('fetch', (event) => {
  try {
    event.respondWith(fetch('http://ws-origin/', event.request)
      .catch((err) => new Response(err.toString()))
  } catch (err) {
    evend.respondWith(new Response(err.toString()))
  }
});

But I highly recommend using an async function so that you can handle both together:

addEventListener('fetch', (event) => {
  event.respondWith(handle(event.request));
});

async function handle(request) {
  try {
    return await fetch('http://ws-origin/', event.request)
  } catch (err) {
    return new Response(err.toString())
  }
}

Another thing you could do is try to debug in the preview. In that case, the exceptions should be printed to the console. WebSockets are supported in preview, you’ll just have to make sure you direct the WebSocket request at the right host with the right cookie.

1 Like
#6

@Xaq you don’t have to do anything special for WebSocket pass-through. As long as you don’t change the headers on the request, and you pass the response from fetch() directly to event.respondWith(), it should Just Work.

1 Like
#7

@KentonVarda, following your recommendation, I tried the same procedures with your code snippet gave me the same result using curl.

In the editor, pseudo-websocket requests to rawhttp.cloudflareworkers.com fail with status code 502 and the dev tools console remains blank.

#8

Can you show us the curl command you’re using?

#9

My curl command is:

curl -v --include \
     --no-buffer \
     --header "Connection: upgrade" \
     --header "Upgrade: websocket" \
     --header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
     --header "Sec-WebSocket-Version: 13" \
     "https://worker-domain/"

The same command works when directly accessing my origin.

#10

By default curl will use HTTP/2 if the server supports it, as Cloudflare does, but maybe your origin doesn’t. WebSocket, though, only works over HTTP/1.1. So you’ll need to add --http1.1 to your curl command line.

After doing that, I find that that curl command does in fact work to establish a WebSocket that passes through a Worker.

#11

Explicitly using http1.1 does not seem to change the result.

I have verified that my origin will take both http2 and http1.1 requests

This is the exact payload curl outputs:

curl output
*   Trying x.x.x.x...
* TCP_NODELAY set
* Connected to worker-domain (x.x.x.x) port 443 (#0)
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-ECDSA-CHACHA20-POLY1305
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: OU=Domain Control Validated; OU=PositiveSSL Multi-Domain; CN=sni254505.cloudflaressl.com
*  start date: Apr 23 00:00:00 2019 GMT
*  expire date: Oct 30 23:59:59 2019 GMT
*  subjectAltName: host "worker-domain" matched cert's "*.worker-domain"
*  issuer: C=GB; ST=Greater Manchester; L=Salford; O=COMODO CA Limited; CN=COMODO ECC Domain Validation Secure Server CA 2
*  SSL certificate verify ok.
> GET / HTTP/1.1
> Host: worker-domain
> User-Agent: curl/7.58.0
> Accept: */*
> Connection: upgrade
> Upgrade: websocket
> Origin: http://worker-domain
> Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==
> Sec-WebSocket-Version: 13
> 
< HTTP/1.1 500 Internal Server Error
HTTP/1.1 500 Internal Server Error
< Date: Fri, 26 Apr 2019 03:55:01 GMT
Date: Fri, 26 Apr 2019 03:55:01 GMT
< Content-Type: text/html; charset=UTF-8
Content-Type: text/html; charset=UTF-8
< Content-Length: 3693
Content-Length: 3693
< Connection: keep-alive
Connection: keep-alive
< Set-Cookie: __cfduid=dcd3122fe997f7a32694662280244c35e1556250901; expires=Sat, 25-Apr-20 03:55:01 GMT; path=/; domain=.worker-domain; HttpOnly
Set-Cookie: __cfduid=dcd3122fe997f7a32694662280244c35e1556250901; expires=Sat, 25-Apr-20 03:55:01 GMT; path=/; domain=.worker-domain; HttpOnly
< Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< Server: cloudflare
Server: cloudflare
< CF-RAY: 4cd59e63cce6c9a3-SEA
CF-RAY: 4cd59e63cce6c9a3-SEA

< 
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en-US"> <![endif]-->
<!--[if IE 7]>    <html class="no-js ie7 oldie" lang="en-US"> <![endif]-->
<!--[if IE 8]>    <html class="no-js ie8 oldie" lang="en-US"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en-US"> <!--<![endif]-->
<head>
<title>Rendering error | worker-domain | Cloudflare</title></title>
<meta charset="UTF-8" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
<meta name="robots" content="noindex, nofollow" />
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1" />
<link rel="stylesheet" id="cf_styles-css" href="/cdn-cgi/styles/cf.errors.css" type="text/css" media="screen,projection" />
<!--[if lt IE 9]><link rel="stylesheet" id='cf_styles-ie-css' href="/cdn-cgi/styles/cf.errors.ie.css" type="text/css" media="screen,projection" /><![endif]-->
<style type="text/css">body{margin:0;padding:0}</style>


<!--[if gte IE 10]><!--><script type="text/javascript" src="/cdn-cgi/scripts/zepto.min.js"></script><!--<![endif]-->
<!--[if gte IE 10]><!--><script type="text/javascript" src="/cdn-cgi/scripts/cf.common.js"></script><!--<![endif]-->



</head>
<body>
  <div id="cf-wrapper">
    <div class="cf-alert cf-alert-error cf-cookie-error" id="cookie-alert" data-translate="enable_cookies">Please enable cookies.</div>
    <div id="cf-error-details" class="cf-error-details-wrapper">
      <div class="cf-wrapper cf-header cf-error-overview">
        <h1>
          <span class="cf-error-type" data-translate="error">Error</span>
          <span class="cf-error-code">1101</span>
          <small class="heading-ray-id">Ray ID: 4cd59e63cce6c9a3 &bull; 2019-04-26 03:55:01 UTC</small>
        </h1>
        <h2 class="cf-subheadline" data-translate="error_desc">Rendering error</h2>
      </div><!-- /.header -->

      <section></section><!-- spacer -->

      <div class="cf-section cf-wrapper">
        <div class="cf-columns two">
          <div class="cf-column">
            <h2 data-translate="what_happened">What happened?</h2>
            <p>You've requested a page on a website (worker-domain) that is on the <a data-orig-proto="https" data-orig-ref="www.cloudflare.com/5xx-error-landing?utm_source=error_100x" target="_blank">Cloudflare</a> network. An unknown error occurred while rendering the page.</p>
          </div>

          
          <div class="cf-column">
            <h2 data-translate="what_can_i_do">What can I do?</h2>
            <p><strong>If you are the owner of this website:</strong><br />you should <a data-orig-proto="https" data-orig-ref="www.cloudflare.com/login?utm_source=error_100x" target="_blank">login to Cloudflare</a> and check the error logs for worker-domain.</p>
          </div>
          
        </div>
      </div><!-- /.section -->

      <div class="cf-error-footer cf-wrapper">
  <p>
    <span class="cf-footer-item">Cloudflare Ray ID: <strong>4cd59e63cce6c9a3</strong></span>
    <span class="cf-footer-separator">&bull;</span>
    <span class="cf-footer-item"><span>Your IP</span>: 184.70.5.250</span>
    <span class="cf-footer-separator">&bull;</span>
    <span class="cf-footer-item"><span>Performance &amp; security by</span> <a href="https://www.cloudflare.com/5xx-error-landing?utm_source=error_footer" id="brand_link" target="_blank">Cloudflare</a></span>
    
  </p>
</div><!-- /.error-footer -->


    </div><!-- /#cf-error-details -->
  </div><!-- /#cf-wrapper -->

  <script type="text/javascript">
  window._cf_translation = {};
  
  
</script>

</body>
</html>
#12

Hi @ssttevee,

Unfortunately I cannot reproduce this. It might help if you told me your actual domain so that I can try it directly and look at the exact script that is deployed.

#13

@KentonVarda, I’d be happy to provide more information if we take this conversation to a more private channel.

#14

You can e-mail me at kenton at cloudflare. Please note I can’t guarantee I’ll be able to help, but I’m curious to have a look.