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