http協議總結

爲何要進行URI編碼

1.在傳遞數據的過程當中,若是存在用在分隔符的保留字怎麼辦?
2.對可能產生歧義性的數據進行編碼
  1.再也不ascii碼範圍內的字符
  2.ascii碼中不可顯示的字符
  3.uri中規定的保留字符
  4.不安全字符,空格,引號,尖括號
  
示例:
https://www.baidu.com/s?wd=?#! 若是不進行編碼#後面的後所有截斷

https://www.baidu.com/s?wd=中文

https://www.baidu.com/s?wd='>中文

http協議

Http協議,全稱是HyperText Tansfer Protocol,中文叫超文本傳輸協議,是互聯網最多見的協議。Http最重要的是www(World Wide Web)服務,也叫web服務器,中文叫「萬維網」。
web服務端口默認是80,另一個加密的www服務應用https默認端口是443,主要用於支付,網銀相關業務

http協議版本

http協議誕生以來有若干個版本,主要是http/1.0 http/1.1

http/1.0規定瀏覽器和服務器只能保持短暫的鏈接,瀏覽器的每次請求都須要和服務器創建一個TCP鏈接,服務器完成請求後即斷開TCP鏈接,服務器不跟蹤每一個連接,也不記錄請求

http/1.1是對HTTP的缺陷進行重點修復,從可擴展性,緩存,帶寬優化,持久鏈接,host頭,錯誤通知等訪問改進。
http/1.1支持長鏈接,增長了更多的請求頭和響應頭信息,例如配置請求頭的Connection的值爲keep-alive,表示請求結果返回後保持鏈接

http請求方法

1    GET    請求指定的頁面信息,並返回實體主體。
2    HEAD    相似於get請求,只不過返回的響應中沒有具體的內容,用於獲取報頭
3    POST    向指定資源提交數據進行處理請求(例如提交表單或者上傳文件)。數據被包含在請求體中。POST請求可能會致使新的資源的創建和/或已有資源的修改。
4    PUT    從客戶端向服務器傳送的數據取代指定的文檔的內容。
5    DELETE    請求服務器刪除指定的頁面。
6    CONNECT    HTTP/1.1協議中預留給可以將鏈接改成管道方式的代理服務器。
7    OPTIONS    容許客戶端查看服務器的性能。
8    TRACE    回顯服務器收到的請求,主要用於測試或診斷。

http狀態碼

HTTp狀態碼錶示web服務器響應http請求狀態的數字代碼
常見狀態碼以及做用是
1**    信息,服務器收到請求,須要請求者繼續執行操做
2**    成功,操做被成功接收並處理
3**    重定向,須要進一步的操做以完成請求
4**    客戶端錯誤,請求包含語法錯誤或沒法完成請求
5**    服務器錯誤,服務器在處理請求的過程當中發生了錯誤

http報文

http請求報文

HTTP請求由請求行,請求頭部,空行,請求報文主體幾個部分組成

請求報文的格式:
起始行: <method> <request-URL> <version>
頭部:   <headers>
主體:   <entity-body>
GET /books/?sex=man&name=Professional HTTP/1.1
 Host: www.example.com  主機名
 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)Gecko/20050225 Firefox/1.0.1  客戶端類型
 Accept-Encoding:gzip,deflate    支持壓縮
 Accept-Language:zh-cn  支持語言類型
 Connection: Keep-Alive  長連接
 
sex=man&name=Professional
 
請求行:

 請求報文第一行,表示客戶端想要什麼
由請求方法 url    協議版本   組成

請求頭:

 請求頭由    
關鍵字    :    值    組成
經過客戶端把請求相關信息告訴服務器


空行

請求頭信息以後是一個空行,發送回車和換行符,通知web服務器如下沒有請求頭信息了


請求體:

請求體中包含了要發送給web服務器的數據信息,請問報文主體不用於get方法,而是用於post方法。
post方法適用於客戶端填寫表單的場合。

