Bagaimana cara mengatur batas waktu pada http.request () di Node?

94

Saya mencoba menyetel waktu tunggu pada klien HTTP yang menggunakan http.request tanpa hasil. Sejauh ini yang saya lakukan adalah:

var options = { ... }
var req = http.request(options, function(res) {
  // Usual stuff: on(data), on(end), chunks, etc...
}

/* This does not work TOO MUCH... sometimes the socket is not ready (undefined) expecially on rapid sequences of requests */
req.socket.setTimeout(myTimeout);  
req.socket.on('timeout', function() {
  req.abort();
});

req.write('something');
req.end();

Ada petunjuk?

Claudio
sumber
1
Menemukan jawaban ini (berfungsi juga) tetapi saya ingin tahu apakah ada sesuatu yang berbeda untuk http.request () stackoverflow.com/questions/6129240/…
Claudio

Jawaban:

28

Sekadar untuk memperjelas jawaban di atas :

Sekarang dimungkinkan untuk menggunakan timeoutopsi dan acara permintaan yang sesuai:

// set the desired timeout in options
const options = {
    //...
    timeout: 3000,
};

// create a request
const request = http.request(options, response => {
    // your callback here
});

// use its "timeout" event to abort the request
request.on('timeout', () => {
    request.abort();
});
Sergei Kovalenko
sumber
1
Bertanya-tanya apakah ini berbeda dari hanyasetTimeout(req.abort.bind(req), 3000);
Alexander Mills
@Alexanderills, maka Anda mungkin ingin menghapus batas waktu secara manual, saat permintaan berfungsi dengan baik.
Sergei Kovalenko
4
Perhatikan bahwa ini hanyalah connectbatas waktu, setelah soket dipasang, tidak akan berpengaruh. Jadi ini tidak akan membantu dengan server yang membuat soket terbuka terlalu lama (Anda masih perlu menggulirkan soket Anda sendiri dengan setTimeout). timeout : A number specifying the socket timeout in milliseconds. This will set the timeout before the socket is connected.
UpTheCreek
Inilah yang saya cari pada percobaan koneksi yang macet. Koneksi yang terputus dapat terjadi sedikit ketika mencoba mengakses port di server yang tidak mendengarkan. BTW, API telah berubah menjadi request.destroy( aborttidak digunakan lagi.) Juga, ini berbeda darisetTimeout
dturvene
92

Pembaruan 2019

Ada berbagai cara untuk menangani ini dengan lebih elegan sekarang. Silakan lihat beberapa jawaban lain di utas ini. Teknologi bergerak cepat sehingga jawaban sering kali menjadi usang dengan cukup cepat. Jawaban saya akan tetap berfungsi tetapi ada baiknya melihat alternatif juga.

Jawaban 2012

Menggunakan kode Anda, masalahnya adalah Anda belum menunggu soket ditetapkan ke permintaan sebelum mencoba untuk mengatur barang-barang pada objek soket. Semuanya asinkron jadi:

var options = { ... }
var req = http.request(options, function(res) {
  // Usual stuff: on(data), on(end), chunks, etc...
});

req.on('socket', function (socket) {
    socket.setTimeout(myTimeout);  
    socket.on('timeout', function() {
        req.abort();
    });
});

req.on('error', function(err) {
    if (err.code === "ECONNRESET") {
        console.log("Timeout occurs");
        //specific error treatment
    }
    //other error treatment
});

req.write('something');
req.end();

Peristiwa 'socket' dipicu ketika permintaan diberikan objek socket.

Rob Evans
sumber
Ini benar-benar masuk akal. Masalahnya adalah sekarang saya tidak dapat menguji masalah khusus ini (waktu berlalu ...). Jadi saya hanya bisa memberi suara positif pada jawabannya untuk saat ini :) Terima kasih.
Claudio
Jangan khawatir. Perlu diingat ini hanya berfungsi pada versi node terbaru sejauh yang saya tahu. Saya menguji pada versi sebelumnya (5.0.3-pra) saya pikir dan itu tidak menyalakan acara soket.
Rob Evans
1
Cara lain untuk menangani ini adalah dengan menggunakan panggilan setTimeout standar-rawa. Anda harus menyimpan id setTimeout dengan: var id = setTimeout (...); sehingga Anda dapat membatalkannya jika Anda menerima data, dll. Cara yang baik adalah dengan menyimpannya di objek permintaan itu sendiri lalu clearTimeout jika Anda mendapatkan beberapa data.
Rob Evans
1
Anda hilang); di akhir req.on. Karena ini bukan 6 karakter, saya tidak dapat mengeditnya untuk Anda.
JR Smith
Ini berfungsi (terima kasih!), Tetapi perlu diingat bahwa tanggapan pada akhirnya akan tetap datang. req.abort()tampaknya tidak menjadi fungsi standar saat ini. Karenanya, socket.onAnda mungkin ingin menyetel variabel seperti timeout = truedan kemudian menangani batas waktu dengan tepat di penangan respons
Jonathan Benn
40

