简体   繁体   English

SSL/HTTPS 的服务器发送事件 (SSE) 问题

[英]Server-Sent Events (SSE) problem with SSL/HTTPS

Hello I am developing a web application in React that receives SSE data from an Express server with Nginx.您好,我正在 React 中开发一个 Web 应用程序,该应用程序从带有 Nginx 的 Express 服务器接收 SSE 数据。

SERVER.JS服务端JS

const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const crypto = require('crypto');

const app = express();

var lastClientRes = null;
function eventsHandler(req, res, next) {
  const headers = {
    'Content-Type': 'text/event-stream',
    'Connection': 'keep-alive',
    'Cache-Control': 'no-cache'
  };
  res.writeHead(200, headers);

  const clientId = Date.now();
  const newClient = {
    id: clientId,
    nonce: null,
    cart: null,
    res
  };
  requests.push(newClient);


  const data = `data: ${JSON.stringify({client: clientId})}\n\n`;
  res.write(data);

  req.on('close', () => {
    console.log(`${clientId} Connection closed`);
    clients = clients.filter(c => c.id !== clientId);
  });
}


function sendEventsToAll(newNest) {
  clients.forEach(c => c.res.write(`data: ${JSON.stringify(newNest)}\n\n`))
}

async function addCart(req, res) {
  const newCart = req.body;


  requests.forEach(r => {
    if(newCart.client == r.id){
      var nonce = crypto.randomBytes(16).toString('base64');
      r.nonce = nonce;
      r.cart = newCart.cart;
      r.res.write(`data: ${JSON.stringify({nonce: nonce})}\n\n`);
    }

  })
}

async function addCart(req, res) {
  const newCart = req.body;


  requests.forEach(r => {
    if(newCart.client == r.id){
      var nonce = crypto.randomBytes(16).toString('base64');
      r.nonce = nonce;
      r.cart = newCart.cart;
      r.res.write(`data: ${JSON.stringify({nonce: nonce})}\n\n`);
    }

  })
}

async function confirmCart(req, res){
  var nonce = req.body.nonce;
  var found = -1;
  requests.forEach((item, i) => {
    if(item.nonce == nonce){
      found = i;
      return;
    }
  });


  if(found)
    {
      console.log("OK");
      requests[found].res.write(`data: ${JSON.stringify({confirm: true})}\n\n`);
    }
}

app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));

app.post('/addCart', addCart);
app.post('/confirmCart', confirmCart);
app.get('/events', eventsHandler);
app.get('/status', (req, res) => res.json({clients: clients.length}));

const PORT = 3001;

let requests= [];
let clients = [];
let nests = [];

app.listen(PORT, () => console.log(`SSE service listening on port ${PORT}`));

INDEX:JS索引:JS

import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';

class App extends React.Component {
  constructor(props) {
     super(props);
     this.state = {
       jsonCart: "",
       cartNonce: "",
       clientId: "",
       cartConfirmed: false,
       cart: Array(),
       timerId: null,
       listening: false,
       cartConfermato: ""
     };
   }

   buy(){
     if (!this.state.listening) {
        const events = new EventSource('https://api.myDomain.com/events', );
        events.onmessage = (event) => {
          const parsedData = JSON.parse(event.data);

          console.log(event.data);

          if(parsedData.client != null)
          {
            this.setState({
              clientId: parsedData.client,
            });
            this.sendCart();
          }

          if(parsedData.nonce != null)
            this.setState({
              cartNonce: parsedData.nonce,
            });

          if(parsedData.confirm == true)
            this.setState({
              cartNonce: "",
              cartConfermato: "Il carrello è stato confermato!"
            });
        };

        this.setState({
          listening: true
        });
      }
   }

   sendCart(){
     var cart = JSON.stringify(this.state.cart.slice());
     this.setState({
       jsonCart: cart
     });

     axios.post(`https://api.myDomain.com/addCart`, {client: this.state.clientId, cart: cart});
   }


   *** ... ***

const events = new EventSource(' https://api.myDomain.com/events ', ); const events = new EventSource(' https://api.myDomain.com/events ', );

axios.post( https://api.myDomain.com/addCart , {client: this.state.clientId, cart: cart}); axios.post( https://api.myDomain.com/addCart , {客户端: this.state.clientId, 购物车: 购物车});

In http everything works perfectly but if I set https generating the certificates with certbot I no longer receive "events" from the express server.在 http 中一切正常,但是如果我设置 https 使用 certbot 生成证书,我将不再从快速服务器接收“事件”。

The only errors that appears in the chrome console is this chrome 控制台中出现的唯一错误是这个

I replaced sub.domain with my domain我用我的域替换了 sub.domain

These errors appear a few minutes after the first request这些错误在第一次请求后几分钟出现

GET https://sub.domain.com/events net::ERR_INCOMPLETE_CHUNKED_ENCODING 200 (OK)
2sub.domain.com/addCart:1 POST https://sub.domain.com/addCart 504 (Gateway Time-out)
(index):1 Access to XMLHttpRequest at 'https://sub.domain.com/addCart' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
createError.js:16 Uncaught (in promise) Error: Network Error
    at e.exports (createError.js:16)
    at XMLHttpRequest.p.onerror (xhr.js:83)
e.exports @ createError.js:16
p.onerror @ xhr.js:83
error (async)
(anonymous) @ xhr.js:80
e.exports @ xhr.js:12
e.exports @ dispatchRequest.js:50
Promise.then (async)
u.request @ Axios.js:61
r.forEach.u.<computed> @ Axios.js:86
(anonymous) @ bind.js:9
value @ index.js:156
state.listening.EventSource.onmessage @ index.js:121
index.js:114 {"client":1579885346578}
index.js:150 send
sub.domain.com/events:1 GET https://sub.domain.com/events net::ERR_INCOMPLETE_CHUNKED_ENCODING 200 (OK)
2sub.domain.com/addCart:1 POST https://sub.domain.com/addCart net::ERR_ABORTED 504 (Gateway Time-out)

As Darren Cook described here You need to disable buffering on your server-side.正如 Darren Cook 在此处描述的那样您需要在服务器端禁用缓冲。 Adding the "X-Accel-Buffering" parameter and set it to "no" in the response header fixed the issue for me.添加“X-Accel-Buffering”参数并将其在响应标头中设置为“no”为我解决了这个问题。

 const headers = { 'Content-Type': 'text/event-stream', 'Connection': 'keep-alive', 'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no' };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM