Perl & Python编写CGI

最近偶然玩了一下CGI,收集点资料写篇在这里留档。 现在想做HTTP Cache回归测试了,为了模拟不同的响应头及数据大小,就需要一个CGI按需要传回指定的响应头和内容,这是从老外的测试页面学习到的经验。

CGI其实就是使用STDIN和环境变量作为输入,  STDOUT做为输出,依照Http协议生成对应的数据。

一. 数据输出

数据输出遵循HTTP协议,分为四部分:

  状态行 (Status Line):

     200 OK

  响应头(Response Headers):

     Content-Type: text/html

     Content-Length: 3072

   空白行(代表响应头已经列完, 只能包含回车和换行符):

   

   数据(文本或二进制数据):

   <html>xxxxxx</html>

 参考: HTTP Protocol

状态行和响应头中每一行都必须以\r\n(回车及换行)结束。使用这个规则,以简单的文本组装就可以完成响应了。

只要注意二进制数据输出方法就可以了。 尝试设定Content-Length时可能没办法设定的准确,至少对于Apache Server是这样,不过不影响最后返回的数据量。

二. 数据输入

CGI的数据输入主要是环境变量,下面这个链接有一串定义,

     CGI Environment Variables

Key

Value

DOCUMENT_ROOT

The root directory of your server

HTTP_COOKIE

The visitor's cookie, if one is set

HTTP_HOST

The hostname of the page being attempted

HTTP_REFERER

The URL of the page that called your program

HTTP_USER_AGENT

The browser type of the visitor

HTTPS

"on" if the program is being called through a secure server

PATH

The system path your server is running under

QUERY_STRING

The query string (see GET, below)

REMOTE_ADDR

The IP address of the visitor

REMOTE_HOST

The hostname of the visitor (if your server has reverse-name-lookups on; otherwise this is the IP address again) 

REMOTE_PORT

The port the visitor is connected to on the web server

REMOTE_USER

The visitor's username (for .htaccess-protected pages)

REQUEST_METHOD

GET or POST

REQUEST_URI

The interpreted pathname of the requested document or CGI (relative to the document root)

SCRIPT_FILENAME

The full pathname of the current CGI

SCRIPT_NAME

The interpreted pathname of the current CGI (relative to the document root)

SERVER_ADMIN

The email address for your server's webmaster

SERVER_NAME

Your server's fully qualified domain name (e.g. www.cgi101.com)

SERVER_PORT

The port number your server is listening on

SERVER_SOFTWARE

The server software you're using (e.g. Apache 1.3) 

当你需要CGI处理POST请求时,CGI就要使用STDIN做为输入来接收数据了。

在Perl里使用下面的代码读取:

  use CGI;

  my $cgi = CGI->new();

  my %params = $cgi->Vars();

而在Python则是:

 import cgi

 cgi.FieldStorage()

参考: Perl CGI Input Test

三. 开发语言

可以写CGI的开发语言太多,下面附上两个分别使用Perl和Python编写的相同功能的CGI, 正好可以做个对比。

这两个脚本可以接收Query String, 然后返回不同的文件,数据大小,以及缓存相关的头信息,可以区分二进制数据和文本数据。

CGI脚本支持以Query String修改以下响应头:

   content type,

   cache control,

   content length (只返回对应大小的数据),

   last modified,

   expires

下面是返回一个指定大小图片的例子:

  /cgi/cache_factory.pl?type=image&size=32&last-modified=Fri, 02 Apr 2014 02:34:06 GMT&cache-control=private,max-age=60&expires=Fri, 22 Apr 2014 02:34:06 GMT

代码很简单,可以做个参考。

Perl版本

#!/usr/bin/perl
use strict;
use warnings;
use CGI;

use constant BUFFER_SIZE     => 4_096;
use constant DATA_DIRECTORY => "/var/www/Cache";

my $cgi = CGI->new();
my %params = $cgi->Vars();

&parserCommonHeaders;

if(exists $params{'type'} && $params{'type'}=="image"){
	&generateImageData;
 }
else{
	&generateTextData;
}

sub parserCommonHeaders{
	if(exists $params{'cache-control'}){
	  print 'Cache-Control:',$params{'cache-control'},"\r\n";
	}

	 if(exists $params{'last-modified'}){
	   print 'Last-Modified:',$params{'last-modified'},"\r\n";
	 }

	 if(exists $params{'expires'}){
	   print 'Expires:',$params{'expires'},"\r\n";
	 }

	 if(exists $params{'etag'}){
	   print 'ETag:ea6186e11526ce1:0',"\r\n";
	 }
}

sub generateImageData{
	my $buffer = "";

	my $targetSize = 100*1024*1024;
	if(exists $params{'size'} && $params{'size'}>0){
		$targetSize = 1024*$params{'size'};
		print "Content-length: $targetSize \r\n";
	}

	my $image = DATA_DIRECTORY .'/images/very_big.jpg';
	my( $type ) = $image =~ /\.(\w+)$/;
	$type eq "jpg" and $type == "jpeg";

	print $cgi->header( -type => "image/$type", -expires => "-1d" );
	binmode STDOUT;

	local *IMAGE;
	open IMAGE, $image or die "Cannot open file $image: $!";

	my $sentSize = 0;
	while ( read( IMAGE, $buffer, BUFFER_SIZE ) ) {
	    print $buffer;
	    $sentSize += BUFFER_SIZE;

	    if($sentSize>=$targetSize){
	    	last;
	    }
	}
	close IMAGE;
}

