GeoWHY 人生苦短,爱生活,爱唧哦歪

2014-05-25

By ZEN麽了啊

记梦器20140522

很有剧情的一个梦:

我是一个社区高中或者学院的老师,在一间小小的教室里,讲一门可能与可视化有关的课,本来就小的教室里也只有4、5个学生。天气似乎很冷,教室里有暖气,窗户雾蒙蒙的。

讲课的过程不记得了,下面的几个学生大部分听得津津有味,我也讲得唾沫横飞。最后布置作业的环节,我让大家写一个小故事。我说,下节课我希望你们每个人成为故事的讲述者,而不仅仅是写在一张纸上拿上来念。几个学生已经开始叽叽喳喳很激动地讨论各自想要讲的话题,只有角落一个胖子一声不吭,背起书包就走。

我急忙追了出去,外面一片冰天雪地,路被大雪覆盖,行走十分艰难,呼气的时候是白茫茫的吐息。胖子也没办法走很快,只能被我跟在后面。我问胖子同学准备写什么话题,胖子同学不耐烦地说,讲故事没意思。
(对话的部分因为现在记不得了,有创作成分)
我问:哦,那什么有意思呢?你觉得下雪有没有意思?
胖子征了一下。
我:你喜欢下雪吧?
胖子冷冷地说:下雪最没意思。
我:看你戴着有雪花的帽子,还以为你喜欢下雪呢。
胖子:这是我爷爷的帽子。
我:你爷爷喜欢下雪?
胖子翻了下白眼:不,他喜欢冲浪。
我:喔,冲浪的爷爷,一定很有趣。
胖子:有趣?浪那么高,很容易死人的,你不知道么。
我故意瞄了瞄:难道你不会游泳?
胖子又白了我一眼:当然会,爷爷说只要我学会游泳,暑假就带我去xx湾。
我:你们去了吗?
胖子:当然去了,爷爷从来说到做到。不过xx湾的浪真大,第一次见到那么高的浪。
我:所以你害怕了,不敢冲了?
胖子:谁说……的……其实…………怕得要死……
我:那后来你去冲了吗?
胖子:当然!有爷爷在,他教我检查蜡,怎么系安全绳,防范水母,然后在浪小的地方练习简单的划水。
我:万事开头难,你会进步的!
胖子:当然!爷爷什么都懂,教会我好多东西,虽然我学得很慢,但他总说,学得越快忘得越快。
我:哈,有道理。瞧,你讲了个有趣的故事!
胖子:也许吧。
我:所以你学会冲浪了吗?
胖子:没有。
我:为什么?半途而废可不好。
胖子:爷爷死了,巨浪给害死的。
我一时语塞:抱歉……
胖子:没什么,也许他去了最想去的地方。
我:你能这么想挺好的。
胖子:可再也学不会冲浪啦,我太笨了,现在也太胖了。
我:难道你以前是个瘦子?
胖子:……
我:下周来讲讲你的故事吧,我觉得你会成为很好的讲述者。
胖子:别看走眼。
我:来吧。
胖子:也许吧。
我:来给我们讲一个你爷爷冲浪的故事。

胖子正了正帽子说,下雪天真没意思。


2014-05-21

By ZEN麽了啊

[转]深入理解HTTP消息头

原文:http://thobian.info/?p=318

什麼是HTTP Headers

HTTP是Hypertext Transfer Protocol的缩寫,整個WWW都在使用這種協定,幾乎你在流覽器裏看到的大部分內容都是通過HTTP協定來傳輸的,比如這篇文章。
HTTP Headers是HTTP請求和相應的核心,它承載了關於用戶端流覽器,請求頁面,伺服器等相關的資訊。

示例

當你在流覽器位址欄裏鍵入一個URL,你的流覽器會將類似如下的HTTP請求:

GET /tutorials/other/top-20-mysql-best-practices/ HTTP/1.1 (Request line)
Host: net.tutsplus.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,* / <em>;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,</em>;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cookie: PHPSESSID=r2t5uvjq435r4q7ib3vtdjq120
Pragma: no-cache
Cache-Control: no-cache

第一行被稱為Request Line 它描述的是這個請求的基本資訊,剩下的就是HTTP Headers了。
請求完成之後,你的流覽器可能會收到如下的HTTP回應:

