삽질하는플머

xmlrpc-epi 로 만든 RPC 서버에서 압축전송된 요청 다루기

탐구생활/WEB 관련
델파이와 PHP 에서 XLMRPC 를 가지고 노는 이야기는 오래전에 위키에 끄적인 적이 있는데... 지금은 날아가버렸다. 
백업된 문서 찾아다 블로그에 정리해야겠다. (... 고 생각한지 벌써 일년이 훌쩍... ㅠㅠ) 

xmlrpc-epi 는 예전에 살펴본 php-xmlrpc 와 기능면에서 거의 같으면서도 C로 만들어진 확장이라 속도가 좀 더 빠르다. 
사용법도 비슷해서 include("xmlrpc.inc"); 를 include("xmlrpc_emu.inc"); 라고 끝에 "_emu" 만 붙여주면 된다. 
무엇보다 대부분의 PHP 배포판에 기본적으로 포함되어있다는 것이 장점이다. 심지어는 시놀로지의 NAS에도 들어있다. 
게다가 나는 팔랑귀이므로 뭐가 조금이라도 좋다고 하면 써봐야 직성이 풀린다. 

 다만 무슨 일인지 우분투 10.04에서는 빠져있는데, 아래와 같이 설치하고 아파치를 재구동하면 올라온다.  (예전에는 들어 있었는데...)

sudo apt-get install php5-xmlrpc



WAMP 에서는 트레이를 클릭하고 PHP -> PHP Extensions -> php_xmlrpc 확장을 선택하면 된다. 





xmlrpc

core library version xmlrpc-epi v. 0.51
php extension version 0.51
author Dan Libby
homepage http://xmlrpc-epi.sourceforge.net
open sourced by Epinions.com



http://xmlrpc-epi.sourceforge.net/ 에서 xmlrpc-epi-php-0.51.tar.gz 를 다운받아 압축을 풀고 util 디렉토리를 include 경로에 포함시키면 사용준비는 완료. 


 

PHP+MySQL+xmlrpc-epi 로 만든 XMLRPC 기반의 데이터 서버와 델파이로 정보를 주고 받는 중인데, DB값의 특성상 키와 값으로 쌍을 이루는 "구조체" 형식을 선호하다 보니 전송 데이터가 상당히 커지게 되었고, 이거 필드에서 제대로 써먹을 수 있을지 슬쩍 겁이나기 시작했다. 

일단 받는 정보부터 압축해보자. PHP.INI 를 다음과 같이 설정하고
 

zlib.output_compression = on
zlib.output_compression_level = 9 



zlib
........

DirectiveLocal ValueMaster Value
zlib.output_compression On On
zlib.output_compression_level 9 9
zlib.output_handler no value no value



RPC 함수 호출시 HTTP 헤더의  Accept-encoding 에 "gzip,deflate" 라고 적어주면 gzip으로 압축된 정보가 날아온다. deflate로 날아온 정보는 ZLib로 바로 풀리고 gzip의 경우는 
DelphiZLib 의 ZLibExGZ 유니트를 사용하면 되므로 일단 한 숨 돌렸는데...
저장하기 위해 서버로 보낼 정보도 만만치 않게 크기 때문에 마찬가지로 압축을 해주기로 하면서 고민이 시작된다.



HTTP 헤더의 Content-Encoding 에 deflate, 또는 gzip 을 적어주고 XML을 압축한 뒤 서버를 호출하니 다음과 같은 에러가 나온다. 

32700: parse error. not well formed. 
error occurred at line 1, column 1, byte index 0



데이터가 요사스러워 첫줄 첫번째 컬럼 제일 앞바이트부터 에러가 난다고 툴툴거리고 있다.
저 에러는 C확장 내부에서 내는 것으로서 한마디로 압축된 그대로 넘겨받았다는 이야기. 


흠... 설정에 output_xxx 만 있을 때 부터 알아봤어야 하는데... 들어오는 정보는 직접 풀어줘야하나보다. 


서버 객체가 정의된 xmlrpcs_emu.inc 파일의 177 라인을 살펴보니 전달된 HTTP_RAW_POST_DATA 를 날것 그대로 던져주고 있다. 
여기에 클라에서 전달받은 Content-encoding 값으로 압축여부를 판단해 압축을 풀어주는 코드를 추가해보자. 

    // public. service the xmlrpc request
    function service() {
       Header("Content-type: text/xml; Content-length: " . strlen($payload));
 
       global $HTTP_RAW_POST_DATA;
       $data=$HTTP_RAW_POST_DATA;
 

      // 데이터가 압축되어있는지 살핀다. 압축되어있다면 풀자. 
         if(isset($_SERVER['HTTP_CONTENT_ENCODING']))
             $content_encoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']);
         else
             $content_encoding = '';
 
         if ($content_encoding == 'gzip' && function_exists('gzinflate') && $degzdata = @gzinflate(substr($data, 10))) {
             //echo 'GZip decompress!';
             $data = $degzdata;
         } else
         if ($content_encoding == 'deflate' && function_exists('gzuncompress') && $degzdata = @gzuncompress($data)) {
             //echo 'Deflate decompress!';
             $data = $degzdata;
         }



        // call server
       echo xmlrpc_server_call_method($this->xmlrpc_server, $data, $this->dmap, 
                                      array(output_type => "xml", version => "xmlrpc", encoding => "UTF-8"));
    }



녹색으로 둘러친 부분이 추가한 내용이다. 이제 함수호출 정보를 gzip, 또는 deflate 로 압축해 던져도 제대로 반응하게 된다.  

XML은 텍스트로 된 정보이므로 압축률이 높다. 크기가 커질수록 효율은 더욱 좋아진다.
3천바이트 가량의 XML이 1/5인 600바이트 정도로 줄어들어 왔다갔다 하는 것을 보면 답답했던 속이 뻥~ 뚫릴 것이다. 



ps. php-xmlrpc 에는 압축 전송된 요청을 풀어주는 코드가 이미 들어있다.
     따라서 이 고민은 "똑같은 PHP에서 XMLRPC 사용인데 xmlrpc-epi 는 왜 안되는거야!!" 하는 사람에게만 유효하다.