node http模塊

http的那些事兒

請問你對http瞭解嗎?javascript

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

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

  • 瀏覽器會因頁面上的css/js/image等靜態資源會屢次發起鏈接請求,能夠把這個過程分紅兩部分css

    • html(jsp/php/aspx) 頁面加載(假設存在簡單的Nginx負載均衡)
    • css/js/image等 網頁靜態資源加載(假設使用CDN)
  • 整個過程大體通過了 DNS解析 --> 負載均衡 --> web服務器 --> 瀏覽器渲染html

    • 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文件等靜態內容,所以開始了第二部分的靜態資源請求。java

經常使用的請求方法有哪些web

  • GET/POST/PUT/DELETE/TRACE/OPTION/HEAD/CONNECT/PATCH

經常使用的Http 請求頭字段有哪些?算法

  • Accept:瀏覽器端能夠接受的MIME類型vim

    • 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
複製代碼

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

相關文章
相關標籤/搜索