Exemplo C simples de fazer um HTTP POST e consumir a resposta

92

Eu gostaria de criar um aplicativo C muito simples que faz uma postagem HTTP. Levará alguns parâmetros e os usará para construir um URL. Gostaria apenas de fazer um HTTP POST simples e obter a resposta sem o uso de curl (as bibliotecas não são e não serão instaladas na máquina que precisa ser executada).

Pseudo-código:

  1. Args de processo 2

  2. Coloque args no URL do modelo: http://api.somesite.com/apikey=ARG1&command=ARG2

  3. Faça POST no URL gerado

  4. Consumir resposta

Minhas pesquisas no Google e no SO não renderam nada sobre esse assunto.

kmarks2
fonte
2
Você usa algum tipo de estrutura de rede? Qual sistema operacional você usa?
cnicutar
Será apenas uma caixa básica do Fedora ou Cent. As estruturas de rede são os usuais sys / socket, netdb, arpa / inet. Só não libcurl.
kmarks2
1
Não é libcurl. Você está disposto a ir com qualquer outra biblioteca ou tem que ser totalmente POSIX.
cnicutar
Todos POSIX, infelizmente. Deve ser totalmente independente em qualquer sistema.
kmarks2
2
Tenho uma amostra que fiz para você, mas não entendo por que você está usando o POST se não há corpo para a mensagem. Se todos os parâmetros estão na string de consulta, por que você não deseja fazer um GET?
Jerry Jeremiah

Respostas:

196

Uma mensagem possui uma parte do cabeçalho e um corpo da mensagem separados por uma linha em branco. A linha em branco é SEMPRE necessária, mesmo se não houver corpo da mensagem. O cabeçalho começa com um comando e tem linhas adicionais de pares de valores-chave separados por dois pontos e um espaço. Se houver um corpo de mensagem, pode ser qualquer coisa que você quiser.

As linhas no cabeçalho e a linha em branco no final do cabeçalho devem terminar com um retorno carraige e um par de alimentação de linha (consulte o estilo de quebra de linha do cabeçalho HTTP ), então é por isso que essas linhas têm \ r \ n no final.

Um URL tem a forma de http://host:port/path?query_string

Existem duas maneiras principais de enviar uma solicitação a um site:

  • GET: A string de consulta é opcional, mas, se especificada, deve ser razoavelmente curta. Por causa disso, o cabeçalho pode ser apenas o comando GET e nada mais. Um exemplo de mensagem pode ser:

  • POST: o que normalmente estaria na string de consulta está no corpo da mensagem. Por causa disso, o cabeçalho precisa incluir os atributos Content-Type: e Content-Length: bem como o comando POST. Um exemplo de mensagem pode ser:

Portanto, para responder à sua pergunta: se o URL para o qual você está interessado em POSTAR for http://api.somesite.com/apikey=ARG1&command=ARG2, então não há corpo ou string de consulta e, conseqüentemente, não há razão para POSTAR porque há não é nada para colocar no corpo da mensagem e, portanto, nada para colocar no Content-Type: e Content-Length:

Eu acho que você poderia POSTAR se realmente quisesse. Nesse caso, sua mensagem seria assim:

Portanto, para enviar a mensagem, o programa C precisa:

  • criar um soquete
  • procure o endereço IP
  • abra a tomada
  • envie o pedido
  • espere pela resposta
  • feche o soquete

As chamadas de envio e recebimento não necessariamente enviarão / receberão TODOS os dados que você fornecer - elas retornarão o número de bytes realmente enviados / recebidos. Depende de você chamá-los em um loop e enviar / receber o restante da mensagem.

O que não fiz neste exemplo é qualquer tipo de verificação de erro real - quando algo falha, eu simplesmente saio do programa. Deixe-me saber se funciona para você:

Como a outra resposta apontada, 4096 bytes não é uma resposta muito grande. Escolhi esse número ao acaso, supondo que a resposta à sua solicitação seria curta. Se puder ser grande, você tem duas opções:

  • leia o cabeçalho Content-Length: da resposta e, em seguida, aloque dinamicamente memória suficiente para conter a resposta inteira.
  • escreva a resposta em um arquivo à medida que as peças chegam

