Есть ли способ имитировать тайм-аут сокета и соединения?

У меня есть определенный фрагмент кода, который интегрируется с третьей стороной, используя HTTP-соединение, которое по-разному обрабатывает тайм-аут сокета и тайм-аут соединения.

Я пытался смоделировать и протестировать все сценарии, которые могли возникнуть у третьей стороны. смог проверить время ожидания соединения, подключившись к порту, который заблокирован брандмауэром сервера, например. порт 81.

Однако я не могу имитировать тайм-аут сокета. Если я правильно понимаю, тайм-аут сокета связан с непрерывным потоком пакетов и разрывом соединения между ними. Есть ли способ, которым я могу смоделировать это?


person Varghese Tony    schedule 08.08.2020    source источник


Ответы (2)


Итак, мы говорим здесь о типах тайм-аутов, один из которых связан с подключением к серверу (тайм-аут подключения), другой тайм-аут произойдет, когда данные не отправляются или не принимаются через сокет в течение некоторого времени (тайм-аут простоя).

Сокеты узла имеют тайм-аут сокета, который можно использовать для синтеза тайм-аута соединения и простоя. Это можно сделать, установив тайм-аут сокета на тайм-аут подключения, а затем, при подключении, установив его на тайм-аут простоя.

пример:

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

Это работает, потому что таймаут в опциях выставляется сразу при создании сокета, функция setTimeout запускается на сокете при подключении!

Во всяком случае, вопрос был о том, как проверить время ожидания подключения. Итак, давайте сначала припаркуем тайм-аут простоя. Мы можем просто проверить это, не отправляя никаких данных в течение некоторого времени, что приведет к тайм-ауту. Проверять!

Тайм-аут подключения проверить немного сложнее, первое, что приходит в голову, это то, что нам нужно место для подключения, к которому не будет ошибок, но и не будет подключаться. Это вызовет тайм-аут. Но как, черт возьми, мы можем смоделировать это в узле?

Если мы подумаем немного нестандартно, то сможем понять, что этот тайм-аут примерно равен времени, которое требуется для подключения. Неважно, почему соединение длится так долго. Нам просто нужно отсрочить время, необходимое для подключения. Это не обязательно серверная вещь, мы также можем сделать это на клиенте. В конце концов, это часть подключения, если мы можем задержать ее там, мы можем проверить тайм-аут.

Так как же мы можем задержать соединение на стороне клиента? Что ж, для этого мы можем использовать поиск 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, // 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));
});

В своих проектах я обычно использую инъекционный агент. Затем агент выполняет отложенный поиск. Как это:

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

Удачного кодирования!

person Elmer    schedule 06.07.2021
comment
Вахаха, я только что понял, что этот вопрос касается Java! Решение, которое я предоставляю, находится в node.js, но я думаю, что это можно сделать и на Java :-) - person Elmer; 12.07.2021

Время ожидания соединения определяет, сколько времени может потребоваться для установления TCP-соединения, и все это происходит до того, как какие-либо данные, связанные с HTTP, будут отправлены по линии. Подключившись к заблокированному порту, вы только частично проверили время ожидания соединения, поскольку соединение не было установлено. Обычно TCP-соединение в вашей локальной сети создается (устанавливается) очень быстро. Но при подключении к серверу на другом конце света установление TCP-соединения может занять секунды.

Время ожидания сокета — несколько вводящее в заблуждение название — оно просто определяет, как долго вы (клиент) будете ждать ответа (данных) от сервера. Другими словами, как долго функция Socket.read() будет блокироваться в ожидании данных.

Надлежащее тестирование этих функций включает в себя создание сокета сервера или (HTTP) веб-сервера, который вы можете изменить, чтобы он работал очень медленно. Описание того, как создать и использовать серверный сокет для тестирования тайм-аута соединения (если это возможно), слишком много, чтобы ответить здесь, но тестирование тайм-аута сокета является распространенным вопросом - см., например, здесь (я только что погуглил макет веб-сервера для тестирования тайм-аутов), что приводит к такому инструменту, как MockWebServer. У MockWebServer также может быть возможность проверки тайм-аутов соединения (я не использовал MockWebServer), но если нет, то это может сделать другой инструмент.

И последнее замечание: хорошо, что вы тестируете использование сторонней HTTP-библиотеки в отношении настроек тайм-аута, даже если это требует некоторых усилий. Худшее, что может случиться, это то, что параметр тайм-аута сокета в вашем коде каким-то образом не используется библиотекой, и используется тайм-аут сокета по умолчанию, равный ожиданию навсегда. Это может привести к тому, что ваше приложение абсолютно ничего не сделает (зависнет) без видимой причины.

person vanOekel    schedule 08.08.2020
comment
«Подключившись к заблокированному порту, вы только частично проверили тайм-аут соединения, поскольку соединение не выполнялось»: false. Он полностью протестировал его. - person user207421; 08.08.2020