1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.handler.codec.http;
17
18 import static org.jboss.netty.buffer.ChannelBuffers.*;
19 import static org.jboss.netty.handler.codec.http.HttpConstants.*;
20
21 import java.io.UnsupportedEncodingException;
22 import java.util.Map;
23
24 import org.jboss.netty.buffer.ChannelBuffer;
25 import org.jboss.netty.buffer.ChannelBuffers;
26 import org.jboss.netty.channel.Channel;
27 import org.jboss.netty.channel.ChannelHandlerContext;
28 import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
29 import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
30 import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
31 import org.jboss.netty.util.CharsetUtil;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public abstract class HttpMessageEncoder extends OneToOneEncoder {
48
49 private static final byte[] CRLF = new byte[] { CR, LF };
50 private static final ChannelBuffer LAST_CHUNK =
51 copiedBuffer("0\r\n\r\n", CharsetUtil.US_ASCII);
52
53 private volatile boolean transferEncodingChunked;
54
55
56
57
58 protected HttpMessageEncoder() {
59 super();
60 }
61
62 @Override
63 protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
64 if (msg instanceof HttpMessage) {
65 HttpMessage m = (HttpMessage) msg;
66 boolean contentMustBeEmpty;
67 if (m.isChunked()) {
68
69 if (HttpCodecUtil.isContentLengthSet(m)) {
70 contentMustBeEmpty = false;
71 transferEncodingChunked = false;
72 HttpCodecUtil.removeTransferEncodingChunked(m);
73 } else {
74
75
76 if (!HttpCodecUtil.isTransferEncodingChunked(m)) {
77 m.addHeader(Names.TRANSFER_ENCODING, Values.CHUNKED);
78 }
79 contentMustBeEmpty = true;
80 transferEncodingChunked = true;
81 }
82 } else {
83 transferEncodingChunked = contentMustBeEmpty = HttpCodecUtil.isTransferEncodingChunked(m);
84 }
85
86 ChannelBuffer header = ChannelBuffers.dynamicBuffer(
87 channel.getConfig().getBufferFactory());
88 encodeInitialLine(header, m);
89 encodeHeaders(header, m);
90 header.writeByte(CR);
91 header.writeByte(LF);
92
93 ChannelBuffer content = m.getContent();
94 if (!content.readable()) {
95 return header;
96 } else if (contentMustBeEmpty) {
97 throw new IllegalArgumentException(
98 "HttpMessage.content must be empty " +
99 "if Transfer-Encoding is chunked.");
100 } else {
101 return wrappedBuffer(header, content);
102 }
103 }
104
105 if (msg instanceof HttpChunk) {
106 HttpChunk chunk = (HttpChunk) msg;
107 if (transferEncodingChunked) {
108 if (chunk.isLast()) {
109 transferEncodingChunked = false;
110 if (chunk instanceof HttpChunkTrailer) {
111 ChannelBuffer trailer = ChannelBuffers.dynamicBuffer(
112 channel.getConfig().getBufferFactory());
113 trailer.writeByte((byte) '0');
114 trailer.writeByte(CR);
115 trailer.writeByte(LF);
116 encodeTrailingHeaders(trailer, (HttpChunkTrailer) chunk);
117 trailer.writeByte(CR);
118 trailer.writeByte(LF);
119 return trailer;
120 } else {
121 return LAST_CHUNK.duplicate();
122 }
123 } else {
124 ChannelBuffer content = chunk.getContent();
125 int contentLength = content.readableBytes();
126
127 return wrappedBuffer(
128 copiedBuffer(
129 Integer.toHexString(contentLength),
130 CharsetUtil.US_ASCII),
131 wrappedBuffer(CRLF),
132 content.slice(content.readerIndex(), contentLength),
133 wrappedBuffer(CRLF));
134 }
135 } else {
136 return chunk.getContent();
137 }
138
139 }
140
141
142 return msg;
143 }
144
145 private static void encodeHeaders(ChannelBuffer buf, HttpMessage message) {
146 try {
147 for (Map.Entry<String, String> h: message.getHeaders()) {
148 encodeHeader(buf, h.getKey(), h.getValue());
149 }
150 } catch (UnsupportedEncodingException e) {
151 throw (Error) new Error().initCause(e);
152 }
153 }
154
155 private static void encodeTrailingHeaders(ChannelBuffer buf, HttpChunkTrailer trailer) {
156 try {
157 for (Map.Entry<String, String> h: trailer.getHeaders()) {
158 encodeHeader(buf, h.getKey(), h.getValue());
159 }
160 } catch (UnsupportedEncodingException e) {
161 throw (Error) new Error().initCause(e);
162 }
163 }
164
165 private static void encodeHeader(ChannelBuffer buf, String header, String value)
166 throws UnsupportedEncodingException {
167 buf.writeBytes(header.getBytes("ASCII"));
168 buf.writeByte(COLON);
169 buf.writeByte(SP);
170 buf.writeBytes(value.getBytes("ASCII"));
171 buf.writeByte(CR);
172 buf.writeByte(LF);
173 }
174
175 protected abstract void encodeInitialLine(ChannelBuffer buf, HttpMessage message) throws Exception;
176 }