Проблема отправленных сервером событий (SSE) с SSL / HTTPS - PullRequest
0 голосов
/ 24 января 2020

Здравствуйте! Я занимаюсь разработкой веб-приложения в React, которое получает данные SSE от сервера Express с Nginx.

SERVER. 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}`));

ИНДЕКС: 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 = новый EventSource ('https://api.myDomain.com/events',);

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

В http все работает отлично, но если я установлю https, генерирующий сертификаты с помощью certbot, я больше не получу «события» от express server.

Единственные ошибки, которые появляются в консоли chrome, это

Я заменил sub.domain своим доменом

Эти ошибки появляются через несколько минут после первый запрос

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)
...