顯示具有 http 標籤的文章。 顯示所有文章
顯示具有 http 標籤的文章。 顯示所有文章

2015年9月29日 星期二

觀察 HTTP/HTTPS 傳輸的內容以及防範 man-in-the-middle attack

看到許多強者推薦 mitmproxy (MITM 是 man-in-the-middle 的意思), 試用了一下的確超好用的。它用 man-in-the-middle attack 的方式觀察 (甚至更改) HTTP/HTTPS 傳輸的內容。

How To: Use mitmproxy to read and modify HTTPS traffic 介紹原理和用法, 設定很簡單。

官方文件也很清楚, 可交叉參考:

這裡摘要一下 mitmproxy 作為 Transparent Proxy 的方式和原理:

  1. 備好一台機器 R 作為觀察目標裝置 D (如手機) 的 gateway, 這樣就完成 man-in-the-middle 的設置。
  2. 設定 R 的 firewall, 重導往外連往 port 80 和 443 的連線到在 R 上執行的 mitmproxy。
  3. 在 D 上安裝 mitmproxy 的 certificate, 這樣 D 會相信 mitmproxy 幫自己簽的 certificate。

測試 Android 手機在 MITM 情況下 App 的反應

我是用 ASUS Zenfone 2, 不過 App 的測試結果應該和手機無關。

結果如下:

  • Android OS 有出現訊息說網路可能被監控了, 但清掉訊息後就沒再出現了, 使用者應該不會注意到。
  • 沒裝 mitmproxy certificate 的情況, Google Play 會出現連線錯誤; 裝 certificate 後可以用 Google Play 安裝 app。
  • 沒裝 mitmproxy certificate 的情況, Chrome 會警告有問題; 有裝 certificate 會放行。
  • 沒裝 mitmproxy certificate 的情況, Gmail 會如常運作, 看不到 HTTPS 傳的內容。裝 certificate 的後可以看到 HTTPS 傳的內容。雖然 Gmail 用自己訂的資料格式, 在搜尋信件時, 可看到搜尋字、回傳信件的標題、內文前面幾句的文字。
  • 另外在有裝 mitmproxy certificate 的情況測了 Hangouts, Facebook, Messenger, Skype, Line。它們的主要傳輸內容都不是用 HTTPS, 改天再用 SSLsplit 看看它們傳什麼。
    • Line 會說 Wi-Fi 有問題無法用需要連線的功能 (訊息、電話、桌機 QR Code 登入), 剩下的 App 都可以用。
    • Facebook 有部份內容用 HTTPS 傳, 並且用 JSON 作為內文編碼。
    • Facebook 和 Messenger 有時候會顯示連線問題, 但放一陣子又通了, 也許它們有偵測網路情況用不同方式連線吧。
    • Hangouts 意外地都沒有安全警告, 有沒有裝 mitmproxy certificate 都一樣, 可以正常使用。

其它 Web Proxy

這些也是很有名的 Web Proxy, 有看到朋友推薦。不確定作為 transparent proxy 的效果如何, 至少監控本機的訊息應該滿方便的, 留著備忘。

防範 man-in-the-middle attack

聽 Scott 提到可用 Certificate Pinning 防 MITM attack。原理是只相信自己指定的 certificate (或幫你簽 certificate 的 certificate), 這樣就會忽視攻擊者裝到作業系統的 root CA。當 SSL handshake 的 certificate 被替換的時候, 會變成 handshake error。Certificate Pinning 似乎有些不同的作法, 先記住關鍵字, 之後再來研究。

另一個好像可行但可能有問題的解法是不用 OS 提供的 certificate, 直接用自己包在 app 裡的 certificate (放 server 用的 certificate 或簽它的 CA 的 certificate, 意思一樣)。雖然這樣不用擔心裝置被裝了有問題的 root certificate, 但和 self-signed certificate 一樣, 需要處理另一個問題 (原本的問題): 如何確定 app 內裝了正確的 certificate? 這個難題原本是由 OS 處理, 假設 OS 沒安裝有問題的 root certificate, 所以可以用它的 root certificate。

2015-10-01 更新

經 Victor 告知, 發現 Gmail 應該有檢查 certificate, 我又測了一下, 並更正內文。

Gmail 和 Google Play 有檢查 certificate, 只是使用者從系統刪除信任的 certificate 時, app 沒有同步更新, 仍會相信這個 certificate。另一方面, Chrome 有同步更新。

HTTP library 支援 gzip response body 的情況

