有没有一种方法可以模拟套接字和连接超时?

huangapple go评论69阅读模式
英文:

Is there a way to simulate Socket and Connection timeout?

问题

我有一段代码,通过HTTP连接与第三方集成,该代码处理套接字超时和连接超时的情况。

我一直在尝试模拟和测试可能出现的所有来自第三方的情况。通过连接到被服务器防火墙阻止的端口,例如端口81,我能够测试连接超时。

但是,我无法模拟套接字超时。如果我理解正确,套接字超时与连续的数据包流和连接中断有关。是否有一种方法可以模拟这种情况?

英文:

I have a certain piece of code that integrates with a third party using HTTP connection, which handles socket timeout and connection timeout differently.

I have been trying to simulate and test all the scenarios which could arise from the third party. was able to test connection timeout by connecting to a port which is blocked by the servers firewall e.g. port 81.

However I'm unable to simulate a socket timeout. If my understanding is not wrong socket timeout is associated with continuous packet flow, and the connection dropping in between. Is there a way I can simulate this?

答案1

得分: 1

这里我们讨论了两种超时情况,一种是连接服务器的超时(连接超时),另一种是在一段时间内没有通过套接字发送或接收数据时发生的超时(空闲超时)。

Node sockets具有套接字超时,可以用来模拟连接和空闲超时。可以通过将套接字超时设置为连接超时,然后在连接成功后将其设置为空闲超时来实现这一点。

示例:

const request = http.request(url, {
    timeout: connectTimeout,
});
request.setTimeout(idleTimeout);

这可以工作是因为选项中的超时在创建套接字时立即设置,而setTimeout函数在连接时在套接字上运行!

无论如何,问题是如何测试连接超时。好的,首先我们先不考虑空闲超时。我们可以简单地通过一段时间不发送任何数据来测试,这将导致超时。检查!

连接超时稍微难以测试,首先想到的是我们需要一个可以连接但不会出错的地方。这将导致超时。但在Node中如何模拟这种情况呢?

如果我们稍微超出思维框架,我们可能会发现这个超时是连接所需的时间。无论连接为什么时间这么长,都不重要。我们只需要延迟连接所需的时间。这不一定是服务器的事情,我们也可以在客户端上执行。毕竟这是连接的一部分,如果我们可以在那里延迟它,就可以测试超时。

那么在客户端端如何延迟连接呢?嗯,我们可以使用DNS查找来实现。在建立连接之前,会进行DNS查找。如果我们简单地延迟了5秒左右,就可以很容易地测试连接超时。

以下是代码示例:

import * as dns from "dns";
import * as http from "http";

const url = new URL("http://localhost:8080");

const request = http.request(url, {
    timeout: 3 * 1000, // 连接超时
    lookup(hostname, options, callback) {
        setTimeout(
            () => dns.lookup(hostname, options, callback),
            5 * 1000,
        );
    },
});
request.setTimeout(10 * 1000); // 空闲超时

request.addListener("timeout", () => {
    const message = !request.socket || request.socket.connecting ?
        `连接超时,正在连接到 ${url.href}` :
        `连接后保持空闲超时,连接到 ${url.href}`;

    request.destroy(new Error(message));
});

在我的项目中,我通常使用一个我注入的代理。代理然后具有延迟的DNS查找,就像这样:

import * as dns from "dns";
import * as http from "http";

const url = new URL("http://localhost:8080");

const agent = new http.Agent({
    lookup(hostname, options, callback) {
        setTimeout(
            () => dns.lookup(hostname, options, callback),
            5 * 1000,
        );
    },
});

const request = http.request(url, {
    timeout: 3 * 1000, // 连接超时
    agent,
});
request.setTimeout(10 * 1000); // 空闲超时

request.addListener("timeout", () => {
    const message = !request.socket || request.socket.connecting ?
        `连接超时,正在连接到 ${url.href}` :
        `连接后保持空闲超时,连接到 ${url.href}`;

    request.destroy(new Error(message));
});

愉快的编码!

英文:

So we are talking about to kinds of timeouts here, one is to connect to the server (connect timeout), the other timeout will happen when no data is send or received via the socket for a while (idle timeout).

Node sockets have a socket timeout, that can be used to synthesize both the connect and the idle timeout. This can be done by setting the socket timeout to the connect timeout and then when connected, setting it to the idle timeout.

example:

        const request = http.request(url, {
            timeout: connectTimeout,
        });
        request.setTimeout(idleTimeout);

This works because the timeout in the options is set immediately when creating the socket, the setTimeout function is run on the socket when connected!

Anyway, the question was about how to test the connect timeout. Ok so let's first park the idle timeout. We can simply test that by not sending any data for some time, that would cause the timeout. Check!