http響應報文

狀態行
響應頭(Response Header)
響應正文
HTTP/1.1 200 OK
Server:Apache Tomcat/5.0.12
Date:Mon,6Oct2003 13:23:42 GMT
Content-Length:112
<html>...

狀態行:

狀態行,用來講明服務器響應客戶端的情況,通常分爲協議版本號,數字狀態碼,狀態狀況

響應頭部:

常見響應頭信息
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Mon, 13 Aug 2018 06:06:54 GMT
Expires: Mon, 13 Aug 2018 06:06:54 GMT

空白行:
通知客戶端空行如下沒有頭部信息了
響應報文主體
主要包含了返回給客戶端的數據,能夠是文本,二進制(圖片,視頻)

https

http存在的問題

竊聽 - 對稱加密
傳遞密鑰 - 非對稱加密
安全速度 - 非對稱加密+對稱加密
中間人攻擊 - 證書
證書僞造 - 消息摘要
摘要僞造 - 數字簽名

如何解決

在OSI七層模型中,應用層是HTTP協議,在應用層之下就是表示層,也就是TLS協議發揮做用的一層。
TLS協議經過(握手、交換祕鑰、告警、加密)等方式作到數據的安全加密。html

openssl

Netscape網景公司建立的第一代瀏覽器,且爲了提升瀏覽器訪問網站的安全性,在TCP/IP層的傳輸層與應用層之間建立了一個SSL(Secure Sockets Layer),安全套接字層。

SSL是一個庫,爲了讓應用層講數據傳遞給傳輸層以前,調用ssl層的功能,對數據進行加密。