為了節省 HTTP response 的傳輸量, HTTP 有提供 request header Accept-Encoding: gzip 要求 server 「可以的話, 用 gzip 壓縮 body 再傳回來」, 還有 response header Content-Encoding: gzip 告知 client 「response body 有用 gzip 壓縮」。

在這樣的規定下, 推測 HTTP library 可能會有兩種實作方式:

  • 不處理。使用者自己加 request header "Accept-Encoding: gzip", 然後自己解 response body
  • 有個 flag 開啟後會自動處理。自動加上 "Accept-Encoding: gzip", 然後自動解 response body 並且移掉 response header "Content-Encoding: gzip"。另外要註明 API 取得的 content length 是否會受 gzip 影響, 結果如何變化。

不過實際情況是:

  • iOS NSURLConnection 文件沒有提到這件事, 但是會自動加 request header 並自動解 response body, 不過沒有移掉 response header "Content-Encoding: gzip" (Ref., 實測結果確實如此, 不需手動加 Accept-Encoding: gzip 就有作用)
  • Android HttpURLConnection 指明預設就會作, 並會清掉 Content-Encoding 和 Content-Length。可以用 Accept-Encoding: identity 覆寫預設值 (Ref.)。
  • curl 開啟 flag CURLOPT_ACCEPT_ENCODING 後會支援, 也是自動加 request header 並自動解 response body, 不過沒有移掉 response header "Content-Encoding: gzip" (Ref.)

結果只有 Android 的實作符合預期。使用前還是要先測過才準啊。

附帶一提, http://httpbin.org/ 會回傳 request/response header, 用來測試 HTTP library 很方便。或是用 mitmproxy 作 transparent proxy 也可以, 相對之下費工一點, 不過看得最清楚。

2015年9月16日 星期三

HTTP POST 使用的編碼格式

三不五時會用到相關的東西, 所以花了點時間查清楚, 本篇重點是參考資料。

用 HTTP 的 POST 的時候, 參數會放在 request body, 然後用 HTTP header Content-Type 決定 request body 編碼方式。

常用的格式 ( Content-Type) 有兩種:

  • application/x-www-form-urlencoded: HTML form 的預設值。和 GET 在 URL 後面帶的參數差不多, 用 URL encoding 編碼 (主要差異是空白字元用 '+' 取代), 適合用在簡單的資料。
  • multipart/form-data: 適合用在 binary data, 上傳檔案時要改用這個, 比較省空間 (檔案內容不需另外編碼)。

application/x-www-form-urlencoded 用 URL encoding 編碼, 遇到大量 binary 資料時, 資料會變大很多 (可能會到三倍)。若先用 base64 [*1] 編碼再套 URL encoding 會好一些 (變成 4/3 倍), 但還是沒有 multipart/form-data 省。

除了上述的格式也可以用 application/json 或其它格式, 不過得看 web server 是否支援。JSON 不支援 binary data, 所以有 binary data 時要先用 base64 編碼, binary 資料一樣會變 4/3 倍大。雖然 base64 會增加 1/3 倍的資料量, 但它簡單易用, 我個人滿喜歡 base64 的設計。

若目的是要壓縮上傳的文字資料, 除了 application layer 自己壓縮資料外, 也可以在 transport layer 作: 使用 HTTP header Content-Encoding: gzip 然後用 gzip 壓縮 request body [*2]。雖然 response 很常用 Content-Encoding: gzip, 但 request 很少這麼用的樣子, 需要改 web server 設定才能作用。如 apache2: 需另外設定 Input Decompression。

備註

1. 嚴格來說, 要用 base64url (最後兩個 byte 不同), 對 URL encoding 比較安全。Google API 有用這個, 轉換的時候要留意。

2. 另一個選擇是用 Transfer-Encoding: gzip, 這個作法語意比較正確, 但一來實作不如 Content-Encoding: gzip 常見, 二來 Content-Encoding 是 end-to-end, 不用擔心 Proxy 改變內容, 所以還是用 Content-Encoding 較好。

參考資料

POST Content-Type 相關:

Content-Encoding 和 Transfer-Encoding 相關:

其它:

2015年8月10日 星期一

用 grequests 作 stress test

作了一個 C10K 的 server 後, 還要作 stress test 才能驗證 server 真的挺得住 C10K 的連線。試了幾套工具, 最後覺得 grequests 最好用 (用 gevent monkey patch 的 requests), 用法就這麼簡單:

import grequests

