Chciałbym stworzyć bardzo prostą aplikację w C, która wysyła post HTTP. Będzie to wymagało kilku parametrów i użyje ich do skonstruowania adresu URL. Chciałbym tylko wykonać prosty HTTP POST i uzyskać odpowiedź bez użycia curl (biblioteki nie są i nie zostaną zainstalowane na maszynie, która musi działać).
Pseudo kod:
Przetwórz 2 argumenty
Umieść argumenty w szablonie URL: http://api.somesite.com/apikey=ARG1&command=ARG2
Wykonaj POST na wygenerowanym adresie URL
Zużyj odpowiedź
Moje wyszukiwania w Google i SO nic nie dały w tej sprawie.
Odpowiedzi:
Wiadomość ma część nagłówkową i treść wiadomości oddzielone pustym wierszem. Pusty wiersz jest ZAWSZE potrzebny, nawet jeśli nie ma treści wiadomości. Nagłówek zaczyna się od polecenia i zawiera dodatkowe wiersze par klucz-wartość oddzielone dwukropkiem i spacją. Jeśli istnieje treść wiadomości, może to być dowolna treść.
Wiersze w nagłówku i pusty wiersz na końcu nagłówka muszą kończyć się zwrotem Carraige i parą wysuwu wiersza (patrz styl podziału wiersza nagłówka HTTP ), dlatego te wiersze mają na końcu \ r \ n.
Adres URL ma postać
http://host:port/path?query_string
Istnieją dwa główne sposoby przesyłania wniosku do witryny internetowej:
POBIERZ: Ciąg zapytania jest opcjonalny, ale jeśli został określony, musi być rozsądnie krótki. Z tego powodu nagłówek może być po prostu poleceniem GET i niczym więcej. Przykładowa wiadomość może być:
GET /path?query_string HTTP/1.0\r\n \r\n
POST: To, co normalnie znajduje się w ciągu zapytania, znajduje się w treści wiadomości. Z tego powodu nagłówek musi zawierać atrybuty Content-Type: i Content-Length: oraz polecenie POST. Przykładowa wiadomość może być:
POST /path HTTP/1.0\r\n Content-Type: text/plain\r\n Content-Length: 12\r\n \r\n query_string
A więc, odpowiadając na twoje pytanie: jeśli adres URL, na który chcesz wysłać post to http://api.somesite.com/apikey=ARG1&command=ARG2, to nie ma treści ani ciągu zapytania, a zatem nie ma powodu do POST, ponieważ nie ma nic do umieszczenia w treści wiadomości, a więc nic do umieszczenia w Content-Type: i Content-Length:
Myślę, że mógłbyś OPUBLIKOWAĆ, gdybyś naprawdę chciał. W takim przypadku Twoja wiadomość wyglądałaby tak:
POST /apikey=ARG1&command=ARG2 HTTP/1.0\r\n \r\n
Aby wysłać wiadomość, program C musi:
Połączenia wysyłające i odbierające niekoniecznie wysyłają / odbierają WSZYSTKIE dane, które im podasz - zwrócą liczbę faktycznie wysłanych / odebranych bajtów. Do Ciebie należy wywołanie ich w pętli i wysłanie / odebranie pozostałej części wiadomości.
To, czego nie zrobiłem w tym przykładzie, to sprawdzenie prawdziwego błędu - kiedy coś się nie powiedzie, po prostu wychodzę z programu. Daj mi znać, jeśli to działa dla Ciebie:
#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; }
Jak wskazała inna odpowiedź, 4096 bajtów nie jest bardzo dużą odpowiedzią. Wybrałem tę liczbę losowo, zakładając, że odpowiedź na twoją prośbę byłaby krótka. Jeśli może być duży, masz dwie możliwości:
Dodatkowe informacje, aby odpowiedzieć na pytanie zadane w komentarzach:
A co jeśli chcesz POST umieścić dane w treści wiadomości? Następnie musisz dołączyć nagłówki Content-Type: i Content-Length:. Content-Length: to rzeczywista długość wszystkiego po pustym wierszu oddzielającym nagłówek od treści.
Oto przykład, który przyjmuje następujące argumenty wiersza poleceń:
Tak więc, w przypadku pierwotnego pytania, możesz uruchomić:
a.out api.somesite.com 80 GET "/apikey=ARG1&command=ARG2"
A jeśli chodzi o pytanie zadane w komentarzach, zadasz:
a.out api.somesite.com 80 POST / "name=ARG1&value=ARG2" "Content-Type: application/x-www-form-urlencoded"
Oto kod:
#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; }
źródło
\r\n
), ale ten kod używa prostych wysuwów wiersza."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"
Chcę pisać jak name = reaz. Odpowiada 400 złych żądańOdpowiedź Jerry'ego jest świetna. Jednak nie obsługuje dużych odpowiedzi. Prosta zmiana, aby sobie z tym poradzić:
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);
źródło
bytes = recv(sockfd, response, 1023, 0)
Po tygodniach badań. Wymyśliłem następujący kod. Uważam, że jest to absolutne minimum potrzebne do nawiązania bezpiecznego połączenia SSL z serwerem WWW.
#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; }
Powyższy kod szczegółowo wyjaśnia, jak nawiązać połączenie TLS ze zdalnym serwerem.
Ważna uwaga : ten kod nie sprawdza, czy klucz publiczny został podpisany przez ważny organ. Oznacza to, że nie używam certyfikatów głównych do weryfikacji. Nie zapomnij o wdrożeniu tego sprawdzenia, w przeciwnym razie nie będziesz wiedział, czy łączysz się z właściwą stroną internetową
Jeśli chodzi o samą prośbę. To nic innego jak ręczne pisanie żądania HTTP.
Możesz również znaleźć pod tym linkiem wyjaśnienie, jak zainstalować openSSL w swoim systemie i jak skompilować kod, aby korzystał z bezpiecznej biblioteki .
źródło
Uchwyt dodany.
Dodano nagłówek Host.
Dodano obsługę linux / windows, przetestowano (XP, WIN7).
OSTRZEŻENIE: BŁĄD: „błąd segmentacji”, jeśli nie ma hosta, ścieżki lub portu jako argumentu.
#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; }
źródło