[첫화면으로]UTF-8이전작업/브라우저GET요청인코딩판별

마지막으로 [b]

1. 브라우저의 GET 요청의 인코딩 판별
1.1. wiki.pl 수정
1.2. 추가 업데이트 내역
1.3. 참고 자료
1.4. 의견

1. 브라우저의 GET 요청의 인코딩 판별

IE의 "URL을..." 옵션에 따라서 EUC-KR로 들어오는 페이지 이름을 UTF-8로 바꿔야 한다.

1.1. wiki.pl 수정

다음 함수는 인자로 받은 스트링을 보고, 인코딩을 추측하여 UTF-8로 변환해준다.

sub guess_and_convert {
    my ($string) = @_;

    # legal UTF-8인지 체크
    if ($HttpCharset =~ /utf-8|utf8/i) {
        if (eval "require Unicode::CheckUTF8;") {
            if (Unicode::CheckUTF8::is_utf8($string)) {
                # ok
                return $string;
            }
        }
    }

    # 추측
    if (eval "require Encode; require Encode::Guess;") {
        my @suspects = qw(euc-kr utf8);
        my $decoder = Encode::Guess::guess_encoding($string, @suspects);
        if (ref($decoder)) {
            # 추측 성공
            return convert_encode($string, $decoder->name, $HttpCharset);
        }
    }

    # 모듈이 없거나, 있지만 추측 실패. 변환 포기
    return $string;
}

EUC-KR 시퀀스로, %-인코딩된 문자열이, 하필 ?뒤에 쿼리로 들어올 경우(예: wiki.pl?%C0%A7%C5%B01)가 있다. 슬래쉬 링크를 쓰는 이상 현재는 이런 경우가 생기지 않는데, 예전에 ext1.*을 쓰던 당시에 구글에 등록된 주소라거나, 다른 사이트에서 링크를 해둔 경우 등이 있을 수 있다. 이 경우는 쿼리스트링 바꿔치기를 하는 시점에는 아스키 문자열이라서 변환이 안 되므로, 결국 나중에 CGI모듈이 디코딩해 준 것을 가지고 변환을 해야 한다. wiki.pl?페이지이름 의 경우와, id= 파라메터로 들어온 것만이라도 처리해 준다.
sub DoBrowseRequest {
    ...
    if ($id) {                    # Just script?PageName

### QUERY_STRING 이 utf-8이 아닌 인코딩인 경우 - 여기 한 줄
        $id = guess_and_convert($id);

        if ($FreeLinks && (!-f &GetPageFile($id))) {
            $id = &FreeToNormal($id);
        }
        if (($NotFoundPg ne '') && (!-f &GetPageFile($id))) {
            $id = $NotFoundPg;
        }
        $DocID = $id;
        &BrowsePage($id)  if &ValidIdOrDie($id);
        return 1;
    }
    $action = lc(&GetParam('action', ''));
    $id = &GetParam('id', '');
### QUERY_STRING 이 utf-8이 아닌 인코딩인 경우 - 여기 두 줄
    $id = guess_and_convert($id);
    $q->param('id', $id);

    $DocID = $id;
    ...
}

CGI 오브젝트를 생성하기 전에 QUERY_STRING 환경변수의 값을 인코딩 검사를 해서 바꿔치기 해버린다. slashlink 관련 처리도 이 시점에 해 버린다.
sub DoWikiRequest {
    ...
### QUERY_STRING이 %-인코딩된 형태로 오는 경우
### guess를 해도 ascii로 판정되기 때문에 변환이 안 된다.
### 이 시점에서 디코딩하여 복원함
#    $ENV{'QUERY_STRING'} =~ s/%([0-9a-fA-F]{2})/chr(hex($1))/ge;

### slashlinks 처리
    if ($SlashLinks && (length($ENV{'PATH_INFO'}) > 1)) {
        $ENV{'QUERY_STRING'} .= '&' if ($ENV{'QUERY_STRING'});
        $ENV{'QUERY_STRING'} .= substr($ENV{'PATH_INFO'}, 1);
    }

### QUERY_STRING 또는 PATH_INFO가 utf-8이 아닌 인코딩인 경우
    $ENV{'QUERY_STRING'} = guess_and_convert($ENV{'QUERY_STRING'});

    &InitLinkPatterns();
    if (!&DoCacheBrowse()) {
        eval $BrowseCode;
        &InitRequest() or return;
        if (!&DoBrowseRequest()) {
            eval $OtherCode;
            &DoOtherRequest();
        }
    }
}
이 지점에서 변경할 때의 장점은, 단점...은,

1.2. 추가 업데이트 내역