def exception_handler(request, exception):
    print "Request failed: %s" % exception

url = 'SERVER_URL'
rs = [grequests.post(url, data={...})
      for i in range(10000)]
rss = grequests.map(rs, size=1000, exception_handler=exception_handler)

這樣就寫好一個會同時產生 1k 個連線, 總共產生 10k 個連線的 client。因為是直接寫程式, 可以自訂各式各樣的內容, 測得出真正的效果, 不會被簡單的 cache 消掉絕大多數的 requeests。若要改用 get 就用 grequests.get(url) 產生 request, 詳細用法見 requests。

幾個測試情境和作法:

  • Connection bound: 提升 size 數, 或多跑幾個 client
  • CPU bound: 同上, 故意使用耗 CPU 的 API
  • Memory bound: 依 API 的性質, 產生 1MB 或更大的資料, 由 data=... 傳過去

grequests.map() 不設 size 的話, 會盡可能產生最多連線, client 有可能自己先爆掉。依自己的網路環境和 server 狀況, 可試看看不同的值。

執行前記得先用 ulimit -n 提升 open file number, 不過要用 root 才能提升, 作法如下:

$ sudo bash
$ ulimit -n 50000
$ su YOUR_ID

這樣可以用一般帳號測試, 但保有 max open file number = 50k。

以下是我試過但不管用的方法, 憑印象寫下放棄使用的原因 (不見得是工具不好):

  • ab: 很久以前用過覺得不夠順手, 查到文章推薦用 httperf, 這回我就跳過 ab 了。
  • httperf: 雖然有 --wsesslog 可以讀文字檔產生不同內容, 還是不夠彈性。另外有遇到 open too many files 的問題, 似乎要編程式才能提升 limit, 就懶得再試了
  • loadtest: 看起來不錯, 可用 -R 指定 script自訂程式, 由 loadtest 幫忙處理其它事, 不過沒有試成功, 就試別套了
  • nodeload: 還不錯用, 可以自訂程式, 執行後還會自動產生報表, 附一個 local http server 跑在 port 8000 可以即時看報表變化。不過量大了以後會炸, 有些 bug, 作者又說沒在維護了, 就放棄它了。
  • Btw, 用這些工具的時候, 都要自己設好 http header 才可以使用 POST, 不如 requests 直覺。

2015年8月8日 星期六

http 1.1 pipeline 失敗的原因

網路連線的基本指標是 latency 和 throughput。用 pipeline 同時提升 latency 和 throughput 還滿直覺的, 但在 http 1.1 上卻意外地以失敗收場, 所以花了點時間查一下為什麼。

What are the disadvantage(s) of using HTTP pipelining? 提到幾個問題:

  • http 1.x 是 stateless 的 protocol, client 沒有能力知道 request 之間的 order (沒有流水號)。
  • 因此, server 必須依 request order 回傳 response, 但是有些 server 實作錯了。
  • 即使 server 作對了, 因為要照 order 回傳 response, server 可能需要暫存大量回應, 有被 DoS 的風險。
  • 因為 response 要依順序回傳, client 有 head-of-line blocking 的風險, 比方說剛好先要一個大圖檔再要一個 JS 檔, 結果 JS 檔因此比較慢收到, 讓網頁比較慢取得較重要的檔案。
  • client 端的實作比想像中複雜, client 要先偵測 server 是否支援 pipeline 接著才能開始用 pipeline, 因此有些延遲。再者, 因為 client 不會真的只開一個連線, client 還需考慮如何分配 request 到不同連線作 pipeline。
  • 因為 http 1.x 過往的限制, 網站早就依 domain 拆開回資料, client 也會同時發多個連線加速。結果是即使 pipeline 都作對了, 可能也沒賺到什麼。
  • 此外, server 和 client 中間的 proxy 也可能不支援 (如擔心 DoS) 或是作錯, 又降低了可行的機率。

如果 http 裡面有含流水號, 由 http client library 自己匯合好再依順序回傳資料給上層程式 (像 TCP 那樣), 問題會少一些。設計協定時決定由 server 或 client 處理還挺關鍵的, 一但作錯決定, 協定普及後就很難改了。

HTTP/2 或 SPDY 直接支援 multiplexing, 在一開始就作對了 pipeline。剩下的問題只有使用 TCP 造成 head-of-line blocking (block 的層級不同, 不像 HTTP/1.1 pipeline 那樣嚴重), 但這個得繞過 TCP 才能解決, 也因此 Chrome 又作了 QUIC。QUIC 看來挺有趣的, 不過不像 HTTP2 看來快要普及了 (從 Chrome 6 開始用 SPDY, 這也花了四年以上的時間...)。

