Common application of HTTP header

Time:2020-12-3

Accept-Language

Accept-LanguageUsually used to implement Multilingualism:

Accept-Language: <language>
Accept-Language: *

// Multiple types, weighted with the quality value syntax:
Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5

;q= (q-factor weighting)The order of priority, expressed by relative quality value, is also called weight.

Simple implementation:

const http = require("http")
Const languages = {zh: "hello", en: "hello", JP: "こんには",}

const server = http.createServer((req, res) => {
  res.setHeader("Content-Type", "text/plain;charset=utf-8")
  let lang = req.headers["accept-language"] // zh-CN,zh;q=0.9,en;q=0.8
  if (!lang) return res.end("")
  lang = lang
    .split(",")
    .filter((t) => t.includes(";"))
    .map((t) => {
      let [name, q] = t.split(";")
      return {
        name,
        q: q.slice(2) * 1,
      }
    })
    .sort((a, b) => a.q > b.q)
  for (let i = 0; i < lang.length; i++) {
    let work = languages[lang[i].name]
    if (work) {
      res.end(work)
      break
    }
  }
  res.end(JSON.stringify(lang))
})

server.listen(3000, () =>console.log(`Serving on: \r\n  http://localhost:3000`))

Accept-Ranges

HTTP request scope

HTTP protocol scope requests allow the server to send only part of the HTTP message to the client. Range requests are useful when transferring large media files, or when used in conjunction with the breakpoint Resume feature of file downloads.

Response status of the scope request:

  • 206 Partial ContentService request successful
  • 416 Requested Range Not SatisfiableRequest range out of range (range value exceeds the size of the resource)
  • 200 OKThe scope request is not supported to return the entire resource. When downloading in sections, you should judge first

RangeRequest header:

Range: bytes=start-end

Range: bytes=10-: data of the 10th and last byteRange: bytes=40-100: data between the 40th and 100th bytes

Note that this indicates [start, end], which contains the start and end bytes of the request header. Therefore, the next request should be the [end + 1, nexend] of the previous request

Content-RangeResponse head

//The server responded to the first (0-10) bytes of data. The resource has a total size of (3103) bytes.
Content-Range: bytes 0-10/3103;
//The server responded to 11 bytes of data (0-10)
Content-Length: 11;

code implementation

Server side pressrangeScope Download

const path = require("path")
const http = require("http")
const fs = require("fs")
const DOWNLOAD_FILE = path.resolve(__dirname, "./server_download.txt")
const TOTAL = fs.statSync(DOWNLOAD_FILE).size;

http.createServer((req, res) => {
  res.setHeader("Content-Type", "text/plain;charset=utf-8")
  // curl http://www.example.com -i -H "Range: bytes=0-50"
  const range = req.headers["range"]
  //No range directly returns the file
  if (!range) return fs.createReadStream(DOWNLOAD_FILE).pipe(res)
  //There are other uses for intercepting range values. The case of separation is not considered here
  let [, start, end] = range.match(/(\d*)-(\d*)/)
  start = start ? start * 1 : 0
  end = end ? end * 1 : TOTAL

  //Range request success status code 206 Partial Content
  res.statusCode = 206
  //Set response header
  res.setHeader("Content-Range", `bytes ${start}-${end}/${TOTAL}`)
  //Return range data
  fs.createReadStream(DOWNLOAD_FILE, { start, end }).pipe(res)
}).listen(3000, () => console.log(`Serving on 3000`))

Client download

const path = require("path")
const http = require("http")
const fs = require("fs")
const DOWNLOAD_FILE = path.resolve(__dirname, "./client_download.txt")
const ws = fs.createWriteStream(DOWNLOAD_FILE)
let start = 0
Let mode = "start" // download mode "start" "pause"