HTTP/1.x 200 OK (state line)
Transfer-Encoding: chunked
Date: Sat, 28 Nov 2009 04:36:25 GMT
Server: LiteSpeed
Connection: close
X-Powered-By: W3 Total Cache/0.8
Pragma: public
Expires: Sat, 28 Nov 2009 05:36:25 GMT
Etag: "pub1259380237;gz"
Cache-Control: max-age=3600, public
Content-Type: text/html; charset=UTF-8
Last-Modified: Sat, 28 Nov 2009 03:50:37 GMT
X-Pingback: http://net.tutsplus.com/xmlrpc.php
Content-Encoding: gzip
Vary: Accept-Encoding, Cookie, User-Agent

第一行被稱為Status Line,它之後就是HTTP Headers,空行完了就開始輸出內容了(在這個案例中是一些HTML輸出)。
但你查看頁面源代碼卻不能看到HTTP Headers,雖然它們連同你能看到的東西一起被傳送至流覽器。
這個HTTP請求也發出了一些其他資源的接收請求,例如圖片,CSS檔,JS文件等等。
下面我們來看看細節。

怎樣才能看到HTTP Headers

下面這些FireFox擴展能夠幫助你分析HTTP Headers:

  1. Firebug

  2. Live HTTP Headers

  3. HTTPFox

  4. 在PHP中:

文章下面將會看到一些使用PHP示範的例子。

HTTP Request 的結構

被稱作first line的第一行包含三個部分:

  • method 表明這是何種類型的請求. 最常見的請求類型有 GET, POSTHEAD.
  • path 體現的是主機之後的路徑. 例如,當你請求 http://net.tutsplus.com/tutorials/other/top-20-mysql-best-practices/時 , path 就會是 /tutorials/other/top-20-mysql-best-practices/.
  • protocol 包含有 HTTP 和版本號, 目前流覽器都會使用1.1.

剩下的部分每行都是一個Name:Value對。它們包含了各式各樣關於請求和你流覽器的資訊。
例如User-Agent就表明了你流覽器版本和你所用的作業系統。
Accept-Encoding會告訴伺服器你的流覽可以接受類似gzip的壓縮輸出。
這些headers大部分都是可選的。

HTTP 請求甚至可以被精簡成這樣子:

GET /tutorials/other/top-20-mysql-best-practices/ HTTP/1.1
Host: net.tutsplus.com

並且你仍舊可以從伺服器收到有效的回應。

請求類型

三種最常見的請求類型是:GETPOSTHEAD ,從HTML的編寫過程中你可能已經熟悉了前兩種。

GET:獲取一個文檔

大部分被傳輸到流覽器的HTML,images,JS,CSS, … 都是通過GET方法發出請求的。它是獲取資料的主要方法。

例如,要獲取Nettuts+ 的文章,http request 的第一行通常看起來是這樣的:

GET /tutorials/other/top-20-mysql-best-practices/ HTTP/1.1

一旦HTML載入完成,流覽器將會發送 GET 請求去獲取圖片,就像下面這樣:

GET /wp-content/themes/tuts_theme/images/header_bg_tall.png HTTP/1.1

表單也可以通過 GET 方法發送,下面是個例子:

First Name: Last Name: 

當這個表單被提交時,HTTP request 就會像這樣:

GET /foo.php?first_name=John&last_name=Doe&action=Submit HTTP/1.1

你可以將表單輸入通過附加進查詢字串的方式發送至伺服器。

POST :發送資料至伺服器

儘管你可以通過 GET 方法將資料附加到URL中傳送給伺服器,但在很多情況下使用 POST 發送資料給伺服器更加合適。通過 GET 發送大量資料是不現實的,它有一定的局限性。
POST 請求來發送表單數據是普遍的做法。我們來吧上面的例子改造成使用 POST 方式:

First Name: Last Name: 

提交這個表單會創建一個如下的HTTP請求:

POST /foo.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,* / <em>;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,</em>;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost/test.php
Content-Type: application/x-www-form-urlencoded
Content-Length: 43
first_name=John&amp;amp;last_name=Doe&amp;amp;action=Submit

這裏有三個需要注意的地方:

  • 第一行的路徑已經變為簡單的 /foo.php , 已經沒了查詢字串。
  • 新增了 Content-TypeContent-Lenght Header,它提供了發送資訊的相關資訊.
  • 所有資料都在headers之後,以查詢字串的形式被發送.

POST方式的請求也可用在AJAX,應用程式,cURL … 之上。並且所有的檔上傳表單都被要求使用 POST 方式。

HEAD:接收Header資訊

