HTTP协议1.1
打开chrome的调试面板,切换到network面板,搞清楚该页上所有的显示项、菜单、弹窗等下级功能的含义
- 什么是TTFB?TTFB包含了哪些部分?
Time to first byte,Timing里面的waiting,从http请求发送到服务器接收第一个字节的时间花费。
包含了TCP连接时间,http请求发送时间和服务器接收到第一个字节的时间。
- network面板里看到一个请求,怎么判断这个请求真的发送到了服务器那里?
根据状态码来看,502和504是没有发送到服务器,304从浏览器读取缓存。
- 一条请求变红了可能是因为哪些原因?
4,5开头的状态码,插件阻拦,浏览器的一些约定和协议
HTTP协议的特征:
- 两方通信
- 非对等的通信双方(server/client)
- 通信的基本单元(request和response)
- 请求与返回的对应关系
请写出一个简单的http请求:(method放前面,协议放后面)
初始行:method + path + 协议(GET /index.html HTTP/1.1\r\n HOST: www.maxtropy.com\r\n\r\n)
注:CRLF
- 回车 = CR = \r
- 换行 = 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]
- HTTP为什么不能是多方通信?
request和response不能一一对应
- 怎么区分client和server?
通过发送的信息的结构
- server怎么向client主动发送数据?
webSocket,SSE,HTTP是不能实现的,因为client会把请求当响应处理报错
https://tools.ietf.org/html/rfc2119
延伸:
- 了解HTTP chunked编码及其使用场景
服务器返回的消息的长度,像前台给后台的Content-Length一样;transfer-coding的阈值为chunked时表示将用chunked编码传输内容;
使用场景:前台下载大文件
- 了解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=
- HTTP/1.1队头阻塞的问题
由于http一发一收的应答机制(请求-应答模型),如果队首的请求处理太慢就会阻塞后面的进程。
解决办法:并发连接,客户端对一个域名同时发起多个长连接,用数量来解决质量问题,但是如果并发数太大,服务器会认为是恶意攻击,拒绝请求,对客户端发起并发请求的数量限制在6-8;域名分片,增加域名的数量,多个域名指向同一个服务器。
长连接:不进行四次握手,数据传输完成TCP连接不断开,同域名下继续使用这个通道,有过期时间限制(keep-alive:timeout)
- 如果GET或DELETE请求,发送者带上了body,服务器应该怎样处理?
直接忽略body的内容(可能会导致下一个接口报错);报400,参数错误;接受处理,返回空数据;丢弃Content-Length和body
- 撰写测试用例