http_header.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: stanley-king
  5. * Date: 16/1/30
  6. * Time: 下午3:46
  7. */
  8. class http_header
  9. {
  10. const COOKIE_EXPIRES = "; expires=";
  11. const COOKIE_MAX_AGE = "; Max-Age=";
  12. const COOKIE_DOMAIN = "; domain=";
  13. const COOKIE_PATH = "; path=";
  14. const COOKIE_SECURE = "; secure";
  15. const COOKIE_HTTPONLY = "; HttpOnly";
  16. private static $stHeader = NULL;
  17. private static $http_status_map = array(100 =>"Continue",
  18. 101 =>"Switching Protocols" ,
  19. 200 =>"OK" ,
  20. 201 =>"Created" ,
  21. 202 =>"Accepted" ,
  22. 203 =>"Non-Authoritative Information" ,
  23. 204 =>"No Content" ,
  24. 205 =>"Reset Content" ,
  25. 206 =>"Partial Content" ,
  26. 300 =>"Multiple Choices" ,
  27. 301 =>"Moved Permanently" ,
  28. 302 =>"Found" ,
  29. 303 =>"See Other" ,
  30. 304 =>"Not Modified" ,
  31. 305 =>"Use Proxy" ,
  32. 307 =>"Temporary Redirect" ,
  33. 308 =>"Permanent Redirect" ,
  34. 400 =>"Bad Request" ,
  35. 401 =>"Unauthorized" ,
  36. 402 =>"Payment Required" ,
  37. 403 =>"Forbidden" ,
  38. 404 =>"Not Found" ,
  39. 405 =>"Method Not Allowed" ,
  40. 406 =>"Not Acceptable" ,
  41. 407 =>"Proxy Authentication Required" ,
  42. 408 =>"Request Timeout" ,
  43. 409 =>"Conflict" ,
  44. 410 =>"Gone" ,
  45. 411 =>"Length Required" ,
  46. 412 =>"Precondition Failed" ,
  47. 413 =>"Request Entity Too Large" ,
  48. 414 =>"Request-URI Too Long" ,
  49. 415 =>"Unsupported Media Type" ,
  50. 416 =>"Requested Range Not Satisfiable" ,
  51. 417 =>"Expectation Failed" ,
  52. 426 =>"Upgrade Required" ,
  53. 428 =>"Precondition Required" ,
  54. 429 =>"Too Many Requests" ,
  55. 431 =>"Request Header Fields Too Large" ,
  56. 500 =>"Internal Server Error" ,
  57. 501 =>"Not Implemented" ,
  58. 502 =>"Bad Gateway" ,
  59. 503 =>"Service Unavailable" ,
  60. 504 =>"Gateway Timeout" ,
  61. 505 =>"HTTP Version Not Supported" ,
  62. 506 =>"Variant Also Negotiates" ,
  63. 511 =>"Network Authentication Required");
  64. private $mHeader = NULL;
  65. private $mStatusLine = '';
  66. private $mStatusCode = 200;
  67. private $mSended = false;
  68. public function start()
  69. {
  70. $this->mHeader = new SplDoublyLinkedList();
  71. $this->mStatusCode = 200;
  72. $this->mSended = false;
  73. }
  74. public function sent()
  75. {
  76. if($this->mSended == false) {
  77. $this->mSended = true;
  78. $sHeader = $this->to_string();
  79. fcgi_echo($sHeader);
  80. }
  81. return $this->mSended;
  82. }
  83. private function to_string()
  84. {
  85. $sheader = '';
  86. $status_line = sprintf("HTTP/1.1 %d %s\r\n",$this->mStatusCode,self::$http_status_map[$this->mStatusCode]);
  87. $sheader .= $status_line;
  88. foreach($this->mHeader as $val)
  89. {
  90. $sheader .= $val . "\r\n";
  91. }
  92. $sheader .= "\r\n";
  93. return $sheader;
  94. }
  95. static public function instance()
  96. {
  97. if(self::$stHeader == NULL) {
  98. self::$stHeader = new http_header();
  99. }
  100. return self::$stHeader;
  101. }
  102. public function setcookie($name, $value = null, $expire = null, $path = null, $domain = null, $secure = null,$url_encode = true, $httponly = null)
  103. {
  104. if(empty($name)) {
  105. return false;
  106. } elseif(strpbrk($name, "=,; \t\r\n\013\014") != false) {
  107. Log::record('Cookie names cannot contain any of the following \'=,; \\t\\r\\n\\013\\014\'');
  108. return false;
  109. }
  110. if(empty($value)) {
  111. $cookie = sprintf("Set-Cookie: %s=deleted; expires=%s; Max-Age=0", $name, gmdate("D, d-M-Y H:i:s T",1));
  112. }
  113. else
  114. {
  115. if(!$url_encode && strpbrk($value,"=,; \t\r\n\013\014") != false) {
  116. Log::record('Cookie valuse cannot contain any of the following \'=,; \\t\\r\\n\\013\\014\'');
  117. return false;
  118. }
  119. if($url_encode) {
  120. $cookie = sprintf("Set-Cookie: %s=%s", $name, !empty($value) ? urlencode($value) : "");
  121. } else {
  122. $cookie = sprintf("Set-Cookie: %s=%s", $name, !empty($value) ? $value : "");
  123. }
  124. if($expire > 0) {
  125. $tmp = self::COOKIE_EXPIRES . gmdate("D, d-M-Y H:i:s T",$expire);
  126. $cookie .= $tmp;
  127. }
  128. $tsdelta = sprintf("%d", $expire - time());
  129. $cookie .= self::COOKIE_MAX_AGE . $tsdelta;
  130. }
  131. if(!empty($path)) {
  132. $cookie .= self::COOKIE_PATH . $path;
  133. }
  134. if(!empty($domain)) {
  135. $cookie .= self::COOKIE_DOMAIN . $domain;
  136. }
  137. if(!empty($secure)) {
  138. $cookie .= self::COOKIE_SECURE . $secure;
  139. }
  140. if(!empty($httponly)) {
  141. $cookie .= self::COOKIE_HTTPONLY . $httponly;
  142. }
  143. $this->mHeader->push($cookie);
  144. return true;
  145. }
  146. private function replace($string)
  147. {
  148. $reg = '/^([^:]*): (.*)/i';
  149. $string = trim($string);
  150. if(preg_match($reg,$string,$m)) {
  151. $sname =strtolower($m[1]);
  152. }
  153. $index = 0;
  154. for($this->mHeader->rewind();$this->mHeader->valid();$this->mHeader->next())
  155. {
  156. $val = $this->mHeader->current();
  157. if(preg_match($reg,$val,$m))
  158. {
  159. $hname = strtolower($m[1]);
  160. if(strcmp($sname,$hname) == 0) {
  161. $this->mHeader->offsetUnset($index);
  162. break;
  163. }
  164. $index++;
  165. }
  166. }
  167. $this->mHeader->push($string);
  168. }
  169. private function newline_check($string)
  170. {
  171. $datas = str_split($string);
  172. foreach($datas as $val)
  173. {
  174. if($val == '\r' || $val == '\n') {
  175. Log::record("Header may not contain more than a single header, new line detected",Log::ERR);
  176. return false;
  177. }
  178. }
  179. return true;
  180. }
  181. private function extract_response_code($header_line)
  182. {
  183. $code = 200;
  184. $nodes = explode($header_line);
  185. if(count($nodes) > 1)
  186. {
  187. $datas = str_split($nodes[1]);
  188. $max = intval('0') + 9;
  189. $min = intval('0');
  190. foreach($datas as $ch)
  191. {
  192. if($ch >$max || $ch < $min) {
  193. return false;
  194. }
  195. }
  196. $code = intval($nodes[1]);
  197. }
  198. return $code;
  199. }
  200. private function special_line($header_line,$http_response_code)
  201. {
  202. $len = strlen($header_line);
  203. if($len >= 5 && strncasecmp($header_line,"HTTP/", 5) == 0)
  204. {
  205. $this->mStatusCode = $this->extract_response_code($header_line);
  206. $this->mStatusLine = $header_line;
  207. return 0;
  208. }
  209. else
  210. {
  211. $colon_offset = strchr($header_line, ':');
  212. if($colon_offset)
  213. {
  214. if (!strncasecmp($header_line, "Location",strlen("Location")))
  215. {
  216. if( ($this->mStatusCode < 300 || $this->mStatusCode > 399) && $this->mStatusCode != 201)
  217. {
  218. $method = strtolower(request_helper::method());
  219. if ($http_response_code) { /* user specified redirect code */
  220. $this->mStatusCode = $http_response_code;
  221. } elseif ($method == 'get') {
  222. $this->mStatusCode = 303;
  223. } else {
  224. $this->mStatusCode = 302;
  225. }
  226. }
  227. }
  228. elseif(!strncasecmp($header_line, "WWW-Authenticate",strlen("WWW-Authenticate"))) {
  229. $this->mStatusCode = 401;
  230. }
  231. }
  232. }
  233. return 1;
  234. }
  235. public function header ($string, $replace = true, $http_response_code = null)
  236. {
  237. if(empty($string)) return;
  238. if($this->newline_check($string) == false) return;
  239. if($this->special_line($string,$http_response_code) == 0) {
  240. return false;
  241. }
  242. if($http_response_code) {
  243. $this->mStatusCode = $http_response_code;
  244. }
  245. if($replace) {
  246. $this->replace($string);
  247. } else {
  248. $this->mHeader->push($string);
  249. }
  250. }
  251. public function remove($name)
  252. {
  253. if(empty($name)) {
  254. $this->mHeader->clear();
  255. }
  256. else
  257. {
  258. $reg = '/^([^:]*): (.*)/i';
  259. $index = 0;
  260. for($this->mHeader->rewind();$this->mHeader->valid();$this->mHeader->next())
  261. {
  262. $val = $this->mHeader->current();
  263. if(preg_match($reg,$val,$m))
  264. {
  265. $hname = strtolower($m[1]);
  266. if(strcmp($name,$hname) == 0) {
  267. $this->mHeader->offsetUnset($index);
  268. break;
  269. }
  270. $index++;
  271. }
  272. }
  273. }
  274. return true;
  275. }
  276. }
  277. function init_cookie($cookie)
  278. {
  279. $regxp = '/([^=]+=[^;]*)[;]?/i';
  280. $val = preg_match_all($regxp,$cookie,$match);
  281. if($val == false) return false;
  282. if(count($match) == 2)
  283. {
  284. foreach($match[1] as $val)
  285. {
  286. $kv = preg_split('/=/',$val);
  287. if(!empty($kv))
  288. {
  289. $k = trim($kv[0]);
  290. $v = trim($kv[1]);
  291. if(!empty($k)) {
  292. $_COOKIE[$k] = $v;
  293. }
  294. //Log::record("cookie {$k} = {$v}",Log::DEBUG);
  295. }
  296. }
  297. }
  298. return true;
  299. }
  300. function fcgi_setcookie($name, $value = null, $expire = null, $path = null, $domain = null, $secure = null, $httponly = null)
  301. {
  302. return http_header::instance()->setcookie($name,$value,$expire,$path,$domain,$secure,true,$httponly);
  303. }
  304. function fcgi_setrawcookie($name, $value = null, $expire = null, $path = null, $domain = null, $secure = null, $httponly = null)
  305. {
  306. return http_header::instance()->setcookie($name,$value,$expire,$path,$domain,$secure,false,$httponly);
  307. }
  308. function fcgi_header($string, $replace = true, $http_response_code = null)
  309. {
  310. http_header::instance()->header($string,$replace,$http_response_code);
  311. }
  312. function fcgi_header_remove($name)
  313. {
  314. return http_header::instance()->remove($name);
  315. }
  316. function fcgi_headers_sent()
  317. {
  318. return http_header::instance()->sent();
  319. }