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.netty5.channel.unix;
17  
18  import io.netty5.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.netty5.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEAGAIN;
30  import static io.netty5.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEBADF;
31  import static io.netty5.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoECONNRESET;
32  import static io.netty5.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEINPROGRESS;
33  import static io.netty5.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoENOENT;
34  import static io.netty5.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoENOTCONN;
35  import static io.netty5.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEPIPE;
36  import static io.netty5.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEWOULDBLOCK;
37  import static io.netty5.channel.unix.ErrorsStaticallyReferencedJniMethods.errorEALREADY;
38  import static io.netty5.channel.unix.ErrorsStaticallyReferencedJniMethods.errorECONNREFUSED;
39  import static io.netty5.channel.unix.ErrorsStaticallyReferencedJniMethods.errorEISCONN;
40  import static io.netty5.channel.unix.ErrorsStaticallyReferencedJniMethods.errorENETUNREACH;
41  import static io.netty5.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 {
103         for (int i = 0; i < ERRORS.length; i++) {
104             // This is ok as strerror returns 'Unknown error i' when the message is not known.
105             ERRORS[i] = strError(i);
106         }
107     }
108 
109     static boolean handleConnectErrno(String method, int err) throws IOException {
110         if (err == ERRNO_EINPROGRESS_NEGATIVE || err == ERROR_EALREADY_NEGATIVE) {
111             // connect not complete yet need to wait for EPOLLOUT event.
112             // EALREADY has been observed when using tcp fast open on centos8.
113             return false;
114         }
115         throw newConnectException0(method, err);
116     }
117 
118     /**
119      * @deprecated Use {@link #handleConnectErrno(String, int)}.
120      * @param method The native method name which caused the errno.
121      * @param err the negative value of the errno.
122      * @throws IOException The errno translated into an exception.
123      */
124     @Deprecated
125     public static void throwConnectException(String method, int err) throws IOException {
126         if (err == ERROR_EALREADY_NEGATIVE) {
127             throw new ConnectionPendingException();
128         }
129         throw newConnectException0(method, err);
130     }
131 
132     private static IOException newConnectException0(String method, int err) {
133         if (err == ERROR_ENETUNREACH_NEGATIVE) {
134             return new NoRouteToHostException();
135         }
136         if (err == ERROR_EISCONN_NEGATIVE) {
137             throw new AlreadyConnectedException();
138         }
139         if (err == ERRNO_ENOENT_NEGATIVE) {
140             return new FileNotFoundException();
141         }
142         return new ConnectException(method + "(..) failed: " + ERRORS[-err]);
143     }
144 
145     public static NativeIoException newConnectionResetException(String method, int errnoNegative) {
146         NativeIoException exception = new NativeIoException(method, errnoNegative, false);
147         exception.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
148         return exception;
149     }
150 
151     public static NativeIoException newIOException(String method, int err) {
152         return new NativeIoException(method, err);
153     }
154 
155     @Deprecated
156     public static int ioResult(String method, int err, NativeIoException resetCause,
157                                ClosedChannelException closedCause) throws IOException {
158         // network stack saturated... try again later
159         if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) {
160             return 0;
161         }
162         if (err == resetCause.expectedErr()) {
163             throw resetCause;
164         }
165         if (err == ERRNO_EBADF_NEGATIVE) {
166             throw closedCause;
167         }
168         if (err == ERRNO_ENOTCONN_NEGATIVE) {
169             throw new NotYetConnectedException();
170         }
171         if (err == ERRNO_ENOENT_NEGATIVE) {
172             throw new FileNotFoundException();
173         }
174 
175         // TODO: We could even go further and use a pre-instantiated IOException for the other error codes, but for
176         //       all other errors it may be better to just include a stack trace.
177         throw newIOException(method, err);
178     }
179 
180     public static int ioResult(String method, int err) throws IOException {
181         // network stack saturated... try again later
182         if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) {
183             return 0;
184         }
185         if (err == ERRNO_EBADF_NEGATIVE) {
186             throw new ClosedChannelException();
187         }
188         if (err == ERRNO_ENOTCONN_NEGATIVE) {
189             throw new NotYetConnectedException();
190         }
191         if (err == ERRNO_ENOENT_NEGATIVE) {
192             throw new FileNotFoundException();
193         }
194 
195         throw new NativeIoException(method, err, false);
196     }
197 
198     private Errors() { }
199 }