Btw, What are the disadvantage(s) of using HTTP pipelining? 附了許多有趣的連結, 有興趣讀讀可以了解更多細節。

2012年10月21日 星期日

https 到 http 沒有 referrer

筆記一下讀到 BobChao the Blogger: HTTPS 的 referrer 狀況筆記 的心得和延伸想法。

近年來強調安全瀏覽, 大網站倡導全程使用 https 連線, 但是從 https 連到 http 時不會傳 referrer, 原因應該是 https 全程加密, 若從 https 連到 http 時有加 referrer, 會讓原本外部不知道的網址流出去。比方我先點 https://a.b.c/, 然後點網頁內連結到 https:/a.b.c/secret.html, 再點連結到外部的 http://x.y.z/, 這樣 x.y.z 網站就會得知 https:/a.b.c/secret.html。

但令我不解的是 https 連到不同網域 https 時, 還是會送 referrer, 所以上述的推論要修正成: 防範的對像不是 x.y.z, 而是其它用 http 的網站 (??) 以及竊聽封包的人。全程使用 https 不會被竊聽到網址, 而最後一步從 https 到 http 時, 若有設 referrer, 就會被竊聽得知。這是我目前想得到唯一合理的解釋了, 雖說我還是覺得沒什麼道理, 挺多 a.b.c 要連到別的網站時, 即使有 https 可用, 仍可選擇 http 以避免送出 referrer, 但這顯然和現今大家關心的方向相反。

於是, 為了兼顧安全性, 以及網站能正確統計 referrer, Google 和 Facebook 會先跳到內部同網址的 http 連結, 再轉到外面 http 的連結。雖然會多加一個 click 增加內部成本, 但為了能讓其它網站了解本站 (Google / Facebook) 為他們帶來的可觀流量, 仍是值得花的代價。

2012年9月1日 星期六

API 使用 http 做為底層 protocol 的好處

以前對 http-based 的 API 一直有疑問 (免責聲明: 我沒有弄懂 RESTful 和這點的具體差別), 為什麼要綁在 http 這個不太相關的協定上? 尤其是在試用 Solr 後, 發覺因為使用 http 提供 api 的緣故, 即使是 localhost 連線做很簡單的查詢, 也有一定的 latency 成本。

不週在多了一些 network programming 經驗後, 對此有了不同想法。

首先, network programming 很痛苦, 一開始要處理兩個問題: 第一點是在 streaming-based 的資料上, 提供 datagram 的概念, 這樣才有辦法傳遞和接受參數。streaming 不同已有完整資料的檔案一般, 資料回傳時間不一定, 每次讀回的 byte 不一定, 提供 datagram-like 的介面, 要費點心思。

第二個問題是, 要將資料 serialization, 這件事本身也很苦, 若要求效率, 要用 binary 格式, 實作苦除錯更苦; 若用 text 表示 (如 JSON), 也許有一天會有效能問題, 若需要傳 binary file, overhead 太大。

第一和第二問題在面對多種語言的 client 時 (比方 PC 上有人想用 Python 或 C++, mobile 上有 Objective C 和 Java), 實作成本更高。

解決這些基本問題後, 若幸運地服務順暢, 用戶成長個 10 倍, 有什麼方法可以擋住 10 倍的使用量? 也就是 scalability 的問題, 這又是一串的麻煩開始。10 倍擋住後, 變 100 倍怎麼辦?

反之, 使用 http-based solution, 以上統統不是問題。有成熟的 web server 包含 scalability 都解決了, http client lib 也是遍地開花, 沒有找不到套件的問題, 只有太多種版本不知選那個好的困擾 (看看 python 那票 urllib, urllib2, httplib, httplib2 以及其它各家 3rd party 實作)。更可以直接用 browser 試用 API, 連寫程式都不用。

照這樣來看, 因為綁定 http, 而「稍微」多些 latency, 似乎不是什麼不得了的負擔。

ps.

  • kcwu 補充說明使用 http 還有減少被阻擋的機率, 有許多地方會擋 port 80、443 以外的 port。此外, http 有許多現成工具提供 encryption, compression, cache, proxy, load balance, authentication 等功能。
  • Socket Programming HOWTO 值得一讀, 簡要地描述 network programming 的基本知識以及實作會遇到的問題。

