chimurai / http-proxy-middleware

:zap: The one-liner node.js http-proxy middleware for connect, express, next.js and more
MIT License
10.68k stars 826 forks source link

ECONNRESET error on React #845

Open KozyWorks opened 1 year ago

KozyWorks commented 1 year ago

Checks

Describe the bug (be clear and concise)

As a part of my React (front) / Express (backend) project, I have a web crawler that crawls buffer, decodes then obtain desired data using cheerio.

(Just to clarify, I am not using any data obtained for commercial use, but only for a personal project.)

server.js:

require("dotenv").config();

const express = require("express");
const cors = require("cors");
const http = require("http");
const axios = require("axios");
const cheerio = require("cheerio");

const app = express();
app.set("port", process.env.PORT || 3001);
app.use(cors());
app.use(
    express.json({
        verify: (request, _, buffer) => {
            request.buffer = buffer;
        },
    })
);

app.get("/", (_, response) => {
    response.send("index");
});

app.get("/:menu_id", async (request, response) => {
    try {
        const buffer = await axios.get("https://cafe.naver.com/ArticleList.nhn", {
            params: {
                "search.clubid": 10050146,
                "search.menuid": request.params.menu_id,
                "search.boardType": "L",
                userDisplay: 10,
            },
            headers: {
                "Content-Type": "application/json",
            },
            responseType: "arraybuffer",
        });

        const decoder = new TextDecoder("EUC-KR");
        const content = decoder.decode(buffer.data);
        const $ = cheerio.load(content);
        const list = $("div.article-board:nth-of-type(4)>table>tbody>tr");

        let articles = [];

        list.each((_, tag) => {
            const url = `https://cafe.naver.com${$(tag).find("a.article").attr("href")}`;
            const number = $(tag).find("div.inner_number").text();
            const title = $(tag).find("a.article").contents().last().text().trim();
            const author = $(tag).find(".p-nick>a").text();
            const date = $(tag)
                .find("td.td_date")
                .text()
                .replace(/(\d{4}).(\d{2}).(\d{2})./g, "$2-$3");
            articles.push({ number, title, author, date, url });
        });

        response.send(articles);
    } catch (error) {
        response.send(error);
    }
});

const server = http.createServer(app);
server.listen(app.get("port"), () => {
    console.log(`App is listening to port: ${app.get("port")}`);
});

Then I have setup a proxy using http-proxy-middleware on front-end.

setupProxy.js:

const { createProxyMiddleware } = require("http-proxy-middleware");

module.exports = (app) => {
    app.use(
        createProxyMiddleware("/api", {
            target: "http://localhost:3001",
            changeOrigin: true,
            pathRewrite: {
                "^/api": "",
            },
        })
    );
};

Articles.js:

import React, { useEffect, useState } from "react";
import axios from "axios";

import Article from "./Article";
import Loading from "./Loading";

const Articles = (props) => {
    const [articles, setArticles] = useState();

    useEffect(() => {
        const getArticles = () => {
            axios
                .get(`/api/naver/cafe/${props.menu.id}`)
                .then((response) => {
                    console.log(response);

                    setArticles(response.data);
                })
                .catch((error) => {
                    console.log(error);
                });
        };

        getArticles();
    }, []);

    return (
        <div className="articles">
            <div className="menuTitle">{props.menu.title}</div>
            {articles ? (
                <ul className="menuContent">
                    {Object.entries(articles).map(([key, article]) => (
                        <Article article={article} key={key} />
                    ))}
                </ul>
            ) : (
                <Loading />
            )}
        </div>
    );
};

export default Articles;

So, this actually works and fetches data as I expected, but then when the server is left running idle, doing nothing, the client fails to fetch data from the server on page refresh (or maybe on a new session) and spits the long, long error below:

/project/node_modules/axios/dist/node/axios.cjs:725
  AxiosError.call(axiosError, error.message, code, config, request, response);
             ^
AxiosError: read ECONNRESET
    at AxiosError.from (/project/node_modules/axios/dist/node/axios.cjs:725:14)
    at RedirectableRequest.handleRequestError (/project/node_modules/axios/dist/node/axios.cjs:2467:25)
    at RedirectableRequest.emit (node:events:513:28)
    at eventHandlers.<computed> (/project/node_modules/follow-redirects/index.js:14:24)
    at ClientRequest.emit (node:events:513:28)
    at TLSSocket.socketErrorListener (node:_http_client:494:9)
    at TLSSocket.emit (node:events:513:28)
    at emitErrorNT (node:internal/streams/destroy:151:8)
    at emitErrorCloseNT (node:internal/streams/destroy:116:3)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
    ...

It just feels like the client loses the connection to the server via proxy.

Here's the things I've tried:

  1. adding connection: "keep-alive" option to createProxyMiddleware header
  2. switching http request client to built in fetch from axios
  3. trying full length(?) url instead of shortified version (/api/naver/cafe/${props.menu.id}http://localhost:3000/api/naver/cafe/${props.menu.id})

and nothing really resolved the issue.

The only way to fix this error is either by refreshing the page until it works or restarting the server.

I do have some Twitch's helix related apis as well, but this error only occurs with the crawling function above.

Literally spent hours and hours to troubleshoot this... and failed.

Any ideas, please?

I am using NodeJS v18.10.0 btw.

Step-by-step reproduction instructions

Please find the code attached on the main text

Expected behavior (be clear and concise)

Fetch data from server with no issue

How is http-proxy-middleware used in your project?

> yarn why http-proxy-middleware
yarn why v1.22.19
[1/4] Why do we have the module "http-proxy-middleware"...?
[2/4] Initialising dependency graph...
[3/4] Finding dependency...
[4/4] Calculating file sizes...
=> Found "http-proxy-middleware@2.0.6"
info Has been hoisted to "http-proxy-middleware"
info Reasons this module exists
   - Specified in "dependencies"
   - Hoisted from "react-scripts#webpack-dev-server#http-proxy-middleware"
info Disk size without dependencies: "156KB"
info Disk size with unique dependencies: "568KB"
info Disk size with transitive dependencies: "4.62MB"
info Number of shared dependencies: 10
Done in 0.41s.

What http-proxy-middleware configuration are you using?

const { createProxyMiddleware } = require("http-proxy-middleware");

module.exports = (app) => {
    app.use(
        createProxyMiddleware("/api", {
            target: "http://localhost:3001",
            changeOrigin: true,
            pathRewrite: {
                "^/api": "",
            },
        })
    );
};

What OS/version and node/version are you seeing the problem?

- Ubuntu 22.04
- Node JS 18.10.0
- react-script 5.x

Additional context (optional)

No response

nfdenuzzo commented 11 months ago

Any 1 ever figure this out?

elawad commented 4 months ago

This worked for us using CRA@5 and HPM@3.

const proxy = createProxyMiddleware({
  target: 'http://localhost:3001',
  changeOrigin: true,
  pathFilter: '/api',
});

module.exports = (app) => app.use(proxy);