## 后臺和前臺的http交互
前后端交互時,http報文作為信息的載體
所以http是一塊很重要的內容,這一部分重點介紹它
### http報文結構
報文一般包括了:`通用頭部`,`請求/響應頭部`,`請求/響應體`
**通用頭部**
這也是開發人員見過的最多的信息,包括如下:
```
Request Url: 請求的web服務器地址
Request Method: 請求方式
(Get、POST、OPTIONS、PUT、HEAD、DELETE、CONNECT、TRACE)
Status Code: 請求的返回狀態碼,如200代表成功
Remote Address: 請求的遠程服務器地址(會轉為IP)
```
譬如,在跨域拒絕時,可能是method為`options`,狀態碼為`404/405`等(當然,實際上可能的組合有很多)
其中,Method的話一般分為兩批次:
```
HTTP1.0定義了三種請求方法: GET, POST 和 HEAD方法。
以及幾種Additional Request Methods:PUT、DELETE、LINK、UNLINK
HTTP1.1定義了八種請求方法:GET、POST、HEAD、OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
```
`HTTP 1.0`定義參考:[https://tools.ietf.org/html/rfc1945](https://tools.ietf.org/html/rfc1945)
`HTTP 1.1`定義參考:[https://tools.ietf.org/html/rfc2616](https://tools.ietf.org/html/rfc2616)
這里面最常用到的就是狀態碼,很多時候都是通過狀態碼來判斷,如(列舉幾個最常見的):
```
200——表明該請求被成功地完成,所請求的資源發送回客戶端
304——自從上次請求后,請求的網頁未修改過,請客戶端使用本地緩存
400——客戶端請求有錯(譬如可以是安全模塊攔截)
401——請求未經授權
403——禁止訪問(譬如可以是未登錄時禁止)
404——資源未找到
500——服務器內部錯誤
503——服務不可用
...
```
再列舉下大致不同范圍狀態的意義
```
1xx——指示信息,表示請求已接收,繼續處理
2xx——成功,表示請求已被成功接收、理解、接受
3xx——重定向,要完成請求必須進行更進一步的操作
4xx——客戶端錯誤,請求有語法錯誤或請求無法實現
5xx——服務器端錯誤,服務器未能實現合法的請求
```

總之,當請求出錯時,狀態碼能幫助快速定位問題,完整版本的狀態可以自行去互聯網搜索
**請求/響應頭部**
請求和響應頭部也是分析時常用到的
常用的請求頭部(部分):
```
Accept: 接收類型,表示瀏覽器支持的MIME類型
(對標服務端返回的Content-Type)
Accept-Encoding:瀏覽器支持的壓縮類型,如gzip等,超出類型不能接收
Content-Type:客戶端發送出去實體內容的類型
Cache-Control: 指定請求和響應遵循的緩存機制,如no-cache
If-Modified-Since:對應服務端的Last-Modified,用來匹配看文件是否變動,只能精確到1s之內,http1.0中
Expires:緩存控制,在這個時間內不會請求,直接使用緩存,http1.0,而且是服務端時間
Max-age:代表資源在本地緩存多少秒,有效時間內不會請求,而是使用緩存,http1.1中
If-None-Match:對應服務端的ETag,用來匹配文件內容是否改變(非常精確),http1.1中
Cookie: 有cookie并且同域訪問時會自動帶上
Connection: 當瀏覽器與服務器通信時對于長連接如何進行處理,如keep-alive
Host:請求的服務器URL
Origin:最初的請求是從哪里發起的(只會精確到端口),Origin比Referer更尊重隱私
Referer:該頁面的來源URL(適用于所有類型的請求,會精確到詳細頁面地址,csrf攔截常用到這個字段)
User-Agent:用戶客戶端的一些必要信息,如UA頭部等
```
常用的響應頭部(部分):
```
Access-Control-Allow-Headers: 服務器端允許的請求Headers
Access-Control-Allow-Methods: 服務器端允許的請求方法
Access-Control-Allow-Origin: 服務器端允許的請求Origin頭部(譬如為*)
Content-Type:服務端返回的實體內容的類型
Date:數據從服務器發送的時間
Cache-Control:告訴瀏覽器或其他客戶,什么環境可以安全的緩存文檔
Last-Modified:請求資源的最后修改時間
Expires:應該在什么時候認為文檔已經過期,從而不再緩存它
Max-age:客戶端的本地資源應該緩存多少秒,開啟了Cache-Control后有效
ETag:請求變量的實體標簽的當前值
Set-Cookie:設置和頁面關聯的cookie,服務器通過這個頭部把cookie傳給客戶端
Keep-Alive:如果客戶端有keep-alive,服務端也會有響應(如timeout=38)
Server:服務器的一些相關信息
```
一般來說,請求頭部和響應頭部是匹配分析的。
譬如,請求頭部的`Accept`要和響應頭部的`Content-Type`匹配,否則會報錯
譬如,跨域請求時,請求頭部的`Origin`要匹配響應頭部的`Access-Control-Allow-Origin`,否則會報跨域錯誤
譬如,在使用緩存時,請求頭部的`If-Modified-Since`、`If-None-Match`分別和響應頭部的`Last-Modified`、`ETag`對應
還有很多的分析方法,這里不一一贅述
**請求/響應實體**
http請求時,除了頭部,還有消息實體,一般來說
請求實體中會將一些需要的參數都放入進入(用于post請求)。
譬如實體中可以放參數的序列化形式(`a=1&b=2`這種),或者直接放表單對象(`Form Data`對象,上傳時可以夾雜參數以及文件),等等
而一般響應實體中,就是放服務端需要傳給客戶端的內容
一般現在的接口請求時,實體中就是對于的信息的json格式,而像頁面請求這種,里面就是直接放了一個html字符串,然后瀏覽器自己解析并渲染。
**CRLF**
CRLF(Carriage-Return Line-Feed),意思是回車換行,一般作為分隔符存在
請求頭和實體消息之間有一個CRLF分隔,響應頭部和響應實體之間用一個CRLF分隔
一般來說(分隔符類別):
```
CRLF->Windows-style
LF->Unix Style
CR->Mac Style
```
如下圖是對某請求的http報文結構的簡要分析

### cookie以及優化
cookie是瀏覽器的一種本地存儲方式,一般用來幫助客戶端和服務端通信的,常用來進行身份校驗,結合服務端的session使用。
場景如下(簡述):
```
在登陸頁面,用戶登陸了
此時,服務端會生成一個session,session中有對于用戶的信息(如用戶名、密碼等)
然后會有一個sessionid(相當于是服務端的這個session對應的key)
然后服務端在登錄頁面中寫入cookie,值就是:jsessionid=xxx
然后瀏覽器本地就有這個cookie了,以后訪問同域名下的頁面時,自動帶上cookie,自動檢驗,在有效時間內無需二次登陸。
```
上述就是cookie的常用場景簡述(當然了,實際情況下得考慮更多因素)
一般來說,cookie是不允許存放敏感信息的(千萬不要明文存儲用戶名、密碼),因為非常不安全,如果一定要強行存儲,首先,一定要在cookie中設置`httponly`(這樣就無法通過js操作了),另外可以考慮rsa等非對稱加密(因為實際上,瀏覽器本地也是容易被攻克的,并不安全)
另外,由于在同域名的資源請求時,瀏覽器會默認帶上本地的cookie,針對這種情況,在某些場景下是需要優化的。
譬如以下場景:
```
客戶端在域名A下有cookie(這個可以是登陸時由服務端寫入的)
然后在域名A下有一個頁面,頁面中有很多依賴的靜態資源(都是域名A的,譬如有20個靜態資源)
此時就有一個問題,頁面加載,請求這些靜態資源時,瀏覽器會默認帶上cookie
也就是說,這20個靜態資源的http請求,每一個都得帶上cookie,而實際上靜態資源并不需要cookie驗證
此時就造成了較為嚴重的浪費,而且也降低了訪問速度(因為內容更多了)
```
當然了,針對這種場景,是有優化方案的(多域名拆分)。具體做法就是:
- 將靜態資源分組,分別放到不同的域名下(如`static.base.com`)
- 而`page.base.com`(頁面所在域名)下請求時,是不會帶上`static.base.com`域名的cookie的,所以就避免了浪費
說到了多域名拆分,這里再提一個問題,那就是:
- 在移動端,如果請求的域名數過多,會降低請求速度(因為域名整套解析流程是很耗費時間的,而且移動端一般帶寬都比不上pc)
- 此時就需要用到一種優化方案:`dns-prefetch`(讓瀏覽器空閑時提前解析dns域名,不過也請合理使用,勿濫用)
關于cookie的交互,可以看下圖總結

### gzip壓縮
首先,明確`gzip`是一種壓縮格式,需要瀏覽器支持才有效(不過一般現在瀏覽器都支持), 而且gzip壓縮效率很好(高達70%左右)
然后gzip一般是由`apache`、`tomcat`等web服務器開啟
當然服務器除了gzip外,也還會有其它壓縮格式(如deflate,沒有gzip高效,且不流行)
所以一般只需要在服務器上開啟了gzip壓縮,然后之后的請求就都是基于gzip壓縮格式的, 非常方便。
### 長連接與短連接
首先看`tcp/ip`層面的定義:
- 長連接:一個tcp/ip連接上可以連續發送多個數據包,在tcp連接保持期間,如果沒有數據包發送,需要雙方發檢測包以維持此連接,一般需要自己做在線維持(類似于心跳包)
- 短連接:通信雙方有數據交互時,就建立一個tcp連接,數據發送完成后,則斷開此tcp連接
然后在http層面:
- `http1.0`中,默認使用的是短連接,也就是說,瀏覽器沒進行一次http操作,就建立一次連接,任務結束就中斷連接,譬如每一個靜態資源請求時都是一個單獨的連接
- http1.1起,默認使用長連接,使用長連接會有這一行`Connection: keep-alive`,在長連接的情況下,當一個網頁打開完成后,客戶端和服務端之間用于傳輸http的tcp連接不會關閉,如果客戶端再次訪問這個服務器的頁面,會繼續使用這一條已經建立的連接
注意: **keep-alive不會永遠保持,它有一個持續時間,一般在服務器中配置(如apache),另外長連接需要客戶端和服務器都支持時才有效**
### http 2.0
http2.0不是https,它相當于是http的下一代規范(譬如https的請求可以是http2.0規范的)
然后簡述下http2.0與http1.1的顯著不同點:
- http1.1中,每請求一個資源,都是需要開啟一個tcp/ip連接的,所以對應的結果是,每一個資源對應一個tcp/ip請求,由于tcp/ip本身有并發數限制,所以當資源一多,速度就顯著慢下來
- http2.0中,一個tcp/ip請求可以請求多個資源,也就是說,只要一次tcp/ip請求,就可以請求若干個資源,分割成更小的幀請求,速度明顯提升。
所以,如果http2.0全面應用,很多http1.1中的優化方案就無需用到了(譬如打包成精靈圖,靜態資源多域名拆分等)
然后簡述下http2.0的一些特性:
- 多路復用(即一個tcp/ip連接可以請求多個資源)
- 首部壓縮(http頭部壓縮,減少體積)
- 二進制分幀(在應用層跟傳送層之間增加了一個二進制分幀層,改進傳輸性能,實現低延遲和高吞吐量)
- 服務器端推送(服務端可以對客戶端的一個請求發出多個響應,可以主動通知客戶端)
- 請求優先級(如果流被賦予了優先級,它就會基于這個優先級來處理,由服務器決定需要多少資源來處理該請求。)
### https
https就是安全版本的http,譬如一些支付等操作基本都是基于https的,因為http請求的安全系數太低了。
簡單來看,https與http的區別就是: **在請求前,會建立ssl鏈接,確保接下來的通信都是加密的,無法被輕易截取分析**
一般來說,如果要將網站升級成https,需要后端支持(后端需要申請證書等),然后https的開銷也比http要大(因為需要額外建立安全鏈接以及加密等),所以一般來說http2.0配合https的體驗更佳(因為http2.0更快了)
一般來說,主要關注的就是SSL/TLS的握手流程,如下(簡述):
```
1. 瀏覽器請求建立SSL鏈接,并向服務端發送一個隨機數–Client random和客戶端支持的加密方法,比如RSA加密,此時是明文傳輸。
2. 服務端從中選出一組加密算法與Hash算法,回復一個隨機數–Server random,并將自己的身份信息以證書的形式發回給瀏覽器
(證書里包含了網站地址,非對稱加密的公鑰,以及證書頒發機構等信息)
3. 瀏覽器收到服務端的證書后
- 驗證證書的合法性(頒發機構是否合法,證書中包含的網址是否和正在訪問的一樣),如果證書信任,則瀏覽器會顯示一個小鎖頭,否則會有提示
- 用戶接收證書后(不管信不信任),瀏覽會生產新的隨機數–Premaster secret,然后證書中的公鑰以及指定的加密方法加密`Premaster secret`,發送給服務器。
- 利用Client random、Server random和Premaster secret通過一定的算法生成HTTP鏈接數據傳輸的對稱加密key-`session key`
- 使用約定好的HASH算法計算握手消息,并使用生成的`session key`對消息進行加密,最后將之前生成的所有信息發送給服務端。
4. 服務端收到瀏覽器的回復
- 利用已知的加解密方式與自己的私鑰進行解密,獲取`Premaster secret`
- 和瀏覽器相同規則生成`session key`
- 使用`session key`解密瀏覽器發來的握手消息,并驗證Hash是否與瀏覽器發來的一致
- 使用`session key`加密一段握手消息,發送給瀏覽器
5. 瀏覽器解密并計算握手消息的HASH,如果與服務端發來的HASH一致,此時握手過程結束,
```
**之后所有的https通信數據將由之前瀏覽器生成的session key并利用對稱加密算法進行加密**
- Web 開發筆記
- 從輸入 URL 到頁面加載完成的過程中都發生了什么事情?
- 從瀏覽器接收url到開啟網絡請求線程
- 開啟網絡線程到發出一個完整的http請求
- 從服務器接收到請求到對應后臺接收到請求
- 后臺和前臺的http交互
- http的緩存
- 解析頁面流程
- HTML解析,構建DOM
- CSS解析,構建CSSOM
- 資源外鏈的下載
- CSS的可視化格式模型
- 包含塊(Containing Block)
- 控制框(Controlling Box)
- BFC(Block Formatting Context)
- IFC(Inline Formatting Context)
- 其它
- JS引擎解析過程
- JS的解釋階段
- JS的預處理階段
- JS的執行階段
- 回收機制
- 參考資料
- JavaScript模塊化編程
- AMD
- requireJS
- CommonJS
- UMD
- ES6模塊
- 參考資料
- 使用 JavaScript 實現一門編程語言
- 如何使用 JavaScript 實現一門編程語言(1) —— 前言
- 如何使用 JavaScript 實現一門編程語言(2) —— 編寫一個解析器
- 如何使用 JavaScript 實現一門編程語言(3) —— Input stream
- 如何使用 JavaScript 實現一門編程語言(4) —— Token stream
- 如何使用 JavaScript 實現一門編程語言(5) —— AST
- 如何使用 JavaScript 實現一門編程語言(6) —— Interpreter
- 完整代碼
- 參考資料
- 前端布局概論
- 參考資料
- Windows 筆記
- 錯誤解決
- win10應用商店無法登錄提示0x80070426解決方法
- 使用技巧
- 設置 Hyper-V 和 VMware 共存
- Powershell
- WSL