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.headers().add(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 return msg;
140 }
141
142 private static void encodeHeaders(ChannelBuffer buf, HttpMessage message) {
143 try {
144 for (Map.Entry<String, String> h: message.headers()) {
145 encodeHeader(buf, h.getKey(), h.getValue());
146 }
147 } catch (UnsupportedEncodingException e) {
148 throw (Error) new Error().initCause(e);
149 }
150 }
151
152 private static void encodeTrailingHeaders(ChannelBuffer buf, HttpChunkTrailer trailer) {
153 try {
154 for (Map.Entry<String, String> h: trailer.trailingHeaders()) {
155 encodeHeader(buf, h.getKey(), h.getValue());
156 }
157 } catch (UnsupportedEncodingException e) {
158 throw (Error) new Error().initCause(e);
159 }
160 }
161
162 private static void encodeHeader(ChannelBuffer buf, String header, String value)
163 throws UnsupportedEncodingException {
164 encodeAscii(header, buf);
165 buf.writeByte(COLON);
166 buf.writeByte(SP);
167 encodeAscii(value, buf);
168 buf.writeByte(CR);
169 buf.writeByte(LF);
170 }
171
172 protected static void encodeAscii(String s, ChannelBuffer buf) {
173 for (int i = 0; i < s.length(); i++) {
174 buf.writeByte(c2b(s.charAt(i)));
175 }
176 }
177
178 private static byte c2b(char c) {
179 if (c > 255) {
180 return '?';
181 }
182 return (byte) c;
183 }
184
185 protected abstract void encodeInitialLine(ChannelBuffer buf, HttpMessage message) throws Exception;
186 }