ext2.4a -
2007-03-21, 이제 보니 QUERY_STRING과 PATH_INFO가 넘어오는 방식이 좀 다르다. -_-; 쿼리스트링이 %-인코딩된 상태로 요청이 오면 위키 스크립트에도 그 상태 그대로 넘어오고, 따라서 guess_and_convert를 해도 변환이 안 되고 (아스키니까 -_-;) 만일 EUC-KR 시퀀스로 %-인코딩된 문자열을 쿼리로 받는 경우는 처리가 안 된다. (일반적인 상황에서는 위키의 인코딩과 다른 형태의 인코딩으로, 그걸 다시 %인코딩해서 쿼리로 넘겨주는 링크가 생길 일이 없는데... 검색엔진에 옛날에 수집되었던 주소나, 남이 옛날에 링크를 했던 것들이 이런 문제가 있다. 예를 들어 게시판에 nyxity님이 적은 경우:
구글로 '모카포트' '에스프레소'를 검색하면 각각 
nyxity.com/wiki/wiki.pl?%B8%F0%C4%AB%C6%F7%C6%AE         <-- 이런 거
nyxity.com/wiki/wiki.pl?%BF%A1%BD%BA%C7%C1%B7%B9%BC%D2 
이렇게 페이지가 검색되는데, 클릭하면 페이지가 연결이 안되네요. 
할 수 없이, QUERY_STRING에서 %+16진수2개 꼴로 이뤄진 형태를 일단 디코딩한 후에 guess_and_convert에 넘겨주기로 함. 이거 고치면 고칠수록 서버의 플랫폼과 웹서버의 환경에 의존하게 되는 듯.

ext2.5b -

1.3. 참고 자료

1.4. 의견

생각해보니... 위키를 불러올때 꼭 "wiki.pl/페이지이름" 아니면 "wiki.pl/action=액션이름&id=아이디" 형태만 있는게 아니라서... 등등 따지고 보면 guess_and_convert를 해야 하는 것들이 한 두 군데가 아니다. 게다가 이런 action들은 앞으로도 얼마든지 늘어날 수 있는데 이때마다 파라메터들의 컨버트를 다 챙길수도 없고...

아예, CGI 오브젝트를 만들기 전에 스크립트로 넘어오는 환경변수를 통채로 바꿔치기 하면 어떨까
sub DoWikiRequest {       # 제일 처음 불리는 함수에서
    ...
### 본격적으로 뭘 해보기 전에 이 시점에서 인코딩을 판단하여 바꿔버리자
    $ENV{'QUERY_STRING'} = guess_and_convert($ENV{'QUERY_STRING'});   # wiki.pl? 뒤에 오는 쿼리 스트링
    $ENV{'PATH_INFO'} = guess_and_convert($ENV{'PATH_INFO'});         # wiki.pl/ 뒤에 오는 여분의 패쓰
### 그러면 이제부터 뭘 하든 이미 다 컨버트 되어 있으니 따로 신경쓸 필요가 없을 것이다

    &InitLinkPatterns();
    if (!&DoCacheBrowse()) {
        eval $BrowseCode;
        &InitRequest() or return;
        if (!&DoBrowseRequest()) {
            eval $OtherCode;
            &DoOtherRequest();
        }
    }
}

위와 같이 생각하여 테스트용 사이트에서 해보니 별 문제 없어 보이긴 하는데, 이렇게 무지막지한 짓을 해도 정말 괜찮은 건지 확신이 안 서고 있습니다. =.=;;
-- Raymundo 2007-3-3 1:03 pm

생각해보면 이미 UseModWiki소스수정/SlashLinks처리를 위해서 PATH_INFO 환경변수를 QUERY_STRING으로 옮기는 코드도 있었으니 이것도 비슷하게 봐 줄 수 있을 법 한데... 그럼 말이 난김에 그 코드도 InitRequest가 아니라 이 곳으로 옮기면 캐쉬를 쓰는 경우까지 되지 않을까 싶음.

### slashlinks 처리                     - 여기서부터 추가
    if ($SlashLinks && (length($ENV{'PATH_INFO'}) > 1)) {
        $ENV{'QUERY_STRING'} .= '&' if ($ENV{'QUERY_STRING'});
        $ENV{'QUERY_STRING'} .= substr($ENV{'PATH_INFO'}, 1);    # wiki.pl/ 뒤에 있는 내용을 QUERY_STRING에 옮긴 후
    }
    $ENV{'QUERY_STRING'} = guess_and_convert($ENV{'QUERY_STRING'});   # QUERY_STRING 값을 컨버트 해 주고

# 이제 진행을 하면
    &InitLinkPatterns();
    if (!&DoCacheBrowse()) {          # 캐쉬도 ok가 되지 않을까
        eval $BrowseCode;
...
-- Raymundo 2007-3-3 1:17 pm

음, "이래도 괜찮은 거냐"라는 질문 자체가 "그렇다"라고 대답하기 쉽지 않은 질문이긴 하지만.... [KLDP 질문글][펄매니아 질문글]에 주말이 지나도록 그렇다 아니다 답글이 없어서, [comp.lang.perl.misc 그룹]에도 올림...
-- Raymundo 2007-3-4 11:37 pm
이름:  
Homepage:
내용:
 

위키위키분류
각주:
1. wiki.pl?위키

마지막 편집일: 2007-3-26 9:50 am (변경사항 [d])
2082 hits | Permalink | 변경내역 보기 [h] | 페이지 소스 보기