HTTP协议1.1

2020-08-14  本文已影响0人  Vivian_0430

打开chrome的调试面板,切换到network面板,搞清楚该页上所有的显示项、菜单、弹窗等下级功能的含义

  1. 什么是TTFB?TTFB包含了哪些部分?

Time to first byte,Timing里面的waiting,从http请求发送到服务器接收第一个字节的时间花费。

包含了TCP连接时间,http请求发送时间和服务器接收到第一个字节的时间。

  1. network面板里看到一个请求,怎么判断这个请求真的发送到了服务器那里?

根据状态码来看,502和504是没有发送到服务器,304从浏览器读取缓存。

  1. 一条请求变红了可能是因为哪些原因?

4,5开头的状态码,插件阻拦,浏览器的一些约定和协议

HTTP协议的特征:

  1. 两方通信
  2. 非对等的通信双方(server/client)
  3. 通信的基本单元(request和response)
  4. 请求与返回的对应关系

请写出一个简单的http请求:(method放前面,协议放后面)

初始行:method + path + 协议(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运行

TCP:两河模型

怎么知道一条请求已经结束了?答:\r\n

怎么区分前一条请求和后一条请求?答:\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()
        })
    })
}

写出一个带有body的http POST请求:

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());

分别写出一个带body和不带body的response:(协议放在前面)

不带body

HTTP/1.1 200 OK
max-age: 0

带body

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

请求及相应结构:

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

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

request和response不能一一对应

  1. 怎么区分client和server?

通过发送的信息的结构

  1. server怎么向client主动发送数据?

webSocket,SSE,HTTP是不能实现的,因为client会把请求当响应处理报错

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

延伸:

  1. 了解HTTP chunked编码及其使用场景

服务器返回的消息的长度,像前台给后台的Content-Length一样;transfer-coding的阈值为chunked时表示将用chunked编码传输内容;

使用场景:前台下载大文件

  1. 了解WebSocket的协议细节,与HTTP协议的不同之处

WebSocket 是基于TCP/IP协议,独立于HTTP协议的通信协议;双向通讯,有状态,客户端一(多)个与服务端一(多)双向实时响应(客户端 ⇄ 服务端);持久化协议;

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队头阻塞的问题

由于http一发一收的应答机制(请求-应答模型),如果队首的请求处理太慢就会阻塞后面的进程。

解决办法:并发连接,客户端对一个域名同时发起多个长连接,用数量来解决质量问题,但是如果并发数太大,服务器会认为是恶意攻击,拒绝请求,对客户端发起并发请求的数量限制在6-8;域名分片,增加域名的数量,多个域名指向同一个服务器。

长连接:不进行四次握手,数据传输完成TCP连接不断开,同域名下继续使用这个通道,有过期时间限制(keep-alive:timeout)

  1. 如果GET或DELETE请求,发送者带上了body,服务器应该怎样处理?

直接忽略body的内容(可能会导致下一个接口报错);报400,参数错误;接受处理,返回空数据;丢弃Content-Length和body

  1. 撰写测试用例
上一篇下一篇

猜你喜欢

热点阅读