C – cgi 编程
关于 http 通信的原理,在之前的 http 通讯 和tinyhttpd —— C 语言实现最简单的 HTTP 服务器 已经讲过。
对于编写 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
测试结果
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$