導(dǎo)語(yǔ)
一屏長(zhǎng)文,更深入的了解HTTP協(xié)議。對(duì)于入門(mén)前端不久的同學(xué)來(lái)說(shuō),可能學(xué)習(xí)前端,就從HTML,CSS,JS學(xué)起,然后再入手一個(gè)框架,但對(duì)于http的理解可能還僅在知道一些面試中關(guān)于http的考題或比較少在代碼層面去真正理解一些理論的知識(shí),看完本篇希望你能對(duì)http有一個(gè)較為深入的理解,并且能在開(kāi)發(fā)中對(duì)你有所幫助
進(jìn)入HTTP
http經(jīng)典圖
[圖片上傳失敗...(image-ee3110-1583916328292)]
瀏覽器輸入U(xiǎn)RL后HTTP請(qǐng)求返回的完整過(guò)程
網(wǎng)絡(luò)協(xié)議分層
經(jīng)典五層模型
[圖片上傳失敗...(image-d92d66-1583916328292)]
后續(xù)小節(jié)我們會(huì)涉及到的知識(shí)點(diǎn)就是應(yīng)用層和傳輸層。
物理層:主要作用就是定義物理設(shè)備如何傳輸數(shù)據(jù)
數(shù)據(jù)鏈路層:在通信的實(shí)體間建立數(shù)據(jù)鏈路連接
網(wǎng)絡(luò)層:在節(jié)點(diǎn)之間傳輸創(chuàng)建邏輯鏈路
傳輸層
它旨在向用戶(hù)提供可靠的端到端的服務(wù),數(shù)據(jù)傳輸過(guò)程可能涉及到分片分包等,以及傳輸過(guò)去如何組裝等,這個(gè)無(wú)需讓開(kāi)發(fā)者來(lái)做,因此傳輸層向高層屏蔽了下層數(shù)據(jù)通信的細(xì)節(jié)。正因?yàn)槿绱耍斫鈧鬏攲拥募?xì)節(jié)能夠讓我們實(shí)現(xiàn)一個(gè)性能更高HTTP實(shí)現(xiàn)方式
應(yīng)用層
它幫我們實(shí)現(xiàn)了http協(xié)議,為應(yīng)用層提供了很多服務(wù),并且構(gòu)建與TCP協(xié)議之上,屏蔽網(wǎng)絡(luò)傳輸相關(guān)細(xì)節(jié)
http的三次握手
http只有請(qǐng)求和響應(yīng)的概念,創(chuàng)建連接是屬于TCP的操作,而連接的請(qǐng)求和響應(yīng)是在tcp連接之上的。這是新手很容搞混的一點(diǎn)。在http1.1中連接可以保持,這樣的好處是因?yàn)閔ttp三次握手是有開(kāi)銷(xiāo)的。http2.0中請(qǐng)求可以在同一個(gè)tcp連接中并發(fā),也是大大節(jié)省了建立連接的開(kāi)銷(xiāo)。具體后續(xù)將詳講,現(xiàn)在說(shuō)回http三次握手,如下圖

