1、CGI工作机理

            CGI即公共网关接口(Common Gateway Interface)是在WEB服务器上定义了

            WEB客户请求与应答的一种方法。客户向服务器的请求只要属于CGI范围,就启

            动WEB服务器的一个CGI(网关)程序。它的任务是把客户的请求从网关的环境变

            量(下一节细加以描述)中取出,并进行相应的加工处理。由 CGI程序决定如何

            对客户的请求做出应答。另外, CGI程序定义标准的方法为服务器及客户标准

            的请求与响应信息。因此, CGI是WEB服务器不可缺少的组成部分。

 

            2、环境变量

            CGI的标准规定服务器必须通过其环境变量来为CGI的程序传递信息内容。

            通常HTTPD服务器的cgi-bin目录中常用test-env的小程序来验证 CGI返回的环

            境变量。如:


            SERVER_NAME=lab.fjinfo.sti.ac.cn

            HTTP_CONNECTION=Keep-Alive

            REMOTE_ADDR=168.160.135.21

            HTTP_HOST=lab

            REQUEST_METHOD=GET

            GATEWAY_INTERFACE=CGI/1.1

            QUERY_STRING=

            REMOTE_USER=chencx

            SERVER_SOFTWARE=NCSA/1.5

            SERVER_PROTOCOL=HTTP/1.0

            REMOTE_HOST=chencx.fjinfo.sti.ac.cn

            SERVER_PORT=80

            DOCUMENT_ROOT=/usr/local/etc/httpd/htdocs

            HTTP_USER_AGENT=Mozilla/3.0 (Win95; I)

            HTTP_ACCEPT=image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*

            SCRIPT_NAME=/cgi-bin/test-env

            SERVER_ADMIN=httpd@lab.fjinfo.sti.ac.cn

            AUTH_TYPE=Basic

 


            这些变量作出如下解释: SERVER_SOFTWARE是客户机请求的HTTP服务器的软件名称及其改软件的版 本。
            SERVER_NAME是HTTP服务器的主机域名。 GATEWAY_INTERFACE是指该服务器的CGI版本。
            SERVER_PROTOCOL用于发送请求的协议名称和版本,目前只有HTTP协议支 持CGI的标准。
            SERVER_PORT是用于接受客户请求的服务器端口号,常用的是80。
            REMOTE_HOST是指用于发送请求的客户机的主机域名。只有当你的客户主 机具有指定的域名时,才有其值。
            REMOTE_ADDR是指发送请求的客户机器在HTTP主机上返回的IP地址。 REMOTE_USER是指远程授权用户的用户名。
            AUTH_TYPE 是用于保护客户的授权类型。 REQUEST_METHOD是客户机用于发送请求的方法(有GET/POST两种细节在下
            一节叙述)。 HTTP_ACCEPT是客户机支持的MIME类型清单,各个类型间用逗号分割。星
            号为通配符,它表示可接受任意类型的数据。不过目前许多HTTP服务器尚未使 用这个变量。
            PATH_INFO是接在CGI脚本之后的其他路径名信息。 PATH_TRANSLATED
            QUERY_STRING是接收来自客户浏览器的数据(参见下节)。
            CONTENT_TYPE是用以发送信息的MIME类型。目前常用:application/x- www-form-urlencoded。
            CONTENT_LENGTH是指当客户使用POST方法发送的请求时,计算从客户浏览 器发送的字节数。 3、CGI标题及其GET/POST
            (1)Header 标题:HTTP服务器通过环境变量与CGI程序进行通信。而CGI程序通过标准
            输出把数据以HTML形式送到服务器再到客户。但是, CGI的输出程序必须使用 三个标题行: 第一行,Content-type:
            设定输出的数据的MIME输出类型。常用为: Content-type: plain/text或者Content-type:
            jpeg/gif。 第二行,Location: 是指输出的文档名称。 第三行,Status 是HTTP的状态码。
            见http://www.w3.org/hypertext/WWW/Protocols/HTTP/HTRESP.html
            注意这三行必须单独各占一行,并且每一行之后必须有个空行。只有这样服 务器才能解释CGI产生的文档结果。 (2)POST/GET方法
            我们将在第四章HTML语言中讲述的form元素时,其产生的表格传送至服务 器的
            CGI脚本,即客户浏览器的信息就是通过POST或GET两种方式传至服务器。 POST方法
            当客户端的HTML的表格设计采用POST时,CGI中的环境变量REQUEST_METHOD
            就返回值是POST。而CONTENT_LENGTH计算出客户传送的信息的数据量(以字节 为单位)。
            CGI程序就依CONTENT_LENGTH计算出HTTP何时开始停止阅读从客户
            端传来的信息。用POST方式发送的信息只有application/x-www-form-urlen-
            coded一种MIME类型数据。并把此类型记录在CONTENT_TYPE之中。
            客户不管是以POST还是GET方式向服务器传送信息必须加以编码才能将其 信息数据传至服务器中。
            因此,服务器受到来自客户的信息数据时,必须把 URL之后的数据进行解 码(客户浏览器编码的逆过程)使其数据信息恢复原貌。
            该过程是(一)把原来用&连在一起的HTML表格变量与其值分开; (二)把原来用=连在一起的HTML表格变量与其值分开;
            (三)把各变量和数据中包含十六进制转换成ASCII码等价表示; (四)把各变量和数据中的所有+号替换成空格。
            依次把其编码的信息数据还原成客户端输入时的原来面貌。此时 CGI程序 就可对这些数据进行一系列加工和处理。
            GET是把客户的表格的数据加到URL的末尾,传送至服务器。故,必须对其
            传送的数据加以专门的编码。大家知道URL的标准中保留了若干特殊的字符,
            如:&、#、?、=、空格及其任意不可打印的ASCII控制字符。这些对我们的
            CGI来讲具有重要的作用。如用+来代替原来的空格字符,保留的字符用%后接 相应的十六进制的
            ASCII码值替代。大多数的客户浏览器均可对其进行正确的 编码。但是值得注意的是应防止一些非法的用户通过 CGI的脚本造成对系统安
            全的威胁。(如sendmail) GET方法 基于GET方法的CGI程序与POST的程序的运行机理基本相同,只是服务器发
            送的数据采用的方式稍微有些差异。它不是把客户浏览器上传到服务器的数据发
            送到CGI程序的标准输入,而是将这些数据经编码后由QUERY_STRING环境变量 传输,服务器上的
            CGI程序只要识别QUERY_STRING即可。另外,环境变量 REQUEST_METHOD的缺省值为:GET。 4、几种常用数据库接口
            (Sybase,mSQL,Informix,Oracle,) CGI 程序可以完成与一些大型数据库的接口。通过标准的客户浏览器达到
            对大型数据库的检索或产生动态的页面。近一两年来,国际上各大公司对此都非
            常重视,都相应开发出一系列产品,使二者间的接口更容易。例如:微软公司的 Information Server与MS
            SQL接口、还有Sybase、Informix、Orcale等 等。你也可以编写一些CGI使浏览器能对Foxpro数据库进行访问。
            5、几种常用CGI及其WEB开发语言(C,Perl,Shell,Tcl,Java,VB)
            用于开发CGI的几种常用语言有C,Perl,Sh,Tcl等等。除了C是编译型外,
            其他几种都是解释型的开发语言。以下只是举一些简单的例子加以说明。至于 每一种语言细节不是本文的范围。 (1)SH

            #!/bin/sh

 

            echo Content-type: text/plain

            echo

            echo CGI/1.0 test script report:

            echo

            echo argc is $#. argv is "$*".

            echo

 

            echo SERVER_SOFTWARE = $SERVER_SOFTWARE

            echo SERVER_NAME = $SERVER_NAME

            echo GATEWAY_INTERFACE = $GATEWAY_INTERFACE

            echo SERVER_PROTOCOL = $SERVER_PROTOCOL

            echo SERVER_PORT = $SERVER_PORT

            echo REQUEST_METHOD = $REQUEST_METHOD

            echo HTTP_ACCEPT = "$HTTP_ACCEPT"

            echo PATH_INFO = $PATH_INFO

            echo PATH_TRANSLATED = $PATH_TRANSLATED

            echo SCRIPT_NAME = $SCRIPT_NAME

            echo QUERY_STRING = $QUERY_STRING

            echo REMOTE_HOST = $REMOTE_HOST

            echo REMOTE_ADDR = $REMOTE_ADDR

            echo REMOTE_USER = $REMOTE_USER

            echo CONTENT_TYPE = $CONTENT_TYPE

            echo CONTENT_LENGTH = $CONTENT_LENGTH

 


            这是用UNIX上的SH编写的简单CGI小程序,是用来检测WEB服务器的 CGI的 环境变量值。大家可以从这些环境变量编写更深入的程序。
            (2)Perl (Practical Extraction and Report Language)
            Perl是一种优秀的解释型的文本处理语言。目前已被一些 CGI专家们广泛 使用。目前不仅有UNIX版本,还有Windows
            或其他OS的版本。其语法具有同样 的规则。 #程序名:cgi_handlers.pl #用
            途:处理由HTML的客户端的FORM的输入的数据,并处理成为 CGI 内部可用参数。包括输入、输出及其中间代码转化等全过程。

            sub get_request {

 

            if ($ENV{'REQUEST_METHOD'} eq "POST") {

            read(STDIN, $request, $ENV{'CONTENT_LENGTH'});

            } elsif ($ENV{'REQUEST_METHOD'} eq "GET" ) {

            $request = $ENV{'QUERY_STRING'};

            }

 

            %rqpairs = &url_decode(split(/[&=]/, $request));

            }

 

            sub url_decode {

 

            foreach (@_) {

            tr/+/ /;

            s/%(..)/pack("c",hex($1))/ge;

            }

            @_;

            }

 

            sub html_header {

 

            local($title) = @_;

 

            print "Content-type: text/html/n/n";

            print "< html >< head >/n";

            print "< title >$title< /title >/n";

            print "< /head >/n< body >/n";

            }

 

            sub html_trailer {

 

            local($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)

            = gmtime;

 

            local($mname) = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",

            "Aug", "Sep", "Oct", "Nov", "Dec")[$mon];

            local($dname) = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri",

            "Sat")[$wday];

 

            print "< p >/nGenerated by: $0< br >/n";

            print "Date: $hour:$min:$sec UT on $dname $mday $mname $year.< p
            >/n";

            print "< /body >< /html >/n";

            }

            1;

 


            (3)C语言 用C语言编写CGI程序类似于其他语言,只是要加以编译成可执行文件即可。
            因此,用C语言进行CGI程序的编写具有更高的安全性。下面是一个用C编写CGI 小程序,仅供参考。

            #include

            #include

 

            #define MAX_ENTRIES 10000

            typedef struct {

            char *name;

            char *val;

            } entry;

 

            char *makeword(char *line, char stop);

            char *fmakeword(FILE *f, char stop, int *len);

            char x2c(char *what);

            void unescape_url(char *url);

            void plustospace(char *str);

 

            main(int argc, char *argv[]) {

            entry entries[MAX_ENTRIES];

            register int x,m=0;

            int cl;

 

            if(strcmp(getenv("REQUEST_METHOD"),"POST")) exit(1);

            if(strcmp(getenv("CONTENT_TYPE"),"application/x-www-form-urlencoded"))
            exit(

            cl = atoi(getenv("CONTENT_LENGTH"));

 

            for(x=0;cl>0 && (!feof(stdin));x++) {

            m=x;

            entries[x].val = fmakeword(stdin,'&',&cl);

            plustospace(entries[x].val);

            unescape_url(entries[x].val);

            entries[x].name = makeword(entries[x].val,'=');

            }

 

            for(x=0; x <= m; x++)

            printf("%s %s%/n",entries[x].name, entries[x].val);

            }

 


            (4)TCL TCL即Tool Command Language,它是高级的跨平台的 Script语言,它与 Perl、C
            shell具有相识的机理不同的语法。

            proc subhex {v} {

            set nv $v

            while {[regexp {%[0-9A-F][0-9A-F]} $v blah]} {

            scan $blah "%%%x" cv

            if {[ctype char $cv]=="&"} {

            regsub -all $blah $v //& nv

            } else {

            regsub -all $blah $v [ctype char $cv] nv

            }

            set v $nv

            }

            return $v

            }

            proc http_proc_args {arg} {

            global ar artype

            upvar $arg argv

            set bigarg ""

            #join words with +'s

 

            if [llength $argv]==2 {

            set bigarg [split [lindex $argv 1] &]

            set path [lindex $argv 0]

            set args [split [crange $path 1 end] /]

            } else {

            set a0 [lindex $argv 0]

            if {[cindex $a0 0]=="/"} {

            set bigarg {}

            set args [split [crange $a0 1 end] /]

            } else {

            set bigarg [split $a0 &]

            set args {}

            }

            }

            foreach arg [concat $bigarg $args] {

            if [regexp {(.*)=(.*)} $arg foo name value] {

            set name [subhex $name]

            if ![info exists argcount($name)] {

            set argcount($name) 1

            } else {

            incr argcount($name)

            }

            }

            }

 

            #split assignments on the '&'

            foreach arg [concat $bigarg $args] {

            if [regexp {(.*)=(.*)} $arg foo name value] {

            regsub -all {/+} $value { } newval

            set name [subhex $name]

            set val [subhex $newval]

            if ![info exists ar($name)] {

            if [info exists artype($name)] {

            if {$artype($name)=="list"} {

            if {$val!=""} {

            set ar($name) [list $val]

            } else {

            set ar($name) {}

            }

            } else {

            set ar($name) $val

            }

            } else {

            set ar($name) $val

            }

            } else {

            # multiple selection

            lappend ar($name) $val

            }

            }

            }

            }

 

            #makes URL extended pathname to encode current state

            proc pkg_attrs {var attrs} {

            upvar $var ar

            set al ""

            foreach i $attrs {

            if [info exists ar($i)] {

            # regsub { } $ar($i) {+} att

            set al "$al/$i=$att"

            }

            }

            return $al

            }

 

            proc http_proc_cgi_args {} {

            global ar artype

            global env

            set bigarg ""

            #join words with +'s

            if [info exists env(PATH_INFO)] {

            set args [split [crange $env(PATH_INFO) 1 end] /]

            } else {

            set args {}

            }

            if [info exists env(QUERY_STRING)] {

            set bigarg [split $env(QUERY_STRING) &]

            } else {

            set bigarg {}

            }

 

            foreach arg [concat $bigarg $args] {

            if [regexp {(.*)=(.*)} $arg foo name value] {

            set name [subhex $name]

            if ![info exists argcount($name)] {

            set argcount($name) 1

            } else {

            incr argcount($name)

            }

            }

            }

 

            #split assignments on the '&'

            foreach arg [concat $bigarg $args] {

            if [regexp {(.*)=(.*)} $arg foo name value] {

            regsub -all {/+} $value { } newval

            set name [subhex $name]

            set val [subhex $newval]

            if ![info exists ar($name)] {

            if [info exists artype($name)] {

            if {$artype($name)=="list"} {

            if {$val!=""} {

            set ar($name) [list $val]

            } else {

            set ar($name) {}

            }

            } else {

            set ar($name) $val

            }

            } else {

            set ar($name) $val

            }

            } else {

            # multiple selection

            lappend ar($name) $val

            }

            }

            }

            }

 

【作者: xieshilei 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