PHP로 만든 단순한 CDN 서비스 모듈

 

트래픽 부하를 줄이기 위해 CloudFlare 를 사용하였는데 연결위치가 LAX(로스엔젤레스)라 속도가 많이 느려지는것을 확인하고 급하게 CDN 형태로 사용하고자 만들었습니다.

해당 서버는 도쿄(Vultr)에 위치해 있습니다.

원리는 kilho.net/{파일} 을 cdn.kilho.net/{파일} 로 연결하여 메인 호스팅의 트래픽을 분산합니다.


<?
$cfg['url'] = 'kilho.net';
$cfg['cache'] = '../data/cache';

$url = $_SERVER['REQUEST_SCHEME'].'://'.$cfg['url'].$_SERVER['REQUEST_URI'];
$url_arr = parse_url($url);

switch($_SERVER['REQUEST_METHOD'])
{
case 'GET':
$filename = $cfg['cache'].$url_arr['path'];
$dirname = dirname($filename);
if(!is_file($filename))
{
if(!is_dir($dirname)) mkdir($dirname, 0707, true);

$ch = curl_init();
$fp = fopen($filename, 'w');
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_REFERER, $_SERVER['REQUEST_SCHEME'].'://'.$cfg['url']);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_exec($ch);
fclose($fp);

switch($code = curl_getinfo($ch, CURLINFO_HTTP_CODE))
{
case 200:
break;

default:
touch($filename, $code);
}
curl_close($ch);
}

if(is_file($filename))
{
$filetime = filemtime($filename);

if($filetime<1000)
{
header('HTTP/1.0 '.$filetime);
} else {
include '_mime.php';
$filetype = get_mime($filename);
$lastmodified = filemtime($filename);
$etag = md5_file($filename);

header('Content-Type: '.$filetype);
header('Expires: '.gmdate('D, d M Y H:i:s', time()+77760000).' GMT');
header('Cache-Control: public, max-age=77760000');
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $lastmodified).' GMT');
header('Etag: '.$etag);
header('Access-Control-Allow-Origin: *');

if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']))
{
if($_SERVER['HTTP_IF_MODIFIED_SINCE']==gmdate('D, d M Y H:i:s', $lastmodified).' GMT'||$_SERVER['HTTP_IF_NONE_MATCH']==$etag)
{
header('HTTP/1.0 304 Not Modified');
exit;
}
}
}

if(strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip'))
if(in_array($filetype, array('text/plain', 'text/html', 'text/css', 'text/javascript', 'application/json', 'application/xml')))
ob_start('ob_gzhandler');

$fp = fopen($filename, 'rb');
fpassthru($fp);
fclose ($fp);

exit;
}

break;

case 'POST':

case 'PUT':

case 'DELETE';
break;
}

header('HTTP/1.0 404 File Not Found.');
?>

HTTP 접속 정보 기록

php-151199_640

 

OpenAPI 이용시 테스트 겸 접속 정보가 제대로 됬는지 확인용

접속시 메소스값, 호스트, POST 값


<?
$log = $_SERVER['REQUEST_METHOD'].' '.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']."\n\n";

foreach($_SERVER as $k => $v)
{
if($k=='HTTP_HOST') continue;
if(substr($k, 0,5)!='HTTP_') continue;
$log .= '$k: $v'."\n";
}

$log .= "\n";

$post = array_map('urldecode', explode('&', file_get_contents('php://input')));
foreach($post as $v)
{
$log .= "$v\n";
}

$log .= "-----------------------------------\n";

file_put_contents('log.txt', $log, FILE_APPEND | LOCK_EX);
?>

워드프레스, 그누보드 연동

현재 길호넷에서 워드프레스와 그누보드를 연동한 방식입니다.

