Browse Source

已经可以在本地正常工作。

调整:
1.加入防盗链功能
2.使用SAE Stroage的URL访问而不是read,效率可能有所提高
3.加入本地I/O的CDN功能
4.加入修改HTTP_HOST的功能
5.缓存时的文件名为key而不是原有路径名,增强兼容性
oott123 11 years ago
parent
commit
aa854154f7
12 changed files with 282 additions and 71 deletions
  1. 1 0
      .gitignore
  2. 8 0
      .htaccess
  3. 1 1
      README.md
  4. 3 0
      app.conf
  5. 1 1
      config.yaml
  6. 93 32
      include/controller.php
  7. 40 0
      include/lib.php
  8. 8 2
      include/start.php
  9. 6 30
      include/storage.php
  10. 42 0
      include/storages/Local.php
  11. 43 0
      include/storages/Sae.php
  12. 36 5
      index.php

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+/cdn

+ 8 - 0
.htaccess

@@ -0,0 +1,8 @@
+RewriteEngine on
+RewriteBase /LayerBAE/
+RewriteCond $1 ^(index\.php)?$ [OR]
+RewriteCond %{REQUEST_FILENAME} -f [OR]
+RewriteCond %{REQUEST_FILENAME} -d
+
+RewriteRule ^(.*)$ - [S=1]
+RewriteRule ^(.*)$ index.php/$1 [L]

+ 1 - 1
README.md

