前端工程師,揭開HTTP的神祕面紗

原文博客地址: https://finget.github.io/2018/07/03/http/

我只是個初學者,圖片文字有些來自網上,寫的不對的地方,還望大佬指出!javascript

瀏覽器輸入URL後HTTP請求返回過程

網絡協議分層

OSI七層協議

五層協議

五層協議只是OSI和TCP/IP的綜合,實際應用仍是TCP/IP的四層結構。html

TCP/IP 協議

TCP(Transmission Control Protocol)傳輸控制協議

TCP/IP協議將應用層、表示層、會話層合併爲應用層,物理層和數據鏈路層合併爲網絡接口層java

三種模型結構

各層的做用

1.物理層:
主要定義物理設備標準,如網線的接口類型、光纖的接口類型、各類傳輸介質的傳輸速率等。它的主要做用是傳輸比特流(就是由一、0轉化爲電流強弱來進行傳輸,到達目的地後在轉化爲一、0,也就是咱們常說的數模轉換與模數轉換)。這一層的數據叫作比特。
  
2.數據鏈路層:
定義瞭如何讓格式化數據以進行傳輸,以及如何讓控制對物理介質的訪問。這一層一般還提供錯誤檢測和糾正,以確保數據的可靠傳輸。  
 
3.網絡層:
在位於不一樣地理位置的網絡中的兩個主機系統之間提供鏈接和路徑選擇。Internet的發展使得從世界各站點訪問信息的用戶數大大增長,而網絡層正是管理這種鏈接的層。
  
4.傳輸層:
定義了一些傳輸數據的協議和端口號(WWW端口80等),如:
TCP(transmission control protocol –傳輸控制協議,傳輸效率低,可靠性強,用於傳輸可靠性要求高,數據量大的數據)
UDP(user datagram protocol–用戶數據報協議,與TCP特性偏偏相反,用於傳輸可靠性要求不高,數據量小的數據,如QQ聊天數據就是經過這種方式傳輸的)。 主要是將從下層接收的數據進行分段和傳輸,到達目的地址後再進行重組。經常把這一層數據叫作段。
  
5.會話層:
經過運輸層(端口號:傳輸端口與接收端口)創建數據傳輸的通路。主要在你的系統之間發起會話或者接受會話請求(設備之間須要互相認識能夠是IP也能夠是MAC或者是主機名)   linux

6.表示層:
可確保一個系統的應用層所發送的信息能夠被另外一個系統的應用層讀取。例如,PC程序與另外一臺計算機進行通訊,其中一臺計算機使用擴展二一十進制交換碼(EBCDIC),而另外一臺則使用美國信息交換標準碼(ASCII)來表示相同的字符。若有必要,表示層會經過使用一種通格式來實現多種數據格式之間的轉換。   webpack

7.應用層:
是最靠近用戶的OSI層。這一層爲用戶的應用程序(例如電子郵件、文件傳輸和終端仿真)提供網絡服務。git

HTTP 發展歷史

HTTP/0.9

  • 只有一個命令GET
  • 響應類型: 僅 超文本
  • 沒有header等描述數據的信息
  • 服務器發送完畢,就關閉TCP鏈接

HTTP/1.0

  • 增長了不少命令(post HESD )
  • 增長status code 和 header
  • 多字符集支持、多部分發送、權限、緩存等
  • 響應:再也不只限於超文本 (Content-Type 頭部提供了傳輸 HTML 以外文件的能力 — 如腳本、樣式或媒體文件)

HTTP/1.1

  • 持久鏈接。TCP三次握手會在任何鏈接被創建以前發生一次。最終,當發送了全部數據以後,服務器發送一個消息,表示不會再有更多數據向客戶端發送了;則客戶端纔會關閉鏈接(斷開 TCP)
  • 支持的方法: GET , HEAD , POST , PUT , DELETE , TRACE , OPTIONS
  • 進行了重大的性能優化和特性加強,分塊傳輸、壓縮/解壓、內容緩存磋商、虛擬主機(有單個IP地址的主機具備多個域名)、更快的響應,以及經過增長緩存節省了更多的帶寬

HTTP2

  • 全部數據以二進制傳輸。HTTP1.x是基於文本的,沒法保證健壯性,HTTP2.0絕對使用新的二進制格式,方便且健壯
  • 同一個鏈接裏面發送多個請求再也不須要按照順序來
  • 頭信息壓縮以及推送等提升效率的功能

三次握手

客服端和服務端在進行http請求和返回的工程中,須要建立一個 TCP connection(由客戶端發起),http不存在鏈接這個概念,它只有請求和響應。請求和響應都是數據包,它們之間的傳輸通道就是 TCP connection

位碼即tcp標誌位,有6種標示:SYN(synchronous創建聯機) ACK(acknowledgement 確認) PSH(push傳送) FIN(finish結束) RST(reset重置) URG(urgent緊急)Sequence number(順序號碼) Acknowledge number(確認號碼)github