HEADGET很相似,只不過 HEAD 不接受HTTP回應的內容部分。當你發送了一個 HEAD 請求,那就意味著你只對HTTPHeader感興趣,而不是文檔本身。
這個方法可以讓流覽器判斷頁面是否被修改過,從而控制緩存。也可判斷所請求的文檔是否存在。
例如,假如你的網站上有很多鏈結,那麼你就可以簡單的給他們分別發送 HEAD 請求來判斷是否存在死鏈,這比使用GET要快很多。

HTTP回應結構

當流覽器發送了HTTP請求之後,伺服器就會通過一個 HTTP response 來回應這個請求。如果不關心內容,那麼這個請求看起來會是這樣的:

第一個有價值的資訊就是協定。目前伺服器都會使用 HTTP/1.x 或者 HTTP/1.1
接下來一個簡短的資訊代表狀態。代碼200意味著我們的請求已經發送成功了,伺服器將會返回給我們所請求的文檔,在Header資訊之後。
我們都見過404頁面。當我向伺服器請求一個不存在的路徑時,伺服器就用用404來代替200回應我們。
餘下的回應內容和HTTP請求相似。這些內容是關於伺服器軟體的,頁面/檔何時被修改過,mime type 等等…
同樣,這些Header資訊也是可選的。

HTTP狀態碼

  • 200 用來表示請求成功.
  • 300 來表示重定向.
  • 400 用來表示請求出現問題.
  • 500 用來表示伺服器出現問題.

200 成功 (OK)

前文已經提到,200 是用來表示請求成功的。

206 部分內容 (Partial Content)

如果一個應用只請求某範圍之內的檔,那麼就會返回206.

這通常被用來進行下載管理,中斷點續傳或者檔分塊下載。

404 沒有找到 (Not Found)

很容易理解

401 未經授權 (Unauthorized)

受密碼保護的頁面會返回這個狀態。如果你沒有輸入正確的密碼,那麼你就會在流覽器中看到如下的資訊:

注意這只是受密碼保護頁面,請求輸入密碼的彈出框是下面這個樣子的:

403 被禁止(Forbidden)

如果你沒有許可權訪問某個頁面,那麼就會返回403狀態。這種情況通常會發生在你試圖打開一個沒有index頁面的檔夾。如果伺服器設置不允許查看目錄內容,那麼你就會看到403錯誤。

其他一些方式也會發送許可權限制,例如你可以通過IP位址進行阻止,這需要一些htaccess的協助。

order allow,deny
deny from 192.168.44.201
deny from 224.39.163.12
deny from 172.16.7.92
allow from all

302(或307)臨時移動(Moved Temporarily) 和 301 永久移動(Moved Permanently)

這兩個狀態會出現在流覽器重定向時。例如,你使用了類似 bit.ly 的網址縮短服務。這也是它們如何獲知誰點擊了他們鏈結的方法。
302和301對於流覽器來說是非常相似的,但對於搜索引擎爬蟲就有一些差別。打個比方,如果你的網站正在維護,那麼你就會將用戶端流覽器用302重定向到另外一個位址。搜索引擎爬蟲就會在將來重新索引你的頁面。但是如果你使用了301重定向,這就等於你告訴了搜索引擎爬蟲:你的網站已經永久的移動到了新的位址。

500 伺服器錯誤(Internal Server Error)

這個代碼通常會在頁面腳本崩潰時出現。大部分CGI腳本都不會像PHP那樣輸出錯誤資訊給流覽器。如果出現了致命的錯誤,它們只會發送一個500的狀態碼。這時需要查看伺服器錯誤日誌來排錯。

完整的列表

你可以在這裏找到完整的HTTP 狀態碼說明。

HTTP Headers 中的 HTTP請求

現在我們來看一些在HTTP headers中常見的HTTP請求資訊。
所有這些Header資訊都可以在PHP的 $_SERVER 陣列中找到。你也可以用getallheaders() 函數一次性獲取所有的Header資訊。

Host

一個HTTP請求會發送至一個特定的IP位址,但是大部分伺服器都有在同一IP位址下託管多個網站的能力,那麼伺服器必須知道流覽器請求的是哪個功能變數名稱下的資源。

Host: rlog.cn

這只是基本的主機名,包含功能變數名稱和子級功能變數名稱。
在PHP中,可以通過 $_SERVER['HTTP_HOST']$_SERVER['SERVER_NAME'] 來查看。

User-Agent

User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)