download()
function download() {
  const downloadConfig = {
    hostname: "localhost",
    port: 3000,
    encoding: "utf-8",
    headers: {
      Range: `bytes=${start}-${start + 100}`,
    },
  }
  const request = (res) => {
    let total = res.headers["content-range"].split("/")[1] * 1
    res.on("data", (chunk) => {
      ws.write(chunk)
      if (start <= total) {
        start += 101
        //Print download progress
        console.clear();
        console.log (` download progress:${ Math.min (parseInt ((start / total) * 100), 100)}% \ \ press P and enter to pause ')
        setTimeout(()=>{
            //If mode is start mode, continue downloading
            mode === "start" ? download() :  console.log ("pause download, press any key to enter to download)"
        },1000)
      } else {ws.end()}
    })
    res.on("end", () => {
      if (total > start)  return;
        console.log (download complete)
        process.exit(1)
    })
  }
  http.get(downloadConfig, request)
}

process.stdin.on("data", (chunk) => {
  if (chunk.toString().includes("p")) {
    //Keyboard P pause Download
    mode = "pause"
  } else {
    mode = "start"
    download()
  }
})

User-Agent

User-AgentThe first part contains a character string, which enables the peer of the network protocol to identify the application type, operating system, software developer and version number of the user agent software that initiates the request.

User-AgentDetermine whether the mobile terminal is mobile and redirect to the new address:

require("http")
  .createServer((req, res) => {
    const ua = req.headers["user-agent"];
    const isMobile = /(iPhone|iPad|iPod|iOS|Android)/i.test(ua);
    const redirectUrl = isMobile ? "https://m.58.com/gz" : "https://gz.58.com";
    
    res.statusCode = 302;
    res.setHeader("Location", redirectUrl)
    res.end()
  })
  .listen(3000, () => console.log(`Serving on: \r\n  http://localhost:3000`))

Referer

RefererThe request header contains the address of the source page of the current request page, which means that the current page is entered through the link in the source page. The server generally uses the referer request header to identify the access source, which may be used for statistical analysis, logging and cache optimization.

Examples

Referer: https://developer.mozilla.org/en-US/docs/Web/JavaScript

Referers are not sent in the following two cases:

  • The protocol adopted by the source page is the “file” or “data” URI representing the local file
  • The current request page adopts the non security protocol, while the source page uses the secure protocol (HTTPS)

Example of judging chain theft:

const url = require("url");
const http = require("http");

http.createServer((req, res) => {
    let referer = req.headers["referer"];

    if (referer) {
        let refererHost = url.parse(referer).host
        let host = req.headers["host"];
        if(refererHost!==host){
            //Stolen chain
        }
    }
}).listen(3000, () => console.log(`Serving on: \r\n  http://localhost:3000`))

Content-Encoding

Content-EncodingIs an entity message header used to compress data of a specific media type. When the header appears, its value indicates how the message body performs content encoding transformation. The header of this message is used to tell the client how to decode the media type content marked in the content type.

It is generally recommended that the data be compressed as much as possible, so that the header of this message appears.However, for certain types of files, such as JPEG image files, are already compressed. Sometimes additional compression does not help to reduce the load volume, but may increase it.

usegzipMode

Server configurationContent-EncodingField:

Content-Encoding:gzip

Client useAccept-EncodingField description receiving method:

Accept-Encoding: gzip, deflate

code implementation

const fs = require("fs");
const zlib = require("zlib")
const http = require("http");

http
  .createServer((req, res) => {
    let encoding = req.headers["accept-encoding"]
    if(!encoding) return fs.createWriteStream("./test.html").pipe(res);

    if(/\bgzip\b/.test(encoding)){
        res.setHeader("Content-Encoding","gzip");
        return fs.createWriteStream("./test.html").pipe(zlib.createGzip()).pipe(res)
    }
    if(/\bdeflate\b/.test(encoding)){
        res.setHeader("Content-Encoding", "bdeflate")
        return fs.createWriteStream("./test.html").pipe(zlib.createDeflate()).pipe(res)
    }
  })
  .listen(3000, () => console.log(`Serving on: \r\n  http://localhost:3000`))

matters needing attention

According to the HTTP specification, the field name of the HTTP message header is case insensitive

3.2. Header Fields
Each header field consists of a case-insensitive field name followed by a colon (”:“), optional leading whitespace, the field value, and optional trailing whitespace.