View Javadoc
1   /*
2    * Copyright 2015 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.channel.unix;
17  
18  import io.netty.util.internal.EmptyArrays;
19  
20  import java.io.FileNotFoundException;
21  import java.io.IOException;
22  import java.net.ConnectException;
23  import java.net.NoRouteToHostException;
24  import java.nio.channels.AlreadyConnectedException;
25  import java.nio.channels.ClosedChannelException;
26  import java.nio.channels.ConnectionPendingException;
27  import java.nio.channels.NotYetConnectedException;
28  
29  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEAGAIN;
30  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEBADF;
31  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoECONNRESET;
32  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEINPROGRESS;
33  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoENOENT;
34  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoENOTCONN;
35  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEPIPE;
36  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEWOULDBLOCK;
37  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errorEALREADY;
38  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errorECONNREFUSED;
39  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errorEISCONN;
40  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errorENETUNREACH;
41  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.strError;
42  
43  /**
44   * <strong>Internal usage only!</strong>
45   * <p>Static members which call JNI methods must be defined in {@link ErrorsStaticallyReferencedJniMethods}.
46   */
47  public final class Errors {
48      // As all our JNI methods return -errno on error we need to compare with the negative errno codes.
49      public static final int ERRNO_ENOENT_NEGATIVE = -errnoENOENT();
50      public static final int ERRNO_ENOTCONN_NEGATIVE = -errnoENOTCONN();
51      public static final int ERRNO_EBADF_NEGATIVE = -errnoEBADF();
52      public static final int ERRNO_EPIPE_NEGATIVE = -errnoEPIPE();
53      public static final int ERRNO_ECONNRESET_NEGATIVE = -errnoECONNRESET();
54      public static final int ERRNO_EAGAIN_NEGATIVE = -errnoEAGAIN();
55      public static final int ERRNO_EWOULDBLOCK_NEGATIVE = -errnoEWOULDBLOCK();
56      public static final int ERRNO_EINPROGRESS_NEGATIVE = -errnoEINPROGRESS();
57      public static final int ERROR_ECONNREFUSED_NEGATIVE = -errorECONNREFUSED();
58      public static final int ERROR_EISCONN_NEGATIVE = -errorEISCONN();
59      public static final int ERROR_EALREADY_NEGATIVE = -errorEALREADY();
60      public static final int ERROR_ENETUNREACH_NEGATIVE = -errorENETUNREACH();
61  
62      /**
63       * Holds the mappings for errno codes to String messages.
64       * This eliminates the need to call back into JNI to get the right String message on an exception
65       * and thus is faster.
66       *
67       * The array length of 512 should be more then enough because errno.h only holds < 200 codes.
68       */
69      private static final String[] ERRORS = new String[512];
70  
71      /**
72       * <strong>Internal usage only!</strong>
73       */
74      public static final class NativeIoException extends IOException {
75          private static final long serialVersionUID = 8222160204268655526L;
76          private final int expectedErr;
77          private final boolean fillInStackTrace;
78  
79          public NativeIoException(String method, int expectedErr) {
80              this(method, expectedErr, true);
81          }
82  
83          public NativeIoException(String method, int expectedErr, boolean fillInStackTrace) {
84              super(method + "(..) failed: " + ERRORS[-expectedErr]);
85              this.expectedErr = expectedErr;
86              this.fillInStackTrace = fillInStackTrace;
87          }
88  
89          public int expectedErr() {
90              return expectedErr;
91          }
92  
93          @Override
94          public synchronized Throwable fillInStackTrace() {
95              if (fillInStackTrace) {
96                  return super.fillInStackTrace();
97              }
98              return this;
99          }
100     }
101 
102     static final class NativeConnectException extends ConnectException {
103         private static final long serialVersionUID = -5532328671712318161L;
104         private final int expectedErr;
105         NativeConnectException(String method, int expectedErr) {
106             super(method + "(..) failed: " + ERRORS[-expectedErr]);
107             this.expectedErr = expectedErr;
108         }
109 
110         int expectedErr() {
111             return expectedErr;
112         }
113     }
114 
115     static {
116         for (int i = 0; i < ERRORS.length; i++) {
117             // This is ok as strerror returns 'Unknown error i' when the message is not known.
118             ERRORS[i] = strError(i);
119         }
120     }
121 
122     static boolean handleConnectErrno(String method, int err) throws IOException {
123         if (err == ERRNO_EINPROGRESS_NEGATIVE || err == ERROR_EALREADY_NEGATIVE) {
124             // connect not complete yet need to wait for EPOLLOUT event.
125             // EALREADY has been observed when using tcp fast open on centos8.
126             return false;
127         }
128         throw newConnectException0(method, err);
129     }
130 
131     /**
132      * @deprecated Use {@link #handleConnectErrno(String, int)}.
133      * @param method The native method name which caused the errno.
134      * @param err the negative value of the errno.
135      * @throws IOException The errno translated into an exception.
136      */
137     @Deprecated
138     public static void throwConnectException(String method, int err) throws IOException {
139         if (err == ERROR_EALREADY_NEGATIVE) {
140             throw new ConnectionPendingException();
141         }
142         throw newConnectException0(method, err);
143     }
144 
145     private static IOException newConnectException0(String method, int err) {
146         if (err == ERROR_ENETUNREACH_NEGATIVE) {
147             return new NoRouteToHostException();
148         }
149         if (err == ERROR_EISCONN_NEGATIVE) {
150             throw new AlreadyConnectedException();
151         }
152         if (err == ERRNO_ENOENT_NEGATIVE) {
153             return new FileNotFoundException();
154         }
155         return new ConnectException(method + "(..) failed: " + ERRORS[-err]);
156     }
157 
158     public static NativeIoException newConnectionResetException(String method, int errnoNegative) {
159         NativeIoException exception = new NativeIoException(method, errnoNegative, false);
160         exception.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
161         return exception;
162     }
163 
164     public static NativeIoException newIOException(String method, int err) {
165         return new NativeIoException(method, err);
166     }
167 
168     @Deprecated
169     public static int ioResult(String method, int err, NativeIoException resetCause,
170                                ClosedChannelException closedCause) throws IOException {
171         // network stack saturated... try again later
172         if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) {
173             return 0;
174         }
175         if (err == resetCause.expectedErr()) {
176             throw resetCause;
177         }
178         if (err == ERRNO_EBADF_NEGATIVE) {
179             throw closedCause;
180         }
181         if (err == ERRNO_ENOTCONN_NEGATIVE) {
182             throw new NotYetConnectedException();
183         }
184         if (err == ERRNO_ENOENT_NEGATIVE) {
185             throw new FileNotFoundException();
186         }
187 
188         // TODO: We could even go further and use a pre-instantiated IOException for the other error codes, but for
189         //       all other errors it may be better to just include a stack trace.
190         throw newIOException(method, err);
191     }
192 
193     public static int ioResult(String method, int err) throws IOException {
194         // network stack saturated... try again later
195         if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) {
196             return 0;
197         }
198         if (err == ERRNO_EBADF_NEGATIVE) {
199             throw new ClosedChannelException();
200         }
201         if (err == ERRNO_ENOTCONN_NEGATIVE) {
202             throw new NotYetConnectedException();
203         }
204         if (err == ERRNO_ENOENT_NEGATIVE) {
205             throw new FileNotFoundException();
206         }
207 
208         throw new NativeIoException(method, err, false);
209     }
210 
211     private Errors() { }
212 }