usenodenativehttpModule to createserver

const http = require('http');

function callback(req, res) {};
const server = http.createServer(callback);
// Execute the callback using the event listener
server.on('request', callback);
server.listen(3000);
Copy the code

It is easy to create a server using the native HTTP module in Node by simply calling the createServer method and specifying the callback to be performed when a client request is received. IncomingMessage, which represents a client request, and http.ServerResponse, which represents a ServerResponse object. If no callback is passed into the createServer, the callback can also be executed using the server listening for the request event. Then call the LISTEN method to specify the port, address, and so on that the server needs to listen to.

server.listen(port, [host], [backlog], [callback]);
Copy the code

In the LISTEN method, you can use four arguments, the last three of which are optional. The port value specifies the port number to be listened on. A value of 0 will assign a random port number to the HTTP server, and the HTTP server will listen on client connections from this random port number. The host parameter specifies the address to listen on. If omitted, the server will listen on client connections from any IPv4 address. The backlog parameter is an integer value that specifies the maximum number of client connections in the waiting queue. Once the backlog length is exceeded, the HTTP server will start denying connections from new clients. The default value for this parameter is 511. The callback argument specifies the callback function to be called when the Listening event is triggered. This callback function takes no arguments. If you don’t pass a callback argument to the LISTEN method, you can also use the event listener.

server.on('listening', () = > {});Copy the code

usekoacreateserver

const Koa = require('koa');

const app = new Koa();
app.use(async (ctx, next) => {
	console.log('enter middleware');
	await next();
	console.log('out middleware');
});
app.listen(3000);
Copy the code

Creating a server using KOA is very different from creating a server using Node’s native HTTP module, but we know that KOA is the Web framework for Base Node.js. Therefore, the underlying node.js technology is only further wrapped on this basis, so that various middleware operating contexts and req and RES objects with independent functions can be used to realize request parsing and response return.

Koa source

Koa source code logic includes two parts, one part is the logic of KOA itself, mainly for the creation of services, CTX, REQ, RES these three objects management and operation. The other part is its unique onion model of middleware process control, mainly koA-compose (KOA-Compose source code parsing) middleware to achieve this function.

koa
lib
application.js
koa
context.js
ctx
request.js
req
response.js
res

Initialization phase

The initialization phase includes KOA initialization, which is mainly to mount properties and methods of the instantiated APP using KOA. Server initialization. This part of initialization is to create the server and specify exactly what the request is to perform. In code, it looks like this:

// KoA initialization
const app = new Koa();
// Server initialization
app.listen(3000, () = > {console.log('server running: http://localhost:3000');
});
Copy the code
koaInitialize the

Now let’s get rid of the unnecessary code and see what the KOA initialization does.

module.exports = class Application extends Emitter {
	constructor(options) {
		super(a); options = options || {};this.proxy = options.proxy || false;
	    this.subdomainOffset = options.subdomainOffset || 2;
	    this.proxyIpHeader = options.proxyIpHeader || 'X-Forwarded-For';
	    this.maxIpsCount = options.maxIpsCount || 0;
	    this.env = options.env || process.env.NODE_ENV || 'development';
	    if (options.keys) this.keys = options.keys;
	    this.middleware = [];
	    this.context = Object.create(context);
	    this.request = Object.create(request);
	    this.response = Object.create(response);
	    if (util.inspect.custom) {
	      this[util.inspect.custom] = this.inspect; } } listen(... args) {} toJSON() {} inspect() {} use(fn)() {} callback() {} handleRequest(ctx, fnMiddleware) {} createContext(req, res) {} onerror(err) {} };Copy the code

The initialization of KOA is mainly to mount various properties and methods for app, as shown above. In constructor, we mainly mount properties, including context object, request object, response object, middleware array, etc., which we often use. Env environment parameters, proxy proxy operations. The handleRequest, createContext, and onError methods are private and are intended to be used by callback methods. The purpose is handleRequest to process the request through compose middleware, createContext is to create a context object and mount a state object in the context to the view for data transmission, and onError is mainly for global error. The other five methods are as follows: Listen is initialized using node’s native HTTP module createServer. ToJSON and inspect are used to extract subdomainOffset, proxy, and env attributes from app using only. The use function manages middleware functions by pushing them into the Middleware array and returning this in a chain call. The callback function returns handleRequest to specify the action to be performed when the middleware request is received.

