http的那些事兒
請問你對http瞭解嗎?
- 還比較瞭解。HTTP是一個應用層協議,由請求和響應構成,是一個標準的客戶端伺服器模型。HTTP是一個無狀態的協議, 同一個客戶端的這次請求和上次請求是沒有對應關係。HTTP協議永遠都是客戶端發起請求,伺服器回送響應。見下圖:
一個網頁的載入過程是什麼樣的?
-
瀏覽器會因頁面上的css/js/image等靜態資源會多次發起連線請求,可以把這個過程分成兩部分
- html(jsp/php/aspx) 頁面載入(假設存在簡單的Nginx負載均衡)
- css/js/image等 網頁靜態資源載入(假設使用CDN)
-
整個過程大致經過了
DNS解析
-->負載均衡
-->web伺服器
-->瀏覽器渲染
- DNS解析: 將域名地址解析為對應的ip地址的過程。
- 建立TCP連線
- 發起Http請求,請求網頁, 其中, 請求方式的格式為:統一資源識別符號(URL)、協議版本號,後邊是MIME資訊包括請求修飾符、客戶機資訊和可能的內容。
- 伺服器返回請求頁面:其格式為一個狀態行,包括資訊的協議版本號、一個成功或錯誤的程式碼,後邊是MIME資訊包括伺服器資訊、實體資訊和可能的內容。
- 瀏覽器渲染: 根據頁面內容,生成DOM Tree;根據CSS內容,生成CSS Rule Tree; 呼叫JS執行引擎執行js程式碼。根據DOM Tree和CSS Rule Tree生成Render Tree(呈現樹)。根據Render Tree渲染網頁.
-
但是在瀏覽器解析頁面內容的時候,會發現頁面引用了其他未載入的image、css檔案、js檔案等靜態內容,因此開始了第二部分的靜態資源請求。
常用的請求方法有哪些
- GET/POST/PUT/DELETE/TRACE/OPTION/HEAD/CONNECT/PATCH
常用的Http 請求頭欄位有哪些?
-
Accept:瀏覽器端可以接受的MIME型別
- MIME型別:
- application: 某種二進位制附件,對於沒有subtype的情況,預設是application/octet-stream
- text: 文字,理論上可讀,對於沒有subtype的情況,預設是text/plain
- image: 影象
- audio: 音訊
- video: 視訊
- multipart: 多部分文件檔案(複合文件檔案)
- MIME型別:
-
Accept-Charset 設定接受的字元編碼
-
Accept-Encoding 設定接受的編碼格式
-
Accept-Language 設定接受的語言
-
Authorization 設定HTTP身份驗證的憑證
-
Cache-Control 設定請求響應鏈上所有的快取機制必須遵守的指令
-
Content-Length 設定請求體的位元組長度
-
Content-MD5 設定基於MD5演算法對請求體內容進行Base64二進位制編碼
-
Content-Type 設定請求體的MIME型別(適用POST和PUT請求)
-
Cookie 設定伺服器使用Set-Cookie傳送的http cookie
-
Date 設定訊息傳送的日期和時間
-
Expect 標識客戶端需要的特殊瀏覽器行為
-
Host 設定伺服器域名和TCP埠號,如果使用的是服務請求標準埠號,埠號可以省略
-
If-Match 設定客戶端的ETag,當時客戶端ETag和伺服器生成的ETag一致才執行,適用於更新自從上次更新之後沒有改變的資源
-
If-Modified-Since 設定更新時間,從更新時間到服務端接受請求這段時間內如果資源沒有改變,允許服務端返回304 Not Modified
-
If-None-Match 設定客戶端ETag,如果和服務端接受請求生成的ETage相同,允許服務端返回304 Not Modified
-
If-Range 設定客戶端ETag,如果和服務端接受請求生成的ETage相同,返回缺失的實體部分;否則返回整個新的實體
-
Forwarded 披露客戶端通過http代理連線web服務的源資訊
- Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43
- Forwarded: for=192.0.2.43, for=198.51.100.17
-
If-Unmodified-Since 設定更新時間,只有從更新時間到服務端接受請求這段時間內實體沒有改變,服務端才會傳送響
-
Max-Forwards 限制代理或閘道器轉發訊息的次數
-
Origin 標識跨域資源請求(請求服務端設定Access-Control-Allow-Origin響應欄位)
-
Pragma 設定特殊實現欄位,可能會對請求響應鏈有多種影響
-
Proxy-Authorization 為連線代理授權認證資訊
-
Range 請求部分實體,設定請求實體的位元組數範圍,具體可以參見HTTP/1.1中的Byte serving
- Range: bytes=500-999
-
Referer 設定前一個頁面的地址,並且前一個頁面中的連線指向當前請求,意思就是如果當前請求是在A頁面中傳送的,那麼referer就是A頁面的url地址
-
Upgrade 請求服務端升級協議
-
User-Agent 使用者代理的字串值
-
Via 通知伺服器代理請求
-
Warning 實體可能會發生的問題的通用警告
常用的Http 響應頭欄位有哪些?
- Access-Control-Allow-Origin 指定哪些站點可以參與跨站資源共享
- Accept-Patch 指定伺服器支援的補丁文件格式,適用於http的patch方法
- Accept-Patch: text/example;charset=utf-8
- Accept-Ranges 伺服器通過byte serving支援的部分內容範圍型別
- Age 物件在代理快取中暫存的秒數
- Allow 設定特定資源的有效行為,適用方法不被允許的http 405錯誤
- Content-Encoding 設定資料使用的編碼型別
- Content-Language 為封閉內容設定自然語言或者目標使用者語言
- Content-Length 響應體的位元組長度
- Content-Location 設定返回資料的另一個位置
- Content-MD5 設定基於MD5演算法對響應體內容進行Base64二進位制編碼
- Content-Range 標識響應體內容屬於完整訊息體中的那一部分
- Content-Type 設定響應體的MIME型別
- ETag 特定版本資源的識別符號,通常是訊息摘要
- Expires 設定響應體的過期時間
- Last-Modified 設定請求物件最後一次的修改日期
- Set-Cookie 設定HTTP Cookie
- Server 伺服器名稱
- Status 設定HTTP響應狀態
- Transfer-Encoding 設定傳輸實體的編碼格式,目前支援的格式: chunked, compress, deflate, gzip, identity
- Upgrade 請求客戶端升級協議
http的響應狀態碼
-
1xx Informational(資訊性狀態碼)
-
2XX Success(成功狀態碼)
- 200(OK 客戶端發過來的資料被正常處理
- 204(Not Content 正常響應,沒有實體
- 206(Partial Content 範圍請求,返回部分資料,響應報文中由Content-Range指定實體內容
-
3xx Redirection(重定向)
- 301(Moved Permanently) 永久重定向
- 302(Found) 臨時重定向,規範要求方法名不變,但是都會改變
- 303(See Other) 和302類似,但必須用GET方法
- 304(Not Modified) 狀態未改變 配合(If-Match、If-Modified-Since、If-None_Match、If-Range、If-Unmodified-Since)
- 307(Temporary Redirect) 臨時重定向,不該改變請求方法
-
4XX Client Error(客戶端錯誤狀態碼)
- 400(Bad Request) 請求報文語法錯誤
- 401 (unauthorized) 需要認證
- 403(Forbidden) 伺服器拒絕訪問對應的資源
- 404(Not Found) 伺服器上無法找到資源
-
5xx Server Error(伺服器錯誤狀態碼)
- 500(Internal Server Error)伺服器故障
- 503(Service Unavailable) 伺服器處於超負載或正在停機維護
url模組
url.parse(urlStr,[parseQueryString]);
複製程式碼
- href 被轉換的原URL字串
- protocal 客戶端發出請求時使用的協議
- slashes 在協議與路徑之間是否使用了//分隔符
- host URL字串中的完整地址和埠號
- auth URL字串中的認證部分
- hostname URL字串中的完整地址
- port URL字串中的埠號
- pathname URL字串的路徑,不包含查詢字串
- search 查詢字串,包含?
- path 路徑,包含查詢字串
- query 查詢字串,不包含起始字串?
- hash 雜湊字串,包含#
http的應用
1. 範圍請求 206
客戶端需要傳送一個Range:bytes=0-5 伺服器 Sccept-Range:bytes Content-Range: bytes 0-5/705
// 在命令列模式範圍請求
curl -v --header "Range: bytes=0-5" http://www.baidu.com/img/baidu_jgylogo3.gif
複製程式碼
server.js
let http = require('http')
let path = require('path')
let p = path.resolve(__dirname, '1.txt');
let fs = require('mz/fs')
const port = 3000;
async function listener(req, res) {
let range = req.headers['range'];
if (range) {
let [, start, end] = range.match(/(\d*)-(\d*)/);
let statObj = await fs.stat(p);
let total = statObj.size;
start = start ? Number(start) : 0;
end = end ? Number(end) : total - 1;
res.statusCode = 206;
res.setHeader('Accept-Ranges', 'bytes');
res.setHeader('Content-Range', `bytes ${start}-${end}/${total}`);
fs.createReadStream(p, {start, end, encoding: 'utf8'}).pipe(res);
} else {
// 讀取檔案 並響應給客戶端
fs.createReadStream(p).pipe(res)
}
}
let server = http.createServer(listener);
server.listen(port, () => {
console.log(`server start at ${port}`)
})
複製程式碼
client.js
// 定時發起請求,並把請求到的結果寫入download.txt,實現斷點續傳的功能
let http = require('http');
let fs = require('fs');
let ws = fs.createWriteStream(__dirname + '/download.txt')
let start = 0;
const unit = 4;
let config = {
host: 'localhost',
port: 3000
}
function download() {
config.headers = {
'Range': `bytes=${start}-${start + unit}`
}
start += 5;
let client = http.request(config, res => {
let total = res.headers['content-range'].split('/')[1];
let buffers = [];
res.on('data', data => {
buffers.push(data);
});
res.on('end', () => {
let result = Buffer.concat(buffers);
ws.write(result)
setTimeout(() => {
console.log('start <= total', start, total)
if (start <= total) {
download();
}
}, 1000);
})
})
client.end(); // 必須呼叫end 否則不會傳送內容
}
download();
複製程式碼
2. 防盜鏈
server.js
/**
* 實現原理
* host: 表示資源的地址,一般是伺服器地址,預設埠號80
* refer/referered: 使用資源的端地址
*
* 在伺服器端配置資源可訪問的白名單 WHITELIST,
* 當host與refer/referered的域名不相同,並且refer/referered的地址在WHITELIST中時,
* 表示可以該端可以訪問伺服器中的資源,否則不可訪問.
* */
let http = require('http');
let fs = require('fs');
let url = require('url');
let path = require('path');
let static = path.resolve(__dirname);
const WHITELIST = ['linda_1.cn'];
let server = http.createServer(async (req, res) => {
let { pathname } = url.parse(req.url);
let p = path.join(static, pathname);
let flag = true;
try{
fs.accessSync(p)
} catch(e) {
flag = false;
}
if (flag) {
let refer = req.headers['referer'] || req.headers['referered'];
if (refer) {
let hostname = req.headers['host'].split(':')[0];
let refername = url.parse(refer).hostname;
if (refername != hostname && !WHITELIST.includes(refername)) {
fs.createReadStream(path.join(static, '/images/girl.png')).pipe(res);
} else {
fs.createReadStream(p).pipe(res);
}
} else {
fs.createReadStream(p).pipe(res);
}
} else{
res.statusCode = 404;
res.end();
}
})
server.listen(3000, () => {
console.log('server is starting on 3000...')
})
複製程式碼
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<img src="http://localhost:3000/images/boy.png"/>
</body>
</html>
複製程式碼
mac下配置域名與主機名對映
vim /etc/hosts
127.0.0.1 linda_1.cn
127.0.0.1 linda_2.cn
複製程式碼
配置好後,可在瀏覽器中輸入不同的域名,來測試圖片的顯示情況。