Nodejs error handling process record

Nodejs error handling process record

This article takes the connection error ECONNREFUSED as an example to see the process of nodejs handling errors. Suppose we have the following code

1. const net = require('net');  
2. net.connect({port: 9999})

If port 9999 is not listening on this machine, we will get the following output.

1. events.js:170  
2. throw er; // Unhandled 'error' event  
3. ^  
4.    
5. Error: connect ECONNREFUSED 127.0.0.1:9999  
6. at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1088:14)  
7. Emitted 'error' event at:  
8. at emitErrorNT (internal/streams/destroy.js:91:8)  
9. at emitErrorAndCloseNT (internal/streams/destroy.js:59:3)  
10. at processTicksAndRejections (internal/process/task_queues.js:81:17)

Let's take a quick look at the call process of connect.

1. const req = new TCPConnectWrap();  
2. req.oncomplete = afterConnect;  
3. req.address = address;  
4. req.port = port;  
5. req.localAddress = localAddress;  
6. req.localPort = localPort;  
7. // Start three-way handshake to establish connection 8. err = self._handle.connect(req, address, port);

Next, let's look at the logic of the C++ layer connect

1. err = req_wrap->Dispatch(uv_tcp_connect,  
2. &wrap->handle_,  
3. reinterpret_cast<const sockaddr*>(&addr),  
4. AfterConnect);

The C++ layer directly calls Libuv's uv_tcp_connect and sets the callback to AfterConnect. Next, let's look at the implementation of libuv.

1. do {  
2. errno = 0;  
3. // Non-blocking call 4. r = connect(uv__stream_fd(handle), addr, addrlen);  
5. } while (r == -1 && errno == EINTR);  
6. // Connection error, determine the error code 7. if (r == -1 && errno != 0) {  
8. // Still connecting, not an error, waiting for the connection to complete and the event to become readable 9. if (errno == EINPROGRESS)  
10. ; /* not an error */  
11. else if (errno == ECONNREFUSED)  
12. // Connection refused 13. handle->delayed_error = UV__ERR(ECONNREFUSED);  
14. else  
15. return UV__ERR(errno);  
16. }  
17. uv__req_init(handle->loop, req, UV_CONNECT);  
18. req->cb = cb;  
19. req->handle = (uv_stream_t*) handle;  
20. QUEUE_INIT(&req->queue);  
21. // Mount to handle and wait for writable event 22. handle->connect_req = req;  
23. uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);

We see that Libuv calls the operating system asynchronously, then mounts the request into the handle and registers to wait for writable events. When the connection fails, the uv stream_io callback is executed. Let's take a look at Libuv's processing (uv stream_io).

1. getsockopt(uv__stream_fd(stream),  
2. SOL_SOCKET,  
3. SO_ERROR,  
4. &error,  
5. &errorsize);  
6. error = UV__ERR(error);  
7. if (req->cb)  
8. req->cb(req, error);

After getting the error information, call back the AfterConnect of the C++ layer.

1. Local<Value> argv[5] = {  
2. Integer::New(env->isolate(), status),  
3. wrap->object(),  
4. req_wrap->object(),  
5. Boolean::New(env->isolate(), readable),  
6. Boolean::New(env->isolate(), writable)  
7. };  
8.    
9. req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv);

Then call the oncomplete callback of the JS layer.

1. const ex = exceptionWithHostPort(status,  
2. 'connect',  
3. req.address,  
4. req.port,  
5. details);  
6. if (details) {  
7. ex.localAddress = req.localAddress;  
8. ex.localPort = req.localPort;  
9. }  
10. // Destroy the socket  
11. self.destroy(ex);

exceptionWithHostPort constructs an error message, then destroys the socket and triggers an error event with ex as the parameter. Let's look at the implementation of uvExceptionWithHostPort.

1. function uvExceptionWithHostPort(err, syscall, address, port) {  
2. const [ code, uvmsg ] = uvErrmapGet(err) || uvUnmappedError;  
3. const message = `${syscall} $[code]: ${uvmsg}`;  
4. let details = '';  
5.    
6. if (port && port > 0) {  
7. details = ` ${address}:${port}`;  
8. } else if (address) {  
9. details = ` ${address}`;  
10. }  
11. const tmpLimit = Error.stackTraceLimit;  
12. Error.stackTraceLimit = 0;  
13. const ex = new Error(`${message}${details}`);  
14. Error.stackTraceLimit = tmpLimit;  
15. ex.code = code;  
16. ex.errno = err;  
17. ex.syscall = syscall;  
18. ex.address = address;  
19. if (port) {  
20. ex.port = port;  
twenty one. }  
22. // Get the call stack information but exclude the currently called function uvExceptionWithHostPort, and inject the stack field into ex23. Error.captureStackTrace(ex, excludedStackFn || uvExceptionWithHostPort);  
24. return ex;  
25. }

We see that error information is mainly obtained through uvErrmapGet

1. function uvErrmapGet(name) {  
2. uvBinding = lazyUv();  
3. if (!uvBinding.errmap) {  
4. uvBinding.errmap = uvBinding.getErrorMap();  
5. }  
6. return uvBinding.errmap.get(name);  
7. }  
8.    
9. function lazyUv() {  
10. if (!uvBinding) {  
11. uvBinding = internalBinding('uv');  
12. }  
13. return uvBinding;  
14. }