這個Header可以攜帶如下幾條資訊:

  • 流覽器名和版本號.
  • 作業系統名和版本號.
  • 默認語言.

這就是某些網站用來收集訪客資訊的一般手段。例如,你可以判斷訪客是否在使用手機訪問你的網站,然後決定是否將他們引導至一個在低解析度下表現良好的移動網站。

在PHP中,可以通過 $_SERVER['HTTP_USER_AGENT'] 來獲取User-Agent

if ( strstr($_SERVER['HTTP_USER_AGENT'],'MSIE 6') ) {
echo "Please stop using IE6!";
}

Accept-Language

Accept-Language: en-us,en;q=0.5

這個資訊可以說明用戶的默認語言設置。如果網站有不同的語言版本,那麼就可以通過這個資訊來重定向用戶的流覽器。
它可以通過逗號分割來攜帶多國語言。第一個會是首選的語言,其他語言會攜帶一個 q 值,來表示用戶對該語言的喜好程度 (0~1)

在PHP中用 $_SERVER["HTTP_ACCEPT_LANGUAGE"] 來獲取這一資訊。

if (substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2) == 'fr') {
header('Location: http://french.mydomain.com');
}

Accept-Encoding

Accept-Encoding: gzip,deflate

大部分的流覽器都支援gzip壓縮,並會把這一資訊報告給伺服器。這時伺服器就會壓縮HTML發送給流覽器。這可以減少近80%的檔案大小,以節省下載時間和頻寬。

在PHP中可以使用 $_SERVER["HTTP_ACCEPT_ENCODING"] 獲取該資訊。
然後調用 ob_gzhandler() 方法時會自動檢測該值,所以你無需手動檢測。

// enables output buffering
// and all output is compressed if the browser supports it
ob_start('ob_gzhandler');

If-Modified-Since

如果一個頁面已經在你的流覽器中被cache,那麼你下次流覽時流覽器將會檢測文檔是否被修改過,那麼它就會發送這樣的Header:

If-Modified-Since: Sat, 28 Nov 2009 06:38:19 GMT

如果自從這個時間以來未被修改過,那麼伺服器將會返回 304 Not Modified ,而且不會再返回內容。流覽器將自動去緩存中讀取內容
在PHP中,可以用 $_SERVER['HTTP_IF_MODIFIED_SINCE'] 來檢測。

// assume $last_modify_time was the last the output was updated
// did the browser send If-Modified-Since header?
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
// if the browser cache matches the modify time
if ($last_modify_time == strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
// send a 304 header, and no content
header("HTTP/1.1 304 Not Modified");
exit;
}
}

還有一個叫 Etag 的HTTP頭資訊,它被用來確定緩存的資訊是否正確,稍後我們將會解釋它。

Cookie

顧名思義,他會送出流覽器中存儲的Cookie資訊給伺服器。

Cookie: PHPSESSID=r2t5uvjq435r4q7ib3vtdjq120; foo=bar

它是用分號分割的一組名值對。Cookie也可以包含session id。
在PHP中,單一的Cookie可以訪問 $_COOKIE 陣列獲得。你可以直接用 $_SESSION array 獲取session變數。如果你需要session id,那麼你可以使用 session_id() 函數代替cookie。

echo $_COOKIE['foo'];
// output: bar
echo $_COOKIE['PHPSESSID'];
// output: r2t5uvjq435r4q7ib3vtdjq120
session_start();
echo session_id();
// output: r2t5uvjq435r4q7ib3vtdjq120

Referer

顧名思義, Header將會包含referring url信息。
例如,我訪問Nettuts+的主頁並點擊了一個鏈結,這個Header資訊將會發送到流覽器:

Referer: http://net.tutsplus.com/

在PHP中,可以通過 $_SERVER['HTTP_REFERER'] 獲取該值。

if (isset($_SERVER['HTTP_REFERER'])) {
$url_info = parse_url($_SERVER['HTTP_REFERER']);
// is the surfer coming from Google?
if ($url_info['host'] == 'www.google.com') {
parse_str($url_info['query'], $vars);
echo "You searched on Google for this keyword: ". $vars['q'];
}
}
// if the referring url was:
// http://www.google.com/search?source=ig&amp;hl=en&amp;rlz=&amp;=&amp;q=http+headers&amp;aq=f&amp;oq=&amp;aqi=g-p1g9
// the output will be:
// You searched on Google for this keyword: http headers

You may have noticed the word “referrer” is misspelled as “referer”. Unfortunately it made into the official HTTP specifications like that and got stuck.

