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:
Args de processo 2
Coloque args no URL do modelo: http://api.somesite.com/apikey=ARG1&command=ARG2
Faça POST no URL gerado
Consumir resposta
Minhas pesquisas no Google e no SO não renderam nada sobre esse assunto.
Respostas:
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:
GET /path?query_string HTTP/1.0\r\n \r\n
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:
POST /path HTTP/1.0\r\n Content-Type: text/plain\r\n Content-Length: 12\r\n \r\n query_string
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:
POST /apikey=ARG1&command=ARG2 HTTP/1.0\r\n \r\n
Portanto, para enviar a mensagem, o programa C precisa:
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ê:
#include <stdio.h> /* printf, sprintf */ #include <stdlib.h> /* exit */ #include <unistd.h> /* read, write, close */ #include <string.h> /* memcpy, memset */ #include <sys/socket.h> /* socket, connect */ #include <netinet/in.h> /* struct sockaddr_in, struct sockaddr */ #include <netdb.h> /* struct hostent, gethostbyname */ void error(const char *msg) { perror(msg); exit(0); } int main(int argc,char *argv[]) { /* first what are we going to send and where are we going to send it? */ int portno = 80; char *host = "api.somesite.com"; char *message_fmt = "POST /apikey=%s&command=%s HTTP/1.0\r\n\r\n"; struct hostent *server; struct sockaddr_in serv_addr; int sockfd, bytes, sent, received, total; char message[1024],response[4096]; if (argc < 3) { puts("Parameters: <apikey> <command>"); exit(0); } /* fill in the parameters */ sprintf(message,message_fmt,argv[1],argv[2]); printf("Request:\n%s\n",message); /* create the socket */ sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) error("ERROR opening socket"); /* lookup the ip address */ server = gethostbyname(host); if (server == NULL) error("ERROR, no such host"); /* fill in the structure */ memset(&serv_addr,0,sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(portno); memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length); /* connect the socket */ if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0) error("ERROR connecting"); /* send the request */ total = strlen(message); sent = 0; do { bytes = write(sockfd,message+sent,total-sent); if (bytes < 0) error("ERROR writing message to socket"); if (bytes == 0) break; sent+=bytes; } while (sent < total); /* receive the response */ memset(response,0,sizeof(response)); total = sizeof(response)-1; received = 0; do { bytes = read(sockfd,response+received,total-received); if (bytes < 0) error("ERROR reading response from socket"); if (bytes == 0) break; received+=bytes; } while (received < total); if (received == total) error("ERROR storing complete response from socket"); /* close the socket */ close(sockfd); /* process response */ printf("Response:\n%s\n",response); return 0; }
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:
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:
Portanto, para a pergunta original, você executaria:
a.out api.somesite.com 80 GET "/apikey=ARG1&command=ARG2"
E para a pergunta feita nos comentários que você faria:
a.out api.somesite.com 80 POST / "name=ARG1&value=ARG2" "Content-Type: application/x-www-form-urlencoded"
Aqui está o código:
#include <stdio.h> /* printf, sprintf */ #include <stdlib.h> /* exit, atoi, malloc, free */ #include <unistd.h> /* read, write, close */ #include <string.h> /* memcpy, memset */ #include <sys/socket.h> /* socket, connect */ #include <netinet/in.h> /* struct sockaddr_in, struct sockaddr */ #include <netdb.h> /* struct hostent, gethostbyname */ void error(const char *msg) { perror(msg); exit(0); } int main(int argc,char *argv[]) { int i; /* first where are we going to send it? */ int portno = atoi(argv[2])>0?atoi(argv[2]):80; char *host = strlen(argv[1])>0?argv[1]:"localhost"; struct hostent *server; struct sockaddr_in serv_addr; int sockfd, bytes, sent, received, total, message_size; char *message, response[4096]; if (argc < 5) { puts("Parameters: <host> <port> <method> <path> [<data> [<headers>]]"); exit(0); } /* How big is the message? */ message_size=0; if(!strcmp(argv[3],"GET")) { message_size+=strlen("%s %s%s%s HTTP/1.0\r\n"); /* method */ message_size+=strlen(argv[3]); /* path */ message_size+=strlen(argv[4]); /* headers */ if(argc>5) message_size+=strlen(argv[5]); /* query string */ for(i=6;i<argc;i++) /* headers */ message_size+=strlen(argv[i])+strlen("\r\n"); message_size+=strlen("\r\n"); /* blank line */ } else { message_size+=strlen("%s %s HTTP/1.0\r\n"); message_size+=strlen(argv[3]); /* method */ message_size+=strlen(argv[4]); /* path */ for(i=6;i<argc;i++) /* headers */ message_size+=strlen(argv[i])+strlen("\r\n"); if(argc>5) message_size+=strlen("Content-Length: %d\r\n")+10; /* content length */ message_size+=strlen("\r\n"); /* blank line */ if(argc>5) message_size+=strlen(argv[5]); /* body */ } /* allocate space for the message */ message=malloc(message_size); /* fill in the parameters */ if(!strcmp(argv[3],"GET")) { if(argc>5) sprintf(message,"%s %s%s%s HTTP/1.0\r\n", strlen(argv[3])>0?argv[3]:"GET", /* method */ strlen(argv[4])>0?argv[4]:"/", /* path */ strlen(argv[5])>0?"?":"", /* ? */ strlen(argv[5])>0?argv[5]:""); /* query string */ else sprintf(message,"%s %s HTTP/1.0\r\n", strlen(argv[3])>0?argv[3]:"GET", /* method */ strlen(argv[4])>0?argv[4]:"/"); /* path */ for(i=6;i<argc;i++) /* headers */ {strcat(message,argv[i]);strcat(message,"\r\n");} strcat(message,"\r\n"); /* blank line */ } else { sprintf(message,"%s %s HTTP/1.0\r\n", strlen(argv[3])>0?argv[3]:"POST", /* method */ strlen(argv[4])>0?argv[4]:"/"); /* path */ for(i=6;i<argc;i++) /* headers */ {strcat(message,argv[i]);strcat(message,"\r\n");} if(argc>5) sprintf(message+strlen(message),"Content-Length: %d\r\n",strlen(argv[5])); strcat(message,"\r\n"); /* blank line */ if(argc>5) strcat(message,argv[5]); /* body */ } /* What are we going to send? */ printf("Request:\n%s\n",message); /* create the socket */ sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) error("ERROR opening socket"); /* lookup the ip address */ server = gethostbyname(host); if (server == NULL) error("ERROR, no such host"); /* fill in the structure */ memset(&serv_addr,0,sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(portno); memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length); /* connect the socket */ if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0) error("ERROR connecting"); /* send the request */ total = strlen(message); sent = 0; do { bytes = write(sockfd,message+sent,total-sent); if (bytes < 0) error("ERROR writing message to socket"); if (bytes == 0) break; sent+=bytes; } while (sent < total); /* receive the response */ memset(response,0,sizeof(response)); total = sizeof(response)-1; received = 0; do { bytes = read(sockfd,response+received,total-received); if (bytes < 0) error("ERROR reading response from socket"); if (bytes == 0) break; received+=bytes; } while (received < total); if (received == total) error("ERROR storing complete response from socket"); /* close the socket */ close(sockfd); /* process response */ printf("Response:\n%s\n",response); free(message); return 0; }
fonte
\r\n
), mas este código usa alimentações de linha simples."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 RequestA resposta de Jerry é ótima. No entanto, ele não lida com grandes respostas. Uma mudança simples para lidar com isso:
memset(response, 0, sizeof(response)); total = sizeof(response)-1; received = 0; do { printf("RESPONSE: %s\n", response); // HANDLE RESPONSE CHUCK HERE BY, FOR EXAMPLE, SAVING TO A FILE. memset(response, 0, sizeof(response)); bytes = recv(sockfd, response, 1024, 0); if (bytes < 0) printf("ERROR reading response from socket"); if (bytes == 0) break; received+=bytes; } while (1);
fonte
bytes = recv(sockfd, response, 1023, 0)
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.
#include <stdio.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/bio.h> #define APIKEY "YOUR_API_KEY" #define HOST "YOUR_WEB_SERVER_URI" #define PORT "443" int main() { // // Initialize the variables // BIO* bio; SSL* ssl; SSL_CTX* ctx; // // Registers the SSL/TLS ciphers and digests. // // Basically start the security layer. // SSL_library_init(); // // Creates a new SSL_CTX object as a framework to establish TLS/SSL // or DTLS enabled connections // ctx = SSL_CTX_new(SSLv23_client_method()); // // -> Error check // if (ctx == NULL) { printf("Ctx is null\n"); } // // Creates a new BIO chain consisting of an SSL BIO // bio = BIO_new_ssl_connect(ctx); // // Use the variable from the beginning of the file to create a // string that contains the URL to the site that you want to connect // to while also specifying the port. // BIO_set_conn_hostname(bio, HOST ":" PORT); // // Attempts to connect the supplied BIO // if(BIO_do_connect(bio) <= 0) { printf("Failed connection\n"); return 1; } else { printf("Connected\n"); } // // The bare minimum to make a HTTP request. // char* write_buf = "POST / HTTP/1.1\r\n" "Host: " HOST "\r\n" "Authorization: Basic " APIKEY "\r\n" "Connection: close\r\n" "\r\n"; // // Attempts to write len bytes from buf to BIO // if(BIO_write(bio, write_buf, strlen(write_buf)) <= 0) { // // Handle failed writes here // if(!BIO_should_retry(bio)) { // Not worth implementing, but worth knowing. } // // -> Let us know about the failed writes // printf("Failed write\n"); } // // Variables used to read the response from the server // int size; char buf[1024]; // // Read the response message // for(;;) { // // Get chunks of the response 1023 at the time. // size = BIO_read(bio, buf, 1023); // // If no more data, then exit the loop // if(size <= 0) { break; } // // Terminate the string with a 0, to let know C when the string // ends. // buf[size] = 0; // // -> Print out the response // printf("%s", buf); } // // Clean after ourselves // BIO_free_all(bio); SSL_CTX_free(ctx); return 0; }
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 .
fonte
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.
#include <stdio.h> /* printf, sprintf */ #include <stdlib.h> /* exit, atoi, malloc, free */ #include <unistd.h> /* read, write, close */ #include <string.h> /* memcpy, memset */ #ifdef __linux__ #include <sys/socket.h> /* socket, connect */ #include <netdb.h> /* struct hostent, gethostbyname */ #include <netinet/in.h> /* struct sockaddr_in, struct sockaddr */ #elif _WIN32 #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #pragma comment(lib,"ws2_32.lib") //Winsock Library #else #endif void error(const char *msg) { perror(msg); exit(0); } int main(int argc,char *argv[]) { int i; struct hostent *server; struct sockaddr_in serv_addr; int bytes, sent, received, total, message_size; char *message, response[4096]; int portno = atoi(argv[2])>0?atoi(argv[2]):80; char *host = strlen(argv[1])>0?argv[1]:"localhost"; char *path = strlen(argv[4])>0?argv[4]:"/"; if (argc < 5) { puts("Parameters: <host> <port> <method> <path> [<data> [<headers>]]"); exit(0); } /* How big is the message? */ message_size=0; if(!strcmp(argv[3],"GET")) { printf("Process 1\n"); message_size+=strlen("%s %s%s%s HTTP/1.0\r\nHost: %s\r\n"); /* method */ message_size+=strlen(argv[3]); /* path */ message_size+=strlen(path); /* headers */ if(argc>5) message_size+=strlen(argv[5]); /* query string */ for(i=6;i<argc;i++) /* headers */ message_size+=strlen(argv[i])+strlen("\r\n"); message_size+=strlen("\r\n"); /* blank line */ } else { printf("Process 2\n"); message_size+=strlen("%s %s HTTP/1.0\r\nHost: %s\r\n"); message_size+=strlen(argv[3]); /* method */ message_size+=strlen(path); /* path */ for(i=6;i<argc;i++) /* headers */ message_size+=strlen(argv[i])+strlen("\r\n"); if(argc>5) message_size+=strlen("Content-Length: %d\r\n")+10; /* content length */ message_size+=strlen("\r\n"); /* blank line */ if(argc>5) message_size+=strlen(argv[5]); /* body */ } printf("Allocating...\n"); /* allocate space for the message */ message=malloc(message_size); /* fill in the parameters */ if(!strcmp(argv[3],"GET")) { if(argc>5) sprintf(message,"%s %s%s%s HTTP/1.0\r\nHost: %s\r\n", strlen(argv[3])>0?argv[3]:"GET", /* method */ path, /* path */ strlen(argv[5])>0?"?":"", /* ? */ strlen(argv[5])>0?argv[5]:"",host); /* query string */ else sprintf(message,"%s %s HTTP/1.0\r\nHost: %s\r\n", strlen(argv[3])>0?argv[3]:"GET", /* method */ path,host); /* path */ for(i=6;i<argc;i++) /* headers */ {strcat(message,argv[i]);strcat(message,"\r\n");} strcat(message,"\r\n"); /* blank line */ } else { sprintf(message,"%s %s HTTP/1.0\r\nHost: %s\r\n", strlen(argv[3])>0?argv[3]:"POST", /* method */ path,host); /* path */ for(i=6;i<argc;i++) /* headers */ {strcat(message,argv[i]);strcat(message,"\r\n");} if(argc>5) sprintf(message+strlen(message),"Content-Length: %d\r\n",(int)strlen(argv[5])); strcat(message,"\r\n"); /* blank line */ if(argc>5) strcat(message,argv[5]); /* body */ } printf("Processed\n"); /* What are we going to send? */ printf("Request:\n%s\n",message); /* lookup the ip address */ total = strlen(message); /* create the socket */ #ifdef _WIN32 WSADATA wsa; SOCKET s; printf("\nInitialising Winsock..."); if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) { printf("Failed. Error Code : %d",WSAGetLastError()); return 1; } printf("Initialised.\n"); //Create a socket if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET) { printf("Could not create socket : %d" , WSAGetLastError()); } printf("Socket created.\n"); server = gethostbyname(host); serv_addr.sin_addr.s_addr = inet_addr(server->h_addr); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(portno); memset(&serv_addr,0,sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(portno); memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length); //Connect to remote server if (connect(s , (struct sockaddr *)&serv_addr , sizeof(serv_addr)) < 0) { printf("connect failed with error code : %d" , WSAGetLastError()); return 1; } puts("Connected"); if( send(s , message , strlen(message) , 0) < 0) { printf("Send failed with error code : %d" , WSAGetLastError()); return 1; } puts("Data Send\n"); //Receive a reply from the server if((received = recv(s , response , 2000 , 0)) == SOCKET_ERROR) { printf("recv failed with error code : %d" , WSAGetLastError()); } puts("Reply received\n"); //Add a NULL terminating character to make it a proper string before printing response[received] = '\0'; puts(response); closesocket(s); WSACleanup(); #endif #ifdef __linux__ int sockfd; server = gethostbyname(host); if (server == NULL) error("ERROR, no such host"); sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) error("ERROR opening socket"); /* fill in the structure */ memset(&serv_addr,0,sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(portno); memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length); /* connect the socket */ if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0) error("ERROR connecting"); /* send the request */ sent = 0; do { bytes = write(sockfd,message+sent,total-sent); if (bytes < 0) error("ERROR writing message to socket"); if (bytes == 0) break; sent+=bytes; } while (sent < total); /* receive the response */ memset(response, 0, sizeof(response)); total = sizeof(response)-1; received = 0; printf("Response: \n"); do { printf("%s", response); memset(response, 0, sizeof(response)); bytes = recv(sockfd, response, 1024, 0); if (bytes < 0) printf("ERROR reading response from socket"); if (bytes == 0) break; received+=bytes; } while (1); if (received == total) error("ERROR storing complete response from socket"); /* close the socket */ close(sockfd); #endif free(message); return 0; }
fonte