web-dev-qa-db-ja.com

REST Unixパイプを使用したサービスへのデータのストリーミング

に基づいて 別の質問への回答 curlを使用して、1つのプロセスのstdoutPOSTリクエストのエンティティとしてストリーミングしています。

myDataGeneratingApp \
| curl -H "Content-Type: application/json" -H "Transfer-Encoding: chunked" -X POST -d @- http://localhost:12000

残念ながら、curlはデータの送信を開始する前にstdoutからのEOFを待っています。私はこれを知っています。アプリケーションをスタンドアロンで実行でき、データがすぐにコンソールに出力されますが、パイプを使用してカールすると、サービスがデータの受信を開始するまでに大幅な遅延が発生します。

アプリケーションから標準から利用可能になったときに、curlを使用してデータをすぐにストリーミングするにはどうすればよいですか?カールで不可能である場合、別の解決策がありますか(たとえば、wget)?

Curlコード transfer.c を見ると、プログラムはチャンクプロトコルを使用して(curlからサーバーに)要求データを再パッケージ化できるようです。データの各チャンクには、 16進数のASCIIのチャンクで、末尾に_\r\n_を付けます。

サーバーへの接続後に_-T -_を使用すると、これをストリーミングで使用する方法のようです。この例を考えてみましょう:

_for i in $(seq 5)
do date
   sleep 1
done | 
dd conv=block cbs=512 |
strace -t -e sendto,read -o /tmp/e \
 curl --trace-ascii - \
 -H "Transfer-Encoding: chunked" \
 -H "Content-Type: application/json" \
 -X POST -T -  http://localhost/...
_

このスクリプトは、それぞれが日付で始まり、ddによって512バイトにパディングされた5ブロックのデータをパイプに送信します。ここで、straceは_curl -T -_を実行してパイプを読み取ります。ターミナルで見ることができます

_== Info: Connected to localhost (::1) port 80 (#0)
=> Send header, 169 bytes (0xa9)
0000: POST /... HTTP/1.1
001e: Host: localhost
002f: User-Agent: curl/7.47.1
0048: Accept: */*
0055: Transfer-Encoding: chunked
0071: Content-Type: application/json
0091: Expect: 100-continue
00a7: 
<= Recv header, 23 bytes (0x17)
0000: HTTP/1.1 100 Continue
_

これは、接続と送信されたヘッダーを示しています。特に、curlは_Content-length:_ヘッダーを提供していませんが、サーバー(Apache)がContinueに返信した_Expect:_ヘッダーを提供しています。その直後に、データの最初の512バイト(16進数で200)があります。

_=> Send data, 519 bytes (0x207)
0000: 200
0005: Fri Sep 14 15:58:15 CEST 2018                                   
0045:                                                                 
0085:                                                                 
00c5:                                                                 
0105:                                                                 
0145:                                                                 
0185:                                                                 
01c5:                                                                 
=> Send data, 519 bytes (0x207)
_

strace出力ファイルを見ると、パイプからタイムスタンプが付けられた各readが確認され、sendtoが接続に書き込みます。

_16:00:00 read(0, "Fri Sep 14 16:00:00 CEST 2018   "..., 16372) = 512
16:00:00 sendto(3, "200\r\nFri Sep 14 16:00:00 CEST 20"..., 519, ...) = 519
16:00:00 read(0, "Fri Sep 14 16:00:01 CEST 2018   "..., 16372) = 512
16:00:01 sendto(3, "200\r\nFri Sep 14 16:00:01 CEST 20"..., 519, ...) = 519
16:00:01 read(0, "Fri Sep 14 16:00:02 CEST 2018   "..., 16372) = 512
16:00:02 sendto(3, "200\r\nFri Sep 14 16:00:02 CEST 20"..., 519, ...) = 519
16:00:02 read(0, "Fri Sep 14 16:00:03 CEST 2018   "..., 16372) = 512
16:00:03 sendto(3, "200\r\nFri Sep 14 16:00:03 CEST 20"..., 519, ...) = 519
16:00:03 read(0, "Fri Sep 14 16:00:04 CEST 2018   "..., 16372) = 512
16:00:04 sendto(3, "200\r\nFri Sep 14 16:00:04 CEST 20"..., 519, ...) = 519
16:00:04 read(0, "", 16372)             = 0
16:00:05 sendto(3, "0\r\n\r\n", 5, ...) = 5
_

ご覧のように、それらは1秒間隔であり、データが受信されているときに送信されていることを示しています。データはfread()によって読み取られるため、送信するには少なくとも512バイトが必要です。

5
meuh

以下の編集を参照してください

あなたが望むものは不可能です。 POSTデータを送信するには、長さがわかっている必要があるため、curlはまずデータ全体を読み取って長さを決定する必要があります。

Transfer-Encoding: chunkedはその制限を回避する方法ですが、サーバーからの応答のためだけです。

その理由は、chunkedはHTTP/1.1でのみサポートされていますが、リクエストを送信するとき、クライアントはサーバーがHTTP/1.1を理解しているかどうかを認識できないためです。その情報には答えが含まれていますが、要求を送信するには遅すぎます。

編集

これは、wgetマニュアルによると、wgetの制限のようです。

WgetはPOSTデータのサイズを事前に知っている必要があることに注意してください。したがって、-post-fileへの引数は通常のファイルである必要があります。FIFOまたは/ dev/stdinのようなものは機能しません。HTTP/ 1.0に固有のこの制限を回避する方法は明確ではありません。HTTP/ 1.1では、事前にリクエストの長さを知る必要がないチャンク転送が導入されていますが、クライアントは、HTTP/1.1サーバーと通信していることを認識していない限り、チャンクを使用できません。また、クライアントが応答を受信するまでは、要求を完了する必要があることを認識できません。これは、鶏と卵の問題です。

問題は存在しますが、 RFC 72 で認識されます。

クライアントが、サーバーがHTTP/1.1(またはそれ以降)の要求を処理することを認識していない限り、Transfer-Encodingを含む要求を送信してはなりません(MUST NOT)。そのような知識は、特定のユーザー構成の形式であるか、以前に受信した応答のバージョンを覚えていることによります。

したがって、チャンクされたPOSTデータを送信することは可能であり、他の回答が示すように、curlはすでにそれをサポートしています。

1
RalfFriedl