Authorization

當一個頁面需要授權,流覽器就會彈出一個登入視窗,輸入正確的帳號後,流覽器會發送一個HTTP請求,但此時會包含這樣一個Header:

Authorization: Basic bXl1c2VyOm15cGFzcw==

包含在Header的這部分資訊是base64 encoded。例如,base64_decode('bXl1c2VyOm15cGFzcw==') 會被轉化為 myuser:mypass
在PHP中,這個值可以用 $_SERVER['PHP_AUTH_USER'] 和 $_SERVER[‘PHP_AUTH_PW’]` 獲得。
更多細節我們會在WWW-Authenticate部分講解。

HTTP Headers 中的 HTTP回應

現在讓我瞭解一些常見的HTTP Headers中的HTTP回應資訊。
在PHP中,你可以通過 header() 來設置Header回應資訊。PHP已經自動發送了一些必要的Header資訊,如 載入的內容,設置 cookies 等等… 你可以通過 headers_list() 函數看到已發送和將要發送的Header資訊。你也可以使用 headers_sent() 函數來檢查Header資訊是否已經被發送。

Cache-Control

w3.org 的定義是:”The Cache-Control general-header field is used to specify directives which MUST be obeyed by all caching mechanisms along the request/response chain.” 其中”caching mechanisms” 包含一些你ISP可能會用到的 閘道和代理資訊。

例如:

Cache-Control: max-age=3600, public

public 意味著這個回應可以被任何人cache,max-age 則表明了該cache有效的秒數。允許你的網站被cache降大大減少下載時間和帶寬,同時也提高的流覽器的載入速度。
也可以通過設置 no-cache 指令來禁止緩存:

Cache-Control: no-cache

更多詳情請參見w3.org

Content-Type

這個Header包含了文檔的 mime-type 。流覽器將會依據該參數決定如何對文檔進行解析。例如,一個html頁面(或者有html輸出的php頁面)將會返回這樣的東西:

Content-Type: text/html; charset=UTF-8

text 是文檔類型,html 則是文檔子類型。 這個Header還包括了更多資訊,例如 charset
如果是一個圖片,將會發送這樣的回應:

Content-Type: image/gif

流覽器可以通過 mime-type 來決定使用外部程式還是自身擴展來打開該文檔。如下的例子降調用Adobe Reader:

Content-Type: application/pdf

直接載入,Apache通常會自動判斷文檔的mime-type並且添加合適的資訊到Header去。並且大部分流覽器都有一定程度的容錯,在Header未提供或者錯誤提供該資訊的情況下它會去自動檢測mime-type。
你可以在這裏找到一個常用mime-type列表。

在PHP中你可以通過 finfo_file() 來檢測檔的ime-type。

Content-Disposition

這個Header資訊將告訴流覽器打開一個檔下載視窗,而不是試圖解析該回應的內容。例如:

Content-Disposition: attachment; filename="download.zip"

他會導致流覽器出現這樣的對話方塊:

注意,適合它的 Content-Type 頭資訊同時也會被發送

Content-Type: application/zip
Content-Disposition: attachment; filename="download.zip"

Content-Length

當內容將要被傳輸到流覽器時,伺服器可以通過該Header告知流覽器將要傳送檔的大小(bytes)。

Content-Length: 89123

對於檔下載來說這個資訊相當的有用。這就是為什麼流覽器知道下載進度的原因。
例如,這裏我寫了一段虛擬腳本,來模擬一個慢速下載。

// it's a zip file
header('Content-Type: application/zip');
// 1 million bytes (about 1megabyte)
header('Content-Length: 1000000');
// load a download dialogue, and save it as download.zip
header('Content-Disposition: attachment; filename="download.zip"');
// 1000 times 1000 bytes of data
for ($i = 0; $i &lt; 1000; $i++) {
echo str_repeat(".",1000);
// sleep to slow down the download
usleep(50000);
}

結果將會是這樣的:

現在,我將Content-LengthHeader注釋掉:

// it's a zip file
header('Content-Type: application/zip');
// the browser won't know the size
// header('Content-Length: 1000000');
// load a download dialogue, and save it as download.zip
header('Content-Disposition: attachment; filename="download.zip"');
// 1000 times 1000 bytes of data
for ($i = 0; $i &lt; 1000; $i++) {
echo str_repeat(".",1000);
// sleep to slow down the download
usleep(50000);
}

結果就變成了這樣:

這個流覽器只會告訴你已下載了多少,但不會告訴你總共需要下載多少。而且進度條也不會顯示進度。

Etag

這是另一個為緩存而產生的Header資訊。它看起來會是這樣:

Etag: "pub1259380237;gz"

伺服器可能會將該資訊和每個被發送檔一起回應給流覽器。該值可以包含文檔的最後修改日期,檔大小或者檔校驗和。流覽會把它和所接收到的文檔一起緩存。下一次當流覽器再次請求同一檔時將會發送如下的HTTP請求:

If-None-Match: "pub1259380237;gz"

如果所請求的文檔Etag值和它一致,伺服器將會發送304狀態碼,而不是200。並且不返回內容。流覽器此時就會從緩存載入該檔。

Last-Modified

顧名思義,這個Header資訊用GMT格式表明了文檔的最後修改時間:

Last-Modified: Sat, 28 Nov 2009 03:50:37 GMT
$modify_time = filemtime($file);
header("Last-Modified: " . gmdate("D, d M Y H:i:s", $modify_time) . " GMT");

它提供了另一種緩存機制。流覽器可能會發送這樣的請求:

If-Modified-Since: Sat, 28 Nov 2009 06:38:19 GMT

在If-Modified-Since一節我們已經討論過了。

Location

這個Header是用來重定向的。如果回應代碼為 301 或者 302 ,伺服器就必須發送該Header。例如,當你訪問 http://www.nettuts.com 時流覽器就會收到如下的回應:

HTTP/1.x 301 Moved Permanently

Location: http://net.tutsplus.com/

在PHP中你可以通過這種方式對訪客重定向:

header('Location: http://net.tutsplus.com/');

默認會發送302狀態碼,如果你想發送301,就這樣寫:

header('Location: http://net.tutsplus.com/', true, 301);

Set-Cookie

當一個網站需要設置或者更新你流覽的cookie資訊時,它就會使用這樣的Header:

Set-Cookie: skin=noskin; path=/; domain=.amazon.com; expires=Sun, 29-Nov-2009 21:42:28 GMT
Set-Cookie: session-id=120-7333518-8165026; path=/; domain=.amazon.com; expires=Sat Feb 27 08:00:00 2010 GMT

每個cookie會作為單獨的一條Header資訊。注意,通過js設置cookie將不會出現在HTTP頭中。

在PHP中,你可以通過 setcookie() 函數來設置cookie,PHP會發送合適的HTTP 頭。

setcookie("TestCookie", "foobar");

它會發送這樣的頭資訊:

Set-Cookie: TestCookie=foobar

如果未指定到期時間,cookie就會在流覽器關閉後被刪除。

WWW-Authenticate

一個網站可能會通過HTTP發送這個Header資訊來驗證用戶。當流覽器看到Header有這個回應時就會打開一個彈出窗。

WWW-Authenticate: Basic realm="Restricted Area"

它會看起來像這樣:

在PHP手冊的一章中就有一段簡單的代碼演示了如果用PHP做這樣的事情:

if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'Text to send if user hits Cancel button';
exit;
} 
else {
echo 
"Hello {$_SERVER['PHP_AUTH_USER']}.";
echo 
"You entered {$_SERVER['PHP_AUTH_PW']} as your password.";
}

Content-Encoding

這個Header通常會在返回內容被壓縮時設置。


2014-05-09

By ZEN麽了啊

用Highcharts制作特殊甘特图

关于Highcharts

Highcharts是一个纯正的JS图表库,内置了十几种图表,支持数组操作,从兼容性、可扩展性包括文档来看目前是最强大的免费图表库。研究了一段时间,不需要很多编程技巧也可以轻松做出动态图表来。

特殊甘特图的需求

手里有一个关于中国40年时间内真实通货膨胀系数的选题。除了需要用曲线图把通胀指数绘制出来,标示出40年间中国经济政策的主要管理者的人选变化,用甘特图来表示历届国家总理、央行行长、财政部长的人选和任期。

效果图(Excel制作):
gantt

用Highcharts来实现整个图表,有两个难点,一是任期变化的甘特图在Highcharts中并没有现成的例子,二是需要将上面的曲线图与下面的甘特图联动mouse event和share tooltips。
翻遍了官方论坛和stackoverflow,最后确定还是写一组函数来处理任期数据,比把任期数据写进data series更科学。

效果和代码如下:

暂时还没解决上下两部分联动,努力学习中。


2014-04-27

By Asiapan Talks

《歷史與思想》三十八年(余英時)

《歷史與思想》面世已整整三十八年。這是我在台灣刊行的第一部論文集,而我和聯經的文字因緣也從此書開始。這是我個人出版史上一件最值得珍惜的大事。現在本書重排新版,我願意借機對這段往事略作回顧,以為紀念。

在我的記憶中留下印象最深的是關於本書第一篇論文:〈反智論與中國政治傳統〉。這篇長文是應香港《明報月刊》的特約而撰寫的,當時尚未刊出。但我將原稿副本與其他已刊論文一併收入《歷史與思想》中,直接寄給聯經發行人劉國瑞先生。不料國瑞先生對此稿特加賞識,竟提前送交《聯合報.副刊》刊佈,連載了很多天。更意外的是此文無意中觸動了台灣學術和文化界的政治神經,因而引起相當廣泛而持續的強烈反響。這一反響在當時充滿着反諷的意味,因為最初我寫此文,完全針對着大陸的「文革」而發。我想揭示的是:造成「文革」的政治勢力雖然在意識型態和組織方式上取法於現代西方的極權系統,但是在實際政治操作上則繼承了許多傳統君權的負面作風,而集中表現在對於知識人的敵視和迫害以及對理性與知識的輕鄙上面。題目中特標「反智論」,我的立論所指是相當明顯的。在撰寫過程中,我完全沒有聯想到台灣的政治狀態。也許是因為當時台灣的思想與言論自由也受到了嚴重的限制,這才引起不少讀者對於這篇文字的共鳴。就我個人而言,這真是一個絕對意想不到的後果。

反響當然不可能一面倒,反對和批評同樣大有人在,而且筆下也充滿着激情。但是使我最感遺憾的則是此文竟給先師錢先生(賓四)帶來了困擾。〈反智論〉在《聯合報.副刊》上刊出不久,台北的同門友人便先後來信告訴我,錢先生認為我仍然盲從梁啟超以來的流行說法,以「帝王專制」四字來抹殺中國的政治傳統,持論過於偏激。我聽到這些轉述的批評之後,心中極為不安。細閱原稿,也發現其中確有立言欠妥,足以引起誤讀的地方。因此我立刻進行了兩個系列的補過工作:第一是修改舊稿,第二是增寫新篇。我手頭已沒有初登在報上的舊文本,不能與書中的改本互校。不過我仍清楚地記得:全文結尾處我作了一個基本的變更。舊本引譚嗣同《仁學》中的話:

二千年來之政,秦政也,皆大盜也;二千年來之學,荀學也,皆鄉愿也。惟大盜利用鄉愿,惟鄉愿工媚大盜。

這樣斬釘截鐵的否定論斷雖然讀起來十分動人有力,但究竟經不起歷史分析。因此我改用朱熹〈答陳同甫〉中語以代之並引申其言曰:

二千三百年之間,只是架漏牽補過了時日。堯、舜、三王、周公、孔子所傳之道,未嘗一日得行於天地之間也。

為了進一步澄清〈反智論〉的旨趣,我則在一九七六年先後補寫了〈「君尊臣卑」下的君權與相權〉和〈唐、宋、明三帝老子注中之治術發微〉。當時我還在哈佛任教,所以這兩篇文字都曾得益於先師楊聯陞教授的商榷。

錢先生是否曾寓目我的補過之作,不得而知。但他還是親自寫了一篇萬言的〈皇帝與士人〉刊載在一九七六年七月十日和十一日的《聯合報》上,對我的原文進行了不指名的駁斥。最後我必須說明,先師此舉完全是就學論學,對於我個人則採取了寬恕的態度。在我們以後無數次的歡聚中,他從無一語及此,我也沒有向他作出任何解釋,師生之間的感情絲毫未受學術異同的影響。(按:錢先生此文後來易名為〈帝王與士人〉,收在《晚學盲言(上)》,《錢賓四先生全集》本,台北:聯經,一九九八,頁七八五—七九九。)

本書對於我自己來說,還有兩點特別值得紀念之處。第一是這部選集將我的治學取向相當準確地呈現了出來,例如中國文、史、哲之間的相互關聯以及中、西文化與思想之間異同的比較正是全書的重點所在。不但如此,以具體的研究論題而言,當時集中所收的少數論文後來多發展成為篇幅很大的專書,如《論戴震與章學誠》、《紅樓夢的兩個世界》和《陳寅恪晚年詩文釋證》便是顯例。

第二是我中年以後改用中文為我個人學術著作的主要媒介,本書是最早的一個見證。本來我在美國教書和研究,著作自然應該用英文刊佈。但是一九七一年夏天初訪日本和台北,並重回香港母校(新亞書院)會晤師友,我發生了一個很深切的感觸。我發現我的英文專著和學報論文,在整個東方學界的同行中,根本無人問津。尤其是在日本京都大學的人文研究所訪談之後,這一印象更是牢牢地銘刻於心。我已萌生了用中文著述的念頭,希望我的研究成果可以傳佈到西方漢學的小圈子以外(當時西方漢學遠不及今天這樣流行)。恰巧一九七三至七五兩年,我回到香港工作,重新運用中文變成了理所當然之事。這是我的幸運。從那時起,我便決定先用中文寫出比較詳盡的研究報告,然後再以英文另撰簡要的論文。因為我的教研崗位畢竟是在美國,發表英文論著仍是我義不容辭的專業任務之一。我在初版〈自序〉中曾指出,本書百分之七十以上都是在香港兩年的作品。現在我要補充一句:這些作品正是我為了轉換書寫媒介而特意撰寫的。所以《歷史與思想》在我個人的學術生命中具有極不尋常的意義。

三十八年來本書不斷重印,是我的著作中流傳最廣而且持續最久的一部。讓我在這裏對於讀者的長期支持表達我最誠摯的感謝!


2014-04-27

By Asiapan Talks

不捨(林青霞)

林青霞與董橋合影

林青霞與董橋合影

依依不捨,依依不捨。二○一○年的六月四號,我這株小草以一篇〈仙人〉開始,在《蘋果樹下》和許多好朋友及一些傑出的作家,在大家長董橋的呵護下各「書」己見。

二○一四年的四月二十七日,是大家分手道別的日子,《蘋果樹下》這版將從此告別《蘋果日報》。董橋說:「你畢業了,可以戴方帽子了。」直到今天我都沒搞懂作者跟報社的關係,每次寫完稿請大家長指點後,他都說:「這個禮拜天登。」我就順理成章的上了《蘋果樹下》,到禮拜天刊登的日子又興高采烈的買十幾份寄給各方好友。

《蘋果樹下》就像一個大家庭,裏面的作家都是家庭的一分子,他們跟你分享他們的思想,他們所知道的人、事、情。還記得邵綃紅寫抗戰時期美國女作家項美麗冒生命危險幫她父親邵洵美搬家,在大卡車從淪陷區到上海租借地中間的橋上,被日本兵攔截盤問的驚心動魄畫面。還記得楊凡寫張大千送給張夫人的《憶遠圖》,上面題的字「雲山萬重,寸心千里」。還記得顧媚寫畫家趙無極的前妻朱纓自殺身亡前給她的最後一封信,只有零亂的七個字「一片冰心在玉壺」。還記得金聖華寫傅雷曾說的「赤子之心,永遠不老」,文中並提到文革初期傅雷夫婦不堪受辱,以死明志,雙雙自盡前還留下現鈔五十三點三元作為他們的火葬費。還記得…。這許許多多的記憶豐富了我的生命。

董橋經常寫他收藏的文玩字畫、舊書裝幀,文章不分段落,我總是一口氣讀完,雖然不容易懂,有時重看一、兩次,每看一次都有新的得着。

被退過一次稿才知道大家長不是來者不拒,有一篇以擬人法來寫婚紗,用婚紗做第一人稱,題目是〈婚紗歷險紀〉。董橋說good try但吃力不討好,從此〈婚紗歷險紀〉就被打入冷宮。好友怕我氣餒安慰我:「沒有一個作家不被退稿的,這表示你是個作家。」我不但不氣餒反而特別高興,這表示董橋以前對我文章的讚賞是真的,同時也免了我獻醜。我回了一封簡訊:「我知道你會看着我的。謝謝!」他寫道:「不過是一篇文章而已,偶然一篇不滿意,改寫一篇不就完了。對不?」大家長以為我會失望,怕打擊到我的信心,其實我倒覺得被退稿的經驗蠻好。

在《蘋果樹下》的大家庭裏,大家長永遠在右上角,小草永遠在左上角,楊凡永遠在左邊中間佔據一大片版位,把所有作家都擠得周圍散去,我取笑他是大肚子。

樹下消磨了不少溫馨愉快的日子,沒想到現在是互道珍重各奔前程的時候。

後會有期。


← Before After →