CGI基本概念并用C写CGI

一直以来对CGI这块知识都是空白的,仅仅大概了解CGI是通用网关接口,用来实现HTTP服务器与实现动态页面语言的交互,这几天对CGI补了下课,记录如下。

1. 一些常识

HTTP(Hyper Text Transfer Protocol),如其字面意思,仅仅是个超文本传输协议,超文本的超指的是以超链接的形式,将不同空间的文字信息组织在一起的网状文本。归根结底,HTTP传输是文本文件。HTTP服务器不具备执行脚本语言的能力,CGI(Common Gateway Protocol)就被引入了进来。

2. CGI

2.1. 定义

摘自Wikipedia CGI条目[1]

Common Gateway Interface (CGI) offers a standard protocol for web servers to execute programs that execute like Console applications (also called Command-line interface programs) running on a server that generates web pages dynamically.

The specifics of how the script is executed by the server are determined by the server. In the common case, a CGI script executes at the time a request is made and generates HTML.

In brief, the CGI program receives HTTP forms data via Unix/Linux standard input, and most other data (such as URL paths, URL arguments, and HTTP header data) via well-known Unix/Linux process environment variables.

简单翻译下:

CGI为Web服务器执行控制台应用程序动态生成Web页面提供了一套标准协议。

服务器如何执行脚本的具体规范由服务器制定。常规情况下,CGI脚本在请求发出生成HTML页面时被执行。

简单来说,CGI通过Unix/Linux标准输入接受HTTP表单数据,通过Unix/Linux进程环境变量接受其他数据(如URL路径、URL参数、HTTP头文件)。

通俗来说,比如一个网站要实现如果用户登录,则在右上方显示用户名的功能,首先用户的浏览器要将用户名传递至HTTP服务器(比如在URL后附加参数),HTTP服务器必须要再提交至某个脚本。反过来,这个脚本必须为客户端HTTP Request提供出一个Response由HTTP服务器返回客户浏览器。在一开始,没有标准去规范这个过程,每家HTTP服务器标准各不相同,IETF便组织制定了CGI标准(RFC-3875)。CGI定义并规范了HTTP服务器与脚本之间的通讯,也就是HTTP服务器唤起页面生成程序进行操作的过程。

2.2. CGI规范

摘自RFC-3875 CGI Version 1.1规范[2]

2.2.1. CGI请求

Information about a request comes from two different sources; the
request meta-variables and any associated message-body.

一个CGI请求通过两种途径传递信息:请求中的环境变量用来传递请求头信息和标准输入用来传递与请求相关的消息正文。

2.2.1.1. 环境变量

环境变量有以下种类:

meta-variable-name = "AUTH_TYPE" | "CONTENT_LENGTH" |
"CONTENT_TYPE" | "GATEWAY_INTERFACE" |
"PATH_INFO" | "PATH_TRANSLATED" |
"QUERY_STRING" | "REMOTE_ADDR" |
"REMOTE_HOST" | "REMOTE_IDENT" |
"REMOTE_USER" | "REQUEST_METHOD" |
"SCRIPT_NAME" | "SERVER_NAME" |
"SERVER_PORT" | "SERVER_PROTOCOL" |
"SERVER_SOFTWARE" | scheme |
protocol-var-name | extension-var-name

protocol-var-name = ( protocol | scheme ) "_" var-name
scheme = alpha *( alpha | digit | "+" | "-" | "." )
var-name = token
extension-var-name = token

在此借用[3]一张图:

请尤其注意QUERY_STRING和CONTENT_LENGTH。他们分别是GET和POST获取数据的方法。

2.2.1.2. 请求消息正文
Request data is accessed by the script in a system-defined method;
unless defined otherwise, this will be by reading the 'standard
input' file descriptor or file handle.

请求消息正文则是CGI脚本通过标准输入进行获取,当环境变量CONTENT_LENGTH不为0时,可以认为该请求包含正文。

2.2.2. CGI响应

A script MUST always provide a non-empty response, and so there is a
system-defined method for it to send this data back to the server.
Unless defined otherwise, this will be via the 'standard output' file
descriptor.

CGI响应默认使用标准输出返回HTTP服务器。

The response comprises a message-header and a message-body, separated
by a blank line. The message-header contains one or more header
fields. The body may be NULL.

响应包含一个消息头和消息正文,通过空行隔开。消息正文可以为空。

2.2.2.1

3. 使用C语言写一个最简单的CGI脚本

3.1. 测试环境

- Debian GNU/Linux 9.4-amd64
- XAMPP 5.6.34 (Apache HTTP Server 2.4.29)
- GCC 6.3.0

3.2. 用CGI写Hello World

我们按照规范,第一行输出一个CGI响应头部,指明Content-Type为Plain/Text,然后用空行将头部与正文隔开,在正文直接向标准输出输出Hello World,代码如下:

#include <stdio.h>

int main() {
    printf ("Content-type: text/plain\n\n");    
    printf ("Hello World with CGI\n");
    return 0;
}

将该文件编译成可执行文件

$ gcc -o hello.cgi hello.c

然后将其移动至xampp的cgi-bin文件夹

$ cp hello.cgi /opt/lampp/cgi-bin/

直接在浏览器打开这个页面,大功告成。

3.3. GET与POST

显然,动态页面最大的特点在于GET与POST的实现。下面我们讨论下使用。

我们先来做一个最简单的有表单的HTTP页面,代码如下:

<html>
<body>
    <form id="form" method="get" action="/cgi-bin/test.cgi">
        <p>Content: <input type="text" name="content" id="content" /></p>
        <p><input type="submit" name="submit" id="submit" value="Submit" /></p>
    </form>
</body>
</html>

这个表单只有一个文本框和一个Submit按钮,点击提交后触发test.cgi脚本。
界面如下:

编译出test.cgi的test.c代码如下

#include <stdio.h>
#include <stdlib.h>

int main() {
    printf ("Content-type:text/html\n\n");
    printf ("%s",getenv("QUERY_STRING");
    return 0;
}

打开页面,Content输入123,执行结果如下:

就得到了GET的字符串。

再将表单method属性GET替换为POST,action属性test.cgi替换为test2.cgi,编译出test.cgi的test.c代码如下

#include <stdio.h>
#include <stdlib.h>

int main() {    
    char post[1000];
    printf ("Content-type:text/html\n\n");
    if (getenv("CONTENT_LENGTH")>0) {
        gets(post);
    }
    printf ("%s",post);
    return 0;
}

输出内容相同:

4. 参考文献

[1]https://en.wikipedia.org/wiki/Common_Gateway_Interface
[2]https://tools.ietf.org/html/rfc3875
[3]https://www.cnblogs.com/liuzhang/p/3929198.html


title: CGI基本概念并用C写CGI
time: 2018-04-10 16:05
tags: C,CGI
category: Study

标签: CGI C

发表评论: