1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.handler.codec.http2;
17
18 import static org.jboss.netty.buffer.ChannelBuffers.*;
19 import static org.jboss.netty.handler.codec.http2.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.http2.HttpHeaders.Names;
29 import org.jboss.netty.handler.codec.http2.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
48
49
50
51
52
53 public abstract class HttpMessageEncoder extends OneToOneEncoder {
54
55 private static final ChannelBuffer LAST_CHUNK =
56 copiedBuffer("0\r\n\r\n", CharsetUtil.US_ASCII);
57
58 private volatile boolean chunked;
59
60
61
62
63 protected HttpMessageEncoder() {
64 super();
65 }
66
67 @Override
68 protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
69 if (msg instanceof HttpMessage) {
70 HttpMessage m = (HttpMessage) msg;
71 boolean chunked;
72 if (m.isChunked()) {
73
74
75 if (!HttpCodecUtil.isTransferEncodingChunked(m)) {
76 m.addHeader(Names.TRANSFER_ENCODING, Values.CHUNKED);
77 }
78 chunked = this.chunked = true;
79 } else {
80 chunked = this.chunked = HttpCodecUtil.isTransferEncodingChunked(m);
81 }
82 ChannelBuffer header = ChannelBuffers.dynamicBuffer(
83 channel.getConfig().getBufferFactory());
84 encodeInitialLine(header, m);
85 encodeHeaders(header, m);
86 header.writeByte(CR);
87 header.writeByte(LF);
88
89 ChannelBuffer content = m.getContent();
90 if (!content.readable()) {
91 return header;
92 } else if (chunked) {
93 throw new IllegalArgumentException(
94 "HttpMessage.content must be empty " +
95 "if Transfer-Encoding is chunked.");
96 } else {
97 return wrappedBuffer(header, content);
98 }
99 }
100
101 if (msg instanceof HttpChunk) {
102 HttpChunk chunk = (HttpChunk) msg;
103 if (chunked) {
104 if (chunk.isLast()) {
105 chunked = false;
106 if (chunk instanceof HttpChunkTrailer) {
107 ChannelBuffer trailer = ChannelBuffers.dynamicBuffer(
108 channel.getConfig().getBufferFactory());
109 trailer.writeByte((byte) '0');
110 trailer.writeByte(CR);
111 trailer.writeByte(LF);
112 encodeTrailingHeaders(trailer, (HttpChunkTrailer) chunk);
113 trailer.writeByte(CR);
114 trailer.writeByte(LF);
115 return trailer;
116 } else {
117 return LAST_CHUNK.duplicate();
118 }
119 } else {
120 ChannelBuffer content = chunk.getContent();
121 int contentLength = content.readableBytes();
122
123 return wrappedBuffer(
124 copiedBuffer(
125 Integer.toHexString(contentLength),
126 CharsetUtil.US_ASCII),
127 wrappedBuffer(CRLF),
128 content.slice(content.readerIndex(), contentLength),
129 wrappedBuffer(CRLF));
130 }
131 } else {
132 if (chunk.isLast()) {
133 return null;
134 } else {
135 return chunk.getContent();
136 }
137 }
138
139 }
140
141
142 return msg;
143 }
144
145 private 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 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 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 }