sub generateTextData{
	my $startHeader = '<html><head><title>HTTP Cache Testing HTML</title></head><body>';
	my $tailPart = '</body></html>';

	if(exists $params{'type'}){
		print "Content-type:$params{'type'}\r\n";
	}
	else{
		print "Content-type:text/html\r\n";
	}

	my $targetTextSize = 100*1024*1024;
	if(exists $params{'size'} && $params{'size'}>0){
		$targetTextSize = 1024*$params{'size'};
		print "Content-length: $targetTextSize \r\n";
	}
	print "\r\n";

	$targetTextSize -= length($startHeader) + length($tailPart);

	print "$startHeader";

	my $filepath = DATA_DIRECTORY .'/files/big_text.txt';

	open(FILE, $filepath) or die $!;
	my @lines = <FILE>;
	close(FILE);

	foreach my $line (@lines) {
    	if( length($line)<=$targetTextSize ){
    		print $line;
    	}
    	else{
    		print substr($line,0,$targetTextSize);
    	}

    	$targetTextSize -= length($line);

    	if($targetTextSize<=0){
    		last;
    	}
	}

	print "$tailPart";
}

Python版本

#!/usr/bin/python
import os
import cgi
import sys

BUFFER_SIZE = 4096
DATA_DIRECTORY = "/var/www/Cache"

def parserCommonHeaders(formQuery):
  if('cache-control' in formQuery.keys()):
    print 'Cache-Control:',formQuery['cache-control'].value,"\r\n",  

  if('last-modified' in formQuery.keys()):
     print 'Last-Modified:',formQuery['last-modified'].value,"\r\n",  

  if('expires' in formQuery.keys()):
     print 'Expires:',formQuery['expires'].value,"\r\n",

  if('etag' in formQuery.keys()):
     print 'ETag:ea6186e11526ce1:0',"\r\n",

def generateImageData(formQuery):
  targetSize = 100*1024*1024;
  if('size' in formQuery.keys()):
    targetSize = 1024*int(formQuery['size'].value)
    print "Content-length:",targetSize,"\r\n",

  image = DATA_DIRECTORY+'/images/very_big.jpg'
  print "Content-Type:image/jpeg\r\n",

  print 

  sentSize = 0
  f = open(image, 'rb')
  while True:
    data = f.read(4096)
    sentSize = sentSize + BUFFER_SIZE
    sys.stdout.write(data)
    if sentSize>targetSize:
        break

  sys.stdout.flush()
  close(f)

def generateTextData(formQuery):
  startHeader = '<html><head><title>HTTP Cache Testing HTML</title></head><body>'
  tailPart = '</body></html>'

  targetTextSize = 2.3*1024*1024;
  if('size' in formQuery.keys()):
    targetTextSize = 1024*int(formQuery['size'].value)
    print "Content-length:",targetTextSize,"\r\n",

  if('type' in formQuery.keys()):
    print "Content-type: %s\r\n"%(formQuery['type'].value),
  else:
    print "Content-type: text/html\r\n",

  print

  print startHeader

  targetTextSize = targetTextSize - len(startHeader) - len(tailPart)

  filepath = DATA_DIRECTORY + '/files/big_text.txt'

  file = open(filepath)
  lines = file.readlines()
  file.close()

  for line in lines:
      if( len(line) <= targetTextSize ):
        print line
      else:
        print line[0:targetTextSize]

      targetTextSize = targetTextSize - len(line)

      if(targetTextSize<=0):
        break

  print tailPart

if __name__ =="__main__":
  formQuery = cgi.FieldStorage() #os.environ['QUERY_STRING']

  parserCommonHeaders(formQuery)

  if('type' in formQuery.keys() and formQuery['type'].value=="image"):
    generateImageData(formQuery)
  else:
    generateTextData(formQuery)

四. 服务器

  服务器端使用Apache Server+WANem, 配合CGI完成灵活的需求,开通SFTP端口供相关同学编辑,方便共享测试用例。

     .-----.   .-------.

     | CGI |   | WANem |

     '-----'---'-------'

     | Apache Server   |

     '-----------------'

              ^

              |

         SFTP & HTTP

              |

    .------------------.

    | Web Page Editor  |

    | and Browser      |

    '------------------'

 

  配有WANem最大的好处就是可以根据需求进网络状态调整, 甚至可以用类似下面的方式在测试用例动态调整。

  

参考:

   Apache配置 (不要忘记给CGI脚本加上可执行权限)

   CGI Programming 101 (Perl)

   Debugging CGI Programs (Perl)

   Python CGI编程

   Python CGI Debugging

时间: 2024-11-03 22:03:04

Perl &amp; Python编写CGI的相关文章

使用Perl编写CGI时需要注意的几个问题

