乐趣区

C-cgi编程

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$
退出移动版