http-proxy-middleware icon indicating copy to clipboard operation
http-proxy-middleware copied to clipboard

ECONNRESET error on React

Open KozyWorks opened this issue 2 years ago • 2 comments

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 "[email protected]"
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

KozyWorks avatar Oct 27 '22 12:10 KozyWorks

Any 1 ever figure this out?

nfdenuzzo avatar Oct 02 '23 09:10 nfdenuzzo

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

elawad avatar Apr 28 '24 02:04 elawad