node.js - 送信 - データイベントからHTTPアップロードをキャンセルするには?



nodejs response multipart (3)

ここで行う適切な仕様に準拠したものは、HTTP 413応答を早期に送信するだけです。つまり、クライアントが処理したい以上のバイトを送信したことを検出するとすぐにHTTP 413応答を送信します。 エラー応答を送信した後にソケットを終了するかどうかは、あなた次第です。 これはRFC 2616:(重点が追加された)

413要求エンティティが大きすぎます

要求エンティティがサーバーが処理できる、または処理できるよりも大きいため、サーバーは要求の処理を拒否しています。 サーバーは、クライアントが要求を続行できないように接続を閉じることができます。

次に起こることは理想的ではありません。

  • ソケットを開いたままにすると、すべてのブラウザ(Chrome 30、IE 10、Firefox 21)はファイル全体がアップロードされるまでデータの送信を続けます。 次に、ブラウザにエラーメッセージが表示されます。 これは、ユーザーがアップロードを完了するのにファイル全体を待つ必要があるため、サーバーがそれを拒否したことを知るためには本当にうんざりです。 また、帯域幅を浪費します。

    ブラウザの現在の動作はRFC 2616§8.2.2に違反しています:

    message-bodyを送信しているHTTP / 1.1(またはそれ以降)のクライアントは、要求を送信している間、エラー状態のネットワーク接続を監視すべきです(SHOULD)。 クライアントがエラー状態を見た場合、それは直ちに本文の送信を止めるべきである(SHOULD)。 本文が "チャンク"エンコーディング(セクション3.6)を使用して送信されている場合、ゼロ長のチャンクと空のトレーラを使用して、メッセージの終わりを時期尚早にマークすることができる(MAY)。 本文の前にContent-Lengthヘッダーがある場合、クライアントは接続を終了する必要があります。

    開いているChromeFirefox問題はありますが、すぐに修正を予定していません。

  • HTTP 413レスポンスを送信した直後にソケットを閉じると、すべてのブラウザは明らかにすぐにアップロードを停止しますが、レスポンスで送信するHTMLではなく、「接続のリセット」エラーなどが表示されます。

    繰り返しますが、これはおそらく仕様の違反です(これによりサーバーは早期に応答を送信して接続を閉じることができます)。

    アップデート: 4/15現在、Chrome 接続を早期に終了すると413 HTMLを表示することがあります。 これは、ブラウザがLinuxおよびMac OS Xで実行されている場合にのみ機能しますERR_CONNECTION_RESETでは、送信したHTMLではなく、 ERR_CONNECTION_RESETネットワークエラーがChromeに表示されます。 (IE 11とFirefox 37は、すべてのプラットフォームでネットワークエラーを表示し続けるだけです。)

伝統的なプレーンなHTTPアップロードの選択肢は次のとおりです。

  • フレンドリなエラーメッセージを表示しますが、アップロードが完了するまで実行されません。 これは時間と帯域幅を無駄にします。

  • 速やかに失敗するが、ユーザーには謎めいたブラウザエラー画面が表示される。

おそらく、AJAXアップローダーを使用して、ユーザーエクスペリエンスをより詳細に制御できる場所に置くことをお勧めします。 あなたはまだフォールバックとしてtradtionalアップロードフォームを提供する必要があり、私は無駄な時間と帯域幅を防ぐために "失敗高速"オプション(ソケットを閉じる)を使用したいと思います。

1kBを超えるリクエストを殺すコードの例をいくつか示します。 私はExpressを使っていますが、ノードのvanilla HTTPライブラリにも同じことが適用されます。

注意:実際には、アップロードを処理するにはformidable multipartyを使用する必要があります(Connect / Expressが使用するものです)。アップロードデータを監視する独自の方法があります。

var express = require("express")
    , app = express();

app.get('/', function(req, res) {
    res.send('Uploads &gt; 1 kB rejected<form action="/upload" method="post" enctype="multipart/form-data"><input type="file" name="file"><input type="submit"></form>');
});

app.post('/upload', function(req, res) {
    var size = 0;

    var gotData = function(d) {
        size += d.length; // add this chunk's size to the total number of bytes received thus far
        console.log('upload chunk', size);
        if (size > 1024) {
            console.log('aborting request');
            req.removeListener('data', gotData); // we need to remove the event listeners so that we don't end up here more than once
            req.removeListener('end', reqEnd);
            res.header('Connection', 'close'); // with the Connection: close header set, node will automatically close the socket...
            res.send(413, 'Upload too large'); // ... after sending a response
        }
    };

    var reqEnd = function() {
       res.send('ok, got ' + size + ' bytes');
    }

    req.on('data', gotData);

    req.on('end', reqEnd);
});

app.listen(3003);

https://code.i-harness.com

このシンプルなWebサーバーコードを考えると、

console.log('starting');
var server = require('http').createServer();

server.on('connection',function(socket){console.log('*server/connection');});
server.on(
    'request',
    function(request, response){
        console.log('*server/request');
        request.on(
            'data',
            function(chunk){
                console.log('*request/data');
                // <!> How do I abort next data calls from here?
            }
        );
        request.on(
            'readable',
            function(chunk){
                console.log('*request/readable');
                // <!> How do I abort next readable calls from here?
            }
        );
        request.on(
            'end',
            function(){
                console.log('*request/end');
                response.writeHead(200,"OK");
                response.write('Hello');
                response.end();
            }
        );
        request.on('close',function(){ console.log('*request/close'); } );
        request.on('error',function(){ console.log('*request/error'); } );
    }
);
server.on('close',function(){console.log('server/close');});
server.on('checkContinue',function(request, response){console.log('*server/checkContinue');});
server.on('connect',function(request, socket, head){console.log('*server/connect');});
server.on('upgrade',function(request, socket, head){console.log('*server/upgrade');});
server.on('clientError',function(exception, socket){console.log('*server/clientError');});

server.listen(8080);
console.log('started');

POSTまたはFILEがサブミットされると、私のデータ機能が1回または複数回トリガーされます。 ときどき(巨大な大きなファイルが送信されたような) データイベントでこれを取り消し、ユーザーにオンエンド機能をトリガーしたい (後で "あなたの投稿/ファイルが大きすぎます"と表示されます)。 どうすればいいのですか?


ここに私のソリューションです:

var maxSize = 30 * 1024 * 1024;    //30MB
app.post('/upload', function(req, res) {

    var size = req.headers['content-length'];
    if (size <= maxSize) {
        form.parse(req, function(err, fields, files) {
            console.log("File uploading");
            if (files && files.upload) {
                res.status(200).json({fields: fields, files: files});
                fs.renameSync(files.upload[0].path, uploadDir + files.upload[0].originalFilename);
            }
            else {
              res.send("Not uploading");
            }
        });
    }
    else {
        res.send(413, "File to large");
    }

また、クライアントのアップロード時間を無駄にしてから応答を受け取る場合は、クライアントのJavaScriptで制御してください。

if (fileElement.files[0].size > maxSize) {
    ....
}

要求パラメータは、ストリームを停止することを許可しないhttp.IncomingMessageクラスです。

しかし、あなたは基礎となるsocketアクセスすることができ、それを打ち切ることができます:

request.socket.end('too big !');

しかし、私はブラウザがそれを楽しむことを確信していません...彼はおそらく不平を言い、接続が不適切に閉鎖されたことを示します。





node.js