第一次握手:主機A發送位碼爲syn=1,隨機產生seq number=1234567的數據包到服務器,主機B由SYN=1知道,A要求創建聯機;(第一次握手,由瀏覽器發起,告訴服務器我要發送請求了)web

第二次握手:主機B收到請求後要確認聯機信息,向A發送ack number=(主機A的seq+1),syn=1,ack=1,隨機產生seq=7654321的包;(第二次握手,由服務器發起,告訴瀏覽器我準備接受了,你趕忙發送吧)json

第三次握手:主機A收到後檢查ack number是否正確,即第一次發送的seq number+1,以及位碼ack是否爲1,若正確,主機A會再發送ack number=(主機B的seq+1),ack=1,主機B收到後確認seq值與ack=1則鏈接創建成功(第三次握手,由瀏覽器發送,告訴服務器,我立刻就發了,準備接受吧)跨域

謝希仁著《計算機網絡》中講「三次握手」的目的是「爲了防止已失效的鏈接請求報文段忽然又傳送到了服務端,於是產生錯誤」

URI、URL、URN

URI: Uniform Resource Identifier/統一資源標識符
URL: Uniform Resource Locator/統一資源定位器
URN: Uniform Resource Name/永久統一資源定位符

web上的各類資源(html、圖片、視頻、音頻等)都由一個URI標識定位。URI至關於它們的詳細「家庭住址」。

URI包含了URL和URN。

URL是URI的一種,不只標識了Web 資源,還指定了操做或者獲取方式,同時指出了主要訪問機制和網絡位置。

URN是URI的一種,用特定命名空間的名字標識資源。使用URN能夠在不知道其網絡位置及訪問方式的狀況下討論資源。

網上的一個例子:

// 這是一個URI
http://bitpoetry.io/posts/hello.html#intro

// 資源訪問方式
http://

// 資源存儲位置
bitpoetry.io/posts/hello.html

#intro // 資源

// URL
http://bitpoetry.io/posts/hello.html

// URN
bitpoetry.io/posts/hello.html#intro

HTTP報文

請求報文:

響應報文:

HTTP 各類特性

curl

curl命令是一個利用URL規則在命令行下工做的文件傳輸工具。它支持文件的上傳和下載,因此是綜合傳輸工具,但按傳統,習慣稱curl爲下載工具。做爲一款強力工具,curl支持包括HTTP、HTTPS、ftp等衆多協議,還支持POST、cookies、認證、從指定偏移處下載部分文件、用戶代理字符串、限速、文件大小、進度條等特徵。作網頁處理流程和數據檢索自動化,curl能夠祝一臂之力。

更詳細的CURL,點這裏。

curl 訪問 baidu.com:

返回的內容中,html部分只有一個meta標籤,<meta http-equiv="refresh" content="0;url=http://www.baidu.com/">,這是由於咱們訪問的是baidu.com,在瀏覽器中,瀏覽器會自動解析這個meta標籤並重定向到http://www.baidu.com/,然而命令行中並無解析的功能。

curl 訪問 www.baidu.com:

curl經常使用命令

-v 顯示詳細的請求信息

-X 指定請求方式

curl -X GET www.xxxx.com/xx/xx?xx=123

curl -X POST www.xxxx.com/xx/xx?xx=123

-o / -O 保存下載的文件

// 將文件下載到本地並命名爲mygettext.html
curl -o mygettext.html http://www.gnu.org/software/gettext/manual/gettext.html

// 將文件保存到本地並命名爲gettext.html
curl -O http://www.gnu.org/software/gettext/manual/gettext.html

CORS跨域請求的限制與解決

// server1.js
const http = require('http')
const fs = require('fs')

http.createServer(function (request, response) {
  console.log('request come', request.url)

  const html = fs.readFileSync('test.html', 'utf8')
  response.writeHead(200, {
    'Content-Type': 'text/html'
  })
  response.end(html)
}).listen(8888)

console.log('server listening on 8888')
// server2.js
const http = require('http')

http.createServer(function (request, response) {
  console.log('request come', request.url)

  response.end('123')
}).listen(8887)

console.log('server listening on 8887')
// test.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
    
</body>
<script>
  fetch('http://127.0.0.1:8887');
</script>
</html>

處理方法:
1.服務器端處理

// server2.js 服務器端設置容許跨域
response.writeHead(200, {
  'Access-Control-Allow-Origin': '*' // * 表示任何域名下均可以訪問這個服務,也能夠指定域名
})

2.jsonp

// test.html
<script src="http://127.0.0.1:8887"></script>
就算存在跨域,請求仍是會發送,響應也會返回,只是瀏覽器端發現了存在跨域問題就將返回內容屏蔽了,並報錯提示。

CORS 預請求