serverInitialize the

The server initializes primarily as the createServer and specifies the callback in server.on(‘request’, callback). The main thing here is to execute the instantiated APP listen function. Let’s take a look at listen.

listen(... args) {const server = http.createServer(this.callback());
    returnserver.listen(... args); }Copy the code

Look at the LISTEN source code in the same way we created the Server using Node’s native HTTP module. CreateServer and specify the address and port number to listen on. Where this.callback specifies the action to be performed after the request is received. Let’s look at the details of this callback function.

callback() {
	const fn = compose(this.middleware);
	if (!this.listenerCount('error')) this.on('error'.this.onerror);

    const handleRequest = (req, res) = > {
      	const ctx = this.createContext(req, res);
      	return this.handleRequest(ctx, fn);
    };

    return handleRequest;
}
Copy the code

Callback uses the closure Return handleRequest to process the request to the end. We will explain how to process the request in detail in the request processing phase. For example, compose(this.middleware) and this.on(‘error’, this.onError) are used to control the compose process and register global error event handlers for the midpoint, respectively.

Request processing phase

When a request comes, it is processed in the handleRequest function returned by the callback function.

const handleRequest = (req, res) = > {
  	const ctx = this.createContext(req, res);
  	return this.handleRequest(ctx, fn);
};
Copy the code

HandleRequest performs two main operations. One is to call the private method createContext to create the context object, and the other is to call the private method handleRequest to process the request.

createContext(req, res) {
   const context = Object.create(this.context);
   const request = context.request = Object.create(this.request);
   const response = context.response = Object.create(this.response);
   context.app = request.app = response.app = this;
   context.req = request.req = response.req = req;
   context.res = request.res = response.res = res;
   request.ctx = response.ctx = context;
   request.response = response;
   response.request = request;
   context.originalUrl = request.originalUrl = req.url;
   context.state = {};
   return context;
}
handleRequest(ctx, fnMiddleware) {
   const res = ctx.res;
   res.statusCode = 404;
   const onerror = err= > ctx.onerror(err);
   const handleResponse = (a)= > respond(ctx);
   onFinished(res, onerror);
   return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}
Copy the code

CreateContext creates a context object, that is, the CTX object in the middleware input parameter, and mounts various properties for the context, request, and response objects. HandleRequest handles the request, which is handled by the compose middleware, known as the fnMiddleware operation. We know that koa-compose returns an anonymous function to the middleware, which in this case corresponds to fnMiddleware, which returns a promise for a request and then a reponse for a resolve or a catch for a resolve. Where Response is a private method defined in KOA.

function respond(ctx) {
  // allow bypassing koa
  if (false === ctx.respond) return;

  if(! ctx.writable)return;

  const res = ctx.res;
  let body = ctx.body;
  const code = ctx.status;

  // ignore body
  if (statuses.empty[code]) {
    // strip headers
    ctx.body = null;
    return res.end();
  }

  if ('HEAD' === ctx.method) {
    if(! res.headersSent && ! ctx.response.has('Content-Length')) {
      const { length } = ctx.response;
      if (Number.isInteger(length)) ctx.length = length;
    }
    return res.end();
  }

  // status body
  if (null == body) {
    if (ctx.req.httpVersionMajor >= 2) {
      body = String(code);
    } else {
      body = ctx.message || String(code);
    }
    if(! res.headersSent) { ctx.type ='text';
      ctx.length = Buffer.byteLength(body);
    }
    return res.end(body);
  }

  // responses
  if (Buffer.isBuffer(body)) return res.end(body);
  if ('string'= =typeof body) return res.end(body);
  if (body instanceof Stream) return body.pipe(res);

  // body: json
  body = JSON.stringify(body);
  if(! res.headersSent) { ctx.length = Buffer.byteLength(body); } res.end(body); }Copy the code

other

Js, request.js, and response.js are all simple module.exports = {}, and it’s not very complicated to expose an object to context, request, and response. Interested students can directly see the source of the three files (KOA source).