首先客戶(hù)端發(fā)送一個(gè)要?jiǎng)?chuàng)建連接的數(shù)據(jù)包請(qǐng)求到服務(wù)端,包含一個(gè)標(biāo)志位SYN=1和seq=Y。
然后服務(wù)端會(huì)開(kāi)啟一個(gè)TCP的socket端口,返回一個(gè)標(biāo)志位SYN=1,確認(rèn)位ACK=x+1和seq=y的數(shù)據(jù)包
最后客戶(hù)端再發(fā)送一個(gè)ACK=Y+1,Seq=Z的數(shù)據(jù)包到服務(wù)端
這就是HTTP的三次握手全過(guò)程,三次握手的原因是防止服務(wù)端開(kāi)啟一些無(wú)用連接,因?yàn)榫W(wǎng)絡(luò)連接是有延遲的,如果沒(méi)有第三次連接,由于網(wǎng)絡(luò)延遲,客戶(hù)端關(guān)閉了連接,而服務(wù)端一直在等待客戶(hù)端請(qǐng)求發(fā)送過(guò)來(lái),這就造成了資源浪費(fèi),有了三次握手,就能確認(rèn)請(qǐng)求發(fā)送和響應(yīng)請(qǐng)求沒(méi)有問(wèn)題。
HTTP報(bào)文
[圖片上傳失敗...(image-ada0fb-1583916328292)]
請(qǐng)求報(bào)文中首行包括一些請(qǐng)求方法 請(qǐng)求資源地址和http協(xié)議版本。
響應(yīng)報(bào)文中首行包括協(xié)議版本、http狀態(tài)碼和狀態(tài)碼含義等
HTTP方法
用來(lái)定義對(duì)于資源的操作
HTTP方法:GET, POST,HEAD,OPTIONS,PUT,DELETE,TRACE和CONNECT
GET: 通常用于請(qǐng)求服務(wù)器發(fā)送某些資源
HEAD: 請(qǐng)求資源的頭部信息, 并且這些頭部與 HTTP GET 方法請(qǐng)求時(shí)返回的一致. 該請(qǐng)求方法的一個(gè)使用場(chǎng)景是在下載一個(gè)大文件前先獲取其大小再?zèng)Q定是否要下載, 以此可以節(jié)約帶寬資源
OPTIONS: 用于獲取目的資源所支持的通信選項(xiàng)
POST: 發(fā)送數(shù)據(jù)給服務(wù)器
PUT: 用于新增資源或者使用請(qǐng)求中的有效負(fù)載替換目標(biāo)資源的表現(xiàn)形式
DELETE: 用于刪除指定的資源
PATCH: 用于對(duì)資源進(jìn)行部分修改
CONNECT: HTTP/1.1協(xié)議中預(yù)留給能夠?qū)⑦B接改為管道方式的代理服務(wù)器
TRACE: 回顯服務(wù)器收到的請(qǐng)求,主要用于測(cè)試或診斷
參考:面試官(9):可能是全網(wǎng)最全的http面試答案
Http Code碼
2XX 成功
200 OK,表示從客戶(hù)端發(fā)來(lái)的請(qǐng)求在服務(wù)器端被正確處理
201 Created 請(qǐng)求已經(jīng)被實(shí)現(xiàn),而且有一個(gè)新的資源已經(jīng)依據(jù)請(qǐng)求的需要而建立
202 Accepted 請(qǐng)求已接受,但是還沒(méi)執(zhí)行,不保證完成請(qǐng)求
204 No content,表示請(qǐng)求成功,但響應(yīng)報(bào)文不含實(shí)體的主體部分
206 Partial Content,進(jìn)行范圍請(qǐng)求
3XX 重定向
301 moved permanently,永久性重定向,表示資源已被分配了新的 URL
302 found,臨時(shí)性重定向,表示資源臨時(shí)被分配了新的 URL
303 see other,表示資源存在著另一個(gè) URL,應(yīng)使用 GET 方法丁香獲取資源
304 not modified,表示服務(wù)器允許訪(fǎng)問(wèn)資源,但因發(fā)生請(qǐng)求未滿(mǎn)足條件的情況
307 temporary redirect,臨時(shí)重定向,和302含義相同
4XX 客戶(hù)端錯(cuò)誤
400 bad request,請(qǐng)求報(bào)文存在語(yǔ)法錯(cuò)誤
401 unauthorized,表示發(fā)送的請(qǐng)求需要有通過(guò) HTTP 認(rèn)證的認(rèn)證信息
403 forbidden,表示對(duì)請(qǐng)求資源的訪(fǎng)問(wèn)被服務(wù)器拒絕
404 not found,表示在服務(wù)器上沒(méi)有找到請(qǐng)求的資源
408 Request timeout, 客戶(hù)端請(qǐng)求超時(shí)
409 Confict, 請(qǐng)求的資源可能引起沖突
5XX 服務(wù)器錯(cuò)誤
500 internal sever error,表示服務(wù)器端在執(zhí)行請(qǐng)求時(shí)發(fā)生了錯(cuò)誤
501 Not Implemented 請(qǐng)求超出服務(wù)器能力范圍,例如服務(wù)器不支持當(dāng)前請(qǐng)求所需要的某個(gè)功能,或者請(qǐng)求是服務(wù)器不支持的某個(gè)方法
503 service unavailable,表明服務(wù)器暫時(shí)處于超負(fù)載或正在停機(jī)維護(hù),無(wú)法處理請(qǐng)求
505 http version not supported 服務(wù)器不支持,或者拒絕支持在請(qǐng)求中使用的 HTTP 版本
通過(guò)node創(chuàng)建一個(gè)簡(jiǎn)單的node服務(wù)
server.js
const http = require('http')
http.createServer(function(request, response) {
console.log('request come',request.url)
response.end('hello world')
}).listen(8888)
console.log('server.listening on 8888')
終端進(jìn)入到server.js文件下,執(zhí)行node server.js 瀏覽器輸入localhost:8888,即可看見(jiàn)'hello world'
HTTP特性總覽
瀏覽器就是最常見(jiàn)的客戶(hù)端,瀏覽器為了保證數(shù)據(jù)傳輸?shù)陌踩?,具有同源策略,所謂同源是指:域名、協(xié)議、端口相同
同源策略又可以分為以下兩種:
DOM同源策略:禁止對(duì)不同源頁(yè)面DOM進(jìn)行操作。這里主要場(chǎng)景就是iframe跨域的情況,不同域名的iframe是限制互相訪(fǎng)問(wèn)的
XMLHttpRequest同源策略: 靜止使用XHR對(duì)象向不同源的服務(wù)器發(fā)起HTTP請(qǐng)求
了解了瀏覽器同源策略的作用,如果不同源發(fā)出請(qǐng)求,就會(huì)產(chǎn)生跨域。但是在實(shí)際開(kāi)發(fā)中,我們很多時(shí)候需要突破這樣的限制,方法有以下幾種(后面會(huì)有方法實(shí)踐):
JSONP: 利用script的src標(biāo)簽不受同源限制,動(dòng)態(tài)創(chuàng)建script標(biāo)簽
CORS: 服務(wù)端設(shè)置access-allow-origin
通過(guò)window.name跨域
通過(guò)document.domain
通過(guò)Html5的postMessage
跨域知識(shí)詳細(xì)可參考前端跨域整理
通過(guò)代碼來(lái)看下具體是怎么樣的
cors跨域
創(chuàng)建server.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)
server.js同目錄下創(chuàng)建hello.html,js代碼如下(地址換成自己電腦ip地址)
var xhr = new XMLHttpRequest()
xhr.open('GET','http://0.0.0.0:8887')
xhr.send()
同目錄下創(chuàng)建server2.js
const http = require('http')
http.createServer(function (request, response) {
console.log('request come',request.url)
response.end('hello world')
}).listen(8887)
console.log('server listening on 8887')
分別啟動(dòng)server.js和server2.js,并在瀏覽器輸入localhost:8888
[圖片上傳失敗...(image-b633cb-1583916328292)]
解決方案:在server2.js中加入
response.writeHead(200, {
'Access-Control-Allow-Origin': '*'
})
跨域請(qǐng)求成功
<font color='red'> 注意 </font>:當(dāng)我們沒(méi)有加跨域請(qǐng)求頭的時(shí)候,可以發(fā)現(xiàn)服務(wù)端(也就是運(yùn)行server2.js的終端)依然能收到請(qǐng)求,只是返回的內(nèi)容在瀏覽器端沒(méi)有接收到,因此跨域并不是發(fā)不出請(qǐng)求,只是返回的內(nèi)容被瀏覽器攔截了而已
CORS跨域限制以及預(yù)檢請(qǐng)求驗(yàn)證
修改hello.html,js改為
fetch('http://192.168.0.106:8887/', {
method: 'POST',
headers: {
'Test-Cors': '123'
}
})
瀏覽器訪(fǎng)問(wèn)localhost:8888,出現(xiàn)
[圖片上傳失敗...(image-94a6dd-1583916328292)]
原因是什么呢,且聽(tīng)我慢慢道來(lái)
瀏覽器的請(qǐng)求在跨域的時(shí)候默認(rèn)允許的方法為
GET、HEAD、POST,其他方法不允許,需要有預(yù)檢請(qǐng)求
允許的Content-Type為
text/plain
multipart/form-data
application/x-www-form-urlencoded
其他Type也需要預(yù)檢請(qǐng)求
其他限制包括header 詳見(jiàn)默認(rèn)允許header,XMLHttpRequestUpload對(duì)象均沒(méi)有注冊(cè)任何事件監(jiān)聽(tīng)器以及請(qǐng)求中沒(méi)有使用ReadableStream對(duì)象。后兩個(gè)實(shí)際接觸不多,可以不深究
說(shuō)回預(yù)檢請(qǐng)求,先看下圖
[圖片上傳失敗...(image-3b5b54-1583916328292)]
注意:新版chorme瀏覽器改了,在network里面看不到了,換個(gè)瀏覽器
如果我們需要這個(gè)請(qǐng)求頭,在server2.js中的response.writeHead里面添加
'Access-Control-Allow-Headers': 'X-Test-Cors'
同理,如果需要添加允許的方法,可以添加
'Access-Control-Allow-Headers': 'Delete,PUT'
如果我們希望在某一段時(shí)間內(nèi)發(fā)送的跨域請(qǐng)求不再發(fā)送預(yù)檢請(qǐng)求,可以在response.writeHead中設(shè)置
'Access-Control-Max-Age': '100'
JSONP跨域
去掉server.js中的請(qǐng)求頭,并修改hello.html中js為
<script src="http://192.168.0.107:8887/"></script>
這就是一個(gè)簡(jiǎn)單的jsonp跨域,具體的可以參考上面的跨域文章
瀏覽器的緩存
為了減少請(qǐng)求,加快頁(yè)面訪(fǎng)問(wèn)速度。開(kāi)發(fā)者可以根據(jù)需要對(duì)資源進(jìn)行緩存。分為強(qiáng)緩存和協(xié)商緩存,通過(guò)http首部字段進(jìn)行設(shè)置
強(qiáng)緩存
Expires是一個(gè)絕對(duì)時(shí)間,即服務(wù)器時(shí)間。瀏覽器檢查當(dāng)前時(shí)間,如果還沒(méi)到失效時(shí)間就直接使用緩存
但是該方法存在一個(gè)問(wèn)題:服務(wù)器時(shí)間與客戶(hù)端時(shí)間可能不一致。因此該字段已經(jīng)很少使用
cache-control中的max-age保存一個(gè)相對(duì)時(shí)間。例如Cache-Control: max-age = 484200,表示瀏覽器收到文件,緩存在484200S內(nèi)均有效。如果同時(shí)存在cache-control和Expires,瀏覽器總是優(yōu)先使用cache-control
協(xié)商緩存
last-modified是第一次請(qǐng)求資源時(shí),服務(wù)器返回的字段,表示最后一段更新的時(shí)間。下一次瀏覽器
請(qǐng)求資源時(shí)就發(fā)送if-modified-since字段。服務(wù)器用本地last-modified時(shí)間與if-modified-since
時(shí)間比較,如果不一致則認(rèn)為緩存已過(guò)期并返回新資源給瀏覽器;如果時(shí)間一致則發(fā)送304狀態(tài)碼,讓瀏覽器繼續(xù)使用緩存
Etag 資源的實(shí)體標(biāo)識(shí)(哈希字符串),當(dāng)資源內(nèi)容更新時(shí),Etag會(huì)改變。服務(wù)器會(huì)判斷Etag是否發(fā)送變化。如果變化則返回新資源,否則返回304
接下來(lái)我們?cè)敿?xì)看下Cache-Control
- 可緩存性
public、private、no-cache、no-store
public指的是http返回的內(nèi)容所經(jīng)過(guò)的任何路徑(包括代理服務(wù)器和客戶(hù)端瀏覽器)當(dāng)- 中都可以被緩存
private指的是只有發(fā)起請(qǐng)求的瀏覽器才可以緩存
no-cache可以在本地緩存,可以在代理服務(wù)器緩存,但是這個(gè)緩存要服務(wù)器驗(yàn)證才可以使用
no-store 徹底得禁用緩沖,本地和代理服務(wù)器都不緩沖,每次都從服務(wù)器獲取
- 到期
指的緩存時(shí)間,最常用的就是max-age,單位是秒,指的就是緩存的有效期是多長(zhǎng)時(shí)間
s-max-age這個(gè)是代理服務(wù)器的緩存時(shí)間,只在代理服務(wù)器生效
- 重新驗(yàn)證
must-revalidate如果設(shè)置的緩存已經(jīng)過(guò)期了,必須去原服務(wù)端請(qǐng)求,然后重新驗(yàn)證數(shù)據(jù)是否已經(jīng)過(guò)期
proxy-revalidate應(yīng)用于代理服務(wù)器緩存
理論說(shuō)完了,接下來(lái)我們通過(guò)實(shí)戰(zhàn)看看
修改test.html,js部分修改為
<script src="./script.js"></script>
修改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=2020',
// 'Last-Modified': '2020',
//'Etag': '20200217'
})
response.end('console.log("script loaded")')
}
}).listen(8888)
console.log('server start on the 8888')
打開(kāi)開(kāi)發(fā)者工具,我們可以看到scripts第一次加載之后,再請(qǐng)求就會(huì)從緩存中獲取,看下圖黃色圈中部分,注意需要把紅色勾選去掉
[圖片上傳失敗...(image-ac4970-1583916328292)]
再看下響應(yīng)的header
[圖片上傳失敗...(image-6ec159-1583916328292)]
如果沒(méi)有設(shè)置緩存,每次請(qǐng)求都會(huì)從服務(wù)器獲取。需要驗(yàn)證可以自行測(cè)試下
緩存命中可以查看這張圖
[圖片上傳失敗...(image-3c9111-1583916328292)]
協(xié)商緩存驗(yàn)證頭(Last-Modified,Etag)
現(xiàn)在我們并不是真正需要驗(yàn)證資源,而是為了驗(yàn)證瀏覽器是否會(huì)把驗(yàn)證頭帶過(guò)來(lái),因此我們可以隨便設(shè)個(gè)Last-Modified和Etag,在server.js中修改response.writeHead
response.writeHead(200, {
'Content-Type': 'text/javascript',
'Cache-Control':'max-age=2020, no-cache',
'Last-Modified': '2020',
'Etag': '20200217'
})
啟動(dòng)服務(wù),下圖是第一次請(qǐng)求,可以看到響應(yīng)頭里面有Last-Modify和Etag
[圖片上傳失敗...(image-50dd66-1583916328292)]
再發(fā)送請(qǐng)求,可以看到在Request Headers中出現(xiàn),if-Modified-since和if-None-Match
[圖片上傳失敗...(image-b31b81-1583916328292)]
到這里還沒(méi)有結(jié)束,當(dāng)我們驗(yàn)證緩存完,如果還沒(méi)有過(guò)期,我們希望直接拿緩存,但是我們?cè)倏聪挛覀兊膔esponse
[圖片上傳失敗...(image-e93fbc-1583916328292)]
由圖發(fā)現(xiàn)response中還是有資源返回,并且code碼是200,這是為啥呢,原因很簡(jiǎn)單,我們?cè)诜?wù)端還沒(méi)有對(duì)if-Modified-since和if-None-Match進(jìn)行處理,我們把server.jshttp.createServer修改為
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') {
const etag = request.headers['if-none-match']
if (etag === '20200217') {
response.writeHead(304, {
'Content-Type': 'text/javascript',
'Cache-Control': 'max-age=2020,no-cache',
'Last-Modified': '2020',
'Etag': '20200217'
})
response.end('')
} else {
response.writeHead(200, {
'Content-Type': 'text/javascript',
'Cache-Control': 'max-age=2020,no-cache',
'Last-Modified': '2020',
'Etag': '20200217'
})
response.end('console.log("script loaded twice")')
}
}
}).listen(8888)
不管是否需要傳資源,我們都要在最后response.end,不然本次請(qǐng)求一直沒(méi)有結(jié)束。修改完之后,我們可以看到請(qǐng)求code碼變成了304,時(shí)間縮短了,但是在response中還是有資源,這又是什么情況,這時(shí)候我們確實(shí)成功驗(yàn)證了緩存,并拿取的是緩存資源,在瀏覽器的response中,瀏覽器會(huì)自動(dòng)把拿到的緩存資源顯示出來(lái),并沒(méi)有在服務(wù)器獲取。如果需要驗(yàn)證,可以自行在第一個(gè)response.end中添加其他內(nèi)容,再看瀏覽器接口的response
剛才讓瀏覽器去做協(xié)商緩存,是因?yàn)槲覀冊(cè)O(shè)置了no-cahce,我們把no-cache刪除,瀏覽器應(yīng)該是直接拿緩存(因?yàn)槲覀冊(cè)O(shè)置的max-age=2020),驗(yàn)證之前,我們得在剛才打開(kāi)的頁(yè)面去清楚瀏覽器的緩存,然后刪除代碼中的no-cache,重復(fù)刷新,都可以看到script.js 是from mermory cache。no-store也可再自行驗(yàn)證下
最后再提一下關(guān)于last-modify和Etag,last-modify我們可以在把數(shù)據(jù)庫(kù)取出的時(shí)候,拿取一個(gè)時(shí)間,最為數(shù)據(jù)的update time.Etag的話(huà),數(shù)據(jù)取出的時(shí)候做個(gè)數(shù)據(jù)簽名,存入Etag
cookie和session
http是不保存狀態(tài)的協(xié)議,因此我們需要一個(gè)身份能來(lái)證明訪(fǎng)問(wèn)服務(wù)器的是誰(shuí),這里我們用到的就是cookie和session
cookie的特性:通過(guò)Set-Cookie設(shè)置、下次請(qǐng)求會(huì)自動(dòng)帶上、鍵值對(duì)形式,可以設(shè)置多個(gè)
cookie的屬性:max-age和expires設(shè)置過(guò)期時(shí)間、HttpOnly無(wú)法通過(guò)document.cookie訪(fǎng)問(wèn)
接下來(lái)通過(guò)代碼看下cookie,在server.js中修改response.writeHead
{
'Content-Type': 'text/html',
'Set-Cookie': ['id=123;max-age=2','time=2020']
}
啟動(dòng)服務(wù)后,可以在application中的cookie看到兩個(gè)cookie或者network中的接口中。id=123這個(gè)cookie設(shè)置了過(guò)期時(shí)間,過(guò)一會(huì)兒再刷新可以看到id=123這個(gè)cookie消失了
前面說(shuō)過(guò),cookie跨域不共享,但是如果我想一級(jí)域名下的二級(jí)域名共享cookie,這時(shí)候我可以通過(guò)設(shè)置document.domain來(lái)實(shí)現(xiàn),具體如下
{
'Content-Type': 'text/html',
'Set-Cookie': ['id=123;max-age=2','time=2020;domain=test.com']
}
修改后,可添加host自行驗(yàn)證下
HTTP長(zhǎng)連接
長(zhǎng)連接指的是在一次請(qǐng)求完成后,是否要關(guān)閉TCP連接。如果TCP連接一直開(kāi)著,會(huì)有一定資源消耗,但是如果還有請(qǐng)求,就可以繼續(xù)在本次TCP連接上發(fā)送,這樣可以不用再三次握手,節(jié)省了時(shí)間。實(shí)際情況中,網(wǎng)站并發(fā)量比較大,因此是保持長(zhǎng)連接的,并且長(zhǎng)連接是可以設(shè)置超時(shí)時(shí)間的,如果在這個(gè)時(shí)間里都沒(méi)有發(fā)送請(qǐng)求了,那么連接就會(huì)關(guān)閉
接下來(lái)我們可以分析下實(shí)際場(chǎng)景,以百度首頁(yè)為例,打開(kāi)開(kāi)發(fā)者面板,然后network中,右擊name屬性,勾選Connection ID
我們看到大部分連接都有復(fù)用,在http1.1中,一個(gè)域名下最大TCP連接數(shù)為6個(gè)(Chorme),因此剛開(kāi)始的時(shí)候會(huì)一下創(chuàng)建6個(gè)連接,后面的請(qǐng)求會(huì)復(fù)用這些連接。
通過(guò)代碼來(lái)驗(yàn)證下這部分內(nèi)容,首先創(chuàng)建一個(gè)test.html
<body>
<img src="/test1.jpg" alt="">
<img src="/test2.jpg" alt="">
<img src="/test3.jpg" alt="">
<img src="/test4.jpg" alt="">
<img src="/test5.jpg" alt="">
<img src="/test6.jpg" alt="">
<img src="/test7.jpg" alt="">
<img src="/test8.jpg" alt="">
</body>
新建server.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')
const img = fs.readFileSync('timg.jpg')
if (request.url === '/') {
response.writeHead(200, {
'Content-Type': 'text/html',
// 'Connection': 'close'
})
response.end(html)
} else {
response.writeHead(200, {
'Content-Type': 'image/jpg',
// 'Connection': 'close'
})
response.end(img)
}
}).listen(8888)
console.log('server start on the 8888')
啟動(dòng)服務(wù)
[圖片上傳失敗...(image-b3942d-1583916328292)]
可以看下Waterfall,網(wǎng)絡(luò)請(qǐng)求分時(shí)過(guò)程。如果需要關(guān)閉長(zhǎng)連接,Connection的值可以寫(xiě)為close
這里再簡(jiǎn)單提下http2.0現(xiàn)在使用信道復(fù)用技術(shù),只需要?jiǎng)?chuàng)建一個(gè)TCP連接,所有同域下請(qǐng)求都可以并發(fā)。如果要使用http2.0,需要保證請(qǐng)求時(shí)https協(xié)議,并且后端需要做較大的改變,因此現(xiàn)在http2.0的使用目前還沒(méi)大面積
Redirect
當(dāng)我們通過(guò)url去訪(fǎng)問(wèn)一個(gè)資源的時(shí)候,該資源已經(jīng)不再u(mài)rl指定的位置了,服務(wù)器應(yīng)通知客戶(hù)端該資源現(xiàn)在所處的位置,瀏覽器再去請(qǐng)求該資源。
通過(guò)代碼來(lái)看下,新建一個(gè)server.js
const http = require('http')
const fs = require('fs')
http.createServer(function (request, response) {
console.log('request comme', request.url)
if (request.url === '/') {
response.writeHead(302, {
'Location': '/new'
})
response.end()
}
if (request.url === '/new') {
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end('<div>hello world</div>')
}
}).listen(8888)
console.log('server listening on 8888')
此處測(cè)試是在同域的情況下,所以只寫(xiě)了一個(gè)路由,如果不相同,則把真正的地址替換/new.啟動(dòng)服務(wù),輸入localhost:8888之后,會(huì)直接跳轉(zhuǎn)到資源真正的位置,并且在network中也可查看發(fā)現(xiàn),除了圖標(biāo),有兩個(gè)請(qǐng)求。
代碼中我們寫(xiě)的code碼是302,如果我們改成200,就會(huì)發(fā)現(xiàn)沒(méi)有辦法重定向。302是臨時(shí)重定向,301是永久重定向,前面我們已經(jīng)說(shuō)過(guò)。如果我們把上面的302code碼改成301,我們會(huì)在終端中發(fā)現(xiàn),除了第一次,不管我們后面再輸入localhost:8888多少次,終端打印請(qǐng)求都只有重定向后的請(qǐng)求,只是因?yàn)闉g覽器記住了原地址被永久重定向了,所以,不會(huì)向原路徑發(fā)起請(qǐng)求。在實(shí)際開(kāi)發(fā)中,應(yīng)當(dāng)謹(jǐn)慎使用永久重定向,因?yàn)橐坏┯谰弥囟ㄏ蛄?,?huì)在瀏覽器盡可能長(zhǎng)的時(shí)間保留定向后的資源路徑而不會(huì)請(qǐng)求原路徑
結(jié)束語(yǔ)
本次分享目的是通過(guò)代碼來(lái)把原來(lái)我們知道的一些知識(shí)點(diǎn)可以再深入一些,梳理好Http知識(shí)的來(lái)龍去脈。希望能對(duì)一些小伙伴有所幫助,如果大家喜歡我的行文風(fēng)格的話(huà),我接下來(lái)將帶入web 服務(wù)器Nginx的一些實(shí)戰(zhàn),在實(shí)際開(kāi)發(fā)中我們會(huì)用nginx做代理和一些cache,因此作為一個(gè)http服務(wù),掌握它當(dāng)然也不可或缺