HTTP協(xié)議1.1

打開(kāi)chrome的調(diào)試面板俐芯,切換到network面板晒屎,搞清楚該頁(yè)上所有的顯示項(xiàng)喘蟆、菜單、彈窗等下級(jí)功能的含義

  1. 什么是TTFB鼓鲁?TTFB包含了哪些部分蕴轨?

Time to first byte,Timing里面的waiting骇吭,從http請(qǐng)求發(fā)送到服務(wù)器接收第一個(gè)字節(jié)的時(shí)間花費(fèi)橙弱。

包含了TCP連接時(shí)間,http請(qǐng)求發(fā)送時(shí)間和服務(wù)器接收到第一個(gè)字節(jié)的時(shí)間燥狰。

  1. network面板里看到一個(gè)請(qǐng)求棘脐,怎么判斷這個(gè)請(qǐng)求真的發(fā)送到了服務(wù)器那里?

根據(jù)狀態(tài)碼來(lái)看龙致,502和504是沒(méi)有發(fā)送到服務(wù)器蛀缝,304從瀏覽器讀取緩存。

  1. 一條請(qǐng)求變紅了可能是因?yàn)槟男┰颍?/li>

4目代,5開(kāi)頭的狀態(tài)碼屈梁,插件阻攔嗤练,瀏覽器的一些約定和協(xié)議

HTTP協(xié)議的特征:

  1. 兩方通信
  2. 非對(duì)等的通信雙方(server/client)
  3. 通信的基本單元(request和response)
  4. 請(qǐng)求與返回的對(duì)應(yīng)關(guān)系

請(qǐng)寫出一個(gè)簡(jiǎn)單的http請(qǐng)求:(method放前面,協(xié)議放后面)

初始行:method + path + 協(xié)議(GET /index.html HTTP/1.1\r\n HOST: www.maxtropy.com\r\n\r\n)

注:CRLF

  1. 回車 = CR = \r
  2. 換行 = LF = \n

HTTP基于TCP運(yùn)行

TCP:兩河模型

怎么知道一條請(qǐng)求已經(jīng)結(jié)束了在讶?答:\r\n

怎么區(qū)分前一條請(qǐng)求和后一條請(qǐng)求煞抬?答:\r\n\r\n

const str = "GET /index.html HTTP/1.1\r\nHost: www.maxtropy.com\r\n\r\nGET /main.js HTTP/1.1\r\nHost: www.maxtropy.com\r\n\r\n"
function parseRequest(str) {
    const result = [];
    const arr = str.split('\r\n\r\n');
    arr.forEach(item => {
        const data = item.split('\r\n');
        result.push({
            requestLine: data[0],
            headers: data[1].split()
        })
    })
}

寫出一個(gè)帶有body的http POST請(qǐng)求:

POST /index.html HTTP/1.1\r\n

HOST: www.maxtropy.com\r\n

Content-Length: 14\r\n\r\n

body: {json}