Informações adicionais para responder à pergunta feita nos comentários:

E se você quiser POSTAR dados no corpo da mensagem? Em seguida, você precisa incluir os cabeçalhos Content-Type: e Content-Length :. O Content-Length: é o comprimento real de tudo após a linha em branco que separa o cabeçalho do corpo.

Aqui está um exemplo que leva os seguintes argumentos de linha de comando:

  • hospedeiro
  • porta
  • comando (GET ou POST)
  • caminho (não incluindo os dados da consulta)
  • dados de consulta (colocados na string de consulta para GET e no corpo para POST)
  • lista de cabeçalhos (Content-Length: é automático se estiver usando POST)

Portanto, para a pergunta original, você executaria:

E para a pergunta feita nos comentários que você faria:

Aqui está o código:

Jerry Jeremiah
fonte
Quais argumentos devem ser transmitidos quando chamados?
Santiago Martí Olbrich
Você precisa passar algo que será usado como apikey como primeiro parâmetro e algo no segundo parâmetro que será usado como comando. Se você deseja usar uma string de consulta completamente diferente, você precisa alterar a string de formato, o número de parâmetros e a mensagem de uso.
Jerry Jeremiah
2
Este código emite uma solicitação HTTP malformada. HTTP especifica que as linhas de solicitação devem ser encerradas por pares de retorno de carro / alimentação de linha ( \r\n), mas este código usa alimentações de linha simples.
John Bollinger
@JohnBollinger Isso é verdade. Obrigado por apontar isso. Esperançosamente, a resposta editada é melhor.
Jerry Jeremiah
O que há de errado com esta mensagem de postagem? "POST /variableName=%s&value=%s HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 4\r\n\r\n\r\n"Eu quero postar como name = reaz. Ele responde 400 Bad Request
Reaz Murshed
12

A resposta de Jerry é ótima. No entanto, ele não lida com grandes respostas. Uma mudança simples para lidar com isso:

Peter Washington
fonte
3
Você poderia apenas aumentar o array de resposta no meu exemplo. Presumi que ele estava apenas recebendo json e não estava baixando um arquivo enorme, mas é claro que até json pode ter megabytes, dependendo da consulta ...
Jerry Jeremiah
1
Sou iniciante em C e sua resposta pode estar correta. Mas você pode acrescentar uma explicação à sua resposta?
boop
2
Este é realmente apenas um comentário sobre a resposta aceita e não deveria ter sido feito como uma tentativa de resposta separada.
Michael Gaskill
1
Apenas uma coisa a adicionar aqui, isso funciona muito bem, mas você deve ler o tamanho do buffer - 1 byte. E para ver isso corretamente, eu não usaria uma nova linha nessa declaração de impressão. Deve ser assim:bytes = recv(sockfd, response, 1023, 0)
xjsc16x
11

Após semanas de pesquisa. Eu vim com o seguinte código. Eu acredito que este é o mínimo necessário para fazer uma conexão segura com SSL para um servidor web.

O código acima explicará em detalhes como estabelecer uma conexão TLS com um servidor remoto.

Nota importante : este código não verifica se a chave pública foi assinada por uma autoridade válida. O que significa que não uso certificados raiz para validação. Não se esqueça de implementar esta verificação, caso contrário você não saberá se está conectando o site certo

Quando se trata do próprio pedido. Nada mais é do que escrever a solicitação HTTP manualmente.

Você também pode encontrar neste link uma explicação de como instalar o openSSL em seu sistema e como compilar o código para que ele use a biblioteca segura .

David Gatti
fonte
2
Boa explicação!
Satyam Koyani
não, a próxima variável é a porta, já estamos conectando na porta certa.
David Gatti
3

Alça adicionada.
Adicionado cabeçalho de host.
Adicionado suporte a linux / windows, testado (XP, WIN7).
AVISO: ERRO: "falha de segmentação" se não houver host, caminho ou porta como argumento.

William Desportes
fonte