날짜: 1996-04-19 | 글쓴이: 도아 | 7216 번 | 프린트 | 메일로보내기

제 19강 - CGI 입력 디코딩


CGI의 입력 문자열을 디코딩하고 각 변수의 이름과 값들을 추출하기 위해서는 다음과 같이 하면 된다.

 1: $method = $ENV{'REQUEST_METHOD'};
 2: $length = $ENV{'CONTENT_LENGTH'};
 3:
 4: if($method eq 'GET' || $method eq 'HEAD') {	# GET 방법
 5: 	$inputstring = $ENV{'QUERY_STRING'};
 6: } elsif($method eq 'POST') {			# POST 사용
 7: 	$ret = read(STDIN, $inputstring, $length);
 8:	if($ret != $length) {
 9:		&cgi_die("오류", 
			"STDIN을 읽는 중에 알 수 없는 오류가 발생했습니다");
10:	}
11: } else {
12: 	print "Off-Line Mode: name1=value1&name2=value2\n";
13: 	chop($inputsting = <STDIN>);
14: }
15: 
16: @in = split(/&/, $inputstring);
17: for(0 .. $#in) {			# 0 인덱스부터 마지막 인덱스까지
18:  	$in[$_] =~ s/\+/ /g;		# '+'를 공백문자로
19:  	($name, $value) = split(/=/, $in[$_], 2);
20:  
21:  	# %HH 형식을 해당 문자로 바꿈
22: 	$name =~ s/%(..)/pack("c", hex())/ge;
23: 	$value =~ s/%(..)/pack("c", hex())/ge;
24: 
25: 	# 연관 배열 구성
26:	$in{$name} .= "
$inputstring = $ENV{'QUERY_STRING'};
" if defined($in{$name}); 27: $in{$name} .= $value; 28: }
사용자 입력 읽기

사용자의 입력은 요청한 방법이 GET이냐 POST이냐에 따라 달라진다. 따라서 사용자의 입력을 읽기위해서는 먼저 REQUEST_METHOD를 이용해서 요청 방법을 판정해야 한다. GET

사용자의 요청 방법이 GET인 경우 사용자의 입력은 QUERY_STRING이라는 환경 변수에 저장 되므로 %ENV를 이용해서 사용자의 입력을 읽으면 된다.

	@query = <STDIN>;	# STDIN으로 전달되는 모든 값을 읽음
			# <INPUT> 태그의 Type을 FILE로 하고
			# <FORM> 태그의 ENCTYPE을 multipart/form-data로 
			# 한경우에 사용된다.
$query = <STDIN>;	# STDIN으로 전달되는 값중 한줄만 읽는다.
			# Method가 POST인 경우에 사용.
$ret = read(STDIN, $inputstring, $length); 
			# Method가 POST인 경우, 전부 사용할 수 있다.
			# 읽을 바이트의 수는 CONTENT_LENGTH로 판정한다.
POST

사용자의 요청방법이 POST라면 사용자의 입력 STDIN을 통해 전달된다. 따라서 사용자의 입력은 다음과 같은 방법으로 읽을 수 있다.

	read FILEHANDLE,SCALAR,LENGTH 
	read FILEHANDLE,SCALAR,LENGTH,OFFSET 

	FILEHANDLE	: 읽고자하는 파일핸들.
	SCALAT		: 읽은 내용을 저장할 스칼라변수
	LENGTH		: 읽을 바이트의 수
	OFFSET		: 건너뛸 바이트의 수
@query = <STDIN>;

Method가 POST인 경우 사용자의 입력은 STDIN으로 전달된다. 펄에서는 파일핸들을 처리하는 상당히 다양한 방법을 제공하는데, 그중 하나가 <> 연산자 이다. <> 연산자는 앞어 언급했든 파일을 읽을 때 줄단위(개행문자까지)로 읽는다.

<STDIN>을 @query와 같이 배열(배열환경)로 받으면 한줄이아니라 STDIN으로 전달된 모든 데이터를 읽을 수 있다.

이 방법은 주로 <INPUT> 태그의 Type을 FILE로 하고 <FORM> 태그의 ENCTYPE을 multipart/form-data로 한경우에 사용된다. $query = <STDIN>;

위의 문장과 동일한 문장이다. 다만 <STDIN>을 $query(스칼라 환경)로 받으면 STDIN으로 전달된 데이터의 전부가 아니라 오로지 한줄만 읽게된다.

이 방법은 주로 <FORM> 태그의 Method가 POST이고, ENCTYPE이 application/x-www-form-urlencoded인 경우에 사용된다. $ret = read(STDIN, $inputstring, $length);

STDIN을 <> 연산자를 사용해서 읽는 경우 전송된 데이터를 잘못읽을 수 있으며, 오류가 발생한 경우에도 오류가 발생한 사실을 알 수 없다. 따라서 사용자의 입력을 처리하는 일반적인 방법은 read 함수이다. 사용형식

if($ret != $length) {
	&cgi_die("오류", "STDIN을 읽는 중에 알 수 없는 오류가 발생했습니다");
}
설명

read 함수는 FILEHANDLE로부터 데이터를 LENGTH 바이트만큼 읽어 SCALAR 변수에 저장하며 읽을 바이트 수를 리턴한다.

따라서 read 함수를 이용해서 파일핸들을 읽는 경우 리턴 값과 LENGTH를 조사해서 데이터가 정상적으로 읽혀졌는지는 조사해야 한다.

print "Off-Line Mode: name1=value1&name2=value2\n";
chop($inputsting = <STDIN>); # 키보드에서 직접읽는 것이므로 chop 함수를
			# 이용해서 개행문자를 삭제한다.
GET이나 POST가 아닌 경우.

사용자의 요청이 GET이나 POST가 아닌 경우는 Unix 명령행에서 CGI 프로그램을 실행한 경우이다.

일반적으로 CGI 프로그램을 디버깅하는 것은 상당히 까다로운데 그 이유는 CGI 프로그램의 입력은 웹상에서만 주어지며, Unix 명령행에서는 그 입력을 줄수 없기 때문이다.

따라서 Method가 GET이나 POST가 아닌 경우에는 CGI 입력을 명령행에서 읽어들이도록 처리한 것이다. 즉, CGI를 Unix 명령행에서 실행하고 URL 인코딩 형식으로 입력을 주면된다.

name1=value1&name2=value2&name2=value2&name2=value2&name2=value2&name2=value2
name=value 쌍 분리

위의 방법을 이용하면 요청방법이 무엇이든지 사용자의 입력은 $inputstring라는 변수에 저장되며 사용자의 입력은 URL 인코딩되서 전달되므로 다음과 같은 형식을 갖게된다.

16: @in = split(/&/, $inputstring);

따라서 $inputstring를 split을 이용해서 분리하면 사용자의 입력과 <FORM>의 이름을 분리할 수 있다.

19: ($name, $value) = split(/=/, $in[$_], 2);

name=value 쌍이 몇 개가 될지 모르므로 split한 결과를 @in이라는 배열로 받았다.

18:  	$in[$_] =~ s/\+/ /g;		# '+'를 공백문자로

일단 name=value 쌍으로 분리되며, 이 값은 항상 name과 value 값으로 구성되므로 ($name, $value)로 받은 것이다. +를 공백으로 변환

폼에서 사용자가 입력한 공백은 +로 변환되어 CGI에 전달된다. 따라서 이러한 +를 공백으로 대치하기위해서는 앞에서 배운 패턴대치를 사용하면 된다.

22: 	$name =~ s/%(..)/pack("c", hex())/ge;

+는 정규식에서 하나 이상의 앞문자를 반복시키는 메타문자이므로 역슬래쉬(\)를 사용했다. %HH를 원래의 문자로 변환

몇몇 구두점과 영어 대소문자를 제외한 모든 문자는 %HH 형식으로 변환되어 CGI로 전달된다. 따라서 %HH 형식을 원래의 문자로 변환해야 한다.

pack TEMPLATE,LIST
TEMPLATE
	A		ASCII 문자열. 공백이 삽입됨
	a		ASCII 문자열. Null이 삽입됨
	b	비트 문자열(내림차순)
	B	비트 문자열(오르차순)
	h	16진수 문자열(하위 4비트를 먼저 표시)
	H	16진수 문자열(상위 4비트를 먼저 표시)
	c	부호를 갖는 문자값
	C	부호를 갖지 않는 문자값
	s	부호를 갖는 정수(Short 형)
	S	부호를 갖지 않는 정수(Short 형)
	I	부호를 갖는 정수
	I	부호를 갖지 않는 정수
	l	부호를 갖는 정수(Long 형)
	L	부호를 갖지 않는 정수(Long 형)
%(..)

%HH 형식의 패턴을 검색하고, 일치되는 패턴이 있는 경우 $1에 저장 hex($1)

$1(HH)을 10진수로 변환. 펄에서 연산의 기준은 항상 10진수 이므로 16진수인 HH를 10진수 변환하기위해 사용되었다. pack("c", hex($1))

10진수로 변환된 $1을 부호를 갖는 문자로 변환. 사용형식

$a = '%B1%E8%C0%E7%B1%D9';
$b = '%B1%E8%C0%E7%B1%D9';
$c = '%B1%E8%C0%E7%B1%D9';

$a =~ s/%(..)//g;
print $a, "\n";
$b =~ s/%(..)/hex()/ge;
print $b, "\n";
$c =~ s/%(..)/pack("c", hex())/ge;
print $c, "\n";
설명

주어진 LIST를 TEMPLATE에따른 문자열로 변환한다. 다만 주의할 것은 pack는 항상 10진수를 값으로 취한다. ge

패턴대치를 수행하는 중에 함수를 만나면 그 함수를 실행한 결과로 대치(e).

<FORM METHOD="POST" ACTION="/~artech/cgi-bin/env.cgi">
<SELECT NAME=FOOD SIZE=3 MULTIPLE>
	<OPTION value=apple>사과 
	<OPTION value=kiwi>키위 
	<OPTION value=orange> 오랜지
</SELECT>
</FORM>
Select 태그의 Multiple 속성 처리

<FORM> 태그를 설명하며서 얘기했던 규칙을 준수하면 동일한 이름(<FORM> 관련태그의 name)에 다른 값을 갖는 경우는 Select 태그에서 Multiple 속성을 부여한 경우에만 발생한다.

FOOD=apple&FOOD=kiwi

위와 같은 폼에서 사과와 키위를 선택한다면 CGI에는

26:	$in{$name} .= "
$in{$name} = $in{$name} . $value;
" if defined($in{$name}); 27: $in{$name} .= $value;

가 전달된다. 이와 같은 다중선택 데이터를 처리하기위해

$in{$name} = $value;

문장을 사용하였다. $in{$name} .= "\0" if defined($in{$name});

$in{$name}가 초기화되었다면 $in{$name}에 '\0'가 추가된다. 초기화 여부를 검사하는데 defined 함수가 사용되었다. $in{$name} .= $value;

$in{$name}의 끝부분에 $value를 추가. 이 문장은

[code=xml]$in{$name} = $in{$name} . $value;[/code]

와 동일한 문장이므로 $in{$name}가 값을 갖고 있는 경우에는 $value가 $in{$name}에 덧붙여지며, $in{$name}가 값을 갖고 있지 않는 경우에는

[code=xml]$in{$name} = $value;[/code]

와 동일한 문장이된다. 설명

위의 폼 문서를 예로 각 변수 값의 변화를 보면 다음과 같다.

FOOD=apple&FOOD=kiwi 인 경우
$in{$name}(시작) $name $value $in{$name}(끝)
초기화안됨 FOOD apple apple
apple FOOD kiwi apple\0kiwi
연습 - 방명록

guest.cgi의 디코딩 부분을 최종적으로 변경한다. 방명록의 모든 데이타(%HH 포함)는 사용자가 입력한 그대로 출력되어야 하며, Select 태그에서 Multiple 속성을 사용한 경우에도 정상적으로 동작해야 한다.



다음글: 제 20강 - 서브루틴 (5855)1996-04-20
이전글: 제 18강 - 대치와 변환 (6741)1996-04-18

세상사는 이야기

  • 찾아라! 아이폰 순정용 >
  • 만원대 피젯 스피너를 >
  • 망하는 길을 택한 쿠팡 >
  • 물놀이에 적당한 가성 >
  • 컴퓨터를 IPTV로 2, po >
  • 컴퓨터를 IPTV로 만들 >
  • Warning.or.kr도 우회 >
  • 한국의 100대 부자, 어 >
  • 세상을 바꾼 크롬: 크 >
  • 장난(?)으로 시작한 여 >


  • RSS 구독 (익명 | 회원 | 강좌 | 포럼)
    (C) 1996 ~ 2017 QAOS.com All rights reserved.