Call Me by Your name-node.js small Beautiful

Source: Internet
Author: User
Tags openssl rsa openssl x509 browser cache node server

Personal Summary: It takes 20 minutes to finish reading this post

Call Me by Your name-node.js small Beautiful

Node's appearance, really lets use JS's front-end engineer to meet the love, and then strides toward the back end, although sometimes will be questioned, is not understood. What's the matter with that?

This article is the "one station in the end---the basis of the network" code collation. But it's also a separate node 0 Basic learning note. First you need to install the node environment. It's good to see the tutorials by yourself. This article is the same idea as the functional programming piece. Let's start with the first implementation. If there is a chance we go back to the theory, in fact, the API has nothing to fill, there is time we write the node asynchronous queue and DC algorithm, but you have nothing to understand with the document can be viewed. OK, the usual, let's see, this article has finished the content.

This article is code on GitHub

    • Using node to build a TCP server
    • Using node to build an HTTP server
    • Read the file with the node file FS module and write it as a stream
    • Node routing is completed using the URL path module.
    • Path module to determine file type
    • Compress files with gzip
    • Implementation of the browser cache protocol
    • Node handles cross-domain
    • The construction of the HTTPS node server
    • The HTTP2 node server is built

1 node creating a TCP Server
const net = require(‘net‘);let server = net.createServer((socket)=>{   socket.on(‘data‘,function (res) {      console.log(res.toString())   });});server.listen({   host: ‘localhost‘, port: 8080});

    • First you need to know that node uses modular thinking, you can require some modules,
    • NET is a TCP network API. We first use it to create a TCP server
    • We introduced the net module and created a service through the Createserver method.
    • When the data is received, the "data" event is triggered and the spliced message is given to us in the form of parameters.
    • The message is the binary buffer data, we need to convert the ToString method into a string
    • Then we set up a TCP service that listens to the localhost,8080 port
    • We execute node tcp1.js in terminal, and this server is started.
    • We are now accessing localhost:8080 in the browser
    • The server will trigger the ' data ' event when it receives
    • We saw the request header in the terminal.

Here we talk about node's event mechanism:

    //events 模块只提供了一个对象: events.EventEmitter    //EventEmitter 的核心就是事件触发与事件监听器功能的封装。    var EventEmitter = require(‘events‘).EventEmitter;     //一个socket对象    var socket = new EventEmitter();    //我们在socket对象上绑定data事件,如果是多个函数会被先后调用    socket.on(‘data‘, function(res) {         console.log(res);     });     socket.on(‘data‘, function(res) { console.log(res + ‘111‘); }); //我们用emit的方法去触发事件,在1秒后我们出发,我们触发事件时,可以传递参数。 setTimeout(function() { socket.emit(‘data‘ , "hello" ); }, 1000); 

We will see the following information in the console.

At this point we will look at the bottom left corner of the browser, is not always showing waiting for the response, because we have not returned the data ah, then we give it back some data. We know to conform to HTTP format.

We use Socket.write (RESPONSEDATATPL) to return data in an HTTP-compliant format

let responseDataTpl = `HTTP/1.1 200 OKConnection:keep-aliveDate: ${new Date()}Content-Length: 12Content-Type: text/plainHello world!`;
    • We're triggering node 01-tcp02.js.
    • In the browser, we can see the returned Hello world!

Question: We have found that writing a fixed format HTTP Response Packet Cup is still more troublesome, why can't we encapsulate a layer?


