• TCP and HTTP
  • HTTP Packet Format
    • The request message
      • The request line
      • Request header
      • Request body
    • The response message
      • Response line
      • Response headers
      • Response body
    • About the url
    • About the method
    • About the Request Body
    • About Status codes
      • The 2XX request is normal
      • 3XX caching and redirection
      • 4XX Client error
      • 5XX Server error
  • Get the request packet from node
  • Create an HTTP server and client in Node
    • Creating a server
    • Create a client
      • Response Considerations
  • About response entities and Content-Types
  • Use TCP to implement a simple HTTP server
    • Register the response callback
    • Parser separates request packets from transmitting requests
    • Parsing request headers
    • The demo code
  • HTTP other
    • The short connection
    • pipelines

pre-notify

previously:

TCP is the cornerstone of HTTP. For those who are not familiar with TCP, check out NodeJS and TCP: A Book

This article is mainly for personal knowledge review and will probably be long, So we will copy a table of contents specially at the beginning for the link on the lower right and click the sections to read the preferences (your guys are the guy you guys are)

TCP and HTTP

First, HTTP is based on TCP, and only when a TCP connection is successfully established can the browser client send HTTP requests to the server. (See TCP Three-way handshake for details.)

When TCP allows one PC to connect end-to-end to another, data can be exchanged between the two machines, but this data does not undergo any additional processing. It is pure data, that is, whatever data the user enters, the server gets back.

HTTP is slightly different. An HTTP request wraps the user’s input with the browser and sends it to the server. The wrapped data is called an HTTP request. The server responds to the browser as an HTTP response packet and sends the response to the client.

At the transport level, HTTP is just a subset of TCP, a reencapsulation.

HTTP Packet Format

HTTP packet format is the most intuitive interpretation of HTTP and the most efficient way to learn HTTP.

HTTP packets are divided into two types: request packets and response packets. Request packets and response packets are divided into three parts: line, header, and body. The header and body are separated by blank lines.

The request message

The following image is from Android Web Programming Meditation (2)

The request line

The request line is divided into the following three sections, each separated by a space

  • Method: Identifies whether to transmit or retrieve data
  • Path: indicates the URL address
  • Protocol Indicates the HTTP protocol version

Request header

The request header, unlike the request line, is multiple lines, each of which is a set of key-value pairs separated by colons and Spaces.

  • Request header: Host:xxx.com
  • Common header: both request and response, for exampleConnection:keep-alive
  • Entity header: toContent-At the beginning of
  • other

Request body

Seriously, the data the user wants to send to the server

The response message

The following image is from Android Web Programming Meditation (2)

Response line

  • Protocol Indicates the HTTP version
  • StatusCode status code
  • StatusCode -reason indicates the explanation of the statusCode

Response headers

With the request header

Response body

With the request body

About the url

The hash is removed from the URL when the client encapsulates the request (the query is retained).

About the method

GET Obtain resources

POST wants to send data to the server, transmitting the entity body

PUT Transfers files. RESTful operations: Updates or modifies files

HEAD Obtains the packet HEAD

DELETE DELETE a file

OPTIONS queries supported methods. Exploratory methods, such as cross-domain, will first ask the server whether cross-domain is possible

TRACE TRACE path

About the Request Body

When the submitted form contains only one piece of data and the form type is default, the request message looks like this

If there are multiple pieces of data, they are separated by blank lines

However, in the case of multipart/form-data encoding, special delimiters are used to separate the data from each other in the request body

Even a single piece of data is wrapped in a special delimiter

Multiple data segments

About Status codes

There are five main types of status codes

  • 1xx imformational(Information status code) Websocket
  • 2xx Success
  • 3xx Redirect
  • 4xx Client Error
  • 5xx Server Error

2xx

  • 200 OK The data sent by the client is processed normally
  • 204 Not Content The response is normal, with no entity
  • 206 Partial Content Indicates a Partial Content request. The Content specified by content-range is returned

3xx

  • 301 Moved Permanently Redirected Permanently
  • 302 Found Temporary redirection does not necessarily go to a different location Nginx
  • 303 See Other is similar to 302, but the GET method must be used
  • This parameter must be used with (if-match, if-modified-since, if-none_match, if-range, if-unmodified-since)
  • 307 Temporary Redirect Temporary redirection without changing the request method

4xx

  • 400 Bad Request The syntax of the Request packet is incorrect
  • 401 Unauthorized Requires authentication
  • 403 Forbidden The server denies access to corresponding resources
  • 404 Not Found The resource could Not be Found on the server

5xx

  • 500 Internal Server Error The Server is faulty
  • 503 Service Unavailable The server is overloaded or is being stopped for maintenance

Get the request packet from node

console.log(req.method); // Request method console.log(req.url); / / url address the console log (the req. HttpVersion); // HTTP version console.log(req.headers); / / request headerCopy the code
Req.on ('data'.function(data){
    console.log(data.toString());
})
Copy the code

Create an HTTP server and client in Node

Creating a server

let http = require('http');
let server = http.createServer();
server.on('request'.function(req,res){
  res.end('ok');
});
server.listen(8080);
Copy the code

You could you could abbreviate it like this

let http = require('http');
let server = http.createServer(function(req,res){
  res.end('ok');
});
server.listen(8080);
Copy the code

Create a client

