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