可是SSL(v2 v3)是網景公司定義的,協議不夠開放,所以TSL(傳輸層安全協議)出現了,流行的版本是(TSL v1== SSL v3

ssl密鑰協商過程

https完整創建鏈接的過程

證書的申請頒發過程

證書的有效性鏈式證實

websocket

目的

即時通信,替代輪詢

網站上的即時通信是很常見的,好比網頁的QQ,聊天系統等。按照以往的技術能力一般是採用輪詢、Comet技術解決。

HTTP協議是非持久化的,單向的網絡協議,在創建鏈接後只容許瀏覽器向服務器發出請求後,服務器才能返回相應的數據。當須要即時通信時,經過輪詢在特定的時間間隔(如1秒),由瀏覽器向服務器發送Request請求,而後將最新的數據返回給瀏覽器。這樣的方法最明顯的缺點就是須要不斷的發送請求,並且一般HTTP request的Header是很是長的,爲了傳輸一個很小的數據 須要付出巨大的代價,是很不合算的,佔用了不少的寬帶。

缺點:會致使過多沒必要要的請求,浪費流量和服務器資源,每一次請求、應答,都浪費了必定流量在相同的頭部信息上

然而WebSocket的出現能夠彌補這一缺點。在WebSocket中,只須要服務器和瀏覽器經過HTTP協議進行一個握手的動做,而後單獨創建一條TCP的通訊通道進行數據的傳送。

原理

WebSocket同HTTP同樣也是應用層的協議,可是它是一種雙向通訊協議,是創建在TCP之上的。

鏈接過程(握手過程)

1. 瀏覽器、服務器創建TCP鏈接,三次握手。這是通訊的基礎,傳輸控制層,若失敗後續都不執行。
2. TCP鏈接成功後,瀏覽器經過HTTP協議向服務器傳送WebSocket支持的版本號等信息。(開始前的HTTP握手)
3. 服務器收到客戶端的握手請求後,一樣採用HTTP協議回饋數據。
4. 當收到了鏈接成功的消息後,經過TCP通道進行傳輸通訊。

websocket與http的關係

相同點
1. 都是同樣基於TCP的,都是可靠性傳輸協議。
2. 都是應用層協議。
不一樣點
1. WebSocket是雙向通訊協議,模擬Socket協議,能夠雙向發送或接受信息。HTTP是單向的。
2. WebSocket是須要握手進行創建鏈接的。

websocket與http的關係

WebSocket在創建握手時,數據是經過HTTP傳輸的。可是創建以後,在真正傳輸時候是不須要HTTP協議的。

WebSocket與Socket的關係

Socket其實並非一個協議,而是爲了方便使用TCP或UDP而抽象出來的一層,是位於應用層和傳輸控制層之間的一組接口。

當兩臺主機通訊時,必須經過Socket鏈接,Socket則利用TCP/IP協議創建TCP鏈接。TCP鏈接則更依靠於底層的IP協議,IP協議的鏈接則依賴於鏈路層等更低層次。

WebSocket則是一個典型的應用層協議。

區別
Socket是傳輸控制層協議,WebSocket是應用層協議。

websocket機制

傳統 HTTP 請求響應客戶端服務器交互圖

websocket 請求響應客戶端服務器交互圖

相對於傳統 HTTP 每次請求-應答都須要客戶端與服務端創建鏈接的模式,WebSocket 是相似 Socket 的 TCP 長鏈接的通信模式,一旦 WebSocket 鏈接創建後,後續數據都以幀序列的形式傳輸。在客戶端斷開 WebSocket 鏈接或 Server 端斷掉鏈接前,不須要客戶端和服務端從新發起鏈接請求。在海量併發及客戶端與服務器交互負載流量大的狀況下,極大的節省了網絡帶寬資源的消耗,有明顯的性能優點,且客戶端發送和接受消息是在同一個持久鏈接上發起,實時性優點明顯。

websocket握手過程

客戶端鏈接報文
GET /webfin/websocket/ HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==
Origin: 
http://localhost
:8080
Sec-WebSocket-Version: 13

Connection: Upgrade:表示要升級協議
Upgrade: websocket:表示要升級到websocket協議
Sec-WebSocket-Version: 13:表示websocket的版本
Sec-WebSocket-Key:與後面服務端響應首部的Sec-WebSocket-Accept是配套的,提供基本的防禦,好比惡意的鏈接,或者無心的鏈接。
WebSocket 服務端響應報文
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=
Sec-WebSocket-Key/Accept的做用
避免服務端收到非法的websocket鏈接
確保服務端理解websocket鏈接
用瀏覽器裏發起ajax請求,設置header時,Sec-WebSocket-Key以及其餘相關的header是被禁止的
Sec-WebSocket-Key主要目的並非確保數據的安全性,由於Sec-WebSocket-Key、Sec-WebSocket-Accept的轉換計算公式是公開的,並且很是簡單,最主要的做用是預防一些常見的意外狀況(非故意的)
Sec-WebSocket-Accept的計算
Sec-WebSocket-Accept根據客戶端請求首部的Sec-WebSocket-Key計算出來。 計算公式爲:

將Sec-WebSocket-Key跟258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接。
經過SHA1計算出摘要,並轉成base64字符串


1.value = headers['Sec-WebSocket-Key'] + magic_string 
拿到客戶端發送的key,拼接magicstring

2.ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
進行sha1,base64加密

3."Sec-WebSocket-Accept: ac
將加密結果返回
import socket, base64, hashlib

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 9527))
sock.listen(5)
# 獲取客戶端socket對象
conn, address = sock.accept()
# 獲取客戶端的【握手】信息
data = conn.recv(1024)
print(data)
"""
b'GET /ws HTTP/1.1\r\n
Host: 127.0.0.1:9527\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\r\n
Accept-Encoding: gzip, deflate\r\n
Sec-WebSocket-Version: 13\r\n
Origin: http://localhost:63342\r\n
Sec-WebSocket-Extensions: permessage-deflate\r\n
Sec-WebSocket-Key: jocLOLLq1BQWp0aZgEWL5A==\r\n
Cookie: session=6f2bab18-2dc4-426a-8f06-de22909b967b\r\n
Connection: keep-alive, Upgrade\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
Upgrade: websocket\r\n\r\n'
"""

