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:
|
>>: Try Docker+Nginx to deploy single page application method
The browser displays TIF format images Copy code T...
In the vue scaffolding, we can see that in the ne...
Table of contents 1. Preprocessing 2. Pretreatmen...
Table of contents How to view the source code of ...
Prelude We all know that nginx is an excellent re...
Preface MySQL officially refers to prepare, execu...
The scroll bar position is retained when scrollin...
Written in front In recent years, the live stream...
By chance, I discovered that a SQL statement prod...
Recently, due to work needs, I need to format num...
1. Purpose: Make the code easier to maintain and ...
I'll record the problems I encountered during...
1. First, let’s have a general introduction to th...
Table of contents What is a plugin Writing plugin...
Dark background style page design is very popular...