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.epoll;
17  
18  import io.netty.channel.Channel;
19  import io.netty.channel.ChannelConfig;
20  import io.netty.channel.ChannelOutboundBuffer;
21  import io.netty.channel.ChannelPipeline;
22  import io.netty.channel.unix.DomainSocketAddress;
23  import io.netty.channel.unix.DomainSocketChannel;
24  import io.netty.channel.unix.FileDescriptor;
25  import io.netty.channel.unix.PeerCredentials;
26  
27  import java.io.IOException;
28  import java.net.SocketAddress;
29  
30  import static io.netty.channel.epoll.LinuxSocket.newSocketDomain;
31  
32  public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel implements DomainSocketChannel {
33      private final EpollDomainSocketChannelConfig config = new EpollDomainSocketChannelConfig(this);
34  
35      private volatile DomainSocketAddress local;
36      private volatile DomainSocketAddress remote;
37  
38      public EpollDomainSocketChannel() {
39          super(newSocketDomain(), false);
40      }
41  
42      EpollDomainSocketChannel(Channel parent, FileDescriptor fd) {
43          this(parent, new LinuxSocket(fd.intValue()));
44      }
45  
46      public EpollDomainSocketChannel(int fd) {
47          super(fd);
48      }
49  
50      public EpollDomainSocketChannel(Channel parent, LinuxSocket fd) {
51          super(parent, fd);
52          local = fd.localDomainSocketAddress();
53          remote = fd.remoteDomainSocketAddress();
54      }
55  
56      public EpollDomainSocketChannel(int fd, boolean active) {
57          super(new LinuxSocket(fd), active);
58      }
59  
60      @Override
61      protected AbstractEpollUnsafe newUnsafe() {
62          return new EpollDomainUnsafe();
63      }
64  
65      @Override
66      protected DomainSocketAddress localAddress0() {
67          return local;
68      }
69  
70      @Override
71      protected DomainSocketAddress remoteAddress0() {
72          return remote;
73      }
74  
75      @Override
76      protected void doBind(SocketAddress localAddress) throws Exception {
77          socket.bind(localAddress);
78          local = (DomainSocketAddress) localAddress;
79      }
80  
81      @Override
82      public EpollDomainSocketChannelConfig config() {
83          return config;
84      }
85  
86      @Override
87      protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
88          if (super.doConnect(remoteAddress, localAddress)) {
89              local = localAddress != null ? (DomainSocketAddress) localAddress : socket.localDomainSocketAddress();
90              remote = (DomainSocketAddress) remoteAddress;
91              return true;
92          }
93          return false;
94      }
95  
96      @Override
97      public DomainSocketAddress remoteAddress() {
98          return (DomainSocketAddress) super.remoteAddress();
99      }
100 
101     @Override
102     public DomainSocketAddress localAddress() {
103         return (DomainSocketAddress) super.localAddress();
104     }
105 
106     @Override
107     protected int doWriteSingle(ChannelOutboundBuffer in) throws Exception {
108         Object msg = in.current();
109         if (msg instanceof FileDescriptor && socket.sendFd(((FileDescriptor) msg).intValue()) > 0) {
110             // File descriptor was written, so remove it.
111             in.remove();
112             return 1;
113         }
114         return super.doWriteSingle(in);
115     }
116 
117     @Override
118     protected Object filterOutboundMessage(Object msg) {
119         if (msg instanceof FileDescriptor) {
120             return msg;
121         }
122         return super.filterOutboundMessage(msg);
123     }
124 
125     /**
126      * Returns the unix credentials (uid, gid, pid) of the peer
127      * <a href=https://man7.org/linux/man-pages/man7/socket.7.html>SO_PEERCRED</a>
128      */
129     public PeerCredentials peerCredentials() throws IOException {
130         return socket.getPeerCredentials();
131     }
132 
133     private final class EpollDomainUnsafe extends EpollStreamUnsafe {
134         @Override
135         void epollInReady() {
136             switch (config().getReadMode()) {
137                 case BYTES:
138                     super.epollInReady();
139                     break;
140                 case FILE_DESCRIPTORS:
141                     epollInReadFd();
142                     break;
143                 default:
144                     throw new Error();
145             }
146         }
147 
148         private void epollInReadFd() {
149             if (socket.isInputShutdown()) {
150                 clearEpollIn0();
151                 return;
152             }
153             final ChannelConfig config = config();
154             final EpollRecvByteAllocatorHandle allocHandle = recvBufAllocHandle();
155 
156             final ChannelPipeline pipeline = pipeline();
157             allocHandle.reset(config);
158 
159             try {
160                 readLoop: do {
161                     // lastBytesRead represents the fd. We use lastBytesRead because it must be set so that the
162                     // EpollRecvByteAllocatorHandle knows if it should try to read again or not when autoRead is
163                     // enabled.
164                     allocHandle.lastBytesRead(socket.recvFd());
165                     switch(allocHandle.lastBytesRead()) {
166                     case 0:
167                         break readLoop;
168                     case -1:
169                         close(voidPromise());
170                         return;
171                     default:
172                         allocHandle.incMessagesRead(1);
173                         readPending = false;
174                         pipeline.fireChannelRead(new FileDescriptor(allocHandle.lastBytesRead()));
175                         break;
176                     }
177                 } while (allocHandle.continueReading());
178 
179                 allocHandle.readComplete();
180                 pipeline.fireChannelReadComplete();
181             } catch (Throwable t) {
182                 allocHandle.readComplete();
183                 pipeline.fireChannelReadComplete();
184                 pipeline.fireExceptionCaught(t);
185             } finally {
186                 if (shouldStopReading(config)) {
187                     clearEpollIn();
188                 }
189             }
190         }
191     }
192 }