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

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

に基づいて他の質問への回答stdout要求されたエンティティにプロセスのストリームを取得するためにカールを使用していますPOST

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

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

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

答え1

カールコードを見る転送.cプログラムは、チャンクプロトコルを使用して要求データ(カールからサーバーへ)を再パッケージできるように見えます。ここで、各データチャンクにはASCII 16進チャンク長が前に付けられます\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/...

このスクリプトは5つのデータチャンクをパイプに送信します。各チャンクは日付で始まり、dd512バイトで埋められ、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提供されず、サーバー(Apache)が応答したヘッダーが提供されます。その後、データの最初の512バイト(16進数200)が続きます。Content-length:Expect:Continue

=> 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パイプラインの各タイムスタンプが表示され、接続に書き込まれます。readsendto

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

ご覧のとおり、2つの間隔は1秒で、データが受信されると同時に送信されていることを示します。データを読み込んでいるため、転送するには少なくとも512バイトが必要ですfread()

答え2

下記の編集内容をご覧ください

あなたが望むことは不可能です。 POSTデータを送信するには長さを知る必要があるため、curlまずデータ全体を読み取って長さを把握する必要があります。

Transfer-Encoding: chunkedこの制限を回避する方法ですが、サーバーからの応答にのみ適用されます。

その理由はchunkedHTTP / 1.1でのみサポートされています。要求を送信すると、クライアントはサーバーがHTTP / 1.1を理解しているかどうかがわからないためです。回答とともにメッセージが来ましたが、リクエストを送信するには遅すぎます。

編集する

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

WgetはPOSTデータのサイズを事前に知っておく必要があります。したがって、--post-fileの引数は通常のファイルでなければなりません。 FIFOや/dev/stdinなどの項目を指定しても機能しません。 HTTP/1.0に固有の制限を解決する方法はまだ明確ではありません。 HTTP/1.1 では、要求の長さを事前に知る必要のないチャンク転送を導入しましたが、クライアントは HTTP/1.1 サーバーと通信していることを知らなければチャンクを使用できません。応答を受けるまでこれを知る方法はありません。応答を受信するには、要求を完了する必要があります。これはチキンと卵の問題です。

問題がありますが認識されます。RFC 7230:

クライアントは、サーバーがHTTP / 1.1(またはそれ以上)要求を処理することを知らない限り、トランスポートエンコーディングを含む要求を送信しないでください。これらの知識は、特定のユーザー構成の形式であるか、以前に受信した応答のバージョンを記憶することによって可能です。 。

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

関連情報