2 node Create HTTP Server 2.1 Create an HTTP server
const http = require(‘http‘);const server = http.createServer((req, res) => {    res.writeHead(200, { ‘Content-Type‘: ‘text/plain‘ });    res.end(‘hello world‘);    //  发送响应数据 })server.on(‘clientError‘, (err, socket) => { socket.end(‘HTTP/1.1 400 Bad Request\r\n\r\n‘)})server.listen(10080) 
    • We introduced a node HTTP module that listens on port 10080 (default address localhost)
    • We created an HTTP service that, when the request was successful, returned a 200 status code
    • Res.end (' Hello World ') is the time to send the response data with ' Hello World '
    • We execute node 02-http-simple.js in terminal, and this server is started.
    • We are now accessing localhost:10080 in the browser
    • We see ' Hello World ' on the browser.

Question: So this time, what if I want to pass in a file instead of a string?


2.2 Node File module (FS)

Node's file module is very powerful, you can read the file, delete and change the search. Here we first talk about how to read. Reads are two kinds of synchronization, one asynchronous.

const fs = require(‘fs‘); const http = require(‘http‘);const server = http.createServer((req, res) => {  res.writeHead(200, { ‘Content-Type‘: ‘text/html‘ });  // 同步  // let data = fs.readFileSync(‘index.html‘); // res.write(data); // res.end(); // 发送响应数据 // 异步 fs.readFile(‘index.html‘, function (err, data) { res.write(data); res.end(); // 发送响应数据 })})server.on(‘clientError‘, (err, socket) => { socket.end(‘HTTP/1.1 400 Bad Request\r\n\r\n‘)})server.listen(8088) 
    • We introduce the file module, the const FS = require (' FS ');
    • When synchronizing, we first read, execute the Write and send function behind
    • Asynchronously, we perform write and send in the callback function of the asynchronous read

Question: So now there is a problem, whether synchronous or asynchronous, we need to read the file before writing, then the file is very large, the pressure on the memory will be very large. Do we have any way to read side by side to write?


2.3 Node Stream (stream)

Stream is an abstract interface that works by writing a little bit of the file. It doesn't have to be a lot of memory. Let's see how it's going to come true.

const fs = require(‘fs‘);const http = require(‘http‘);const server = http.createServer((req, res) => {  res.writeHead(200, { ‘Content-Type‘: ‘text/html‘ });  // let resStream = fs.createReadStream(‘index.html‘); // resStream.pipe(res); //流是可以支持链式操作的 fs.createReadStream(‘index.html‘).pipe(res)})server.on(‘clientError‘, (err, socket) => { socket.end(‘HTTP/1.1 400 Bad Request\r\n\r\n‘)})server.listen(10080)
    • Create a readable stream with Fs.createreadstream (' index.html ').
    • Use Resstream.pipe (res), pipe read and write operations, write response messages
    • You will find that we do not use Res.end () in the above code; Send data. Because by default, the ' End ' event is automatically triggered when data transfer is complete
    • The last stream is a chain-like operation, so you can do it with a single line of code.

Problem: After we have solved the memory problem, you will find that there is a picture in our index.html that is not loaded. The reason is simple. Because no matter what request we send, we only return the same action. So how can we differentiate between different requests?


2.4 Node Routing

We know where to use URLs to represent files in an application protocol. One important task for distinguishing between different requests is to differentiate the paths. Then the path to the processing node provides a URL module, let's take a look at it.

Const FS = require (' FS '); Const HTTP = require (' HTTP '); Const URL = require ("url"); Const SERVER = Http.createserver ((req, res) = = {//pathname is the address that is taken after the port numberLet pathname = Url.parse (req.url). Pathname;if (pathname = =  '/') pathname = / Index.html '; let Respath =  '. ' + pathname;//Determine if the path exists if (!fs.existssync (Respath)) {Res.writehead (404, { ' Content-type ':  ' text/html '}); return res.end ( '  ' Content-type ':  ' text/ HTML '}); Fs.createreadstream (Respath). Pipe (RES)}) Server.on ( ' Clienterror ', (err, socket) + = { Socket.end ( ' http/1.1-Bad request\r\n\r\n ')}) Server.listen (10080)   
    • We have introduced a URL module that helps us to handle the path
    • Url.parse (Req.url) is a path that helps us deal with objects that contain our common path properties
    • One of the properties is pathname, which is the path between the URL port number and the parameter, which is the path we access
    • If we have direct access to the website without the path, we give the default point to/index.html
    • Relative path Access we add a '. ' to the front.
    • Then we use the Existssync method provided by the file module to determine if there is a file on the server
    • If not we return 404, tell no file found. Returns the file if one is available.

Question: So now, we can see the picture of our beautiful big Juan in the browser, but when we learn the HTTP time to know that Content-type is processing file type, then the picture type certainly will not be ' text/html ', although the browser is very smart to help me show it, But we still have to change the mistake.


2.5 path module to determine file type

We know that just change the file type of ' Content-type '.

function Getfiletype (respath) {Const EXT_FILE_TYPES = { ' default ':  ' text/html ',  JS ': Span class= "hljs-string" > ' Text/javascript ',  '. css ':  text/css ',  ' Text/json ',  '. JPEG ': Span class= "hljs-string" > ' image/jpeg ',  '. jpg ':  ' image/jpg ', Span class= "hljs-string" ". png ':  ' image/png ',//...} let path = require ( ' path '); let mime_type = Ext_file_types[path.extname (respath)] | | Ext_file_types[return Mime_type;}           
    • We define a getfiletype function and give the common file types and their content-type values.
    • We have used the path module to remove the extension with the Extname method on the path module
    • Then we match the object we defined, and if we don't find it, we'll give you a default value.

Every time you modify the node file you need to go to the terminal startup is not very troublesome. Now let's make a little hot-start trick.

    • In the global installation supervisor
    • Use supervisor instead of node to start a file
    • So when you modify the node file, you don't have to manually go to the terminal every time.

Question: Our big Juan's picture only then only has more than 100 k, if is the picture is very big we can compress the transmission first


2.5 compressing files with gzip

(1) We first take out the accept-encoding parameter in the request header, and if the argument does not exist, we assign the value "

let acceptEncoding = req.headers[‘accept-encoding‘]; if (!acceptEncoding) { acceptEncoding = ‘‘;};

(2) Then we use the regular to determine whether acceptencoding is using gzip compression, of course, there can be more than one way to determine the compression format. Here we only write one.

if(/\bgzip\b/.test(acceptEncoding)){      //执行压缩,并在响应头中告诉浏览器压缩的格式  }else{      //不执行压缩  }

(3) We need to compress the file by referencing the Zlib module. Here we use gzip to call the Gzip method. We then compress the file stream in one step and write it to the response body.

const zlib = require(‘zlib‘);let raw = fs.createReadStream(resPath);raw.pipe(zlib.createGzip()).pipe(res);

(4) Finally, we need to tell the browser in the response header that my files have been compressed into what format.

‘Content-Encoding‘: gzip

Then we open two terminals respectively with the boot with gzip and no gzip compression

    • Http://localhost:8088/home.html
    • Http://localhost:10080/home.html

In the home file, I put a picture of 5M that I took with my camera at the Summer Palace.

You can open multiple browser windows, first access two files, you can test several times, you will find that gzip compression is significantly slower .

Why this is so, the reason is very simple, because our server and browser are on the same computer, transmission speed quickly. So the time to compress and decompress is magnified. This also tells us that not all scenarios are suitable for compressing files.


2.6 Implementation of the browser cache protocol

This section does not have new knowledge of node, we implement an HTTP browser caching protocol. We also do not need to compress, so the contents of the previous section of compression will not be added.

* * (1) Strong cache * * Strong cache We give a one-week expiration reference code in the response header Cache.js

Cache-Control : max-age = 604800‘


    • We can see that in the second refresh, the resources in the file are taken from the browse cache.
    • If you do not want to take it from the cache, you can force the refresh or open the Disable cache
    • When you have a strong brush, you'll see the cache-control:no-cache in the localhost request header.
    • You will normally refresh the resource file will have Cache-control:no-cache, this is because the resource file is taken from the cache, and Cache-control:no-cache is the last time you have a strong brush to bring up.
    • If a new window is opened, the same page is accessed again, not from the cache
    • This is why, sometimes when you are developing, you change the JS file does not take effect, but in another window open to see the latest file reason

* * (2) Weak cache * * Reference code Cache2.js

The ETag needs a double-quoted string, and then we write it into the response header

let etagStr = "dajuan";  //etag 要加双引号 res.writeHead(200, {     ‘Content-Type‘: getFileType(resPath),    ‘etag‘ : etagStr  });

When we visit again, we need to determine whether the value of the If-none-match band is the same as the ETAGSTR value now. If you return 304 consistently, you do not have to return the file. When the browser sees 304, it knows to take it from the cache.

let etagStr = "dajuan";  //etag 要加双引号   if(req.headers[‘if-none-match‘] === etagStr){    res.writeHead(304, {       ‘Content-Type‘: getFileType(resPath), ‘etag‘ : etagStr }); res.end(); }

Of course, here we just cite the simplest example of a real project where it is impossible to return all the files to the same string.

2.7 node handles post and get requests

(1) We first use Get and post to write a form to submit, let its click All jump to form_result.html, a row hello, name

form.html <form action="form_result.html" method="get" > <p> get: <input type="text" name="name"/></p > <input type="Submit" value="submit"/> </form> <form action="form_result.html" method="POST" > <p> post: <input type="text" name="name"/></p> <input  Type= "submit" value="submit"/> </form>//form_result.html <div> Hello,name</div> 

(2) Get method to process reference code Method.js

let pathAll = url.parse(req.url); let getArgument = pathAll.query;     //取出参数 name=XXX if(pathname === ‘/form_result.html‘ && getArgument != undefined){   let text = fs.readFileSync(‘form_result.html‘).toString().replace(/name/, getArgument) fs.writeFileSync(‘form_result.html‘,text) }
    • We know that URL.PARSL () can read url,query is the parameter of the Get method.
    • When the path to jump is '/form_result.html ' and getargument has a value
    • We use the file module to read the contents of ' form_result.html ' synchronously.
    • After converting to a string, replace name in form with Name=xxx

This time the get submitted form can be processed, but the post parameters are not in the URL, so there is no effect on post

(3) Post method to process reference code Method2.js

  req.on(‘data‘,(data)=>{    let text = fs.readFileSync(‘form_result.html‘).toString().replace(/name/, ‘post‘+ data)    fs.writeFileSync(‘form_result.html‘,text) })
    • The Post method is used to listen to the data event in the request header, which is triggered when there is a request body in the request message.
    • So when we hear that the ' data ' event is triggered, we also perform the above operation
    • And this time, if a GET request is sent, it will not be responded to
    • We learn that we can bind multiple events to ' data ', and each POST request will inevitably trigger. This is a side effect on the server.

2.8 Node handles cross-domain

Reference code: Cors.js cors2.js

  if(req.headers[‘origin‘] ) {    res.writeHead(200, {       ‘Access-Control-Allow-Origin‘: ‘http://localhost:5000‘,      ‘Content-Type‘: ‘text/html‘ }); return fs.createReadStream(resPath).pipe(res) }; 
    • We started two services locally, respectively.
    • Let one port be 5000 and the other port be 9088.
    • We access on the 5000 port, cors.html
    • In HTML, we AJAX calls 9088-Port Data.json
    • This creates a cross-domain, and we allow 5000 ports to access it and return the data
    • If we do not fill, or do not write 5000 port, you will see no data received

Note: Here is still a little bit of a problem, the first I only on the first visit, if the port does not meet the prompt error. I doubt if the browser has added a whitelist to the server address. The second why is not the book written two times pleading AH. The first time I did not write the data, I would not initiate a second request. However, the cross-domain effect is still achieved.

3 HTTPS and http23.1 HTTPS for node server construction

Once we know the principle, we generate a certificate and a private key at the terminal.

(1)openssl genrsa -out server.key 1024 //生成服务器私钥(2)openssl rsa -in server.key -pubout -out server.pem  // 生成公钥  //自己扮演CA机构,给自己服务器颁发证书,CA机构也需要自己私钥,CSR文件(证书签名请求文件),和证书 (3)  openssl genrsa -out ca.key 1024            //生成CA 私钥      openssl req -new -key ca.key -out ca.csr   //生成CA CSR文件      openssl x509 -req -in ca.csr -signkey ca.key  -out ca.crt  //生成CA 证书 //生成证书签名请求文件 (4) openssl req -new -key server.key -out server.csr //生成server CSR文件   //向自己的机构请求生成证书 (5) openssl x509 -req -CA  ca.crt -CAkey ca.key -CAcreateserial -in server.csr   -out server.crt   //生成server 证书

Note: The information is filled in, but the hint has the format to notice ah, the babies ...

const https = require(‘https‘);const fs = require(‘fs‘);const options = {  key: fs.readFileSync(‘./key/server.key‘),  cert: fs.readFileSync(‘./key/server.crt‘)};https.createServer(options, (req, res) => {  res.writeHead(200);  res.end(‘hello world\n‘);}).listen(8000);
    • We introduce the HTTPS module and fill out our certificates and private keys.
    • The rest of the code now seems to be not very simple

Server access: Https://localhost:8000/

    • So we can access HTTPS and request a Web page.
    • Of course, it will prompt us not to be safe.
    • Why is it not safe to remind us, just how to fill out the certificate, the heart is not counted. Ha ha haha
3.2 HTTP2 's node server is built

Node's HTTP2 is the experimental API. If the node version is low, please upgrade first. Mine is v8.11.3.

const http2 = require(‘http2‘);const fs = require(‘fs‘);const server = http2.createSecureServer({  key: fs.readFileSync(‘./key/server.key‘),  cert: fs.readFileSync(‘./key/server.crt‘)});server.on(‘error‘, (err) => console.error(err));server.on(‘stream‘, (stream, headers) => { // stream is a Duplex stream.respond({ ‘content-type‘: ‘text/html‘, ‘:status‘: 200 }); stream.end(‘
    • We're still the private key and the certificate that we created when we introduced HTTPS
    • We create a HTTP2 service
    • The concept of stream in HTTP2. So we write the request header. and returns the request body
    • We are petitioning in the browser ask: https://localhost:8443/

So that we can complete a simple HTTP2 visit.


Call Me by Your name-node.js small Beautiful

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.