2012年8月7日 星期二

cookie 雜記

讀完這兩篇就差不多通了

做實驗的話, 配合 Firefox View Cookies 很方便。

基本知識

  • http 本身是 stateless, 理由大概是利於實作, server overhead 也小。若 server 想維持使用者登入狀態、個人偏好、追蹤瀏覽過程等有狀態的功能, 得透過別的方式, 也就是 cookie
  • cookie 是 http header 裡的其中一個欄位, server 透過 http response header 裡的 Set-Cookie 要求 client 記下資訊; client 之後看到 URI 符合 domain + path 時, 會在 request header 裡加上 Cookie 傳送當初用 Set-Cookie 存下的 key-value 回去
  • cookie 裡有個 expire 欄位表示什麼時候失效。有設這個欄位稱作 persistent cookie, 沒設的話稱作 session cookie, 表示關掉 browser 時, browser 要自行清除它

restore session 會一併回復 session cookie

  • Chrome 和 Firefox 在使用 "restore session" 的功能時, 不只會回復關閉時的頁面, session cookie 也會一併復原, 這是符合此項需求的設計, 不是 bug, 要 100% 重現關閉時的狀態
  • facebook 登入框下面有個 "keep me logged in", 還有 Google account 登入有個 "stay signed in", 有勾的時候會用 persistent cookie 記錄登入資訊, 沒勾時會用 session cookie。但若平時用 Chrome / Firefox 有開啟回復關閉時的頁面, 勾不勾都沒差

third-party cookie 和廣告商

  • 對於 domain A 來說, 可能嵌入其它網站的圖片、iframe 等, 這些物件也會有 cookie, 同時送出 http request 取得這些物件時, 會帶有 Referer 欄位指向目前網址 (即 domain A 下的某一 URL)。這類非 domain A 的 cookie, 稱為 third-party cookie
  • 因此, 網站加速技巧之一, 是將圖片放到另一個 domain, 不只可以增加 browser 同時取得檔案的連線數, 也可以避免不必要的 cookie 傳送
  • 反過來說, 如 Facebook、DoubleClick 這類會在很多網站嵌入他們物件的網站, 可故意放 cookie 來得知使用者更多資料, 因為使用者每到一個有嵌入網站 B 物件的網站, 都會送出目前所有搜集到網站 B 的 Cookie 資訊出去, 同時帶有目前網站網址 (透過 Referer 欄位)

2011年12月9日 星期五

Web basic access authentication

之前都沒注意到, 原來用 apache2 做使用者身份認證 (如使用 htaccess) 時, 跳出來詢問使用者名稱和密碼的對話框並不是網頁, 而是 client 軟體提供的輸入框。

《Basic access authentication》對此有詳細說明, 或用 Firefox 的 HttpFox 觀察 http header request 和 response 也不錯。幾個重點:

  • basic authentication 的傳輸沒有加密, 最好在 SSL 下使用
  • server 發覺沒通過身份認證時會傳 401 並在 header 裡附上 WWW-Authenticate: Basic ... 的訊息
  • client 送出 Authorization: Basic ... 的訊息, 其中 "..." 是 base64(USERNAME + ":" + PASSWORD)
  • 除關掉瀏覽器外, 沒有明確的方式「登出」這種登入方式

2010年11月6日 星期六

Http connection 相關基本知識

HttpKeepAlive 提了些有用的知識:
  • 在 Http 1.0 裡, server 和 client 都需要明確送出 "Connection: Keep-Alive" 才會有 keep-alive。
  • Http 1.1 預設使用 keep-alive, 不需另外送 "Connection: Keep-Alive"。若要取消 keep-alive, 要明確送出 "Connection: close"。
  • 即使有 keep-alive, Http 仍是 stateless protocol。keep-alive 可以擔保的只有減少重建 connection 的負擔。
Python 的 httplib2 不需設定直接支援 keep-alive。trace code 後看到 Http 物件有個 self.connections: key 是 scheme (HTTP 或 HTTPS) 和 HOST、PORT, value 是 connection 物件。

用 netstat 可明確看出是否有重覆使用 connection。在有 keep-alive 的情況下, 開關 connection 連個 1000 次後, 若 "netstat | grep PORT | wc -l" 顯示 1000, 表示沒有重用到 connection; 反之, 顯示 1 表示有重覆使用。不確定在沒有 keep-alive 的情況下, 是不是也會顯示 1。或是直接到 httplib2、httplib 裡, 在 connect 的函式裡塞 log, 更能確保是否只有連線一次。