사이트를 좀 더 안정화를 시킨 후 소스를 공개하려고 하였는데, 그누보드 사용자분의 요청(?)으로 급조하여 만든걸 올립니다. (http://sir.co.kr/bbs/board.php?bo_table=cm_free&wr_id=741180)

추후, 그누보드의 head.php, tail.php 에서 워드프레스에서 생성된 페이지를 직접 불러들이고, 캐쉬 처리 및 컨텐츠 영역을 상하로 나눈 후 출력하는것을 생각하고 있습니다.

– 워드프레스 회원 DB 를 기반으로 그누보드와 연동되어, 게시판 글 작성시 워드프레스의 로그인에 따라 액션을 취합니다.
– 그누보드를 gnu.kilho.net 로 사용하고 있습니다. 따라서 워드프레스에서 서브도메인과 쿠키를 공유할 수 있게 하여야 합니다.
(길호넷에서는 “root Cookie” 라는 플러그인을 사용하였습니다)
– 워드프레스와 그누보드가 각각의 서버로 운영되고 있습니다. 소스 내용 중 자신의 워드프레스가 설치된 서버IP, 워드프레스가 설치된 도메인 정보를 각각 수정하세요.
– 워드프레스가 설치된 서버에 아래 소스를 “wp-sso.php” 로 저장합니다.

<?
	/********************************************************
	 * WordPress SSO(Single Site On) Helper Object 
	 * by Oh Kilho (http://www.kilho.net)
	 *******************************************************/

	include 'wp-load.php';
	
	$wp_cookie = $_COOKIE&#91;'wordpress_logged_in_'.md5(get_site_option('siteurl'))&#93;;
	$wp_user = $_POST&#91;'user'&#93;;
	$wp_pass = $_POST&#91;'pass'&#93;;

	if($wp_user&&$wp_pass)
	{
		if(user_pass_ok($wp_user, $wp_pass))
			$user = get_user_by('login', $wp_user);
	}else{
		if($wp_cookie)
		{
			wp_set_current_user(wp_validate_auth_cookie($wp_cookie, 'logged_in'));
			$user = wp_get_current_user();
		}
	}

	if($user)
	{
		echo '<id>'.$user->user_login.'</id>';
		echo '<nick>'.$user->display_name.'</nick>';
		echo '<email>'.$user->user_email.'</email>';
		echo '<url>'.$user->user_url.'</url>';
	}
?>

– 그누보드가 설치된 서버의 “/extend/” 디렉토리에 아래 소스를 “wp-sso.php”로 저장합니다.

<?
/*
 그누보드 확장 모듈 : 워드프레스 SSO 처리
 */
$wp_server_domain = '워드프레스 설치된 도메인'; // xxx.com
$wp_server_addr = '워드프레스 설치된 서버 IP'; // xxx.xxx.xxx.xxx
$wp_server_path = '워드프레스 설치된 경로'; // 루트일 땐 '/' 로 작성

$wp_cookie_name = 'wordpress_logged_in_'.md5('http://'.$wp_server_domain);
$wp_cookie_value = $_COOKIE&#91;$wp_cookie_name&#93;;

if ($_SESSION&#91;'ss_mb_id'&#93;)
{
	// 총 관리자인 경우 열외 - 그누보드를 자체적으로 살펴볼 권한을 줘야징
	if(!$wp_cookie_value&&$is_admin != 'super')
	{
		// 이호경님 제안 코드
		session_unset(); // 모든 세션변수를 언레지스터 시켜줌 
		session_destroy(); // 세션해제함 

		// 페이지를 재실행
		echo "<script type='text/javascript'> window.location.reload(); </script>";
		exit;
	}
}else{
	if($wp_cookie_value)
	{
		// 메인 서버에서 정보 취득
		$fp = fsockopen($wp_server_addr,80,$errno,$errstr,3);

		if($fp)
		{
			fputs($fp, "GET {$wp_server_path}wp-sso.php HTTP/1.1\r\n");
			fputs($fp, "Accept: */*\r\n");
			fputs($fp, "Accept-Language: ko\r\n");
			fputs($fp, "User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1)\r\n");
			fputs($fp, "Host: $wp_server_domain\r\n");
			fputs($fp, "Connection: Close\r\n");
			fputs($fp, "Cookie: $wp_cookie_name=$wp_cookie_value\r\n");
			fputs($fp, "\r\n");

			$data = '';
			while(!feof($fp))
			{
				$data .= fgets($fp,1024);
			}

			fclose($fp);

			if($data)
			{
				foreach(array('id', 'nick', 'email', 'url', 'password') as $value)
				{
					preg_match('@<'.$value.'>(.*?)</'.$value.'>@',$data,$matches); 
					$wp_user[$value] = $matches[1];
				}
				$wp_user[pass] = substr(str_shuffle('abcefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'), 0, 12);

				$sql = " select mb_id from $g4[member_table] where mb_id='$wp_user[id]' ";
				$row = sql_fetch($sql);
				if($row[mb_id]!=$wp_user[id])
				{
					$sql = " insert into $g4[member_table]
									set mb_id = '$wp_user[id]',
										mb_password = password('$wp_user[pass]'),
										mb_name = '$wp_user[nick]',
										mb_nick = '$wp_user[nick]',
										mb_email = '$wp_user[email]',
										mb_homepage = '$wp_user[url]',
										mb_datetime = '$g4[time_ymdhis]',
										mb_ip = '$_SERVER[REMOTE_ADDR]',
										mb_level = '$config[cf_register_level]' ";
					sql_query($sql);
				}else{
					$sql = " update $g4[member_table]
									set mb_nick = '$wp_user[nick]',
										mb_email = '$wp_user[email]',
										mb_homepage = '$wp_user[url]'
									where mb_id = '$wp_user[id]' ";
					sql_query($sql);
				}
				unset($row);

				$mb = get_member($wp_user[id]);


				// 회원아이디 세션 생성
				set_session('ss_mb_id', $mb[mb_id]);
				// FLASH XSS 공격에 대응하기 위하여 회원의 고유키를 생성해 놓는다. 관리자에서 검사함 - 110106
				set_session('ss_mb_key', md5($mb[mb_datetime] . $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']));

				// 페이지를 재실행
				echo "<script type='text/javascript'> window.location.reload(); </script>";
				exit;
			}
		}else{
			echo 'Connection failed';
			exit;
		}

	}
}
?>

구글에서 jquery 가져다 쓰기

트래픽 때문에 이미지, 플래쉬 파일들은 다른 서버로 옮겨놓고 연결해놨음

jquery 는 서핑중 아래와 같은 소스를 발견 후 구글에 연결

수정한 파일 : 테마/functions.php

//Making jQuery Google API
function modify_jquery() 
{
  if (!is_admin())
  {
    // comment out the next two lines to load the local copy of jQuery
    wp_deregister_script('jquery');
    wp_register_script('jquery', 
    'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js', false, 
    '1.4.4');
    wp_enqueue_script('jquery');
  }
}
add_action('init', 'modify_jquery');

그누보드 연동 완료

워드프레스 회원 DB 를 기반으로 그누보드와 연동 시켜, 게시판에서 로그인 시 글 작성이 가능하게 하였음.

  1. 서브 도메인과 쿠키공유
  2. 워드프레스가 설치된 서버에 아래 소스를 “wp-sso.php” 로 저장
    <?
    	include 'wp-load.php';
    	
    	$cookie_name = 'wordpress_logged_in_'.md5(get_site_option('siteurl'));
    	$cookie = $_COOKIE&#91;$cookie_name&#93;;
    
    	wp_set_current_user(wp_validate_auth_cookie($cookie, 'logged_in'));
    
    	if($user = wp_get_current_user())
    	{
    		echo '<id>'.$user->user_login.'</id>';
    		//echo '<nick>'.$user->user_nicename.'</nick>';
    		echo '<nick>'.$user->display_name.'</nick>';
    		echo '<email>'.$user->user_email.'</email>';
    		echo '<url>'.$user->user_url.'</url>';
    	}
    ?>
  3. 그누보드 세션값이 없고, 워드프레스 쿠키 값이 있을 경우 2번 모듈을 서버에서 처리하여 SSO(Single Sign On) 기능 구현
  4. 그누보드 세션값이 있고, 워드프레스 쿠키 값이 없을 경우 세션을 비우고 해당 페이지 갱싱
  5. 그누보드 페이지 로딩 이벤트 발생시 해당 페이지 리사이징