# magic string爲:258EAFA5-E914-47DA-95CA-C5AB0DC85B11
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'


def get_headers(data):
    header_dict = {}
    header_str = data.decode("utf8")
    for i in header_str.split("\r\n"):
        if str(i).startswith("Sec-WebSocket-Key"):
            header_dict["Sec-WebSocket-Key"] = i.split(":")[1].strip()

    return header_dict


def get_header(data):
    """
     將請求頭格式化成字典
     :param data:
     :return:
     """
    header_dict = {}
    data = str(data, encoding='utf-8')

    header, body = data.split('\r\n\r\n', 1)
    header_list = header.split('\r\n')
    for i in range(0, len(header_list)):
        if i == 0:
            if len(header_list[i].split(' ')) == 3:
                header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')
        else:
            k, v = header_list[i].split(':', 1)
            header_dict[k] = v.strip()
    return header_dict


headers = get_headers(data)  # 提取請求頭信息
# 對請求頭中的sec-websocket-key進行加密
response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
               "Upgrade:websocket\r\n" \
               "Connection: Upgrade\r\n" \
               "Sec-WebSocket-Accept: %s\r\n" \
               "WebSocket-Location: ws://127.0.0.1:9527\r\n\r\n"

value = headers['Sec-WebSocket-Key'] + magic_string
print(value)
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
response_str = response_tpl % (ac.decode('utf-8'))
# 響應【握手】信息
conn.send(response_str.encode("utf8"))

while True:
    msg = conn.recv(8096)
    print(msg)

websocket加密

import struct
msg_bytes = "hello".encode("utf8")
token = b"\x81"
length = len(msg_bytes)

if length < 126:
    token += struct.pack("B", length)
elif length <= 0xFFFF:
    token += struct.pack("!BH", 126, length)
else:
    token += struct.pack("!BQ", 127, length)

msg = token + msg_bytes

print(msg)
websocket數據幀格式
WebSocket客戶端、服務端通訊的最小單位是幀,由1個或多個幀組成一條完整的消息(message)。

發送端:將消息切割成多個幀,併發送給服務端
接收端:接收消息幀,並將關聯的幀從新組裝成完整的消息


  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-------+-+-------------+-------------------------------+
 |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 | |1|2|3|       |K|             |                               |
 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 |     Extended payload length continued, if payload len == 127  |
 + - - - - - - - - - - - - - - - +-------------------------------+
 |                               |Masking-key, if MASK set to 1  |
 +-------------------------------+-------------------------------+
 | Masking-key (continued)       |          Payload Data         |
 +-------------------------------- - - - - - - - - - - - - - - - +
 :                     Payload Data continued ...                :
 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 |                     Payload Data continued ...                |
 +---------------------------------------------------------------+

http安全問題

xss攻擊

1.黑客往網頁裏注入惡意腳本代碼
2.當用戶訪問時獲取到包含惡意代碼的網頁
3.經過惡意腳本,黑客能夠獲取和控制用戶信息
反射型(非持久型)XSS
誘導用戶點擊惡意連接來形成一次性攻擊

1.黑客把帶有惡意腳本代碼參數的URL地址發送給用戶
2.用戶點擊此連接
3.服務器端獲取請求參數而且直接使用,服務器反射回結果頁面
4.反射型XSS攻擊是一次性的,必需要經過用戶點擊連接才能發起
5.一些瀏覽器如Chrome其內置了一些XSS過濾器,能夠防止大部分反射型XSS攻擊
6.反射型XSS其實就是服務器沒有對惡意的用戶輸入進行安全處理就直接反射響應內容,致使惡意代碼在瀏覽器中執行的一種XSS漏洞
const express = require('express');


存儲型(持久型)XSS

黑客將代碼存儲到漏洞服務器中,用戶瀏覽相關頁面發起攻擊

