文章加密

;

2019年8月9日 星期五

request line, status line, HTTP header, content, HTTP狀態碼(status code) ; HTTP Cache 機制 ; PHP下載檔案,簡單四行程式碼!

之所以session可以辨別不同的user,是因為browser與server建立了session_id

http://www.manongjc.com/article/1260.html

PHP下載檔案,簡單四行程式碼!

http://hatsukiakio.blogspot.com/2009/08/php-header.html

header('Content-type:application/force-download'); //告訴瀏覽器 為下載 
header('Content-Transfer-Encoding: Binary'); //編碼方式
header('Content-Disposition:attachment;filename='.$filename); //檔名
@readfile($filename);

HTTP : HyperText Transfer Protocol  超文本傳輸協議

https://nkongkimo.wordpress.com/2010/04/28/http-header%E5%85%A5%E9%96%80/

整個www都在使用這種協定,幾乎你在流覽器裏看到的大部分內容都是通過http協定來傳輸的
它承載了關於用戶端流覽器,請求頁面,伺服器等相關的資訊

Q:什麼是HTTP?
A:
被設計來讓瀏覽器和伺服器進行溝通的協定,但也可做其他用途。HTTP 遵循標準客戶端—伺服器模式,由客戶端連線以發送請求,然後等待接收回應。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,*/*;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,*;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
<!– … rest of the html … –>

第一行被稱為“Status Line”,它之後就是http headers,空行完了就開始輸出內容了(在這個案例中是一些html輸出)。

這個HTTP請求也發出了一些其他資源的接收請求,例如圖片,css檔,js文件等等。


HTTP Request 的結構

6cdc05c8866dc3df9a78880e4756c348.gif (590×247)
被稱作“first line”的第一行包含三個部分:
  • “method” 表明這是何種類型的請求. 最常見的請求類型有 GET, POST 和 HEAD.
  • “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

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



請求類型


三種最常見的請求類型是:GET,POST 和 HEAD ,從html的編寫過程中你可能已經熟悉了前兩種。
Get
<form action="foo.php" method="GET">
First Name: <input name="first_name" type="text" />
Last Name: <input name="last_name" type="text" />
<input name="action" type="submit" value="Submit" />
</form>
當這個表單被提交時,HTTP request 就會像這樣:
GET /foo.php?first_name=John&last_name=Doe&action=Submit HTTP/1.1

POST
<form action="foo.php" method="POST">
First Name: <input name="first_name" type="text" />
Last Name: <input name="last_name" type="text" />
<input name="action" type="submit" value="Submit" />
</form>
提交這個表單會創建一個如下的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,*/*;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,*;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&last_name=Doe&action=Submit
這裏有三個需要注意的地方:
  • 第一行的路徑已經變為簡單的 /foo.php , 已經沒了查詢字串。
  • 新增了 Content-Type 和 Content-Lenght Header,它提供了發送資訊的相關資訊.
  • 所有資料都在headers之後,以查詢字串的形式被發送.
Method
HEAD和GET很相似,只不過HEAD不接受HTTP回應的內容部分。當你發送了一個HEAD請求,那就意味著你只對HTTPHeader感興趣,而不是文檔本身。
這個方法可以讓流覽器判斷頁面是否被修改過,從而控制緩存。也可判斷所請求的文檔是否存在。
例如,假如你的網站上有很多鏈結,那麼你就可以簡單的給他們分別發送HEAD請求來判斷是否存在死鏈,這比使用GET要快很多。


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 Cache 機制

  1. Expires跟max-age都可以決定一個 Response 是否過期,但max-age會蓋過(override
    )Expires
    。在過期之前,瀏覽器「不會」發送出任何 Request。
    -max-age:status code 200 (from memory cache)
    -Expires:status code 200 (from disk cache)
  2. Last-Modified 與 If-Modified-Since:有一種期況,cache已經過期了,但是檔案也沒有修改過,故檔案使否被修改過成為是否更新快取的條件。
  3. Etag 與 If-None-Match:若是你打開檔案什麼都不做,然後存檔,這個編輯時間也會被更新。所以就產生了用「檔案內容更動與否」來當作是否要更新快取的條件,可以把 Etag 想成是這份檔案內容的 hash 值(但其實不是,但原理類似就是了,總之就是一樣的內容會產生一樣的 hash,不一樣的會產生不一樣的 hash)
※以上,list 1是第一個判斷是否更新快取的條件,list2和lsit3都是次要條件,不過list3則是比list2更好的第二個判斷條件。當快取過期之後,可以用If-Modified-Since或是If-None-Match詢問 Server 有沒有新的資源,如果有的話就回傳新的,沒有的話就回傳 Status code 304,代表快取裡面的資源還能繼續沿用。