interface RequestObject {
    requestLine: string;
    headers: string[];
    body?: string;
}
class RequestProcessor {
    feed(rawData: string): void {
        
    }
    poll():RequestObject | undefined {
        return undefined;
    }
}
const str0 = 'POST /user/';
    const str1 = 'create HTTP/1.1\r\nHost: www.';
    const str2 = 'maxtropy.com\r\nqwee: Content-Length: 19\r\nContent';
    const str3 = '-Length: 19\r\n\r\n';
    const str4 = '{"name":"';
    const str5 = 'tom","\r\n"}POST';
    const str6 = ' /user/';
    const str7 = 'create HTTP/1.1\r\nHost: www.';
    const str8 = 'maxtropy.com\r\nContent';
    const str9 = '-Length: 21\r\n\r\n';
    const str10 = '{"name":"';
    const str11 = 'tom","\r\n\r\n"}';

    const test0 = "POST /index.js HTTP/1.1\r\nHost: www.test.com\r\nContent-Length:15\r\n\r\n{'name': 'tom'}GET /main.js HTTP/1.1\r\nHost: www.test.com\r\n\r\n";
    const test1 = "POST /index.js HTTP/1.1\r\nHost: www.test.com\r\nContent-Length: 17\r\n\r\nContent-Length=16GET /main.js HTTP/1.1\r\nHost: www.test.com\r\n\r\n";
    const test2 = "POST /index.js HTTP/1.1\r\nHost: www.test.com\r\nContent-Length: 9\r\n\r\nGET HOST:GET /main.js HTTP/1.1\r\nHost: www.test.com\r\n\r\n";
    const test3 = "POST /index.js HTTP/1.1\r\nHost: www.test.com\r\nContent-Length: 6\r\n\r\n\r\n\r\n\r\nGET /main.js HTTP/1.1\r\nHost: www.test.com\r\n\r\n";
    const test4 = "GET /main.js HTTP/1.1\r\nHost: www.test.com\r\n\r\nPOST /index.js HTTP/1.1\r\nHost: www.test.com\r\nContent-Length: 15\r\n\r\n{'name': 'tom'}";
    const test5 = "GET /main.js HTTP/1.1\r\nHost: www.test";
    const test6 = "POST /index.js HTTP/1.1\r\nHost: www.test.com\r\nContent-Length: 6\r\n\r\n\r\ntestGET /main.js HTTP/1.1\r\nHost: www.test.com\r\n\r\n";
    const test7 = "POST /index.js HTTP/1.1\r\nHost: www.test.com\r\nContent-Length: 15\r\n\r\n{'name': 'tom'}GET /main.js HTTP";
    const test8 = "POST /index.js HTTP/1.1\r\nHost: www.test.com\r\nContent-Length: 15\r\n\r\n{'name': 'tom'}GET /main.js HTTP/1.1\r\nHost: www.test.com";
    const test9 = "POST /index.js HTTP/1.1\r\nHost: www.test.com\r\nContent-Length: 15\r\n\r\n{'name': 'tom'}GET /main.js HTTP/1.1\r\nHost: www.test.com\r\n\r\nGET /main.js HTTP/1.1\r\nHost: www.test.com\r\n\r\n";
    const test10 = "POST /index.js HTTP/1.1\r\nHost: www.test.com\r\nContent-Length: 15\r\n\r\n{'name': GET GET /main.js HTTP/1.1\r\nHost: www.test.com\r\n\r\nGET /main.js HTTP/1.1\r\nHost: www.test.com\r\n\r\n";
    let result = [];
    let newUrl = '';
    function feed(rawData){
        newUrl = newUrl + rawData;
        let requestUrl = '';
        let indexArr = [];
        let firstIndex = -1;
        for (let i = 0;i < newUrl.length;i++) {
            if(String(newUrl[i] + newUrl[i+1] + newUrl[i+2] + newUrl[i+3]) === '\r\n\r\n') {
                requestUrl = newUrl.substring(0,i);
                firstIndex = i;
                break;
            }
        }
        for (let i = 0; i < requestUrl.length; i++) {
            if (String(requestUrl[i] + requestUrl[i + 1]) === '\r\n') {
                indexArr.push(i)
            }
        }
        if (firstIndex !== -1) {
            if (requestUrl.toLocaleUpperCase().indexOf('CONTENT-LENGTH:') === -1) { // 不帶body
                result.push({
                    requestLine: requestUrl.substring(0, indexArr[0]),
                    headers: requestUrl.substring(indexArr[0]+2)
                });
                newUrl = newUrl.substring(requestUrl.length).trim();
                feed('')
            } else {
                const contentLength = Number(requestUrl.toLocaleUpperCase().trim().split('CONTENT-LENGTH:')[1]);
                //---- body尚不完整的情況
                if (newUrl.length >= requestUrl.length + contentLength + 4) {
                    result.push({
                        requestLine: requestUrl.substring(0, indexArr[0]),
                        headers: requestUrl.substring(indexArr[0] + 2, firstIndex).split('\r\n'),
                        body: newUrl.substr(firstIndex+4, contentLength)
                    });
                    newUrl = newUrl.substring(requestUrl.length + contentLength + 4);
                    feed('')
                }
            }
        }
    }
    function poll() {
        if(result.length) {
            const first = result[0];
            result.shift();
            return first
        }else {
            return undefined;
        }
    }
    // feed(str0);
    // console.log(poll());
    // feed(str1);
    // console.log(poll());
    // feed(str2);
    // console.log(poll());
    // feed(str3);
    // console.log(poll());
    // feed(str4);
    // console.log(poll());
    // feed(str5);
    // console.log(poll());
    // feed(str6);
    // console.log(poll());
    // feed(str7);
    // console.log(poll());
    // feed(str8);
    // console.log(poll());
    // feed(str9);
    // console.log(poll());
    // feed(str10);
    // console.log(poll());
    // feed(str11);
    // console.log(poll());
    feed(test3);
    console.log(poll(),poll(),poll());

分別寫出一個(gè)帶body和不帶body的response:(協(xié)議放在前面)

不帶body

HTTP/1.1 200 OK
max-age: 0

帶body

HTTP/1.1 200 OK 
max-age: 0 
Content-Length: 14
{json}

請(qǐng)求及相應(yīng)結(jié)構(gòu):

start-line + *(header-field CRLF) + CRLF + [message-body]

  1. HTTP為什么不能是多方通信?

request和response不能一一對(duì)應(yīng)

  1. 怎么區(qū)分client和server构哺?

通過(guò)發(fā)送的信息的結(jié)構(gòu)

  1. server怎么向client主動(dòng)發(fā)送數(shù)據(jù)此疹?

