View Javadoc
1   /*
2    * Copyright 2014 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    *   http://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.epoll;
17  
18  import io.netty.buffer.ByteBufAllocator;
19  import io.netty.channel.ChannelException;
20  import io.netty.channel.ChannelOption;
21  import io.netty.channel.MessageSizeEstimator;
22  import io.netty.channel.RecvByteBufAllocator;
23  import io.netty.channel.WriteBufferWaterMark;
24  import io.netty.channel.socket.SocketChannelConfig;
25  import io.netty.util.internal.PlatformDependent;
26  
27  import java.io.IOException;
28  import java.net.InetAddress;
29  import java.util.Map;
30  
31  import static io.netty.channel.ChannelOption.ALLOW_HALF_CLOSURE;
32  import static io.netty.channel.ChannelOption.IP_TOS;
33  import static io.netty.channel.ChannelOption.SO_KEEPALIVE;
34  import static io.netty.channel.ChannelOption.SO_LINGER;
35  import static io.netty.channel.ChannelOption.SO_RCVBUF;
36  import static io.netty.channel.ChannelOption.SO_REUSEADDR;
37  import static io.netty.channel.ChannelOption.SO_SNDBUF;
38  import static io.netty.channel.ChannelOption.TCP_NODELAY;
39  
40  public final class EpollSocketChannelConfig extends EpollChannelConfig implements SocketChannelConfig {
41      private final EpollSocketChannel channel;
42      private volatile boolean allowHalfClosure;
43  
44      /**
45       * Creates a new instance.
46       */
47      EpollSocketChannelConfig(EpollSocketChannel channel) {
48          super(channel);
49  
50          this.channel = channel;
51          if (PlatformDependent.canEnableTcpNoDelayByDefault()) {
52              setTcpNoDelay(true);
53          }
54          calculateMaxBytesPerGatheringWrite();
55      }
56  
57      @Override
58      public Map<ChannelOption<?>, Object> getOptions() {
59          return getOptions(
60                  super.getOptions(),
61                  SO_RCVBUF, SO_SNDBUF, TCP_NODELAY, SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, IP_TOS,
62                  ALLOW_HALF_CLOSURE, EpollChannelOption.TCP_CORK, EpollChannelOption.TCP_NOTSENT_LOWAT,
63                  EpollChannelOption.TCP_KEEPCNT, EpollChannelOption.TCP_KEEPIDLE, EpollChannelOption.TCP_KEEPINTVL,
64                  EpollChannelOption.TCP_MD5SIG, EpollChannelOption.TCP_QUICKACK, EpollChannelOption.IP_TRANSPARENT,
65                  EpollChannelOption.TCP_FASTOPEN_CONNECT);
66      }
67  
68      @SuppressWarnings("unchecked")
69      @Override
70      public <T> T getOption(ChannelOption<T> option) {
71          if (option == SO_RCVBUF) {
72              return (T) Integer.valueOf(getReceiveBufferSize());
73          }
74          if (option == SO_SNDBUF) {
75              return (T) Integer.valueOf(getSendBufferSize());
76          }
77          if (option == TCP_NODELAY) {
78              return (T) Boolean.valueOf(isTcpNoDelay());
79          }
80          if (option == SO_KEEPALIVE) {
81              return (T) Boolean.valueOf(isKeepAlive());
82          }
83          if (option == SO_REUSEADDR) {
84              return (T) Boolean.valueOf(isReuseAddress());
85          }
86          if (option == SO_LINGER) {
87              return (T) Integer.valueOf(getSoLinger());
88          }
89          if (option == IP_TOS) {
90              return (T) Integer.valueOf(getTrafficClass());
91          }
92          if (option == ALLOW_HALF_CLOSURE) {
93              return (T) Boolean.valueOf(isAllowHalfClosure());
94          }
95          if (option == EpollChannelOption.TCP_CORK) {
96              return (T) Boolean.valueOf(isTcpCork());
97          }
98          if (option == EpollChannelOption.TCP_NOTSENT_LOWAT) {
99              return (T) Long.valueOf(getTcpNotSentLowAt());
100         }
101         if (option == EpollChannelOption.TCP_KEEPIDLE) {
102             return (T) Integer.valueOf(getTcpKeepIdle());
103         }
104         if (option == EpollChannelOption.TCP_KEEPINTVL) {
105             return (T) Integer.valueOf(getTcpKeepIntvl());
106         }
107         if (option == EpollChannelOption.TCP_KEEPCNT) {
108             return (T) Integer.valueOf(getTcpKeepCnt());
109         }
110         if (option == EpollChannelOption.TCP_USER_TIMEOUT) {
111             return (T) Integer.valueOf(getTcpUserTimeout());
112         }
113         if (option == EpollChannelOption.TCP_QUICKACK) {
114             return (T) Boolean.valueOf(isTcpQuickAck());
115         }
116         if (option == EpollChannelOption.IP_TRANSPARENT) {
117             return (T) Boolean.valueOf(isIpTransparent());
118         }
119         if (option == EpollChannelOption.TCP_FASTOPEN_CONNECT) {
120             return (T) Boolean.valueOf(isTcpFastOpenConnect());
121         }
122         return super.getOption(option);
123     }
124 
125     @Override
126     public <T> boolean setOption(ChannelOption<T> option, T value) {
127         validate(option, value);
128 
129         if (option == SO_RCVBUF) {
130             setReceiveBufferSize((Integer) value);
131         } else if (option == SO_SNDBUF) {
132             setSendBufferSize((Integer) value);
133         } else if (option == TCP_NODELAY) {
134             setTcpNoDelay((Boolean) value);
135         } else if (option == SO_KEEPALIVE) {
136             setKeepAlive((Boolean) value);
137         } else if (option == SO_REUSEADDR) {
138             setReuseAddress((Boolean) value);
139         } else if (option == SO_LINGER) {
140             setSoLinger((Integer) value);
141         } else if (option == IP_TOS) {
142             setTrafficClass((Integer) value);
143         } else if (option == ALLOW_HALF_CLOSURE) {
144             setAllowHalfClosure((Boolean) value);
145         } else if (option == EpollChannelOption.TCP_CORK) {
146             setTcpCork((Boolean) value);
147         } else if (option == EpollChannelOption.TCP_NOTSENT_LOWAT) {
148             setTcpNotSentLowAt((Long) value);
149         } else if (option == EpollChannelOption.TCP_KEEPIDLE) {
150             setTcpKeepIdle((Integer) value);
151         } else if (option == EpollChannelOption.TCP_KEEPCNT) {
152             setTcpKeepCnt((Integer) value);
153         } else if (option == EpollChannelOption.TCP_KEEPINTVL) {
154             setTcpKeepIntvl((Integer) value);
155         } else if (option == EpollChannelOption.TCP_USER_TIMEOUT) {
156             setTcpUserTimeout((Integer) value);
157         } else if (option == EpollChannelOption.IP_TRANSPARENT) {
158             setIpTransparent((Boolean) value);
159         } else if (option == EpollChannelOption.TCP_MD5SIG) {
160             @SuppressWarnings("unchecked")
161             final Map<InetAddress, byte[]> m = (Map<InetAddress, byte[]>) value;
162             setTcpMd5Sig(m);
163         } else if (option == EpollChannelOption.TCP_QUICKACK) {
164             setTcpQuickAck((Boolean) value);
165         } else if (option == EpollChannelOption.TCP_FASTOPEN_CONNECT) {
166             setTcpFastOpenConnect((Boolean) value);
167         } else {
168             return super.setOption(option, value);
169         }
170 
171         return true;
172     }
173 
174     @Override
175     public int getReceiveBufferSize() {
176         try {
177             return channel.socket.getReceiveBufferSize();
178         } catch (IOException e) {
179             throw new ChannelException(e);
180         }
181     }
182 
183     @Override
184     public int getSendBufferSize() {
185         try {
186             return channel.socket.getSendBufferSize();
187         } catch (IOException e) {
188             throw new ChannelException(e);
189         }
190     }
191 
192     @Override
193     public int getSoLinger() {
194         try {
195             return channel.socket.getSoLinger();
196         } catch (IOException e) {
197             throw new ChannelException(e);
198         }
199     }
200 
201     @Override
202     public int getTrafficClass() {
203         try {
204             return channel.socket.getTrafficClass();
205         } catch (IOException e) {
206             throw new ChannelException(e);
207         }
208     }
209 
210     @Override
211     public boolean isKeepAlive() {
212         try {
213             return channel.socket.isKeepAlive();
214         } catch (IOException e) {
215             throw new ChannelException(e);
216         }
217     }
218 
219     @Override
220     public boolean isReuseAddress() {
221         try {
222             return channel.socket.isReuseAddress();
223         } catch (IOException e) {
224             throw new ChannelException(e);
225         }
226     }
227 
228     @Override
229     public boolean isTcpNoDelay() {
230         try {
231             return channel.socket.isTcpNoDelay();
232         } catch (IOException e) {
233             throw new ChannelException(e);
234         }
235     }
236 
237     /**
238      * Get the {@code TCP_CORK} option on the socket. See {@code man 7 tcp} for more details.
239      */
240     public boolean isTcpCork() {
241         try {
242             return channel.socket.isTcpCork();
243         } catch (IOException e) {
244             throw new ChannelException(e);
245         }
246     }
247 
248     /**
249      * Get the {@code TCP_NOTSENT_LOWAT} option on the socket. See {@code man 7 tcp} for more details.
250      * @return value is a uint32_t
251      */
252     public long getTcpNotSentLowAt() {
253         try {
254             return channel.socket.getTcpNotSentLowAt();
255         } catch (IOException e) {
256             throw new ChannelException(e);
257         }
258     }
259 
260     /**
261      * Get the {@code TCP_KEEPIDLE} option on the socket. See {@code man 7 tcp} for more details.
262      */
263     public int getTcpKeepIdle() {
264         try {
265             return channel.socket.getTcpKeepIdle();
266         } catch (IOException e) {
267             throw new ChannelException(e);
268         }
269     }
270 
271     /**
272      * Get the {@code TCP_KEEPINTVL} option on the socket. See {@code man 7 tcp} for more details.
273      */
274     public int getTcpKeepIntvl() {
275         try {
276             return channel.socket.getTcpKeepIntvl();
277         } catch (IOException e) {
278             throw new ChannelException(e);
279         }
280     }
281 
282     /**
283      * Get the {@code TCP_KEEPCNT} option on the socket. See {@code man 7 tcp} for more details.
284      */
285     public int getTcpKeepCnt() {
286         try {
287             return channel.socket.getTcpKeepCnt();
288         } catch (IOException e) {
289             throw new ChannelException(e);
290         }
291     }
292 
293     /**
294      * Get the {@code TCP_USER_TIMEOUT} option on the socket. See {@code man 7 tcp} for more details.
295      */
296     public int getTcpUserTimeout() {
297         try {
298             return channel.socket.getTcpUserTimeout();
299         } catch (IOException e) {
300             throw new ChannelException(e);
301         }
302     }
303 
304     @Override
305     public EpollSocketChannelConfig setKeepAlive(boolean keepAlive) {
306         try {
307             channel.socket.setKeepAlive(keepAlive);
308             return this;
309         } catch (IOException e) {
310             throw new ChannelException(e);
311         }
312     }
313 
314     @Override
315     public EpollSocketChannelConfig setPerformancePreferences(
316             int connectionTime, int latency, int bandwidth) {
317         return this;
318     }
319 
320     @Override
321     public EpollSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) {
322         try {
323             channel.socket.setReceiveBufferSize(receiveBufferSize);
324             return this;
325         } catch (IOException e) {
326             throw new ChannelException(e);
327         }
328     }
329 
330     @Override
331     public EpollSocketChannelConfig setReuseAddress(boolean reuseAddress) {
332         try {
333             channel.socket.setReuseAddress(reuseAddress);
334             return this;
335         } catch (IOException e) {
336             throw new ChannelException(e);
337         }
338     }
339 
340     @Override
341     public EpollSocketChannelConfig setSendBufferSize(int sendBufferSize) {
342         try {
343             channel.socket.setSendBufferSize(sendBufferSize);
344             calculateMaxBytesPerGatheringWrite();
345             return this;
346         } catch (IOException e) {
347             throw new ChannelException(e);
348         }
349     }
350 
351     @Override
352     public EpollSocketChannelConfig setSoLinger(int soLinger) {
353         try {
354             channel.socket.setSoLinger(soLinger);
355             return this;
356         } catch (IOException e) {
357             throw new ChannelException(e);
358         }
359     }
360 
361     @Override
362     public EpollSocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) {
363         try {
364             channel.socket.setTcpNoDelay(tcpNoDelay);
365             return this;
366         } catch (IOException e) {
367             throw new ChannelException(e);
368         }
369     }
370 
371     /**
372      * Set the {@code TCP_CORK} option on the socket. See {@code man 7 tcp} for more details.
373      */
374     public EpollSocketChannelConfig setTcpCork(boolean tcpCork) {
375         try {
376             channel.socket.setTcpCork(tcpCork);
377             return this;
378         } catch (IOException e) {
379             throw new ChannelException(e);
380         }
381     }
382 
383     /**
384      * Set the {@code TCP_NOTSENT_LOWAT} option on the socket. See {@code man 7 tcp} for more details.
385      * @param tcpNotSentLowAt is a uint32_t
386      */
387     public EpollSocketChannelConfig setTcpNotSentLowAt(long tcpNotSentLowAt) {
388         try {
389             channel.socket.setTcpNotSentLowAt(tcpNotSentLowAt);
390             return this;
391         } catch (IOException e) {
392             throw new ChannelException(e);
393         }
394     }
395 
396     @Override
397     public EpollSocketChannelConfig setTrafficClass(int trafficClass) {
398         try {
399             channel.socket.setTrafficClass(trafficClass);
400             return this;
401         } catch (IOException e) {
402             throw new ChannelException(e);
403         }
404     }
405 
406     /**
407      * Set the {@code TCP_KEEPIDLE} option on the socket. See {@code man 7 tcp} for more details.
408      */
409     public EpollSocketChannelConfig setTcpKeepIdle(int seconds) {
410         try {
411             channel.socket.setTcpKeepIdle(seconds);
412             return this;
413         } catch (IOException e) {
414             throw new ChannelException(e);
415         }
416     }
417 
418     /**
419      * Set the {@code TCP_KEEPINTVL} option on the socket. See {@code man 7 tcp} for more details.
420      */
421     public EpollSocketChannelConfig setTcpKeepIntvl(int seconds) {
422         try {
423             channel.socket.setTcpKeepIntvl(seconds);
424             return this;
425         } catch (IOException e) {
426             throw new ChannelException(e);
427         }
428     }
429 
430     /**
431      * @deprecated use {@link #setTcpKeepCnt(int)}
432      */
433     @Deprecated
434     public EpollSocketChannelConfig setTcpKeepCntl(int probes) {
435         return setTcpKeepCnt(probes);
436     }
437 
438     /**
439      * Set the {@code TCP_KEEPCNT} option on the socket. See {@code man 7 tcp} for more details.
440      */
441     public EpollSocketChannelConfig setTcpKeepCnt(int probes) {
442         try {
443             channel.socket.setTcpKeepCnt(probes);
444             return this;
445         } catch (IOException e) {
446             throw new ChannelException(e);
447         }
448     }
449 
450     /**
451      * Set the {@code TCP_USER_TIMEOUT} option on the socket. See {@code man 7 tcp} for more details.
452      */
453     public EpollSocketChannelConfig setTcpUserTimeout(int milliseconds) {
454         try {
455             channel.socket.setTcpUserTimeout(milliseconds);
456             return this;
457         } catch (IOException e) {
458             throw new ChannelException(e);
459         }
460     }
461 
462      /**
463      * Returns {@code true} if <a href="http://man7.org/linux/man-pages/man7/ip.7.html">IP_TRANSPARENT</a> is enabled,
464      * {@code false} otherwise.
465      */
466     public boolean isIpTransparent() {
467         try {
468             return channel.socket.isIpTransparent();
469         } catch (IOException e) {
470             throw new ChannelException(e);
471         }
472     }
473 
474     /**
475      * If {@code true} is used <a href="http://man7.org/linux/man-pages/man7/ip.7.html">IP_TRANSPARENT</a> is enabled,
476      * {@code false} for disable it. Default is disabled.
477      */
478     public EpollSocketChannelConfig setIpTransparent(boolean transparent) {
479         try {
480             channel.socket.setIpTransparent(transparent);
481             return this;
482         } catch (IOException e) {
483             throw new ChannelException(e);
484         }
485     }
486 
487     /**
488      * Set the {@code TCP_MD5SIG} option on the socket. See {@code linux/tcp.h} for more details.
489      * Keys can only be set on, not read to prevent a potential leak, as they are confidential.
490      * Allowing them being read would mean anyone with access to the channel could get them.
491      */
492     public EpollSocketChannelConfig setTcpMd5Sig(Map<InetAddress, byte[]> keys) {
493         try {
494             channel.setTcpMd5Sig(keys);
495             return this;
496         } catch (IOException e) {
497             throw new ChannelException(e);
498         }
499     }
500 
501     /**
502      * Set the {@code TCP_QUICKACK} option on the socket. See <a href="http://linux.die.net/man/7/tcp">TCP_QUICKACK</a>
503      * for more details.
504      */
505     public EpollSocketChannelConfig setTcpQuickAck(boolean quickAck) {
506         try {
507             channel.socket.setTcpQuickAck(quickAck);
508             return this;
509         } catch (IOException e) {
510             throw new ChannelException(e);
511         }
512     }
513 
514     /**
515      * Returns {@code true} if <a href="http://linux.die.net/man/7/tcp">TCP_QUICKACK</a> is enabled,
516      * {@code false} otherwise.
517      */
518     public boolean isTcpQuickAck() {
519         try {
520             return channel.socket.isTcpQuickAck();
521         } catch (IOException e) {
522             throw new ChannelException(e);
523         }
524     }
525 
526     /**
527      * Set the {@code TCP_FASTOPEN_CONNECT} option on the socket. Requires Linux kernel 4.11 or later.
528      * See
529      * <a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=19f6d3f3">this commit</a>
530      * for more details.
531      */
532     public EpollSocketChannelConfig setTcpFastOpenConnect(boolean fastOpenConnect) {
533         try {
534             channel.socket.setTcpFastOpenConnect(fastOpenConnect);
535             return this;
536         } catch (IOException e) {
537             throw new ChannelException(e);
538         }
539     }
540 
541     /**
542      * Returns {@code true} if {@code TCP_FASTOPEN_CONNECT} is enabled, {@code false} otherwise.
543      */
544     public boolean isTcpFastOpenConnect() {
545         try {
546             return channel.socket.isTcpFastOpenConnect();
547         } catch (IOException e) {
548             throw new ChannelException(e);
549         }
550     }
551 
552     @Override
553     public boolean isAllowHalfClosure() {
554         return allowHalfClosure;
555     }
556 
557     @Override
558     public EpollSocketChannelConfig setAllowHalfClosure(boolean allowHalfClosure) {
559         this.allowHalfClosure = allowHalfClosure;
560         return this;
561     }
562 
563     @Override
564     public EpollSocketChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) {
565         super.setConnectTimeoutMillis(connectTimeoutMillis);
566         return this;
567     }
568 
569     @Override
570     @Deprecated
571     public EpollSocketChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead) {
572         super.setMaxMessagesPerRead(maxMessagesPerRead);
573         return this;
574     }
575 
576     @Override
577     public EpollSocketChannelConfig setWriteSpinCount(int writeSpinCount) {
578         super.setWriteSpinCount(writeSpinCount);
579         return this;
580     }
581 
582     @Override
583     public EpollSocketChannelConfig setAllocator(ByteBufAllocator allocator) {
584         super.setAllocator(allocator);
585         return this;
586     }
587 
588     @Override
589     public EpollSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) {
590         super.setRecvByteBufAllocator(allocator);
591         return this;
592     }
593 
594     @Override
595     public EpollSocketChannelConfig setAutoRead(boolean autoRead) {
596         super.setAutoRead(autoRead);
597         return this;
598     }
599 
600     @Override
601     public EpollSocketChannelConfig setAutoClose(boolean autoClose) {
602         super.setAutoClose(autoClose);
603         return this;
604     }
605 
606     @Override
607     @Deprecated
608     public EpollSocketChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) {
609         super.setWriteBufferHighWaterMark(writeBufferHighWaterMark);
610         return this;
611     }
612 
613     @Override
614     @Deprecated
615     public EpollSocketChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) {
616         super.setWriteBufferLowWaterMark(writeBufferLowWaterMark);
617         return this;
618     }
619 
620     @Override
621     public EpollSocketChannelConfig setWriteBufferWaterMark(WriteBufferWaterMark writeBufferWaterMark) {
622         super.setWriteBufferWaterMark(writeBufferWaterMark);
623         return this;
624     }
625 
626     @Override
627     public EpollSocketChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator) {
628         super.setMessageSizeEstimator(estimator);
629         return this;
630     }
631 
632     @Override
633     public EpollSocketChannelConfig setEpollMode(EpollMode mode) {
634         super.setEpollMode(mode);
635         return this;
636     }
637 
638     private void calculateMaxBytesPerGatheringWrite() {
639         // Multiply by 2 to give some extra space in case the OS can process write data faster than we can provide.
640         int newSendBufferSize = getSendBufferSize() << 1;
641         if (newSendBufferSize > 0) {
642             setMaxBytesPerGatheringWrite(getSendBufferSize() << 1);
643         }
644     }
645 }