Saat ini ada metode untuk melakukan ini secara langsung pada objek permintaan:

request.setTimeout(timeout, function() {
    request.abort();
});

Ini adalah metode pintasan yang mengikat acara soket dan kemudian membuat batas waktu.

Referensi: Node.js v0.8.8 Manual & Dokumentasi

Douwe
sumber
3
request.setTimeout "menyetel soket ke batas waktu setelah waktu tunggu milidetik tidak aktif di soket." Saya pikir pertanyaan ini adalah tentang waktu keluarnya permintaan terlepas dari aktivitasnya.
ostergaard
Harap dicatat bahwa, sama seperti dalam jawaban di bawah ini yang menggunakan soket yang terlibat secara langsung, req.abort () menyebabkan kejadian kesalahan, yang harus ditangani oleh ('kesalahan') dll.
KLoozen
4
request.setTimeout tidak akan membatalkan permintaan, kita perlu memanggil batal secara manual di callback timeout.
Udhaya
18

Jawaban Rob Evans bekerja dengan benar untuk saya tetapi ketika saya menggunakan request.abort (), itu terjadi untuk melempar kesalahan menutup soket yang tetap tidak tertangani.

Saya harus menambahkan penangan kesalahan untuk objek permintaan:

var options = { ... }
var req = http.request(options, function(res) {
  // Usual stuff: on(data), on(end), chunks, etc...
}

req.on('socket', function (socket) {
    socket.setTimeout(myTimeout);  
    socket.on('timeout', function() {
        req.abort();
    });
}

req.on('error', function(err) {
    if (err.code === "ECONNRESET") {
        console.log("Timeout occurs");
        //specific error treatment
    }
    //other error treatment
});

req.write('something');
req.end();
Pierre Maoui
sumber
3
dimana myTimeoutfungsinya? (edit: dokumen mengatakan: Sama seperti mengikat ke acara waktu tunggu nodejs.org/api/… )
Ben Muircroft
Perhatikan bahwa ECONNRESETdapat terjadi dalam kedua kasus: klien menutup soket dan server menutup koneksi. Untuk menentukan apakah itu dilakukan oleh klien dengan menelepon abort()ada abortacara spceial
Kirill
9

Ada metode yang lebih sederhana.

Alih-alih menggunakan setTimeout atau bekerja dengan soket secara langsung,
Kami dapat menggunakan 'batas waktu' di 'opsi' dalam penggunaan klien

Di bawah ini adalah kode server dan klien, dalam 3 bagian.

Bagian modul dan opsi:

'use strict';

// Source: https://github.com/nodejs/node/blob/master/test/parallel/test-http-client-timeout-option.js

const assert = require('assert');
const http = require('http');

const options = {
    host: '127.0.0.1', // server uses this
    port: 3000, // server uses this

    method: 'GET', // client uses this
    path: '/', // client uses this
    timeout: 2000 // client uses this, timesout in 2 seconds if server does not respond in time
};

Bagian server:

function startServer() {
    console.log('startServer');

    const server = http.createServer();
    server
            .listen(options.port, options.host, function () {
                console.log('Server listening on http://' + options.host + ':' + options.port);
                console.log('');

                // server is listening now
                // so, let's start the client

                startClient();
            });
}

Bagian klien:

function startClient() {
    console.log('startClient');

    const req = http.request(options);

    req.on('close', function () {
        console.log("got closed!");
    });

    req.on('timeout', function () {
        console.log("timeout! " + (options.timeout / 1000) + " seconds expired");

        // Source: https://github.com/nodejs/node/blob/master/test/parallel/test-http-client-timeout-option.js#L27
        req.destroy();
    });

    req.on('error', function (e) {
        // Source: https://github.com/nodejs/node/blob/master/lib/_http_outgoing.js#L248
        if (req.connection.destroyed) {
            console.log("got error, req.destroy() was called!");
            return;
        }

        console.log("got error! ", e);
    });

    // Finish sending the request
    req.end();
}


startServer();

Jika Anda meletakkan semua 3 bagian di atas dalam satu file, "a.js", lalu jalankan:

node a.js

maka, keluarannya adalah:

startServer
Server listening on http://127.0.0.1:3000

startClient
timeout! 2 seconds expired
got closed!
got error, req.destroy() was called!

Semoga membantu.

Manohar Reddy Poreddy
sumber
2

Bagi saya - ini adalah cara yang tidak terlalu membingungkan untuk melakukan socket.setTimeout

var request=require('https').get(
    url
   ,function(response){
        var r='';
        response.on('data',function(chunk){
            r+=chunk;
            });
        response.on('end',function(){
            console.dir(r);            //end up here if everything is good!
            });
        }).on('error',function(e){
            console.dir(e.message);    //end up here if the result returns an error
            });
request.on('error',function(e){
    console.dir(e);                    //end up here if a timeout
    });
request.on('socket',function(socket){
    socket.setTimeout(1000,function(){
        request.abort();                //causes error event ↑
        });
    });
Ben Muircroft
sumber
2

Menguraikan jawaban @douwe di sini adalah tempat Anda akan memberi waktu tunggu pada permintaan http.

// TYPICAL REQUEST
var req = https.get(http_options, function (res) {                                                                                                             
    var data = '';                                                                                                                                             

    res.on('data', function (chunk) { data += chunk; });                                                                                                                                                                
    res.on('end', function () {
        if (res.statusCode === 200) { /* do stuff with your data */}
        else { /* Do other codes */}
    });
});       
req.on('error', function (err) { /* More serious connection problems. */ }); 

// TIMEOUT PART
req.setTimeout(1000, function() {                                                                                                                              
    console.log("Server connection timeout (after 1 second)");                                                                                                                  
    req.abort();                                                                                                                                               
});

this.abort () juga bagus.

SpiRail
sumber
1

Anda harus meneruskan referensi ke permintaan seperti di bawah ini

var options = { ... }
var req = http.request(options, function(res) {
  // Usual stuff: on(data), on(end), chunks, etc...
});

req.setTimeout(60000, function(){
    this.abort();
}).bind(req);
req.write('something');
req.end();

Peristiwa kesalahan permintaan akan dipicu

req.on("error", function(e){
       console.log("Request Error : "+JSON.stringify(e));
  });

Udhaya
sumber
Menambahkan bind (req) tidak mengubah apapun untuk saya. Apa yang dilakukan bind dalam kasus ini?
SpiRail
-1

Penasaran, apa yang terjadi jika Anda menggunakan straight net.sockets? Berikut beberapa contoh kode yang saya kumpulkan untuk tujuan pengujian:

var net = require('net');

function HttpRequest(host, port, path, method) {
  return {
    headers: [],
    port: 80,
    path: "/",
    method: "GET",
    socket: null,
    _setDefaultHeaders: function() {

      this.headers.push(this.method + " " + this.path + " HTTP/1.1");
      this.headers.push("Host: " + this.host);
    },
    SetHeaders: function(headers) {
      for (var i = 0; i < headers.length; i++) {
        this.headers.push(headers[i]);
      }
    },
    WriteHeaders: function() {
      if(this.socket) {
        this.socket.write(this.headers.join("\r\n"));
        this.socket.write("\r\n\r\n"); // to signal headers are complete
      }
    },
    MakeRequest: function(data) {
      if(data) {
        this.socket.write(data);
      }

      this.socket.end();
    },
    SetupRequest: function() {
      this.host = host;

      if(path) {
        this.path = path;
      }
      if(port) {
        this.port = port;
      }
      if(method) {
        this.method = method;
      }

      this._setDefaultHeaders();

      this.socket = net.createConnection(this.port, this.host);
    }
  }
};

var request = HttpRequest("www.somesite.com");
request.SetupRequest();

request.socket.setTimeout(30000, function(){
  console.error("Connection timed out.");
});

request.socket.on("data", function(data) {
  console.log(data.toString('utf8'));
});

request.WriteHeaders();
request.MakeRequest();
onteria_
sumber
Jika saya menggunakan batas waktu soket, dan saya mengeluarkan dua permintaan satu demi satu (tanpa menunggu yang pertama selesai), permintaan kedua memiliki soket yang tidak ditentukan (setidaknya saat ini saya mencoba untuk mengatur batas waktu) .. mungkin harus ada sesuatu seperti di ("ready") di soket ... Saya tidak tahu.
Claudio
@Claudio Dapatkah Anda memperbarui kode Anda untuk menunjukkan beberapa permintaan sedang dibuat?
onteria_
1
Tentu saja ... agak lama dan saya menggunakan paste2.org jika ini bukan masalah: paste2.org/p/1448487
Claudio
@Claudio Hmm oke, menyiapkan lingkungan pengujian dan menulis beberapa kode pengujian akan memakan waktu, jadi balasan saya mungkin akan datang besok (Waktu Pasifik) sebagai FYI
onteria_
@Claudio sebenarnya memeriksa kode Anda tampaknya tidak cocok dengan kesalahan Anda. Dikatakan bahwa setTimeout dipanggil dengan nilai yang tidak ditentukan, tetapi cara Anda memanggilnya adalah melalui versi global, jadi tidak ada cara yang tidak dapat ditentukan, membuat saya agak bingung.
onteria_