let http = require('http');
let options = {
  host:'localhost'
  ,port:8080
  ,method:'POST'
  ,headers:{
    'Content-Type':'application/x-www-form-urlencoded'
//      ,'Content-Length':15 // Generally this value is calculated automatically}}let req = http.request(options);
req.write('id=999'); // Only calling end actually sends the request to the server req.end(); // When the client receives a response from the server, req.on('response'.function(res){// Only one argument console.log(res.statuscode); console.log(res.headers);let result = [];
  res.on('data'.function(data){
    result.push(data);
  })
  res.on('end'.function(data){
    letstr = Buffer.concat(result); console.log(str.toString()); })})Copy the code

You can also write request() and on(‘response’) together, but at this point you can’t actively send data beyond the header to the server (write() can only be called if http.request (opt) returns req).

http.get(options,function(res){
    ...
    res.on('data'.function(chunk){
      ...
    });
    res.on('end'.function() {... })})Copy the code

Response Considerations

Unable to continue writing after end (writable stream rule)

res.write()
res.end()

<<<
Erorr::write after end!
Copy the code

After setting the status code, the text description of the status code is automatically completed

res.statusCode = 200; / / the defaultCopy the code

We can not only set, but also remove a response header that is ready to be sent to the client

res.setHeader('Content-Type'.'text/plain');
res.setHeader('name'.'ahhh');
res.removeHeader('name'); // Delete a header that is ready to be setCopy the code

Compared to setHead, writeHead can set multiple headers at the same time, along with the status code. But the biggest difference from setHeader is that writeHeader is sent as soon as it is called.

console.log(res.headersSent) //false
res.writeHead(200,{'Content-Type':'text/plain'}); // When writeHead is set, res.setheader cannot be called because writeHead is sent directly.'name'.'zfpx'); //Can't set headers after they are sent.
console.log(res.headersSent) //true
Copy the code

The setHeader header is not sent until the write method is called. Another important point to note is that the header must be set before the write method is called.

console.log('-- -- -- -- -- -- -- -- --')
console.log(res.headersSent); //false
res.setHeader('name'.'ahhh');
console.log(res.headersSent) //false
res.write('ok');
console.log(res.headersSent) //true
res.end('end');
console.log(res.headersSent) //true  
console.log('-- -- -- -- -- -- -- -- --')
Copy the code

About response entities and Content-Types

The content-Type header needs to be set when the client sends a request and when the server replies with a response.

For the server, it needs this header to parse the entity data sent by the client (even though there are many cases where requests have no entity part, such as GET requests).

let buffers = [];
req.on('data'.function(chunk){
    buffers.push(chunk);
})
req.on('end'.function() {let content = Buffer.concat(buffers).toString();
    if(contentType === 'application/json'){
      console.log(JSON.parse(content).name);
    }else if(contentType === 'application/x-www-form-urlencoded') {let queryString = require('querystring'); console.log(queryString.parse(content).name); }})Copy the code

In practice, this can be complicated if you have a request body (entity data). (Previous request body)

The content-Type is usually associated with the suffix of the resource file to be returned to the client. So we usually use an NPM package to convert it for us.

.let mime = require('mime'); . res.setHeader('Content-type',mime.getType(filepath)+'; charset=utf-8');
Copy the code

Use TCP to implement a simple HTTP server

The HTTP server actually does one more thing than the TCP server, that is, parse the request header, and the rest of the request body should still listen on data.

But notice, when was the data, the body of the request, sent? Well, it was launched after the request header was separated and parsed.

Register the response callback

// http.createServer(function(req,res){})

server.on('request'.function(req,res){
    //do somtheing like you are doing at tcp
}
Copy the code

Parser separates request packets from transmitting requests

let server = net.createServer(function(socket){
  parser(socket,function(req,res){
    server.emit('request',req,res);
  });
});

server.listen(3000);
Copy the code

Here, the separation of the request message refers to the request body and the other two parts (request line, request header) divided into two parts, how to divide? Well, as mentioned earlier, the request body and the request header are separated by a blank line, i.e. \r\n\ R \n or 0x0d 0x0A 0x0d 0x0A.

B: well.. That’s how it works, but there’s a catch.

Socket, as a duplex stream, has the same default read value as normal readable streams when reading data from clients, So, which may cause you to read many times before you get the separator \r\n\r\n.

And we may end up reading \r\n\r\n with some extra data that is not part of the “request header”. We also need to push back the extra data that is part of the request body so that we can get the full request body data when we send the requsET event.

B: well.. Pit more, here will not be ugly paste code, interested partners can achieve the following, there are two points to pay attention to

  • Use readable pause mode to read (in order to push back excess data) when reading

  • It is recommended to use the buffer level 0x0d 0x0A rather than the character level \r\n, because characters may cause garbled characters that are difficult to judge and require more processing.

Parsing request headers

The request header here includes the request line and the request header

function parseHeader(head){
  let lines = head.split(/\r\n/);
  let start = lines.shift();
  let lr = start.split(' ');
  let method = lr[0];
  let url = lr[1];
  let httpVersion = lr[2].split('/') [1];let headers = {};
  lines.forEach(line=>{
    let col = line.split(':'); Col [0]] = col[1]; });return {url,method,httpVersion,headers};
}
Copy the code

The demo code

Warehouse: Click me click me!

HTTP other

The short connection

Although HTTP is not as persistent as TCP, we said that it is tcp-based and therefore has the ability to persist connections.

The response header usually contains a Connection:keep-alive field to keep the browser connected for a certain amount of time (about, uh, 2 minutes?), even after the response is received.

pipelines

If an HTTP request contains multiple requests, the next request can be sent without waiting.

The number of concurrent requests is about 6 for Chrome and 4 for Firefox.


To be Continue…