CGI通用网关接口
Common Gatway Interface
原理
简介
CGI 是 Web服务器和程序动态生成数据之间的通用接口
服务器和CGI程序之间是通过标准输入输出来进行数据传递的,需要环境变量的协作才可以实现
基本流程:
- 通过浏览器将用户请求送到服务器
- 服务器接收用户请求并交给CGI程序处理
- CGI程序把处理结果传送给服务器
- 服务器把结果送回到浏览器
对于Windows系统而言,还可以通过profile文件进行数据传输(如ini文件)
每个CGI程序只能处理一个用户请求,在激活一个CGI程序进程时也创建了属于该进程的环境变量。
环境变量
对于CGI程序来说,它继承了系统的环境变量, CGI环境变量在CGI程序启动时初始化,在结束时销毁。
当一个CGI程序不是被HTTP服务器调用时,它的环境变量几乎是系统环境变量的复制。当这个CGI程序被HTTP服务器调用时,它的环境变量就会多了关于HTTP服务器、客户端、CGI传输过程等相关内容。
与请求相关的环境变量
名称 | 说明 |
REQUEST_METHOD | 服务器与CGI程序之间的信息传输方式 |
QUERY_STRING | 采用GET时所传输的信息 |
CONTENT_LENGTH | STDIO中有效信息长度 |
CONTENT_TYPE | 指示所传来的信息的MIME类型 |
CONTENT_FILE | 使用Windows HTTPd/WinCGI标准时,用来传送数据的文件名 |
PATH_INFO | 路径信息 |
PATH_TRANSLATED | CGI程序的完整路径名 |
SCRIPT_NAME | 所调用的CGI程序的名字 |
与服务器相关的环境变量
名称 | 说明 |
GATEWAY_INTERFACE | 服务器所实现的CGI版本 |
SERVER_NAME | 服务器的IP或名字 |
SERVER_PORT | 主机的端口号 |
SERVER_SOFTWARE | 调用的CGI程序名称和版本号 |
与客户端相关的环境变量
名称 | 说明 |
REMOTE_ADDR | 客户机的IP地址 |
REMOTE_HOST | 客户机的主机名 |
ACCEPT | 能接收的应答方式 |
ACCEPT_ENCODING | 支持的编码方式 |
ACCEPT_LANGUAGE | 可接受的语言 |
AUTORIZATION | 认证方式 |
FORM | 客户机地址 |
IF_MODIFIED_SINGCE | 当用get方式请求并且只有当文档比指定的日期更早时才返回数据 |
PRAGMA | 设定将来要用到的服务器代理 |
REFFERER | 链接到当前文档的文档的URL |
USER_AGENT | 客户端浏览器的信息 |
- CGI传送给Web服务器的信息可以用各种格式,通常是以HTML文本或者XML文本的形式
1. 传输HTML,文本第一行输出的内容必须是"content-type:text/html\n\n" 2. 传输XML,文本第一行输出的内容必须是"content-type:text/xml\n\n" 3. 其他的一些格式:JIF(image/gif)、JPEG(image/jpeg)、AVI(video/avi)
两个重要环境变量
QUERY_STRING
:在浏览器端以GET的方法输入的数据,数据的内容就是url问号后的内容
char *data = getenv("QUERY_STRING");
CONTENT_LENGTH
:在浏览器端以POST方法输入的数据的字节数,数据的内容通过标准输入获取
//1. 数据的长度 char *len = getenv("CONTENT_LENGTH"); //2、根据长度 从标准输入设备 获取内容 char data[128]=""; fgets(data, atoi(len)+1, stdin);
POST方法
客户端来的用户数据将存放在CGI进程的标准输入中,同时将用户数据的长度赋予环境变量中的CONTENT_LENGTH
GET方法
客户端来的用户数据存放到到环境变量QUERY_STRING
CGI程序的执行流程:
- 查询与该CGI程序进程相应的环境变量:
REQUEST_METHOD,如果是POST,就从环境变量中获取数据的长度len,然后到该进程相应的标准输入取出len长的数据。
如果是GET,则用户数据就在环境变量的QUERY_STRING中
#include <stdio.h> #include <stdlib.h> #include <string.h> int get_inputs(){ int length; char *method; char *inputstring; method = getenv("REQUEST_METHOD"); //将返回结果赋予指针 if(method == NULL) return 1; //找不到环境变量REQUEST_METHOD if(!strcmp(method, "POST")) { // POST方法 length = atoi(getenv("CONTENT_LENGTH")); //结果是字符,需要转换 if(length != 0) { inputstring = malloc(sizeof(char)*length + 1) //必须申请缓存,因为stdin是不带缓存的。 fread(inputstring, sizeof(char), length, stdin); //从标准输入读取一定数据 } }else if(!strcmp(method, "GET")){ Inputstring = getenv("QUERY_STRING"); length = strlen(inputstring); } }
先判断“REQUEST_METHOD”是否存在,程序会更健壮,否则在某些情况下可能会造成程序崩溃。
假若CGI程序不是由服务器调用的,环境变量集里就没有与CGI相关的环境变量(如REQUEST_METHOD,REMOTE_ADDR等)添加进来
CGI数据输出
在CGI程序中的标准输出stdout是经过重定义了的,没有在服务器上产生任何的输出内容,而是被重定向到客户浏览器
可以用打印来实现客户端新的HTML页面的生成。
CGI标题
CGI的格式输出内容必须组织成标题/内容的形式,CGI标准规定了CGI程序可以使用的三个HTTP标题。
标题必须占据第一行输出,而且必须随后带有一个空行
标题 | 描述 |
Content-Type | 设定随后输出数据的MIME类型 |
Location | 设定输出为另外一个文档 |
Status | 指定HTTP状态码 |
Content-Type
向标准输出发送网页内容要遵循MIME格式
任意输出前面必须要有一个用于定义MIME类型的输出内容,随后必须跟一个空行。
printf("Content-Type:text/html\n\n"); printf("Welcome\n");
MIME类型以类型/子类型(type/subtype)的形式表示。
- 其中type表示一下几种典型文件格式的一种:
text、audio、video、image、application、mutipart、message
- Subtype则用来描述具体所用的数据格式。
类型 | 说明 |
Application/msword | word文档 |
Application/octet-stream | 一种通用二进制文件格式 |
Application/zip | zip压缩文件 |
Application/pdf | pdf文件 |
Location
使用location ,一个CGI可以使用当前用户转而访问同一服务器上的另外一个程序,甚至可以访问一个url
使用Location的格式为:Location:Filename/URL
printf("Location:/test.html\n\n"); //这与直接链接到test.html的效果是一样的。
printf("Location:http:*//www.baidu.com/\n\n"); //由于该URL并不指向当前服务器,用户浏览器并不会直接链接到指定的URL,而是给用户输出提示信息。
Demo
GET方式:计算a + b的值
url: http://ip:port/add?a=nn&b=nn
#include <stdio.h> #include <string.h> #include <stdlib.h> int main(){ char * method = NULL; char *query_string = NULL; printf("Content-Type:text/html\r\n\r\n"); method = getenv("REQUEST_METHOD"); if(method == NULL){ printf("<html> <p> UNKNOWN METHOD</p></html>"); return; } if(strcasecmp(method,"POST") == 0){ printf("<html> <p> UNSUPPORTED METHOD</p></html>"); return 0; } query_string = getenv("QUERY_STRING"); if(query_string == NULL){ printf("<html> <p> PARAMETER ERROR </p></html>"); return 0; } int a = 0; int b = 0; int i = 0; char * start = query_string+2; char * end = NULL; // format: a=xxx&b=xxx for(i = 0;i<strlen(query_string);i++){ if(*(query_string+i) == '&'){ end = query_string+i; break; } } *end = '\0'; a = atoi(start); start = end +3; b = atoi(start); printf("<html> <p> %d + %d = %d </p></html>",a,b,a+b); return 0; }
显示效果如下: