This article is about the cute head of the browser… Boys and girls.
Chap1 found headers
When we open a random website (such as Baidu, which we often use to test the Network), open Network, we will see the following request header, response header:
Chap2 headers purposes
2.1 the content-type
Content-type Specifies the Content Type of the request header or response header. It can be used for body-parser when used as a request header. Sooo~ What is body-parser?
Body-parser is a common node middleware. It is used to:
Parse incoming request bodies in a middleware before your handlers, available under the req.body property.
That is, the body of the POST request is parsed by middleware before the data is processed. An example of body-parser is:
The following example shows how to add a Body Parser to a route. In general, this is the most recommended method of using Body-Parser in Express.
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// create application/json parser
var jsonParser = bodyParser.json()
// create application/x-www-form-urlencoded parser
var urlencodedParser = bodyParser.urlencoded({ extended: false })
// POST /login gets urlencoded bodies
app.post('/login', urlencodedParser, function (req, res) {
if(! req.body)return res.sendStatus(400)
res.send('welcome, ' + req.body.username)
})
// POST /api/users gets JSON bodies
app.post('/api/users', jsonParser, function (req, res) {
if(! req.body)return res.sendStatus(400)
// create user in req.body
})
Copy the code
The core source of body-parser is:
// this uses a switch for static require analysis
switch (parserName) {
case 'json':
parser = require('./lib/types/json')
break
case 'raw':
parser = require('./lib/types/raw')
break
case 'text':
parser = require('./lib/types/text')
break
case 'urlencoded':
parser = require('./lib/types/urlencoded')
break
}
Copy the code
Take JSON as an example:
var contentType = require('content-type') / /... /** * Get the charset of a request. * * @param {object} req * @api private */function getCharset (req) {
try {
return (contentType.parse(req).parameters.charset || ' ').toLowerCase()
} catch (e) {
returnundefined } } //... / / assert charset per RFC 7159 SEC 8.1 var charset = getCharset (the req) | |'utf-8'
if(charset.substr(0, 4) ! = ='utf-') {
debug('invalid charset')
next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', {
charset: charset,
type: 'charset.unsupported'
}))
return
}
Copy the code
By analyzing the Type of the content-Type in the request header, we can process the corresponding data according to different types.
Step1: create server.js:
req.on('end',function (params) { let r = Buffer.concat(arr).toString(); // body-parser parses requests, If (req.headers['content-type'] === www.js){let queryString = require(' queryString '); r = querystring.parse(r); // a=1&b=2 console.log(r,1); } else if (req.headers['content-type'] === 'application/json'){ console.log(JSON.parse(r),2); } else{ console.log(r,3); } res.end('end'); })Copy the code
Step2: Client simulated request:
let opts = {
host:'localhost',
port:3000,
path:'/hello',
headers:{
'a': 1,'Content-Type':'application/json'."Content-Length":7 // Use a length when simulating, otherwise the client will assume that no data is passed}}let http = require('http');
let client = http.request(opts,function (res) {
res.on('data'.function(data) { console.log(data.toString()); })}); client.end("{\"a\":1}"); // Send out the requestCopy the code
Step3: test. {a: 1} 2. The relationship between Content-Type and body-Parser is analyzed here. Next we look at the request header.
Range: 2.2 bytes
The request header uses Range:bytes to request a portion of a resource. Use this field to simulate a partial read. As follows:
http.createServer(function (req, res) { let range = req.headers['range']; })Copy the code
server:
let http = require('http');
let fs = require('fs');
let path = require('path');
// The current size of the file to download
let size = fs.statSync(path.join(__dirname, 'my.txt')).size;
let server = http.createServer(function (req, res) {
let range = req.headers['range']; / / 0 to 3
if (range) {
Curl -v --header "Range:bytes=0-3" http://localhost:3000
let [, start, end] = range.match(/(\d*)-(\d*)/);
start = start ? Number(start) : 0;
end = end ? Number(end) : size - 1; // 10 bytes size 10 (0-9)
res.setHeader('Content-Range'.`bytes ${start}-${end}/${size - 1}`);
fs.createReadStream(path.join(__dirname, 'my.txt'), { start, end }).pipe(res);
} else {
// Write the contents of the file to the client
fs.createReadStream(path.join(__dirname, 'my.txt')).pipe(res);
// A readable stream can be channeled to a writable stream through pipe}}); server.listen(3000);
Copy the code
client:
let opts = {
host:'localhost'.port:3000.headers:{}
}
let http = require('http');
let start = 0;
let fs = require('fs');
function download() {
opts.headers.Range = `bytes=${start}-${start+3}`;
start+=4;
console.log(`start is ${start}`)
let client = http.request(opts,function (res) {
let total = res.headers['content-range'].split('/') [1];
// console.log(half)
res.on('data'.function (data) {
fs.appendFileSync('./download1.txt',data);
});
res.on('end'.function () {
setTimeout((a)= > {
if((! pause)&&(start < total)) download(); },1000); })}); client.end(); } download()Copy the code
Piecewise reading adds pause function to listen for user input
let pause = false;
process.stdin.on('data'.function (data) {
if (data.toString().includes('p')){
pause = true
}else{
pause = false;
download()
}
})
Copy the code
Test results:
Segmented reading has the following benefits:
- Improve reading speed, multithreading parallel, block reading
- Breakpoint continuingly
Simulation parallel download:
let halfFlag = 20
function download() {
opts.headers.Range = `bytes=${start}-${start+3}`;
start+=4;
console.log(`start is ${start}`)
let client = http.request(opts,function (res) {
let total = res.headers['content-range'].split('/') [1];
let halfFlag = Math.floor(total/2)
// console.log(half)
res.on('data'.function (data) {
fs.appendFileSync('./download1.txt',data);
});
res.on('end'.function () {
setTimeout((a)= > {
if((! pause)&&(start < halfFlag)) download(); },1000); })}); client.end(); }let half = halfFlag
function downloadTwo() {
opts.headers.Range = `bytes=${half}-${half+3}`;
half+=4;
console.log(`half is ${half}`)
let client = http.request(opts,function (res) {
let total = res.headers['content-range'].split('/') [1];
res.on('data'.function (data) {
fs.appendFileSync('./download2.txt',data);
});
res.on('end'.function () {
setTimeout((a)= > {
if(! pause&&half < total) downloadTwo(); },1000); })}); client.end(); } download(); downloadTwo();Copy the code
As a result, the original file is divided into two parts and downloaded to download1.txt and download2.txt. Testing:
2.3 Cache-Control and Expires mandatory caching
Response Header Cache-Control: max-age=1233 in the Response Header can set a mandatory Cache for the current time, and the associated Expires can set an absolute point in time to limit the time the Cache can be read. Simulation implementation:
let url = require('url'); // http://username:password@hostname:port/pathname? querylet server = http.createServer(async function (req,res) {
console.log(req.url)
let { pathname,query} = url.parse(req.url,true);
// trueIs to convert a Query to an objectlet readPath = path.join(__dirname, 'public', pathname);
try {
let statObj = await stat(readPath); // The root client says to cache within 10 seconds res.setheader ('Cache-Control'.'max-age=10');
res.setHeader('Expires',new Date(Date.now()+10*1000).toGMTString()); // All requests within 10s will return 200 from the cacheif (statObj.isDirectory()) {
let p = path.join(readPath, 'index.html');
await stat(p); Fs.createreadstream (p).pipe(res); fs.createreadstream (p).pipe(res); }else {
fs.createReadStream(readPath).pipe(res);
}
}catch(e){
res.statusCode = 404;
res.end(`Not found`);
}
}).listen(3000);
Copy the code
Testing:
2.4 Compare last-modified and if-modified-since caches
Comparing the response header last-modified and with the request header if-modified-since, the file modification time can be used to see If the file has been Modified, thus deciding whether to rerequest or remove the cache. The simulation is as follows: step1 do not set mandatory cache
res.setHeader('Cache-Control'.'no-cache');
Copy the code
Step2 apply the file modification time to check whether the file is modified.
res.setHeader('Last-Modified', statObj.ctime.toGMTString());
if (req.headers['if-modified-since'] === statObj.ctime.toGMTString()) {
res.statusCode = 304;
res.end();
return; / / cache
}
fs.createReadStream(readPath).pipe(res);
Copy the code
Testing:
2.5 Compare cache Etag and if-none-match
Compare response header :Etag with request header: if-none-match. If Etag and if-none-match are equal, 304 is returned. How do I add an eTag?
Based on the file contents, generate an MD5 digest and tag the entity.
This method is more performance intensive, but allows a more accurate comparison of whether a file has been modified. Relying on file modification times for comparison is not accurate enough. Sometimes a file is last-modified, but the contents of the file may not have changed at all. So this scenario is superior to 2.4.
Implementation method:
let rs = fs.createReadStream(p);
let md5 = crypto.createHash('md5'); // Can not write the response body before writing the header
let arr = [];
rs.on('data'.function (data) {
md5.update(data);
arr.push(data);
});
Copy the code
Set the Etag
rs.on('end'.function () {
let r = md5.digest('base64');
res.setHeader('Etag', r);
if (req.headers['if-none-match'] === r ){
res.statusCode = 304;
res.end();
return;
}
res.end(Buffer.concat(arr));
})
Copy the code
Testing:
2.6 the Accept – Encoding
Depending on the request header: accept-encoding: gzip, deflate, BR tells the server what data format is acceptable. The server returns with the data format marked by the response format via Content-Encoding. When the client accepts the GZIP format, the back end can be passed through file compression processing for improved performance. The Node API provides the zlib module:
The Zlib module provides compression via Gzip and Deflate/Inflate
Let’s apply zlib and request header Accept-encoding to compress.
let zlib = require('zlib');
let fs = require('fs');
let path = require('path');
function gzip(filePath) {
lettransform = zlib.createGzip(); Fs.createreadstream (filePath).pipe(transform).pipe(fs.createWritestream (filePath+)'.gz'));
}
gzip('2.txt')
Copy the code
Extract:
function gunzip(filePath) {
let transform = zlib.createGunzip();
fs.createReadStream(filePath).pipe(transform).pipe(fs.createWriteStream(path.basename(filePath,'.gz')));
}
Copy the code
Path. basename(filePath,’.gz’) is used to remove the suffix. Gz from the filePath file name.
Depending on the type accepted by the request header:
if(req.url === '/download'){
res.setHeader('Content-Disposition'.'attachment' )
return fs.createReadStream(path.join(__dirname, '1.html')).pipe(res);
}
Copy the code
let http = require('http');
let fs = require('fs');
let path = require('path');
let zlib = require('zlib');
http.createServer(function (req,res) {
if(req.url === '/download'){
res.setHeader('Content-Disposition'.'attachment' )
return fs.createReadStream(path.join(__dirname, '1.html')).pipe(res);
}
let rule = req.headers['accept-encoding'];
if(rule){
if(rule.match(/\bgzip\b/)){
res.setHeader('Content-Encoding'.'gzip');
fs.createReadStream(path.join(__dirname, '1.html'))
.pipe(zlib.createGzip())
.pipe(res);
} else if (rule.match(/\bdeflate\b/)){
res.setHeader('Content-Encoding'.'deflate');
fs.createReadStream(path.join(__dirname, '1.html'))
.pipe(zlib.createDeflate())
.pipe(res);
}else{
fs.createReadStream(path.join(__dirname, '1.html')).pipe(res); }}else{
fs.createReadStream(path.join(__dirname, '1.html')).pipe(res);
}
}).listen(3000);
Copy the code
test deflate:
curl -v --header "Accept-Encoding:deflate" http://localhost:3000
* Rebuilt URL to: http://localhost:3000/
* Trying 127.0.0.1. * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 3000 (# 0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.54.0
> Accept: */*
> Accept-Encoding:deflate
>
< HTTP/1.1 200 OK
< Content-Encoding: deflate
< Date: Thu, 23 Aug 2018 03:01:13 GMT
< Connection: keep-alive
< Transfer-Encoding: chunked
Copy the code
test others:
curl -v --header "Accept-Encoding:nn" http://localhost:3000 * Rebuilt URL to: http://localhost:3000/ * Trying 127.0.0.1... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 3000 (#0) > GET/HTTP/1.1 > Host: Localhost :3000 > user-agent: curl/7.54.0 > Accept: */* > accept-encoding :nn > < HTTP/1.1 200 OK < Date: Thu, 23 Aug 2018 03:02:51 GMT < Connection: keep-alive < Transfer-Encoding: chunked < <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, <meta HTTP-equiv =" x-UA-compatible "content=" IE =edge"> <title>Document</title> </head> <body </body> * Connection #0 to host localhost left intact </html>%Copy the code
2.7 referer
let http = require('http');
let fs = require('fs');
let url = require('url');
let path = require('path'); // This is baidu serverlet server = http.createServer(function (req,res) {
let { pathname } = url.parse(req.url);
let realPath = path.join(__dirname,pathname);
fs.stat(realPath,function(err,statObj) {
if(err){
res.statusCode = 404;
res.end();
}else{
let referer = req.headers['referer'] || req.headers['referred'];
if(referer){
let current = req.headers['host'] // Referer = url.parse(referer).hostif (current === referer){
fs.createReadStream(realPath).pipe(res);
}else{
fs.createReadStream(path.join(__dirname,'images/2.jpg')).pipe(res); }}else{
fs.createReadStream(realPath).pipe(res);
}
}
})
}).listen(3000);
Copy the code
2.8 the Accept – Language
Accept-language: zh-cn,zh; Q =0.9 Multiple languages are separated by ‘,’, weight reuse ‘=’ for ‘, no default weight is 1
The backend accepts a lookup based on the language weight of the request, returns if it finds one, and uses the default language if it cannot find one
let langs = {
en: 'hello world'.'zh-CN':Hello world.zh:'hello'.ja: 'Kosher view of the world'
}
let defualtLanguage = 'en'
// Multilingual server solution: do it (the browser will send a header)
// Implement multiple languages through URLS
let http = require('http');
http.createServer(function (req,res) {
let lan = req.headers['accept-language'];
/ / [[useful, q = 0.9], [useful - CN]] = > [{name: 'useful - CN, q = 1}, {name:' useful ', q: 0.9}]
if(lan){
lan = lan.split(', ');
lan = lan.map(l= >{
let [name,q] = l.split('; ');
q = q?Number(q.split('=') [1) :1
return {name,q}
}).sort((a,b) = >b.q-a.q); // Outputs the weight array
for(let i = 0; i <lan.length; i++){// Take out everyone's name
let name= lan[i].name;
if(langs[name]){ // Go to the language package to find it
res.end(langs[name]);
return;
}
}
res.end(langs[defualtLanguage]); // Default language
}else{
res.end(langs[defualtLanguage]); // Default language
}
}).listen(3000);
Copy the code
Testing:
conclusion
Request headers and response headers are often used in front – end interfacing. Understand their clever use before and after the end of the coordination will be more harmonious smooth ~
Author: Yanni Jia Nickname: Very rabbit Email: [email protected]