關於http通訊的原理,在以前的http通信和tinyhttpd —— C 語言實現最簡單的 HTTP 服務器已經講過。git
對於編寫cgi程序,咱們最好仍是使用cgic庫比較好,如下程序只是提供理解cgic庫的一種思路。編程
//cgitest.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include "url.h" int fgetlen(char *s, size_t len, FILE *stream) { int count = 0; while (1) { s[count] = (char)fgetc(stream); --len; if (feof(stream) || (!(len - 1))) { count++; s[count] = '\0'; break; } count++; } return count; } void handle_get() { char *query_string = getenv("QUERY_STRING"); char *getstring = url_decode(query_string);//url解碼 fprintf(stdout, "Content-type: application/json\n\n"); //必須加應答頭,不然server會判斷此程序不是cgi程序 fprintf(stdout, "\"hello\":{\ \"nihao\":\"你好\"\ }\n");//stdout重定向到了請求端,因此printf打印到請求端 if (getstring != NULL) url_free(getstring); return; } void handle_post() { size_t content_lenth = atoi(getenv("CONTENT_LENGTH")); if (content_lenth == 0) { return; } char *input = (char *)malloc(content_lenth + 1); if (input == NULL) return; fgetlen(input, content_lenth + 1, stdin); char *poststring = url_decode(input); fprintf(stdout, "Content-type: application/json\n\n"); //必須加應答頭,不然server會判斷此程序不是cgi程序 fprintf(stdout, "\"hello\":{\ \"nihao\":\"你好\"\ }\n");//stdout重定向到了請求端,因此printf打印到請求端 if (poststring != NULL) url_free(poststring); return; } void handle_put() { } void handle_delete() { } int cgi_main() { char *request_method = getenv("REQUEST_METHOD"); if (request_method == NULL) { return EXIT_FAILURE; } else if (!strcasecmp(request_method, "GET")) { handle_get(); } else if (!strcasecmp(request_method, "POST")) { handle_post(); } else if (!strcasecmp(request_method, "PUT")) { handle_put(); } else if (!strcasecmp(request_method, "DELETE")) { handle_delete(); } else { return EXIT_FAILURE; } return EXIT_SUCCESS; } int main(int argc, const char *argv[]) { return cgi_main(); }
//url.h #ifndef __URL_H__ #define __URL_H__ char *url_encode(char url[]); char *url_decode(char url[]); void url_free(void *ptr); #endif
//url.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> //two ways to use urldecode: // urldecode stuff // echo stuff | urldecode char *url_decode(const char *input) { if (input == NULL) return NULL; int input_length = strlen(input); size_t output_length = (input_length + 1) * sizeof(char); char *working = malloc(output_length), *output = working; while(*input) { if(*input == '%') { char buffer[3] = { input[1], input[2], 0 }; *working++ = strtol(buffer, NULL, 16); input += 3; } else { *working++ = *input++; } } *working = 0; //null terminate return output; } //two ways to use urlencode: // urlencode stuff // echo stuff | urlencode //note unicode support should happen upstream of this. bool is_non_symbol(char c) { if(c == '\0') return 1; //we want to write null regardless int c_int = (int)c; return (c_int >= 48 && c_int <= 57) || (c_int >= 65 && c_int <= 90) || (c_int >= 97 && c_int <= 122); } char *url_encode(const char *input) { if (input == NULL) return NULL; int end = strlen(input); size_t final_size = (end * 3) + 1; char *working = malloc(final_size * sizeof(char)), *output = working; while(*input) { const char c = *input; if(c < 0) { input++; } else if(is_non_symbol(c)) { *working++ = *input++; } else { char encoded[4] = {0}; snprintf(encoded, 4, "%%%02x", c); *working++ = encoded[0]; *working++ = encoded[1]; *working++ = encoded[2]; input++; } } *working = 0; //null term return output; } void url_free(void *ptr) { free(ptr); } #if 0 int main(int argc, char **argv) { if(argc > 1) { char *input = argv[1]; char *decoded = url_decode(input); printf("%s",decoded); url_free(decoded); } else { char *line = NULL; size_t size; while(getline(&line, &size, stdin) != -1) { char *decoded = url_decode(line); printf("%s", decoded); url_free(decoded); } } return 0; } #endif #if 0 int main(int argc, char **argv) { if(argc > 1) { char *input = argv[1]; char *encoded = url_encode(input); printf("%s", encoded); url_free(encoded); } else { char *line = NULL; size_t size; while(getline(&line, &size, stdin) != -1) { char *encoded = url_encode(line); printf("%s", encoded); url_free(encoded); } } return 0; } #endif
測試結果json
jonathan@cloud:~/test/cgi$ ls cgitest.c url.c url.h jonathan@cloud:~/test/cgi$ gcc cgitest.c url.c -o cgitest jonathan@cloud:~/test/cgi$ sudo cp cgitest /opt/thttpd/www/ jonathan@cloud:~/test/cgi$ curl 127.0.0.1/cgi-bin/cgitest "hello":{ "nihao":"你好" } jonathan@cloud:~/test/cgi$ curl -H "Content-Type:application/json" -X POST --data '{"hello": "world"}' 127.0.0.1/cgi-bin/cgitest "hello":{ "nihao":"你好" } jonathan@cloud:~/test/cgi$