Transmissão de dados para o serviço REST usando Unix Pipes

1

Com base em uma resposta a outra pergunta Estou usando o curl para transmitir o stdout de um processo como a entidade de uma solicitação POST :

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

Infelizmente, o curl está aguardando EOF do stdout antes de começar a enviar os dados. Eu sei disso porque posso executar meu aplicativo autônomo e os dados saem para o console imediatamente, mas quando eu canso para enrolar há um atraso significativo antes que o serviço comece a receber dados.

Como posso usar o curl para transmitir os dados imediatamente à medida que eles se tornam disponíveis a partir do padrão fora do aplicativo? Se não for possível em curvas, existe outra solução (por exemplo, wget)?

    
por Ramon J Romero y Vigil 13.09.2018 / 16:36

2 respostas

2

Analisando o código de curva transfer.c , parece que o programa é capaz de reempacotar dados de requisição (de enrolar para o servidor) usando o protocolo chunking, onde cada pedaço de dados é prefixado pelo comprimento do pedaço em ascii hexadecimal, e sufixado por \r\n .

Parece que a maneira de fazer isso é usar isso de forma contínua, depois de conectar ao servidor é com -T - . Considere este exemplo:

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/...

Este script envia 5 blocos de dados, cada um começando com a data e preenchidos com 512 bytes por dd , para um canal, onde strace executa curl -T - para ler o canal. No terminal podemos ver

== 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

que mostra a conexão e os cabeçalhos enviados. Em particular curl não forneceu um Content-length: cabeçalho, mas um Expect: cabeçalho ao qual o servidor (apache) respondeu Continue . Imediatamente após os primeiros 512 bytes (200 em hexadecimal) de dados:

=> 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)

Examinando o arquivo de saída strace , vemos cada read com timestamp do canal e sendto gravar na conexão:

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

Como você pode ver, eles estão espaçados em 1 segundo, mostrando que os dados estão sendo enviados conforme estão sendo recebidos. Você deve ter pelo menos 512 bytes para enviar, pois os dados estão sendo lidos por fread() .

    
por 14.09.2018 / 16:16
1

Veja abaixo Editar

O que você quer não é possível. Para enviar os dados do POST, o comprimento deve ser conhecido, por isso, curl deve primeiro ler todos os seus dados para determinar o tamanho.

Transfer-Encoding: chunked é uma forma de contornar essa restrição, mas apenas para a resposta do servidor.

O motivo é que chunked é suportado apenas no HTTP / 1.1, mas ao enviar a solicitação, o cliente não pode saber se o servidor entende HTTP / 1.1 ou não. Essa informação vem com a resposta, mas é tarde demais para enviar o pedido.

Editar

Isto parece ser uma limitação no wget, a partir do manual do wget:

Please be aware that Wget needs to know the size of the POST data in advance. Therefore the argument to --post-file must be a regular file; specifying a FIFO or something like /dev/stdin won’t work. It’s not quite clear how to work around this limitation inherent in HTTP/1.0. Although HTTP/1.1 introduces chunked transfer that doesn’t require knowing the request length in advance, a client can’t use chunked unless it knows it’s talking to an HTTP/1.1 server. And it can’t know that until it receives a response, which in turn requires the request to have been completed – a chicken-and-egg problem.

Embora o problema exista, ele é reconhecido em RFC 7230 :

A client MUST NOT send a request containing Transfer-Encoding unless it knows the server will handle HTTP/1.1 (or later) requests; such knowledge might be in the form of specific user configuration or by remembering the version of a prior received response.

Portanto, o envio de dados POST em partes é possível e, como a outra resposta mostra, o curl já suporta isso.

    
por 14.09.2018 / 07:38

Tags