Q:想要即時更新,但如果一周都沒改變,不希望每次user都還來要資料。
A:第一招,你可以用Cache-Control: max-age=0,這就代表說這個 Response 0 秒之後就會過期,意思是瀏覽器一接收到,就會標示為過期。這樣當使用者再次造訪頁面,就會去 Server 詢問有沒有新的資料,再搭配上Etag來使用,就可以保證只會下載到最新的 Response。
第二招,有一個已經規範好的策略叫做:Cache-Control: no-cacheno-cache並不是「完全不使用快取的意思」,而是跟我們上面的行為一樣。每次都會發送 Request 去確認是否有新的檔案。
如果要「完全不使用快取」,是Cache-Control: no-store。這邊不要搞混了。


來個實例:
假設 A 網站是使用Cache-Control: no-store,B 網站是使用Cache-Control: no-cache
當每一次重新造訪同樣一個頁面的時候,無論 A 網站有沒有更新,A 網站都會傳來「整份新的檔案」,假設index.html有 100 kb 好了,造訪了十次,累積的流量就是 1000kb。
B 網站的話,我們假設前九次網站都沒有更新,一直到第十次才更新。所以前九次 Server 只會回傳 Status code 304,這個封包大小我們姑且算作 1kb 好了。第十次因為有新的檔案,會是 100kb。那十次加起來的流量就是 9 + 100 = 109 kb
可以發現 A 跟 B 達成的效果一樣,那就是「只要網站更新,使用者就能立即看到結果」,但是 B 的流量遠低於 A,因為有善用快取策略。只要每一次 Request 都先確認網站有沒有更新即可,不用每一次都抓完整的檔案下來。
這就是no-storeno-cache的差異,永遠不用快取跟永遠檢查快取。



最後一個問題

現在 Web App 當道,許多網站都是採用 SPA 的架構搭配 Webpack 打包。前端只需要引入一個 JavaScript 的檔案,Render 就交給 JavaScript 來做就好。
這類型的網站,HTML 可能長得像這樣:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
  <link rel='stylesheet' href='style.css'></link>
  <script src='script.js'></script>
</head>
<body>
 <!-- body 為空,所有內容都交給 js 去 render -->
</body>
</html>

當 JavaScript 載入之後,利用 JavaScript 把頁面渲染出來。
面對這種情境,我們就會希望這個檔案能夠跟上面的首頁檔案一樣,「只要檔案更新,使用者能夠立即看到新的結果」,因此我們可以用Cache-Control: no-cache來達成這個目標。
可是呢,還記得剛說過no-cache其實就是每一次訪問頁面,都去 Server 問說有沒有新的結果。意思就是無論如何,都會發出 Request。
有沒有可能,連 Request 都不發呢?
意思就是:「只要檔案不更新,瀏覽器就不會發 Request,直接沿用快取裡的即可。只要檔案一更新,瀏覽器就要立即抓取新的檔案」
前者其實就是我們一開始講的max-age在做的事,但max-age沒辦法做到判斷「檔案不更新」這件事情。
所以其實這個目標,沒辦法單靠上面我們介紹的這些瀏覽器的快取機制來達成,需要 Server 那邊一起配合才行。其實說穿了,就是把 Etag 的機制自己實作在檔案裡面。
什麼意思呢?我們直接來看一個範例,我們把index.html改成這樣:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
  <link rel='stylesheet' href='style.css'></link>
  <script src='script-qd3j2orjoa.js'></script>
</head>
<body>
 <!-- body 為空,所有內容都交給 js 去 render -->
</body>
</html>

注意到 JavaScript 的檔名變成:script-qd3j2orjoa.js,後面其實就跟 Etag 一樣,都是代表這個檔案的 hash 值。然後我們把這個檔案的快取策略設成:Cache-Control: max-age=31536000
這樣子這個檔案就會被快取住一年。一年之內都不會對這個 URL 發送新的 Request。
那如果我們要更新的話怎麼辦呢?我們不要更新這個檔案,直接更新index.html,換一個 JavaScript 檔案:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
  <link rel='stylesheet' href='style.css'></link>
  <script src='script-8953jief32.js'></script>
</head>
<body>
 <!-- body 為空,所有內容都交給 js 去 render -->
</body>
</html>

因為index.html的快取策略是no-cache,所以每一次訪問這個頁面,都會去index.html是否更新
以現在這個例子來說,它的確更新了,因此新的這份就會傳回給瀏覽器。而瀏覽器發現有新的 JavaScript 檔案就會去下載並且快取起來(不會render到一樣的js檔,因為被快取了)
藉由把 Etag 的機制實作在index.html裡面,我們就達成了我們的目標:「只要檔案不更新,瀏覽器就不會發 Request,直接沿用快取裡的即可。只要檔案一更新,瀏覽器就要立即抓取新的檔案」
原理就是針對不同的檔案採用不同的快取策略,並且直接用「更換 JavaScript 檔案」的方式強制瀏覽器重新下載。


沒有留言:

張貼留言