// test.html
<script>
  fetch('http://127.0.0.1:8887',{
    method: 'post',
    headers: {
      'X-Test-Cors': '123'
    }
  });
</script>

咱們設置的請求頭中X-Test-Cors在跨域請求的時候,不被容許。

雖然不容許跨域,可是請求仍然會發送,並返回成功。

默認容許的請求方法:

  • GET
  • HEAD
  • POST

其餘的方法(PUT、DELETE)都須要預請求驗證的。

默認容許的Content-Type:

  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

怎樣設置容許咱們設置的請求頭:

// server2.js 
response.writeHead(200, {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'X-Test-Cors' // 加上這個設置
})

首先發送一個預請求,預請求就是告訴瀏覽器接下來要發送的post請求是被容許的。

設置容許的請求方法:

// server2.js 
response.writeHead(200, {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'X-Test-Cors',
  'Access-Control-Allow-Methods': 'POST, PUT, DELETE'
})

設置一個安全時間:

// server2.js 
response.writeHead(200, {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'X-Test-Cors',
  'Access-Control-Allow-Methods': 'POST, PUT, DELETE',
  'Access-Control-Max-Age': '1000'
})

Access-Control-Max-Age的單位是秒,意思就是在多少秒之內,咱們設置的這些容許的請求頭,請求方法,是不須要發送預請求驗證的,直接就能夠經過,併發送。

緩存Cache-Control

經常使用值:

Cache-Control 說明
public 全部內容都將被緩存(客戶端和代理服務器均可緩存)
private 內容只緩存到私有緩存中(僅客戶端能夠緩存,代理服務器不可緩存)
no-cache 必須先與服務器確認返回的響應是否被更改,而後才能使用該響應來知足後續對同一個網址的請求。所以,若是存在合適的驗證令牌 (ETag),no-cache 會發起往返通訊來驗證緩存的響應,若是資源未被更改,能夠避免下載。
no-store 全部內容都不會被緩存到緩存或 Internet 臨時文件中
must-revalidation/proxy-revalidation 若是緩存的內容失效,請求必須發送到服務器/代理以進行從新驗證
max-age=xxx (xxx is numeric) 緩存的內容將在 xxx 秒後失效, 這個選項只在HTTP 1.1可用, 並若是和Last-Modified一塊兒使用時, 優先級較高
// server.js
const http = require('http')
const fs = require('fs')

http.createServer(function (request, response) {
  console.log('request come', request.url)

  if (request.url === '/') {
    const html = fs.readFileSync('test.html', 'utf8')
    response.writeHead(200, {
      'Content-Type': 'text/html'
    })
    response.end(html)
  }

  if (request.url === '/script.js') {
    response.writeHead(200, {
      'Content-Type': 'text/javascript',
      'Cache-Control': 'max-age=20,public' // 緩存20s 多個值用逗號分開
    })
    response.end('console.log("script loaded")')
  }
}).listen(8888)

console.log('server listening on 8888')
// test.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
    
</body>
<script src="/script.js"></script>
</html>

刷新會發現script.js是從緩存中獲取的,請求時間也是0。

咱們但願瀏覽器緩存咱們的圖片,文件、js代碼,可是服務器端代碼更新了,瀏覽器端仍是在緩存中獲取的舊的文件。這就誕生了,webpack打包中出現的文件名後加上hash值,當文件改變時hash值也改變,這樣瀏覽器就會發送新的請求到服務器端。

緩存驗證

驗證頭:

  • Last-Modified

上次修改時間
配合If-Modified-Since或者If-Unmodified-Since使用
對比上次修改時間以驗證資源是否須要更新

  • Etag

數據簽名(內容修改,簽名就會改變)
配合If-Match或者If-Non-Match使用
對比資源的簽名判斷是否使用緩存

Redirect

const http = require('http')

http.createServer(function (request, response) {
  console.log('request come', request.url)

  if (request.url === '/') {
    response.writeHead(302, {  // or 301
      'Location': '/new'
    })
    response.end()
  }
  if (request.url === '/new') {
    response.writeHead(200, {
      'Content-Type': 'text/html',
    })
    response.end('<div>this is content</div>')
  }
}).listen(8888)

console.log('server listening on 8888')

302臨時跳轉,301永久跳轉,301從緩存種獲取跳轉,使用301以後,主動權就掌握在用戶手裏,若是用戶不清理緩存,那就算服務器端改變了也沒用。

Content Security Policy (網頁安全政策)

阮一峯:Content Security Policy 入門教程

HTTPS

HTTPS和HTTP的區別主要爲如下四點:1、https協議須要到ca申請證書,通常免費證書不多,須要交費。2、http是超文本傳輸協議,信息是明文傳輸,https 則是具備安全性的ssl加密傳輸協議。3、http和https使用的是徹底不一樣的鏈接方式,用的端口也不同,前者是80,後者是443。4、http的鏈接很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全。

相關文章
相關標籤/搜索