用 POST 的情況下, 新版 httplib2 比 0.6.0 快很多

httplib2 剛好在 0.6.0 後沒幾版, 修正了 Issue 91: Slow performance with multiple requests over keep-alive HTTP connection。用同一 connection 持續用 POST 傳資料的情況下, 速度有大幅差距。從 patch 提供的簡單測試 script 可看出快了 100 倍 (應該說, 原本慢得不合理)。留言裡有提到這個解法仍有缺點, 我不懂 networking, 沒仔細研究留言提到的問題的影響層面, 在我的環境下測試有 40 倍的差距。

另外詭異的一點是, 我用 POST 竟然比用 GET 快, 再來看看自己那裡寫錯了吧。還是同樣的 "get" 操作, GET 不見得比 POST 快啊?

2010年6月3日 星期四

在 http response header 裡設 expire 以減少 request 數量

High Performance Web Sites: Rule 3 - Add an Expires Header 提到可以在 http response header 裡設 expire time 減少 client 送出 http request。藉由 last modified time 和 etag 只能省去 client 重下載檔案的時間, client 仍會送出 http request。若一個頁面載入十個檔案 (JavaScript、CSS、圖檔等), client 仍會送出十個 http request。別小看 http request 的時間, 數量一多若有其中一兩個被拖到, 網頁就會拖慢。

Apache 設定 expire 的方式很簡單:
  1. a2enmod expires  # enable mod_expires
  2. 編輯 /etc/apache/conf.d/cache.conf (檔名隨便取):

    ExpiresActive On
    ExpiresByType text/css "access plus 4 weeks"
    ExpiresByType text/js "access plus 4 weeks"
    ExpiresByType image/gif "access plus 4 weeks"
    ExpiresByType image/jpeg "access plus 4 weeks"
    ExpiresByType image/png "access plus 4 weeks"
    這樣會把 CSS、JavaScript 和常用圖檔設成使用者拿到檔案的四週後才會過期。
也可以設成依檔案修改時間來設過期時間, 而不是使用者拿到的時間, 參見 mod_expires 看更詳細的說明。

但設了 expire time 後帶出一個新問題, 萬一 server 端在 expire time 前有更新檔案怎麼辦? 在該頁的留言裡有討論幾種解法的優缺點:
  1. 加版本編號到檔名裡, 如 yahoo_2.0.6.js, Yahoo 是這麼做的。缺點是要改 code (HTML header、CSS 內文等), 也得想想怎麼和版本控制整合, 變成每次 deployment 就得改檔名。
  2. 加上 query string, 像是 base.css?v=CREATED_TIMESTAMP。缺點是有些 CDN 業者和 Proxy 不支援靜態檔案的 query string, 若它們忽略 query string, 靜態檔案就不會被更新了。也有人提到 HTTP 1.1 規定有 query string 的檔案不能被 cache, 雖說大部份瀏覽器仍會 cache。但也有人提到 HTTP 1.1 規定有送出 expire time 就能被 cache。無論如何, CDN 業者和 Proxy 的問題較頭大。
  3. 加上 virtual URL, 像是 /media/css/base.css 變成 /media/VERSION/css/base.css, 這樣就無 CDN、Proxy 的問題。不過不知道要怎麼在 web server 做對應的處置較好, 設 alias 忽略中間那段似乎是最簡單的解法? 即設個 alias 將 /media/VERSION/css/base.css 指到 /media/css/base.css。這樣 CSS 內用相對路徑取圖的話, 應該也不用改 CSS code。修改幅度最小。
附帶一提, 檢測用的相關工具:
  • Firefox 的 YSlow 會查那些靜態檔案沒設 expire time。
  • Firefox 的 Live HTTP Headers 會秀出目前連線的所有 http connection 的 request 和 response headers。可以用來查看 Expire 是否有被設對, 還有 client 是否真的沒發出 request。
  • Chrome 的 Speed Tracer 可以秀出頁面載入所有檔案的開始時間, 包含 http request、response 開始和結束的時間。也有 header 內的訊息。多種願望, 一次滿足!!
瀏覽器對待 expire time 的行為如下 (試出來的):
  • Firefox 按重新整理就不會理會 expire time, 在網址列按 Enter 重讀會參考 expire time。
  • Chrome 按重新整理或在網址列按 Enter 重讀都不會理會 expire time, 只有在網頁內點選的連結才會參考 expire time。

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...