一.UNIX与WINDOWS下的差别 由于PERL在最初是UNIX下的工具,现在虽然PERL已经移植到流行的平台上:WINDOWS系统,但是运用起来却是有哪么一点差别,这一点需要引起我们的注意. 1.文件运算符 在UNIX下PERL一共有27个文件运算符可以使用,这些运算符使得我们在不打开文件的情况下就可以获得文件的各种信息.但是在WINDOWS平台下,我们只能使用其中四个运算符,但万幸的是这4个运算符功能不错,基本能满足我们的需要,这就是下面的那4个运算符: -d 测试文件是不是一个目录  -

使用Python编写Storm程序,无法将结果写入MySQL

问题描述 使用Python编写Storm程序,无法将结果写入MySQL 我使用Python作为编程语言写一个关联关系挖掘的程序部署在Storm集群,将jar包提交到集群后无法将结果写入数据库,贴上写入结果的bolt代码,希望大家帮忙看看,不胜感激: #coding=utf-8 import MySQLdb import logging from pyleus.storm import SimpleBolt log = logging.getLogger('log_results') def wr

Python编写脚本使IE实现代理上网的教程

  Python编写脚本使IE实现代理上网的教程         这篇文章主要介绍了用Python编写脚本使IE实现代理上网的教程,"著名的"goagent代理也是基于同样原理实现,需要的朋友可以参考下 厂里上个网需要设置代理服务器,切换各种环境『包括但不仅限于开发环境.QA.预上线.验收.生产环境.压力测试.Demo--』都需要给浏览器设置不同的代理服务器. 虽然俺有神器Firefox+Change Host+HostAdmin+Proxy Selector的组合来轻松切换Host,

用Python编写简单的定时器的方法

  这篇文章主要介绍了用Python编写简单的定时器的方法,主要用到了Python中的threading模块,需要的朋友可以参考下 下面介绍以threading模块来实现定时器的方法. 首先介绍一个最简单实现: ? 1 2 3 4 5 6 7 8 9 10 import threading   def say_sth(str): print str t = threading.Timer(2.0, say_sth,[str]) t.start()   if __name__ == '__main

用Python编写一个基于终端的实现翻译的脚本

  用Python编写一个基于终端的实现翻译的脚本        为什么写这个程序,为什么不给这个程序配备gui?原因很简单,因为我是一个命令行控,Linux习惯了不习惯了鼠标,总觉得点着不如敲命令快,各位在看这篇文章就说明和本人有相同的爱好.这个用python写的翻译工具是通过google来实现的,由于google返回的数据不是很规范(或者说我没有找到规律),现在前三项能正常显示(源词,翻译结果,和汉语拼音).下面的词性和其他释义可能不同,见谅,望大神可以指点下小弟和帮小弟完善,这里赶紧不尽.

Python+Apache+CGI完全配置

操作系统环境:Ubuntu 15.10 0.需求原因     想在我的Linux上架设Apache来运行CGI程序,方便以后用Apache部署Python的Web应用,但遇到各种各样的问题,网上找的答案要么都太旧了(4/5年前跟现在已经差很多了),要么就不完整,所以这里总结一下,作为最新版本吧. 1.Apache安装     采用的是源码编译安装的形式.首先要说明的是,要成功安装Apache,需要依次安装完下面的软件(函数库)才算是成功地安装了Apache: (1)apr安装 下载地址:http

使用Python编写Linux系统守护进程实例_python

守护进程(daemon)是指在UNIX或其他多任务操作系统中在后台执行的电脑程序,并不会接受电脑用户的直接操控.此类程序会被以进程的形式初始化.通常,守护进程没有任何存在的父进程(即PPID=1),且在UNIX系统进程层级中直接位于init之下.守护进程程序通常通过如下方法使自己成为守护进程:对一个子进程调用fork,然后使其父进程立即终止,使得这个子进程能在init下运行.–维基百科 守护进程区别于普通用户登陆系统后运行的进程,它是直接由系统初始化,和系统用户没有关系,而用户开启的进程依存与用

python-树莓派 用Python编写串口通讯,能发送但无法接收

问题描述 树莓派 用Python编写串口通讯,能发送但无法接收 在使用Raspberry Pi2上,使用Python环境(2.7, 3.0都试过了),执行如下程序 import serial ser = serial.Serial("/dev/ttyAMA0", 9600, timeout=5) ser.write("hello") 串口调试助手能够正常收到数据,然后第一次执行如下指令: ser.read(10) 此时在串口调试助手上发送abcde,在屏幕上能够收到

python编写函数中无法出现结果

问题描述 python编写函数中无法出现结果 为什么这个编写函数的时候无法出现结果呢? 这个是我在慕课上面照着那个答案打出来的. 不知道为什么会这个样子 需要加什么语句吗? 我是新手,请原谅我会问这种问题 解决方案 不好意思,这个我已经知道问题出在哪里了,谢谢你们了 解决方案二: 问这种问题不要紧,只是你不发代码,不发网址,不发图片,我咋知道你说的啥呢? 解决方案三: 提供一下源代码吧!我原谅你问这种问题.