Continuing to look down, uvErrmapGet calls the getErrorMap of the uv module in the C++ layer.

1. void GetErrMap(const FunctionCallbackInfo<Value>& args) {  
2. Environment* env = Environment::GetCurrent(args);  
3. Isolate* isolate = env->isolate();  
4. Local<Context> context = env->context();  
5.    
6. Local<Map> err_map = Map::New(isolate);  
7. // Get error information from per_process::uv_errors_map8. size_t errors_len = arraysize(per_process::uv_errors_map);  
9. // Assignment 10. for (size_t i = 0; i < errors_len; ++i) {  
11. // The key of the map is the value in each element of uv_errors_map, and the value is name and message
12. const auto& error = per_process::uv_errors_map[i];  
13. Local<Value> arr[] = {OneByteString(isolate, error.name),  
14. OneByteString(isolate, error.message)}; 
15. if (err_map  
16. ->Set(context,  
17. Integer::New(isolate, error.value),  
18. Array::New(isolate, arr, arraysize(arr)))  
19. .IsEmpty()) {  
20. return;  
twenty one. }  
twenty two. }  
twenty three.   
24. args.GetReturnValue().Set(err_map);  
25. }

We see that the error information exists in per_process::uv_errors_map. Let's take a look at the definition of uv_errors_map.

1. struct UVError {
2. int value;
3. const char* name;
4. const char* message;
5. };
6.  
7. static const struct UVError uv_errors_map[] = {  
8. #define V(name, message) {UV_##name, #name, message},  
9. UV_ERRNO_MAP(V)  
10. #undef V  
11. };

The UV_ERRNO_MAP macro expands to the following:

1. {UV_E2BIG, "E2BIG", "argument list too long"},  
2. {UV_EACCES, "EACCES", "permission denied"},  
3. {UV_EADDRINUSE, "EADDRINUSE", "address already in use"},  
4. …

So the result of exporting to the JS layer is as follows

1. {  
2. // The key is a number, defined by Libuv, which actually encapsulates the definition of the operating system 3. UV_ECONNREFUSED: ["ECONNREFUSED", "connection refused"],    
4. UV_ECONNRESET: ["ECONNRESET", "connection reset by peer"]   
5. ...   
6. }

Node.js will finally assemble this information and return it to the caller. This is the error message we output. So why is it ECONNREFUSED? Let's take a look at the operating system's logic for this error code.

1. static void tcp_reset(struct sock *sk)  
2. {  
3. switch (sk->sk_state) {  
4. case TCP_SYN_SENT:  
5. sk->sk_err = ECONNREFUSED;  
6. break;  
7. // ...
8. }  
9.    
10. }

When the operating system receives an rst packet sent to the socket, it will execute tcp_reset. We can see that when the socket is sending a syn packet and waiting for ack, if a fin packet is received, the error code will be set to ECONNREFUSED. It is this error code that we output.

Summarize

This is the end of this article about the error handling process record of nodejs. For more relevant nodejs error handling content, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • NodeJs high memory usage troubleshooting actual combat record
  • Detailed explanation of using Nodejs built-in encryption module to achieve peer-to-peer encryption and decryption
  • Detailed explanation of asynchronous iterators in nodejs
  • Detailed explanation of nodejs built-in modules
  • Nodejs module system source code analysis
  • A brief discussion on event-driven development in JS and Nodejs
  • How to use module fs file system in Nodejs
  • Summary of some tips for bypassing nodejs code execution
  • Nodejs Exploration: In-depth understanding of the principle of single-threaded high concurrency
  • How to use nodejs to write a data table entity class generation tool for C#

<<:  MySQL5.7.03 Replacement of higher version to MySQL 5.7.17 Installation process and solutions to problems found

>>:  Try Docker+Nginx to deploy single page application method

Recommend

Vuex implements simple shopping cart function

This article example shares the specific code of ...

Tomcat common exceptions and solution code examples

The company project was developed in Java and the...

Several methods to solve the problem of MySQL fuzzy query index failure

When we use the like % wildcard, we often encount...

How to get the intersection/difference/union of two sets in mysql

Common scenarios of MySQL: getting the intersecti...

5 solutions to CSS box collapse

First, what is box collapse? Elements that should...

How to use and limit props in react

The props of the component (props is an object) F...

LinkedIn revamps to simplify website browsing

Business social networking site LinkedIn recently...

Summary of Common Mistakes in Web Design

In the process of designing a web page, designers...

Use the more, less, and cat commands in Linux to view file contents

In Linux, the commands cat, more, and less can al...

How to use flat style to design websites

The essence of a flat website structure is simpli...

How to use Navicat to export and import mysql database

MySql is a data source we use frequently. It is v...

Solution to the ineffective global style of the mini program custom component

Table of contents Too long to read Component styl...

Differences in the hr separator between browsers

When making a web page, you sometimes use a dividi...

Linux process management tool supervisor installation and configuration tutorial

Environment: CentOS 7 Official documentation: htt...