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