node http模組

Linda_0504發表於2018-07-15

http的那些事兒

請問你對http瞭解嗎?

  • 還比較瞭解。HTTP是一個應用層協議,由請求和響應構成,是一個標準的客戶端伺服器模型。HTTP是一個無狀態的協議, 同一個客戶端的這次請求和上次請求是沒有對應關係。HTTP協議永遠都是客戶端發起請求,伺服器回送響應。見下圖:
    pkg

一個網頁的載入過程是什麼樣的?

  • 瀏覽器會因頁面上的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: 多部分文件檔案(複合文件檔案)
  • 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(成功狀態碼)

    1. 200(OK 客戶端發過來的資料被正常處理
    2. 204(Not Content 正常響應,沒有實體
    3. 206(Partial Content 範圍請求,返回部分資料,響應報文中由Content-Range指定實體內容
  • 3xx Redirection(重定向)

    1. 301(Moved Permanently) 永久重定向
    2. 302(Found) 臨時重定向,規範要求方法名不變,但是都會改變
    3. 303(See Other) 和302類似,但必須用GET方法
    4. 304(Not Modified) 狀態未改變 配合(If-Match、If-Modified-Since、If-None_Match、If-Range、If-Unmodified-Since)
    5. 307(Temporary Redirect) 臨時重定向,不該改變請求方法
  • 4XX Client Error(客戶端錯誤狀態碼)

    1. 400(Bad Request) 請求報文語法錯誤
    2. 401 (unauthorized) 需要認證
    3. 403(Forbidden) 伺服器拒絕訪問對應的資源
    4. 404(Not Found) 伺服器上無法找到資源
  • 5xx Server Error(伺服器錯誤狀態碼)

    1. 500(Internal Server Error)伺服器故障
    2. 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
複製程式碼

配置好後,可在瀏覽器中輸入不同的域名,來測試圖片的顯示情況。

相關文章