@@ -4,4 +4,4 @@
 
 forked from [SaeLayerCDN](https://github.com/Slacken/cdn).
 
-想法是将SAE上的知名应用SaeLayerCDN移植到BAE上来工作。
+想法是将SAE上的知名应用SaeLayerCDN移植到BAE上来工作。(现在的计划是SAE/BAE/标准PHP多平台)

+ 3 - 0
app.conf

@@ -0,0 +1,3 @@
+handlers:
+  - regex_url: ^(.*)$
+    script: /index.php/$1

+ 1 - 1
config.yaml

@@ -6,4 +6,4 @@ handle:
 - compress: if ( out_header["Content-type"]=="text/css" ) compress
 - compress: if ( out_header["Content-type"]=="image/jpeg" ) compress
 - compress: if ( out_header["Content-type"]=="image/png" ) compress
-- rewrite: if ( !is_dir() && !is_file() && path ~ "^(.*)$" ) goto "index.php?q=$1"
+- rewrite: if ( !is_dir() && !is_file() && path ~ "^(.*)$" ) goto "index.php/$1"

+ 93 - 32
include/controller.php

@@ -10,6 +10,8 @@ class controller{
 	
 	public $error_type;
 	
+	private $hit = false;
+	
 	public function __construct($request = ''){
 		
 		$this->content_type = 'text/html';
@@ -18,60 +20,106 @@ class controller{
 		
 		$request = ltrim($request,'/');
 		
-		//是否为SAE环境
-		if(!IS_SAE || !class_exists('SaeStorage')){//
-			$this->error_type = 2;
+		//检测环境
+		if(!RUN_ENV){
+			$this->error_type = 'no_run_env';
 			$this->succeed = FALSE;
 		}
 		
 		//请求为空
-		elseif($request === ''){
-			
+		elseif($request === '' && WELCOME_DOC){
 			//显示欢迎页面
-			if(WELCOME_DOC){
-				view::show('welcome');
-				return ;
-			}else{
-				$this->error_type = 1;
-				$this->succeed = FALSE;
-			}
+			view::show('welcome');
+			return ;
 		}
-		
 		else{
-			//匹配文件后缀
-			$temp = array();
-			if(preg_match('/\.(jpg|jpeg|png|pdf|gif|css|js|zip)$/i', $request,$temp)===1){//暂时先就这几种
-				//http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types
-				switch($temp[1]){
-					case 'jpg':{$this->content_type="image/jpeg";}break;
-					case 'gif':{$this->content_type="image/gif";}break;
-					case 'png':{$this->content_type="image/png";}break;
-					case 'css':{$this->content_type="text/css";}break;
-					case 'js':{$this->content_type="text/javascript";}break;
+			//检查防盗链
+			$referer = isset($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:'';
+			if(ALLOW_REGX && !preg_match('/'.ALLOW_REGX.'/i',$referer)){
+				$this->error_type = 'not_allowed_domain';
+				$this->succeed = FALSE;
+			}else{
+				//匹配文件后缀
+//				$temp = array();
+//				if(preg_match('/\.(jpg|jpeg|png|pdf|gif|css|js|zip)$/i', $request,$temp)===1){//暂时先就这几种
+//					//http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types
+//					switch($temp[1]){
+//						case 'jpg':{$this->content_type="image/jpeg";}break;
+//						case 'gif':{$this->content_type="image/gif";}break;
+//						case 'png':{$this->content_type="image/png";}break;
+//						case 'css':{$this->content_type="text/css";}break;
+//						case 'js':{$this->content_type="text/javascript";}break;
+//					}
+//				}
+				$mime_types = array(
+					'jpg' => 'image/jpeg',
+					'gif' => 'image/gif',
+					'png' => 'image/png',
+					'css' => 'text/css',
+					'txt' => 'text/plain',
+					'js' => 'text/javascript',
+					'html' => 'text/html',
+					'htm' => 'text/htm',
+					'rss' => 'application/atom+xml',
+					'json' => 'application/json',
+					'ogg' => 'audio/ogg',
+					'pdf' => 'application/pdf',
+					'xml' => 'text/xml',
+					'zip' => 'application/zip',
+					'rar' => 'application/octet-stream',
+					'gz' => 'application/gzip',
+					'gzip' => 'application/gzip',
+					'wav' => 'audio/vnd.wave',
+					'mp3' => 'audio/mp3',
+					'mp4' => 'video/mp4',
+					'flv' => 'video/x-flv',
+				);
+				$basename = basename($request);
+				$ext = strtolower(substr($basename,strrpos($basename,'.')+1));
+				if(isset($mime_types[$ext])){
+					$this->content_type=$mime_types[$ext];
 				}
 			}
 		}
 		
 		//开始处理
-		$this->handle($request);
+		$delete = false;
+		if(count($purge = explode(PURGE_KEY.'/',$request,2))>1){
+			$delete = true;
+			$request = $purge[1];
+		}
+		$key = md5($request).'_'.strlen($request).'.cache';
+		$this->hit = $key;
+		$this->handle($request,$key,$delete);
 		
 	}
 	/**
 	 * 获取内容并输出
 	 * 如果stroage里面不存在,则从URL里面获取
 	 * */
-	private function handle($filename){
+	private function handle($filename,$key,$delete = false){
 		$content = '';
 		if($this->succeed){
-			$storage = new storage();
-			if($storage->exists($filename)){
-				$content = $storage->read($filename);
+			$storage = storage::gethandle();
+			if($delete){
+				if(!$storage->exists($key)){
+					die(json_encode(array('purge'=>$filename,'key'=>$key,'success'=>'not exists')));
+				}
+				$return = $storage->delete($key);
+				die(json_encode(array('purge'=>$filename,'key'=>$key,'success'=>$return)));
+			}
+			if($storage->exists($key)){
+				if($url = $storage->url($key)){
+					$this->locate($url);
+				}
+				$content = $storage->read($key);
 			}else{
-				$content = @file_get_contents(BASE_URL.$filename);
-				$storage->write($filename, $content);
+				//$content = @file_get_contents(BASE_URL.$filename);
+				$content = lib::fetch_url(BASE_URL.$filename);
+				$storage->write($key, $content);
 			}
 			if(empty($content)){
-				$this->error_type = 3;
+				$this->error_type = 'empty_content';
 				$this->succeed = FALSE;
 			}else{
 				//这里应该有更多的检查
@@ -86,21 +134,34 @@ class controller{
 	 * 输出结果,包括缓存控制等
 	 * */
 	private function render($content=''){
+		ob_end_clean();
 		if(!$this->succeed){
 			$this->error();
 			return ;
 		}else{
+			if($this->hit){
+				header('Layer-Cache: Hit;key='.$this->hit);
+			}else{
+				header('Layer-Cache: Miss');
+			}
 			header("Expires: " . date("D, j M Y H:i:s GMT", time()+2592000));//缓存一月
 			header('Content-type: '.$this->content_type);
 			echo $content;
 		}
 	}
 	
+	private function loacte($url){
+		//302
+		header("HTTP/1.1 302 Moved Temporarily");
+		header("Location:".$url);
+	}
+	
 	/**
 	 * 处理错误
 	 * */
 	private function error(){
-		echo "<strong>something seems wrong.</strong>";
+		$this->content_type = 'text/html';
+		echo json_encode(array('error'=>$this->error_type));
 	}
 	
 	

+ 40 - 0
include/lib.php

@@ -17,5 +17,45 @@ class lib{
 
     }
 	
+	//抓取
+	public static function fetch_url($url){
+		switch(RUN_ENV){
+			case 'SAE':	//使用SAE FetchURL服务
+				$f = new SaeFetchurl();
+				if(STATIC_HOST){
+					$f->setHeader('Host',STATIC_HOST);
+				}
+				$content = $f->fetch($url);
+				return $content;
+			break;
+			case 'BAE':
+			case 'LOCAL':
+			default:
+				if(function_exists('curl_init')){
+					//BAE或普通平台下可使用curl
+					$ch = curl_init();
+					curl_setopt($ch, CURLOPT_URL, $url);
+					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+					curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+					if(!ini_get('safe_mode')){
+						curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
+					}
+					if(STATIC_HOST){
+						curl_setopt($ch, CURLOPT_HTTPHEADER, 'Host: '.STATIC_HOST);
+					}
+					return curl_exec($ch);
+				}else{
+					//否则使用file_get_contents
+					if(STATIC_HOST){
+						$opt=array('http'=>array('header'=>'Host: '.STATIC_HOST));
+						$context=stream_context_create($opt);
+						return file_get_contents($url,false,$context);
+					}else{
+						return file_get_contents($url);
+					}
+				}
+		}
+	}
 
 }

+ 8 - 2
include/start.php

@@ -11,5 +11,11 @@ function includeloader($class){
 }
 
 spl_autoload_register('includeloader');
-
-new controller(isset($_GET['q'])?$_GET['q']:'');
+//die($_SERVER['QUERY_STRING']);
+new controller(isset($_SERVER['PATH_INFO'])?$_SERVER['PATH_INFO']:'');
+//print_r($_SERVER);die();
+//$url = $_SERVER['DOCUMENT_ROOT'].(isset($_SERVER['REQUEST_URI'])?$_SERVER['REQUEST_URI']:'');
+//$url = str_ireplace('\\','/',$url);
+//$base = str_ireplace('\\','/',BASE_PATH);
+//$test = (explode($base,$url));
+//new controller($test[1]);	//做path_info兼容

+ 6 - 30
include/storage.php

@@ -3,38 +3,14 @@
 if ( ! defined('BASE_PATH')) exit('No direct script access allowed');
 
 /**
- * 封装SAE storage
+ * 封装storage
  * */
 class storage{
-	
-	public $instance;
-	
-	public $domain;
-	
-	public function __construct(){
-		
-		$this->domain = DOMAIN;
-		$this->instance = new SaeStorage(SAE_ACCESSKEY,SAE_SECRETKEY);
-	}
-	
-	public function exists($filename){
-		return $this->instance->fileExists($this->domain,$filename);
-	}
-	//这里是效率瓶颈啊!!
-	public function read($filename){
-		return $this->instance->read($this->domain,$filename);
-	}
-	
-	public function write($name,$content){
-		return $this->instance->write($this->domain,$name,$content);
-	}
-	
-	public function url($name){
-		return $this->instance->getUrl($this->domain,$name);
-	}
-	
-	public function error(){
-		return $this->instance->error();
+	public static function gethandle(){
+		$include_dir = dirname(__FILE__).'/storages/';
+		require($include_dir.ucfirst(strtolower(RUN_ENV.'.php')));
+		return new StorageHandle();
+		//$this->instance = new SaeStorage(SAE_ACCESSKEY,SAE_SECRETKEY);
 	}
 	
 }

+ 42 - 0
include/storages/Local.php

@@ -0,0 +1,42 @@
+<?php
+
+if ( ! defined('BASE_PATH')) exit('No direct script access allowed');
+
+class StorageHandle{
+	//本地存储
+	var $data_dir;
+	public function __construct(){
+		$data_dir = BASE_PATH.DOMAIN;
+		$data_dir = rtrim($data_dir,'/');
+		$this->data_dir = $data_dir.'/';
+	}
+	public function exists($filename){
+		return is_file($this->get_file($filename));
+	}
+	public function read($filename){
+		return file_get_contents($this->get_file($filename));
+	}
+	public function write($filename,$content){
+		return file_put_contents($this->get_file($filename),$content);
+	}
+	public function url($filename){
+		return false;	//不提供URL方式读取
+	}
+	public function delete($filename){
+		return unlink($this->get_file($filename));
+	}
+	public function error(){
+		return false;
+	}
+	private function get_file($key){
+		$letter1 = substr($key,0,1);
+		$letter2 = substr($key,0,2);
+		$dir = $this->data_dir.$letter1.'/'.$letter2;
+		if(!is_dir($dir)){
+			if(!mkdir($dir,0777,true)){
+				return $this->data_dir.$key;
+			}
+		}
+		return $dir.'/'.$key;
+	}
+}

+ 43 - 0
include/storages/Sae.php

@@ -0,0 +1,43 @@
+<?php
+
+if ( ! defined('BASE_PATH')) exit('No direct script access allowed');
+
+/**
+ * 封装SAE storage
+ * */
+class StorageHandle{
+	
+	public $instance;
+	
+	public $domain;
+	
+	public function __construct(){
+		$this->domain = DOMAIN;
+		$this->instance = new SaeStorage(SAE_ACCESSKEY,SAE_SECRETKEY);
+	}
+	
+	public function exists($filename){
+		return $this->instance->fileExists($this->domain,$filename);
+	}
+	//这里是效率瓶颈啊!!
+	public function read($filename){
+		return $this->instance->read($this->domain,$filename);
+	}
+	
+	public function write($name,$content){
+		return $this->instance->write($this->domain,$name,$content);
+	}
+	
+	public function url($name){
+		return $this->instance->getUrl($this->domain,$name);
+	}
+	
+	public function error(){
+		return $this->instance->error();
+	}
+	
+	public function delete($name){
+		return $this->instance->delete($this->domain,$name);
+	}
+	
+}

+ 36 - 5
index.php

@@ -1,21 +1,39 @@
 <?php
-
+@ob_start();
 
 /**
  * 设置源静态文件的根目录的URL地址
  * */
-define('STATIC_URL','http://www.baidu.com/');
+define('STATIC_URL','http://bbs.its.csu.edu.cn/');
+define('STATIC_HOST','');	//特殊应用下可以填写源站域名,会作为http头的hosts传递,正常情况请留空
+
+//define('RUN_ENV', 'BAE');	//自定义环境(如不去掉前面的//则自动判断)可选:BAE/SAE/LOCAL 请大写
 
 /**
- * SAE storage的domain
+ * SAE storage的domain,BAE的bucket,本地的存储路径(相对于index.php的相对目录,前无斜杠后有斜杠)
  * */
-define('DOMAIN','cdn');
+define('DOMAIN','cdn');	//SAE or BAE
+//define('DOMAIN','data/cache/');	//本地
+
+define('PURGE_KEY','purge');	//访问http://domain/PURGE_KEY/path/to/file来刷新缓存
+
+define('ALLOW_REGX','.*');	//设置防盗链允许的[域名]正则表达式
+//define('ALLOW_DOMAIN','^(best33\.com|.*\.best33\.com|)$');	//允许best33.com,*.best33.com,浏览器直接访问
+//define('ALLOW_DOMAIN','^(best33\.com|.*\.best33\.com)$');	//允许best33.com,*.best33.com,不允许浏览器直接访问
+//define('ALLOW_DOMAIN','^(.*)$');	//允许任意,允许浏览器访问
+//define('ALLOW_DOMAIN','^(.+)$');	//允许任意,但不允许浏览器访问
+
+define('MIME','text/html');	//默认MIME类型,可以设为application/octet-stream则对未知项目自动弹出下载
+define('DIRECT_EXT','php|html');	//不进入缓存的扩展名
 
 /**
  * 空请求时是否显示文档
  * */
 define('WELCOME_DOC',TRUE);
 
+
+
+
 /**
  * 运行环境:development/testing/production
  * */
@@ -47,7 +65,20 @@ define('BASE_PATH',dirname(__FILE__).'/');
 
 define('BASE_URL', rtrim(STATIC_URL,'/').'/');
 
-define('IS_SAE', defined('SAE_SECRETKEY'));
+//define('IS_SAE', defined('SAE_SECRETKEY'));
+
+//自定义环境
+//define('RUN_ENV', 'BAE');	//可选:BAE/SAE/LOCAL 请大写
+
+if(!defined('RUN_ENV')){
+	if(defined('SAE_SECRETKEY')){
+		define('RUN_ENV','SAE');
+	}elseif(getenv('HTTP_BAE_ENV_SK')){
+		define('RUN_ENV','BAE');
+	}else{
+		define('RUN_ENV','LOCAL');
+	}
+}
 
 require_once BASE_PATH.'include/start.php';