1.黑客將惡意腳本代碼上傳或存儲到漏洞服務器
2.服務器把惡意腳本保存到服務器
3.當正常客戶訪問服務器時,服務器會讀取惡意數據而且直接使用
4.服務器會返回含有惡意腳本的頁面


DOM-Based型XSS
不須要服務器端支持,是因爲DOM結構修改致使的,基於瀏覽器DOM解析的攻擊

1.用戶打開帶有惡意的連接
2.瀏覽器在DOM解析的時候直接使用惡意數據
3.用戶中招
4.常見的觸發場景就是在修改innerHTML outerHTML document.write的時候
xss攻擊對比
類型 反射型 存儲型
持久性 非持久 持久化(存儲在服務器)
觸發時機 須要用戶點擊 不須要用戶交互也能夠觸發
危害 危害較小 危害更大
xss防護
1.對字符進行編碼
function htmlEncode(str) {
  return String(str)
    .replace(/&/g, '&amp;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;');
}

2.對js進行編碼轉譯
   //使用「\」對特殊字符進行轉義,除數字字母以外,小於127使用16進制「\xHH」的方式進行編碼,大於用unicode(很是嚴格模式)。
        var JavaScriptEncode = function (str) {
            var hex = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f');
            function changeTo16Hex(charCode) {
                return "\\x" + charCode.charCodeAt(0).toString(16);
            }

            function encodeCharx(original) {
                var found = true;
                var thecharchar = original.charAt(0);
                var thechar = original.charCodeAt(0);
                switch (thecharchar) {
                    case '\n': return "\\n"; break; //newline
                    case '\r': return "\\r"; break; //Carriage return
                    case '\'': return "\\'"; break;
                    case '"': return "\\\""; break;
                    case '\&': return "\\&"; break;
                    case '\\': return "\\\\"; break;
                    case '\t': return "\\t"; break;
                    case '\b': return "\\b"; break;
                    case '\f': return "\\f"; break;
                    case '/': return "\\x2F"; break;
                    case '<': return "\\x3C"; break;
                    case '>': return "\\x3E"; break;
                    default:
                        found = false;
                        break;
                }
                if (!found) {
                    if (thechar > 47 && thechar < 58) { //數字
                        return original;
                    }

                    if (thechar > 64 && thechar < 91) { //大寫字母
                        return original;
                    }

                    if (thechar > 96 && thechar < 123) { //小寫字母
                        return original;
                    }

                    if (thechar > 127) { //大於127用unicode
                        var c = thechar;
                        var a4 = c % 16;
                        c = Math.floor(c / 16);
                        var a3 = c % 16;
                        c = Math.floor(c / 16);
                        var a2 = c % 16;
                        c = Math.floor(c / 16);
                        var a1 = c % 16;
                        return "\\u" + hex[a1] + hex[a2] + hex[a3] + hex[a4] + "";
                    }
                    else {
                        return changeTo16Hex(original);
                    }

                }
            }

csrf

Cross Site Request Forgery 跨站請求僞造

1.用戶A登陸銀行網站,登陸成功後會設置cookie
2.黑客誘導用戶A登陸到黑客的站點,而後會返回一個頁面
3.用戶訪問這個頁面時,這個頁面會僞造一個轉帳請求到銀行網站
防護
1.用戶不知情 驗證碼 影響用戶體驗
2.跨站請求 使用refer驗證 不可靠
3.參數僞造 token 最主流的防護CSRF

DDOS攻擊

分佈式拒絕服務(Distribute Denial Of Service)

黑客控制大量的肉雞向受害主機發送非正常請求,致使目標主機耗盡資源不能爲合法用戶提供服務
驗證碼是咱們在互聯網十分常見的技術之一。不得不說驗證碼是可以有效地防止屢次重複請求的行爲。
限制請求頻率是咱們最多見的針對 DDOS 攻擊的防護措施。其原理爲設置每一個客戶端的請求頻率的限制
增長機器增長服務帶寬。只要超過了攻擊流量即可以免服務癱瘓
設置本身的業務爲分佈式服務,防止單點失效
使用主流雲系統和 CDN(雲和 CDN 其自身有 DDOS 的防範做用)
優化資源使用提升 web server 的負載能力

HTTP劫持

在用戶的客戶端與其要訪問的服務器通過網絡協議協調後,兩者之間創建了一條專用的數據通道,用戶端程序在系統中開放指定網絡端口用於接收數據報文,服務器端將所有數據按指定網絡協議規則進行分解打包,造成連續數據報文。
用戶端接收到所有報文後,按照協議標準來解包組合得到完整的網絡數據。其中傳輸過程當中的每個數據包都有特定的標籤,表示其來源、攜帶的數據屬性以及要到何處,全部的數據包通過網絡路徑中ISP的路由器傳輸接力後,最終到達目的地,也就是客戶端。
HTTP劫持是在使用者與其目的網絡服務所創建的專用數據通道中,監視特定數據信息,提示當知足設定的條件時,就會在正常的數據流中插入精心設計的網絡數據報文,目的是讓用戶端程序解釋「錯誤」的數據,並以彈出新窗口的形式在使用者界面展現宣傳性廣告或者直接顯示某網站的內容。

http緩存機制

Web 緩存大體能夠分爲:數據庫緩存、服務器端緩存(代理服務器緩存、CDN 緩存)、瀏覽器緩存。

瀏覽器緩存也包含不少內容: HTTP 緩存、indexDB、cookie、localstorage 等等。這裏咱們只討論 HTTP 緩存相關內容。

在具體瞭解 HTTP 緩存以前先來明確幾個術語:

緩存命中率:從緩存中獲得數據的請求數與全部請求數的比率。理想狀態是越高越好。
過時內容:超過設置的有效時間,被標記爲「陳舊」的內容。一般過時內容不能用於回覆客戶端的請求,必須從新向源服務器請求新的內容或者驗證緩存的內容是否仍然準備。
驗證:驗證緩存中的過時內容是否仍然有效,驗證經過的話刷新過時時間。
失效:失效就是把內容從緩存中移除。當內容發生改變時就必須移除失效的內容。
瀏覽器緩存主要是 HTTP 協議定義的緩存機制。HTML meta 標籤,例如

<META HTTP-EQUIV="Pragma" CONTENT="no-store">

含義是讓瀏覽器不緩存當前頁面。可是代理服務器不解析 HTML 內容,通常應用普遍的是用 HTTP 頭信息控制緩存。

瀏覽器緩存分類

瀏覽器緩存分爲強緩存和協商緩存,瀏覽器加載一個頁面的簡單流程以下:

1.瀏覽器先根據這個資源的http頭信息來判斷是否命中強緩存。若是命中則直接加在緩存中的資源,並不會將請求發送到服務器。

2.若是未命中強緩存,則瀏覽器會將資源加載請求發送到服務器。服務器來判斷瀏覽器本地緩存是否失效。若可使用,則服務器並不會返回資源信息,瀏覽器繼續從緩存加載資源。

3.若是未命中協商緩存,則服務器會將完整的資源返回給瀏覽器,瀏覽器加載新資源,並更新緩存。

強緩存

命中強緩存時,瀏覽器並不會將請求發送給服務器。在Chrome的開發者工具中看到http的返回碼是200,可是在Size列會顯示爲(from cache)。

強緩存是利用http的返回頭中的Expires或者Cache-Control兩個字段來控制的,用來表示資源的緩存時間。

Expires

緩存過時時間,用來指定資源到期的時間,是服務器端的具體的時間點。也就是說,Expires=max-age + 請求時間,須要和Last-modified結合使用。但在上面咱們提到過,cache-control的優先級更高。 Expires是Web服務器響應消息頭字段,在響應http請求時告訴瀏覽器在過時時間前瀏覽器能夠直接從瀏覽器緩存取數據,而無需再次請求。

該字段會返回一個時間,好比Expires:Thu,31 Dec 2037 23:59:59 GMT。這個時間表明着這個資源的失效時間,也就是說在2037年12月31日23點59分59秒以前都是有效的,即命中緩存。這種方式有一個明顯的缺點,因爲失效時間是一個絕對時間,因此當客戶端本地時間被修改之後,服務器與客戶端時間誤差變大之後,就會致使緩存混亂。因而發展出了Cache-Control。


Cache-Control

Cache-Control是一個相對時間,例如Cache-Control:3600,表明着資源的有效期是3600秒。因爲是相對時間,而且都是與客戶端時間比較,因此服務器與客戶端時間誤差也不會致使問題。
Cache-Control與Expires能夠在服務端配置同時啓用或者啓用任意一個,同時啓用的時候Cache-Control優先級高。

Cache-Control 能夠由多個字段組合而成,主要有如下幾個取值:

1. max-age 指定一個時間長度,在這個時間段內緩存是有效的,單位是s。例如設置 Cache-Control:max-age=31536000,也就是說緩存有效期爲(31536000 / 24 / 60 * 60)天,第一次訪問這個資源的時候,服務器端也返回了 Expires 字段,而且過時時間是一年後。

在沒有禁用緩存而且沒有超過有效時間的狀況下,再次訪問這個資源就命中了緩存,不會向服務器請求資源而是直接從瀏覽器緩存中取。

2. s-maxage 同 max-age,覆蓋 max-age、Expires,但僅適用於共享緩存,在私有緩存中被忽略。

3. public 代表響應能夠被任何對象(發送請求的客戶端、代理服務器等等)緩存。

4. private 代表響應只能被單個用戶(多是操做系統用戶、瀏覽器用戶)緩存,是非共享的,不能被代理服務器緩存。

5. no-cache 強制全部緩存了該響應的用戶,在使用已緩存的數據前,發送帶驗證器的請求到服務器。不是字面意思上的不緩存。

6. no-store 禁止緩存,每次請求都要向服務器從新獲取數據。

七、must-revalidate指定若是頁面是過時的,則去服務器進行獲取。這個指令並不經常使用,就不作過多的討論了。

協商緩存

若未命中強緩存,則瀏覽器會將請求發送至服務器。服務器根據http頭信息中的Last-Modify/If-Modify-Since或Etag/If-None-Match來判斷是否命中協商緩存。若是命中,則http返回碼爲304,瀏覽器從緩存中加載資源。

Last-Modify/If-Modify-Since

瀏覽器第一次請求一個資源的時候,服務器返回的header中會加上Last-Modify,Last-modify是一個時間標識該資源的最後修改時間,例如Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。

當瀏覽器再次請求該資源時,發送的請求頭中會包含If-Modify-Since,該值爲緩存以前返回的Last-Modify。服務器收到If-Modify-Since後,根據資源的最後修改時間判斷是否命中緩存。

若是命中緩存,則返回http304,而且不會返回資源內容,而且不會返回Last-Modify。因爲對比的服務端時間,因此客戶端與服務端時間差距不會致使問題。可是有時候經過最後修改時間來判斷資源是否修改仍是不太準確(資源變化了最後修改時間也能夠一致)。因而出現了ETag/If-None-Match。


ETag/If-None-Match

與Last-Modify/If-Modify-Since不一樣的是,Etag/If-None-Match返回的是一個校驗碼(ETag: entity tag)。ETag能夠保證每個資源是惟一的,資源變化都會致使ETag變化*。ETag值的變動則說明資源狀態已經被修改。服務器根據瀏覽器上發送的If-None-Match值來判斷是否命中緩存。

ETag擴展說明

咱們對ETag寄予厚望,但願它對於每個url生成惟一的值,資源變化時ETag也發生變化。神祕的Etag是如何生成的呢?以Apache爲例,ETag生成靠如下幾種因子

文件的i-node編號,此i-node非彼iNode。是Linux/Unix用來識別文件的編號。是的,識別文件用的不是文件名。使用命令’ls –I’能夠看到。
文件最後修改時間
文件大小
生成Etag的時候,可使用其中一種或幾種因子,使用抗碰撞散列函數來生成。因此,理論上ETag也是會重複的,只是機率小到能夠忽略。
既生Last-Modified何生Etag?
你可能會以爲使用Last-Modified已經足以讓瀏覽器知道本地的緩存副本是否足夠新,爲何還須要Etag(實體標識)呢?HTTP1.1中Etag的出現主要是爲了解決幾個Last-Modified比較難解決的問題:

1. Last-Modified標註的最後修改只能精確到秒級,若是某些文件在1秒鐘之內,被修改屢次的話,它將不能準確標註文件的修改時間

2. 若是某些文件會被按期生成,當有時內容並無任何變化,但Last-Modified卻改變了,致使文件無法使用緩存

3.有可能存在服務器沒有準確獲取文件修改時間,或者與代理服務器時間不一致等情形

Etag是服務器自動生成或者由開發者生成的對應資源在服務器端的惟一標識符,可以更加準確的控制緩存。Last-Modified與ETag是能夠一塊兒使用的,服務器會優先驗證ETag,一致的狀況下,纔會繼續比對Last-Modified,最後才決定是否返回304。

瀏覽器緩存與用戶行爲有關

用戶操做 Expires/Cache-Control Last-Modified/Etag
地址欄回車 有效 有效
頁面連接跳轉 有效 有效
新開窗口 有效 有效
前進、後退 有效 有效
F5刷新 無效 有效
Ctrl+F5刷新 無效 無效

瀏覽器第一次請求

瀏覽器再次請求

http持久鏈接和管線化節省通訊量

持久鏈接(keep-alive)模式

HTTP1.1規定了默認保持長鏈接(HTTP persistent connection ,也有翻譯爲持久鏈接);數據傳輸完成了保持TCP鏈接不斷開(不發RST包、不四次握手),等待在同域名下繼續用這個通道傳輸數據;相反的就是短鏈接。

HTTP 1.1版本支持持久鏈接 1.0版本不支持

與非持久鏈接的區別:

持久鏈接使客戶端到服務器端鏈接持續有效,避免了從新創建鏈接

大大減小了鏈接的創建以及關閉時延。HTTP鏈接是創建在TCP協議之上的,創建一條TCP鏈接須要三次握手,TCP鏈接關閉時須要四次揮手。這些都是須要時間的

管線化

管線化機制須經過永久鏈接(persistent connection)完成,僅HTTP/1.1支持此技術(HTTP/1.0不支持)

在使用持久鏈接的狀況下,某個鏈接消息的傳遞相似於

請求1 -> 響應1 -> 請求2 -> 響應2

管線化:某個鏈接上的消息變成了相似這樣 

請求1 -> 請求2 -> 請求3 -> 響應1 -> 響應2 -> 響應3

持久鏈接與管線化的區別

 1. 那麼持久鏈接和管線化的區別在於:

  持久鏈接的一個缺點是請求和響應式是順序執行的,只有在請求1的響應收到以後,纔會發送請求2,而管線化不須要等待上一次請求獲得響應就能夠進行下一次請求。實現並行發送請求。 

  2. 只有GET和HEAD要求能夠進行管線化,而POST則有所限制

  3. 初次建立鏈接時也不該啓動管線機制,由於對方(服務器)不必定支持HTTP/1.1版本的協議。

       4.HTTP1.1要求服務器端支持管線化,但並不要求服務器端也對響應進行管線化處理,只是要求對於管線化的請求不失敗,並且如今不少服務器端和代理程序對管線化的支持並很差,現代瀏覽器Chrome和Firefox默認並未開啓管線化支持。
相關文章
相關標籤/搜索