Saya ingin membuat aplikasi C yang sangat sederhana yang melakukan posting HTTP. Ini akan mengambil beberapa parameter, dan menggunakannya untuk membuat URL. Saya hanya ingin melakukan HTTP POST sederhana dan mendapatkan respons tanpa menggunakan curl (pustaka tidak dan tidak akan diinstal pada mesin yang perlu dijalankan ini).
Kode semu:
Proses 2 argumen
Masukkan args ke dalam URL kerangka: http://api.somesite.com/apikey=ARG1&command=ARG2
Lakukan POST pada URL yang dihasilkan
Konsumsi respons
Pencarian Google dan SO saya belum menghasilkan apa-apa tentang masalah ini.
Jawaban:
Pesan memiliki bagian header dan badan pesan yang dipisahkan oleh baris kosong. Baris kosong SELALU diperlukan meskipun tidak ada badan pesan. Header dimulai dengan sebuah perintah dan memiliki baris tambahan dari pasangan nilai kunci yang dipisahkan oleh titik dua dan spasi. Jika ada badan pesan, bisa apa saja yang Anda inginkan.
Garis di header dan baris kosong di akhir header harus diakhiri dengan carraige return dan pasangan linefeed (lihat gaya jeda baris header HTTP ) jadi itulah mengapa baris tersebut memiliki \ r \ n di akhir.
URL memiliki bentuk
http://host:port/path?query_string
Ada dua cara utama untuk mengirimkan permintaan ke situs web:
GET: String kueri bersifat opsional, tetapi jika ditentukan, harus cukup pendek. Karena itu, header hanya bisa menjadi perintah GET dan tidak ada yang lain. Contoh pesan dapat berupa:
GET /path?query_string HTTP/1.0\r\n \r\n
POST: Apa yang biasanya ada di string kueri ada di badan pesan sebagai gantinya. Karena itu, header perlu menyertakan atribut Content-Type: dan Content-Length: serta perintah POST. Contoh pesan dapat berupa:
POST /path HTTP/1.0\r\n Content-Type: text/plain\r\n Content-Length: 12\r\n \r\n query_string
Jadi, untuk menjawab pertanyaan Anda: jika URL yang ingin Anda posting adalah http://api.somesite.com/apikey=ARG1&command=ARG2 maka tidak ada body atau query string dan, akibatnya, tidak ada alasan untuk POST karena ada tidak ada apa-apa untuk dimasukkan ke dalam badan pesan dan karenanya tidak ada yang harus dimasukkan ke dalam Jenis Konten: dan Panjang Konten:
Saya kira Anda bisa POST jika Anda benar-benar ingin. Dalam hal ini pesan Anda akan terlihat seperti:
POST /apikey=ARG1&command=ARG2 HTTP/1.0\r\n \r\n
Jadi untuk mengirim pesan, program C perlu:
Panggilan kirim dan terima tidak serta merta mengirim / menerima SEMUA data yang Anda berikan - mereka akan mengembalikan jumlah byte yang sebenarnya dikirim / diterima. Terserah Anda untuk memanggil mereka dalam satu lingkaran dan mengirim / menerima sisa pesan.
Apa yang tidak saya lakukan dalam contoh ini adalah pemeriksaan kesalahan nyata apa pun - ketika ada yang gagal, saya langsung keluar dari program. Beri tahu saya jika ini berhasil untuk Anda:
#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; }
Seperti jawaban lain yang ditunjukkan, 4096 byte bukanlah respons yang sangat besar. Saya memilih nomor itu secara acak dengan asumsi bahwa tanggapan atas permintaan Anda akan singkat. Jika bisa besar, Anda memiliki dua pilihan:
Informasi tambahan untuk menjawab pertanyaan yang diajukan di komentar:
Bagaimana jika Anda ingin POST data di badan pesan? Kemudian Anda perlu menyertakan header Content-Type: dan Content-Length:. Content-Length: adalah panjang sebenarnya dari segala sesuatu setelah baris kosong yang memisahkan header dari body.
Berikut adalah contoh yang mengambil argumen baris perintah berikut:
Jadi, untuk pertanyaan awal Anda akan menjalankan:
a.out api.somesite.com 80 GET "/apikey=ARG1&command=ARG2"
Dan untuk pertanyaan yang ditanyakan di komentar Anda akan menjalankan:
a.out api.somesite.com 80 POST / "name=ARG1&value=ARG2" "Content-Type: application/x-www-form-urlencoded"
Ini kodenya:
#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; }
sumber
\r\n
), tetapi kode ini menggunakan feed baris kosong."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"
Saya ingin memposting seperti name = reaz. Ini menanggapi 400 Permintaan BurukJawaban Jerry bagus. Namun, itu tidak menangani respons yang besar. Perubahan sederhana untuk menangani ini:
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);
sumber
bytes = recv(sockfd, response, 1023, 0)
Setelah berminggu-minggu penelitian. Saya datang dengan kode berikut. Saya yakin ini adalah kebutuhan minimum untuk membuat koneksi aman dengan SSL ke server 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; }
Kode di atas akan menjelaskan secara detail bagaimana membuat koneksi TLS dengan remote server.
Catatan penting : kode ini tidak memeriksa apakah kunci publik ditandatangani oleh otoritas yang valid. Artinya saya tidak menggunakan sertifikat dasar untuk validasi. Jangan lupa untuk menerapkan pemeriksaan ini jika tidak, Anda tidak akan tahu apakah Anda menghubungkan situs web yang benar
Ketika datang ke permintaan itu sendiri. Tidak lebih dari menulis permintaan HTTP dengan tangan.
Anda juga dapat menemukan di bawah tautan ini penjelasan bagaimana menginstal openSSL di sistem Anda, dan bagaimana mengkompilasi kode sehingga menggunakan pustaka aman .
sumber
Pegangan ditambahkan.
Menambahkan header Host.
Menambahkan dukungan linux / windows, diuji (XP, WIN7).
PERINGATAN: ERROR: "kesalahan segmentasi" jika tidak ada host, jalur, atau port sebagai argumen.
#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; }
sumber