1 /*
2 * Copyright 2012 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 org.jboss.netty.handler.codec.marshalling;
17
18 import org.jboss.marshalling.ByteInput;
19 import org.jboss.marshalling.Unmarshaller;
20 import org.jboss.netty.buffer.ChannelBuffer;
21 import org.jboss.netty.channel.Channel;
22 import org.jboss.netty.channel.ChannelHandlerContext;
23 import org.jboss.netty.channel.ExceptionEvent;
24 import org.jboss.netty.handler.codec.frame.TooLongFrameException;
25 import org.jboss.netty.handler.codec.replay.ReplayingDecoder;
26 import org.jboss.netty.handler.codec.replay.VoidEnum;
27
28 import java.io.ObjectStreamConstants;
29
30 /**
31 * {@link ReplayingDecoder} which use an {@link Unmarshaller} to read the Object out of the {@link ChannelBuffer}.
32 *
33 * If you can you should use {@link MarshallingDecoder}.
34 *
35 *
36 *
37 */
38 public class CompatibleMarshallingDecoder extends ReplayingDecoder<VoidEnum> {
39 protected final UnmarshallerProvider provider;
40 protected final int maxObjectSize;
41 private boolean discardingTooLongFrame;
42
43 /**
44 * Create a new instance of {@link CompatibleMarshallingDecoder}.
45 *
46 * @param provider
47 * the {@link UnmarshallerProvider} which is used to obtain the {@link Unmarshaller}
48 * for the {@link Channel}
49 * @param maxObjectSize
50 * the maximal size (in bytes) of the {@link Object} to unmarshal. Once the size is
51 * exceeded the {@link Channel} will get closed. Use a a maxObjectSize of
52 * {@link Integer#MAX_VALUE} to disable this. You should only do this if you are sure
53 * that the received Objects will never be big and the sending side are trusted, as
54 * this opens the possibility for a DOS-Attack due an {@link OutOfMemoryError}.
55 */
56 public CompatibleMarshallingDecoder(UnmarshallerProvider provider, int maxObjectSize) {
57 this.provider = provider;
58 this.maxObjectSize = maxObjectSize;
59 }
60
61 @Override
62 protected Object decode(
63 ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, VoidEnum state) throws Exception {
64 if (discardingTooLongFrame) {
65 buffer.skipBytes(actualReadableBytes());
66 checkpoint();
67 return null;
68 }
69
70 Unmarshaller unmarshaller = provider.getUnmarshaller(ctx);
71 ByteInput input = new ChannelBufferByteInput(buffer);
72 if (maxObjectSize != Integer.MAX_VALUE) {
73 input = new LimitingByteInput(input, maxObjectSize);
74 }
75 try {
76 unmarshaller.start(input);
77 Object obj = unmarshaller.readObject();
78 unmarshaller.finish();
79 return obj;
80 } catch (LimitingByteInput.TooBigObjectException e) {
81 discardingTooLongFrame = true;
82 throw new TooLongFrameException();
83 } finally {
84 // Call close in a finally block as the ReplayingDecoder will throw an Error if not enough bytes are
85 // readable. This helps to be sure that we do not leak resource
86 unmarshaller.close();
87 }
88 }
89
90 @Override
91 protected Object decodeLast(ChannelHandlerContext ctx, Channel channel,
92 ChannelBuffer buffer, VoidEnum state)
93 throws Exception {
94 switch (buffer.readableBytes()) {
95 case 0:
96 return null;
97 case 1:
98 // Ignore the last TC_RESET
99 if (buffer.getByte(buffer.readerIndex()) == ObjectStreamConstants.TC_RESET) {
100 buffer.skipBytes(1);
101 return null;
102 }
103 }
104
105 Object decoded = decode(ctx, channel, buffer, state);
106 return decoded;
107 }
108
109 /**
110 * Calls {@link Channel#close()} if a TooLongFrameException was thrown
111 */
112 @Override
113 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
114 if (e.getCause() instanceof TooLongFrameException) {
115 e.getChannel().close();
116 } else {
117 super.exceptionCaught(ctx, e);
118 }
119 }
120 }