webSocket,SSE遮婶,HTTP是不能實(shí)現(xiàn)的,因?yàn)閏lient會(huì)把請(qǐng)求當(dāng)響應(yīng)處理報(bào)錯(cuò)

https://tools.ietf.org/html/rfc2119

延伸:

  1. 了解HTTP chunked編碼及其使用場(chǎng)景

服務(wù)器返回的消息的長(zhǎng)度湖笨,像前臺(tái)給后臺(tái)的Content-Length一樣旗扑;transfer-coding的閾值為chunked時(shí)表示將用chunked編碼傳輸內(nèi)容;

使用場(chǎng)景:前臺(tái)下載大文件

  1. 了解WebSocket的協(xié)議細(xì)節(jié)慈省,與HTTP協(xié)議的不同之處

WebSocket 是基于TCP/IP協(xié)議臀防,獨(dú)立于HTTP協(xié)議的通信協(xié)議;雙向通訊边败,有狀態(tài)袱衷,客戶端一(多)個(gè)與服務(wù)端一(多)雙向?qū)崟r(shí)響應(yīng)(客戶端 ? 服務(wù)端);持久化協(xié)議笑窜;

GET /webfin/websocket/ HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==
Origin: http://localhost:8080
Sec-WebSocket-Version: 13
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=
  1. HTTP/1.1隊(duì)頭阻塞的問(wèn)題

由于http一發(fā)一收的應(yīng)答機(jī)制(請(qǐng)求-應(yīng)答模型)致燥,如果隊(duì)首的請(qǐng)求處理太慢就會(huì)阻塞后面的進(jìn)程。

解決辦法:并發(fā)連接排截,客戶端對(duì)一個(gè)域名同時(shí)發(fā)起多個(gè)長(zhǎng)連接嫌蚤,用數(shù)量來(lái)解決質(zhì)量問(wèn)題,但是如果并發(fā)數(shù)太大断傲,服務(wù)器會(huì)認(rèn)為是惡意攻擊脱吱,拒絕請(qǐng)求,對(duì)客戶端發(fā)起并發(fā)請(qǐng)求的數(shù)量限制在6-8认罩;域名分片箱蝠,增加域名的數(shù)量,多個(gè)域名指向同一個(gè)服務(wù)器垦垂。

長(zhǎng)連接:不進(jìn)行四次握手宦搬,數(shù)據(jù)傳輸完成TCP連接不斷開(kāi),同域名下繼續(xù)使用這個(gè)通道乔外,有過(guò)期時(shí)間限制(keep-alive:timeout)

  1. 如果GET或DELETE請(qǐng)求床三,發(fā)送者帶上了body,服務(wù)器應(yīng)該怎樣處理杨幼?

直接忽略body的內(nèi)容(可能會(huì)導(dǎo)致下一個(gè)接口報(bào)錯(cuò))撇簿;報(bào)400聂渊,參數(shù)錯(cuò)誤;接受處理四瘫,返回空數(shù)據(jù)汉嗽;丟棄Content-Length和body

  1. 撰寫測(cè)試用例
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市找蜜,隨后出現(xiàn)的幾起案子饼暑,更是在濱河造成了極大的恐慌,老刑警劉巖洗做,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弓叛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡诚纸,警方通過(guò)查閱死者的電腦和手機(jī)撰筷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)畦徘,“玉大人毕籽,你說(shuō)我怎么就攤上這事【荆” “怎么了关筒?”我有些...
    開(kāi)封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)杯缺。 經(jīng)常有香客問(wèn)我蒸播,道長(zhǎng),這世上最難降的妖魔是什么萍肆? 我笑而不...
    開(kāi)封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任廉赔,我火速辦了婚禮,結(jié)果婚禮上匾鸥,老公的妹妹穿的比我還像新娘蜡塌。我一直安慰自己,他們只是感情好勿负,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布馏艾。 她就那樣靜靜地躺著,像睡著了一般奴愉。 火紅的嫁衣襯著肌膚如雪琅摩。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天锭硼,我揣著相機(jī)與錄音房资,去河邊找鬼。 笑死檀头,一個(gè)胖子當(dāng)著我的面吹牛轰异,可吹牛的內(nèi)容都是我干的岖沛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼搭独,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼婴削!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起牙肝,我...
    開(kāi)封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤唉俗,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后配椭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體虫溜,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年股缸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吼渡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乓序,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出坎背,到底是詐尸還是另有隱情替劈,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布得滤,位于F島的核電站陨献,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏懂更。R本人自食惡果不足惜眨业,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沮协。 院中可真熱鬧龄捡,春花似錦、人聲如沸慷暂。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)行瑞。三九已至奸腺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間血久,已是汗流浹背突照。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留氧吐,地道東北人讹蘑。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓末盔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親衔肢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子庄岖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354