View Javadoc
1   /*
2    * Copyright 2016 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.handler.codec;
17  
18  import io.netty5.buffer.api.Buffer;
19  import io.netty5.channel.AddressedEnvelope;
20  import io.netty5.channel.ChannelHandlerContext;
21  import io.netty5.channel.ChannelPipeline;
22  import io.netty5.channel.socket.DatagramPacket;
23  import io.netty5.handler.codec.bytes.ByteArrayEncoder;
24  import io.netty5.util.concurrent.Future;
25  import io.netty5.util.internal.StringUtil;
26  
27  import java.net.InetSocketAddress;
28  import java.net.SocketAddress;
29  import java.util.List;
30  
31  import static java.util.Objects.requireNonNull;
32  
33  /**
34   * An encoder that encodes the content in {@link AddressedEnvelope} to {@link DatagramPacket} using
35   * the specified message encoder. E.g.,
36   *
37   * <pre><code>
38   * {@link ChannelPipeline} pipeline = ...;
39   * pipeline.addLast("udpEncoder", new {@link DatagramPacketEncoder}(new {@link ByteArrayEncoder}(...));
40   * </code></pre>
41   *
42   * Note: As UDP packets are out-of-order, you should make sure the encoded message size are not greater than
43   * the max safe packet size in your particular network path which guarantees no packet fragmentation.
44   *
45   * @param <M> the type of message to be encoded
46   */
47  public class DatagramPacketEncoder<M> extends MessageToMessageEncoder<AddressedEnvelope<M, InetSocketAddress>> {
48  
49      private final MessageToMessageEncoder<? super M> encoder;
50  
51      /**
52       * Create an encoder that encodes the content in {@link AddressedEnvelope} to {@link DatagramPacket} using
53       * the specified message encoder.
54       *
55       * @param encoder the specified message encoder
56       */
57      public DatagramPacketEncoder(MessageToMessageEncoder<? super M> encoder) {
58          this.encoder = requireNonNull(encoder, "encoder");
59      }
60  
61      @Override
62      public boolean acceptOutboundMessage(Object msg) throws Exception {
63          if (super.acceptOutboundMessage(msg)) {
64              @SuppressWarnings("rawtypes")
65              AddressedEnvelope envelope = (AddressedEnvelope) msg;
66              return encoder.acceptOutboundMessage(envelope.content())
67                      && (envelope.sender() instanceof InetSocketAddress || envelope.sender() == null)
68                      && envelope.recipient() instanceof InetSocketAddress;
69          }
70          return false;
71      }
72  
73      @Override
74      protected void encode(
75              ChannelHandlerContext ctx, AddressedEnvelope<M, InetSocketAddress> msg, List<Object> out) throws Exception {
76          assert out.isEmpty();
77  
78          encoder.encode(ctx, msg.content(), out);
79          if (out.size() != 1) {
80              throw new EncoderException(
81                      StringUtil.simpleClassName(encoder) + " must produce only one message.");
82          }
83          Object content = out.get(0);
84          if (content instanceof Buffer) {
85              // Replace the Buffer with a DatagramPacket.
86              out.set(0, new DatagramPacket((Buffer) content, msg.recipient(), msg.sender()));
87          } else {
88              throw new EncoderException(
89                      StringUtil.simpleClassName(encoder) + " must produce only ByteBuf.");
90          }
91      }
92  
93      @Override
94      public Future<Void> bind(ChannelHandlerContext ctx, SocketAddress localAddress) {
95          return encoder.bind(ctx, localAddress);
96      }
97  
98      @Override
99      public Future<Void> connect(
100             ChannelHandlerContext ctx, SocketAddress remoteAddress,
101             SocketAddress localAddress) {
102         return encoder.connect(ctx, remoteAddress, localAddress);
103     }
104 
105     @Override
106     public Future<Void> disconnect(ChannelHandlerContext ctx) {
107         return encoder.disconnect(ctx);
108     }
109 
110     @Override
111     public Future<Void> close(ChannelHandlerContext ctx) {
112         return encoder.close(ctx);
113     }
114 
115     @Override
116     public Future<Void> deregister(ChannelHandlerContext ctx) {
117         return encoder.deregister(ctx);
118     }
119 
120     @Override
121     public void read(ChannelHandlerContext ctx) {
122         encoder.read(ctx);
123     }
124 
125     @Override
126     public void flush(ChannelHandlerContext ctx) {
127         encoder.flush(ctx);
128     }
129 
130     @Override
131     public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
132         encoder.handlerAdded(ctx);
133     }
134 
135     @Override
136     public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
137         encoder.handlerRemoved(ctx);
138     }
139 
140     @Override
141     public boolean isSharable() {
142         return encoder.isSharable();
143     }
144 }