CGI上传中关闭浏览器导致perl悬挂

CGI上传中关闭浏览器导致perl悬挂

CGI上传中关闭浏览器导致perl悬挂
在IIS5.1/apache2.0.54 for windows(xp)上会有如标题所述现象,跟踪发现是由于在read STDIN的时候出现.

我试着拦截一些信号如SIGPIPE/INT都不行.为了屏蔽可能由CGI模块带来的问题,我没有使用CGI模块作出以下测试代码, 在上载某个文件时关掉ie即能重现问题.

非常感谢谁能给些线索.


use Text::Template;

sub logmsg{
local *F;
open F,">>c:/temp/log.txt";
print F @_,"\n";
close F;
}

sub OnInt{
logmsg("receive interrupt");
exit 0;
}

$SIG{PIPE}=$SIG{TERM}=$SIG{INT}=\&OnInt;

$|=1;
if ($ENV{REQUEST_METHOD} eq 'GET') {
my $tmpl=Text::Template->new(TYPE=>'FILEHANDLE',SOURCE=>\*DATA);
my $t=$tmpl->fill_in();

print &header(), $t;
} elsif($ENV{REQUEST_METHOD} eq 'POST' ) {
# if(0){
#save the posted data to a file
open F,">c:/temp/posted.dat";
binmode(F);
my $msg;
binmode(\*STDIN);
my $len=$ENV{CONTENT_LENGTH};
my $readbytes=8196;
LOOP:
while($len>0){
my $in='';
vec($in,fileno(STDIN),1)=1;
unless(select($in,undef,undef,10)){
#no data available,
logmsg("no data available,$len bytes remains not received");
last LOOP;
};
my $rv=read(\*STDIN, $msg,$readbytes>$len?$len:$readbytes);
if (!defined $rv){
logmsg("error read stdin when reading @".($ENV{CONTENT_LENGTH}-$len).
"error returns $!");
last LOOP;
}
if ($readbytes<$len) {
$len-=$readbytes;
}else{
$len=0;
}
print F $msg;
sleep 1;
$msg='';
}
close F;
# }
print &header(),'<html><head><title>Upload</title></head><body><b>upload is done!content-length=',$ENV{CONTENT_LENGTH},'</body></html>';
}else{
print header(),'<b>unknown request',$ENV{REQUEST_METHOD},'</b>';
}

sub header{
return "200 OK\r\nContent-Type:text/html\r\n\r\n";
}

__END__
<html>
<head>
<title>upload test</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>
<form method="post" action="{$ENV{SCRIPT_NAME}}" enctype="multipart/form-data" id="upload">
<input type="file" name="uploadfile" size="50" maxlength="80" /><input type="submit" name=".submit" /></form>
</body>
</html>





多谢这么快的回答
注意我在测试代码里加入了
$|=1;

这应当是autoflush,没有通过缓冲直接返回到浏览器,不过这主要是对STDOUT的处理.没有影响STDIN的问题.

我也用了select在上述代码中,如:
select($in,undef,undef,10))

但事实上根本不能解决这个问题,甚至没有log产生 (注意我的测试代码中如果select失败会记录log)
是否是web服务器中CGI实现的问题?
我很怀疑是否是IIS对CGI实现上的问题,在ie关掉后没有触发CGI的某些事件,常见的正确实现应是STDIN返回EOF,或PIPE断开异常.
已经证实是服务器对CGI的实现问题
作了以下等价C++程序测试,没上传完就关闭ie也会导致cgi应用hang在内存中,
用visual studio调试发现死在fread里面.没有任何信号,查看了一些国外网页,这方面只有很少的信息,看到有人称这是http/1.1的实现问题.http/1.0会以tcp的fin作post结束而1.1后就以content_length作标准了,真垃圾啊,郁闷...

// test_upload.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <io.h>
#include <windows.h>

const char *fmt_html=
"<html>"
"<head>"
"<title>upload test</title>"
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" />"
"</head>"
"<body>"
"<form method=\"post\" action=\"%s\" enctype=\"multipart/form-data\" id=\"upload\">"
"<input type=\"file\" name=\"uploadfile\" size=\"50\" maxlength=\"80\" />"
"<input type=\"submit\" name=\".submit\" /></form>"
"</body>"
"</html>";

#define LOG "c:\\temp\\c_testlog.txt"
const char *dest_file="c:\\temp\\ctest.dat";
const char *header="200 OK\r\nContent-Type:text/html\r\n\r\n";

void logmsg(char *s)
{
FILE *fp=fopen(LOG,"a+");
fputs(s,fp);
fclose(fp);
}
char *safe_getenv(const char*s)
{
char *p=getenv(s);
return p?p:"";
}

int main(int argc, char* argv[])
{
char buffer[1024];
char *method=safe_getenv("REQUEST_METHOD");

if (stricmp(method,"GET")==0){
sprintf(buffer,fmt_html,safe_getenv("SCRIPT_NAME"));
}//get
else
{
//post

FILE *fp=fopen(dest_file,"w+");
if(NULL==fp){
strcat(strcat(strcpy(buffer,"<B>Error:can't write "),dest_file),"</B>");
}else{
//read stdin and save to destfile
//to make it slower, we loop every BYTES bytes
//change stdin into binary mode
_setmode( _fileno( stdin ), _O_BINARY );
int total_len=atoi(safe_getenv("CONTENT_LENGTH"));
int BYTES=
#ifndef NLOOP
8196;
#else
total_len;
#endif

int remain_len=total_len;
char *ptemp=(char*)malloc(BYTES);
int err=0;

while(remain_len>0){
int to_read;
if (remain_len<BYTES){
to_read=remain_len;
}else{
to_read=BYTES;
}

int nread=fread(ptemp,1,to_read,stdin);

if (nread<to_read){
if (err=ferror(stdin)){
char errstr[512];
sprintf(errstr,"detected error when reading remains %d bytes,errno=%d",remain_len,err);
logmsg(errstr);
}
}
if (nread>0){
fwrite(ptemp,1,nread,fp);
}
remain_len-=nread;
if (nread<=0) break;
Sleep(1000);
}

free(ptemp);
fclose(fp);
sprintf(buffer,"<b>upload is done, content length=%d bytes, remains %d bytes\n</b>",total_len,remain_len);
}//fp opened

}//post
setvbuf(stdin,NULL,_IONBF,0); //autoflush!
printf("%s%s",header,buffer);
return 0;
}

你看看ik,里面有很好的例.
你看看ik,里面有很好的例子