Let’s review building a Web server using native Node.js.

var http = require('http');
var server = http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'})
  res.end('Hello world\n')
})
server.listen(3000)
Copy the code

As you can see, we only need to focus on the callback passed in by http.createserver () and the arguments passed in by server.listen(). In general, server.listen() passes in the port number that the Web server listens on, while http.createserver () passes in the callback function that handles the HTTP request and responds.

The same logic corresponds to Koa, with a similar amount of code.

const koa = require('koa');
const app = new koa();

app.use(ctx= > {
  ctx.body = 'Hello world'
})

app.listen(3000)
Copy the code

A closer look shows that server.listen corresponds to app.listen, while the callback passed in by http.createserver () is passed in by app.use() in Koa. In fact, the processing of requests and responses is done by the function passed in by app.use().

With this in mind, we can begin to analyze the Koa source code as described above.

The source code file

The source code for Koa is only four files. Context.js encapsulates the request and response as context CTX, while Request.js and Response.js provide support for context.js.

The core file is application.js, which consists of two main methods:

1. App.listen () – Listen port

Encapsulation isn’t complicated, just putting the native Node.js operation of starting the Web server in a function.

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

As you can probably guess from here, our logic (handling requests and responses) is in this.callback(). And that’s what I’m going to talk about later.

App.use () – Add middleware

In addition to the validity of the verification parameters, only one sentence is really implemented:

use(fn) {
  // ...
  this.middleware.push(fn);
  // ...
}
Copy the code

This is essentially adding the passed middleware function to this.middleware. Ultimately, it is these middleware functions that make up most of the logic for handling requests and responses.

Who handles the middleware

At the beginning of the file, we had an idea that http.createserver () was passing in a callback function that handles each HTTP request and gives a response. Now we see that this.callback() is passing in a return value. Let’s look at its code.

callback() {
  const fn = compose(this.middleware);

  // ...

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

  return handleRequest;
}
Copy the code

The returned handleRequest local variable is the callback function we’ve been talking about, and it takes both request (REq) and response (RES) arguments, just like the native Node.js server. This function is called every time a request comes in, and it does two things:

  • Create a contextctxEncapsulates the request and response
  • contextctxAnd the functionfnUnder thethis.handleRequest()To deal with

By the way, the first line of this function, which we didn’t cover, uses the middleware passed in by app.use(), this.middleware.

const fn = compose(this.middleware);
Copy the code

The middleware mechanism is a very clever part of the Koa design, with which we can provide a wide variety of functionality to the Web server. For the sake of space, we will only show you how to turn the multiple middleware passed in into the desired callback function.

The NPM package used here is koa-compose, which “bunches” the incoming middleware into a callback function fn, which handles the context CTX, of course, HTTP requests and responses.

Handle requests and responses

As mentioned in the previous section, the context CTX and the function fn are handed to this.handlerequest (), which does the following:

  • inctxSet the response to 404 by default
  • Define error handlersonerror, will be specific byctx.onerror()perform
  • Define the response handler functionhandleResponse, will be specific bythis.respond()perform
  • Calls a single callback function that the middleware “pinches” intofnProcessing contextctx, which returns a Promise object that issues the response (call) in its THENhandleResponse), handle errors if they occur (callonerror)

conclusion

In summary, the working principle of a Web server built by Koa can be divided into two processes:

1. Start the server

Callback () is used to “knead” the middleware into a callback function and pass it to http.createserver, instantiating a Server object and calling its LISTEN method to start the Server.

2. Process the request and respond

This.callback () returns a callback function that the Server calls each time a new request arrives, passing in both request and response parameters. It creates the context CTX with req and RES, calls the callback function fn to handle the CTX, and then issues a response or error. Fn is “pinched” from the middleware passed in by our call to app.use(). That is, middleware is at the heart of the process, processing requests and responses according to the logic we want.