The connect timeout is a bit harder to test, the first thing that comes to mind is that we need a place to connect to that will not error, but also not connect. This would cause a timeout. But how the hell do we simulate that, in node?

If we think a little bit outside the box then we might figure out that this timeout is about the time it takes to connect. It does not matter why the connection takes as long as it does. We simply need to delay the time it takes to connect. This is not necessarily a server thing, we could also do it on the client. After all this is the part connecting, if we can delay it there, we can test the timeout.

So how could we delay the connection on the client side? Well, we can use the DNS lookup for that. Before the connection is made, a DNS lookup is done. If we simply delay that by 5 seconds or so we can test for the connect timeout very easily.

This is what the code could look like:

import * as dns from "dns";
import * as http from "http";

const url = new URL("http://localhost:8080");

const request = http.request(url, {
    timeout: 3 * 1000, // connect timeout
    lookup(hostname, options, callback) {
        setTimeout(
            () => dns.lookup(hostname, options, callback),
            5 * 1000,
        );
    },
});
request.setTimeout(10 * 1000); // idle timeout

request.addListener("timeout", () => {
    const message = !request.socket || request.socket.connecting ?
        `connect timeout while connecting to ${url.href}` :
        `idle timeout while connected to ${url.href}`;

    request.destroy(new Error(message));
});

In my projects I usually use an agent that I inject. The agent then has the delayed lookup. Like this:

import * as dns from "dns";
import * as http from "http";

const url = new URL("http://localhost:8080");

const agent = new http.Agent({
    lookup(hostname, options, callback) {
        setTimeout(
            () => dns.lookup(hostname, options, callback),
            5 * 1000,
        );
    },
});

const request = http.request(url, {
    timeout: 3 * 1000, // connect timeout
    agent,
});
request.setTimeout(10 * 1000); // idle timeout

request.addListener("timeout", () => {
    const message = !request.socket || request.socket.connecting ?
        `connect timeout while connecting to ${url.href}` :
        `idle timeout while connected to ${url.href}`;

    request.destroy(new Error(message));
});

Happy coding!

答案2

得分: -1

"Connection timeout" 确定了TCP连接建立所需的时间,这都发生在发送任何与HTTP相关的数据之前。通过连接到一个被阻塞的端口,你只部分测试了连接超时,因为没有建立连接。通常情况下,本地网络上的TCP连接会很快建立。但当连接到世界另一端的服务器时,建立TCP连接可能需要几秒钟。

"Socket timeout" 是一个有点误导的名称 - 它只确定了你(客户端)等待来自服务器的回应(数据)的时间。换句话说,Socket.read() 函数在等待数据时会阻塞多长时间。

正确测试这些功能涉及创建一个服务器套接字或(HTTP)Web服务器,您可以将其修改为非常慢。描述如何创建和使用用于连接超时测试的服务器套接字(如果可能的话)太多了,无法在这里回答,但套接字超时测试是一个常见的问题 - 例如,这里(我刚刚谷歌搜索了“用于测试超时的模拟Web服务器”),这导致了一个工具,比如MockWebServer。 "MockWebServer" 可能也有用于测试连接超时的选项(我没有使用过 "MockWebServer"),但如果没有,可能会有其他工具。

最后注意一下:很好你正在测试使用第三方HTTP库时关于超时设置的用法,即使这需要一些努力。最糟糕的情况是,你的代码中的套接字超时设置某种方式未被库使用,而默认的套接字超时设置“一直等待”被使用。这可能会导致你的应用程序毫无明显原因地什么都不做(“挂起”)。

英文:

"Connection timeout" determines how long it may take for a TCP connection to be established and this all happens before any HTTP related data is send over the line. By connecting to a blocked port, you have only partially tested the connection timeout since no connection was being made. Typically, a TCP connection on your local network is created (established) very fast. But when connecting to a server on the other side of the world, establishing a TCP connection can take seconds.

"Socket timeout" is a somewhat misleading name - it just determines how long you (the client) will wait for an answer (data) from the server. In other words, how long the Socket.read() function will block while waiting for data.

Properly testing these functions involves creating a server socket or a (HTTP) web-server that you can modify to be very slow. Describing how to create and use a server socket for connection timeout testing (if that is possible) is too much to answer here, but socket timeout testing is a common question - see for example here (I just googled "mock web server for testing timeouts") which leads to a tool like MockWebServer. "MockWebServer" might have an option for testing connection timeouts as well (I have not used "MockWebServer"), but if not, another tool might have.

On a final note: it is good you are testing your usage of the third-party HTTP library with respect to timeout settings, even if this takes some effort. The worst that can happen is that a socket timeout setting in your code is somehow not used by the library and the default socket timeout of "wait forever" is used. That can result in your application doing absolutely nothing ("hanging") for no apparent reason.

huangapple
  • 本文由 发表于 2020年8月8日 13:31:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/63312157.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定