1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.http.impl.nio;
29
30 import java.io.IOException;
31 import java.nio.ByteBuffer;
32 import java.nio.channels.WritableByteChannel;
33 import java.util.List;
34
35 import org.apache.hc.core5.http.FormattedHeader;
36 import org.apache.hc.core5.http.Header;
37 import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics;
38 import org.apache.hc.core5.http.message.BasicLineFormatter;
39 import org.apache.hc.core5.http.nio.SessionOutputBuffer;
40 import org.apache.hc.core5.util.CharArrayBuffer;
41
42
43
44
45
46
47
48 public class ChunkEncoder extends AbstractContentEncoder {
49
50 private final int chunkSizeHint;
51 private final CharArrayBuffer lineBuffer;
52
53
54
55
56
57
58
59
60 public ChunkEncoder(
61 final WritableByteChannel channel,
62 final SessionOutputBuffer buffer,
63 final BasicHttpTransportMetrics metrics,
64 final int chunkSizeHint) {
65 super(channel, buffer, metrics);
66 this.chunkSizeHint = Math.max(chunkSizeHint, 0);
67 this.lineBuffer = new CharArrayBuffer(16);
68 }
69
70 public ChunkEncoder(
71 final WritableByteChannel channel,
72 final SessionOutputBuffer buffer,
73 final BasicHttpTransportMetrics metrics) {
74 this(channel, buffer, metrics, 0);
75 }
76
77 @Override
78 public int write(final ByteBuffer src) throws IOException {
79 if (src == null) {
80 return 0;
81 }
82 assertNotCompleted();
83
84 int total = 0;
85 while (src.hasRemaining()) {
86 int chunk = src.remaining();
87 int avail;
88 avail = this.buffer.capacity();
89
90
91
92
93 avail -= 12;
94 if (avail > 0) {
95 if (avail < chunk) {
96
97 chunk = avail;
98 this.lineBuffer.clear();
99 this.lineBuffer.append(Integer.toHexString(chunk));
100 this.buffer.writeLine(this.lineBuffer);
101 final int oldlimit = src.limit();
102 src.limit(src.position() + chunk);
103 this.buffer.write(src);
104 src.limit(oldlimit);
105 } else {
106
107 this.lineBuffer.clear();
108 this.lineBuffer.append(Integer.toHexString(chunk));
109 this.buffer.writeLine(this.lineBuffer);
110 this.buffer.write(src);
111 }
112 this.lineBuffer.clear();
113 this.buffer.writeLine(this.lineBuffer);
114 total += chunk;
115 }
116 if (this.buffer.length() >= this.chunkSizeHint || src.hasRemaining()) {
117 final int bytesWritten = flushToChannel();
118 if (bytesWritten == 0) {
119 break;
120 }
121 }
122 }
123 return total;
124 }
125
126 @Override
127 public void complete(final List<? extends Header> trailers) throws IOException {
128 assertNotCompleted();
129 this.lineBuffer.clear();
130 this.lineBuffer.append("0");
131 this.buffer.writeLine(this.lineBuffer);
132 writeTrailers(trailers);
133 this.lineBuffer.clear();
134 this.buffer.writeLine(this.lineBuffer);
135 super.complete(trailers);
136 }
137
138 private void writeTrailers(final List<? extends Header> trailers) throws IOException {
139 if (trailers != null) {
140 for (int i = 0; i < trailers.size(); i++) {
141 final Header header = trailers.get(i);
142 if (header instanceof FormattedHeader) {
143 final CharArrayBuffer chbuffer = ((FormattedHeader) header).getBuffer();
144 buffer.writeLine(chbuffer);
145 } else {
146 this.lineBuffer.clear();
147 BasicLineFormatter.INSTANCE.formatHeader(this.lineBuffer, header);
148 buffer.writeLine(this.lineBuffer);
149 }
150 }
151 }
152 }
153
154 @Override
155 public String toString() {
156 final StringBuilder sb = new StringBuilder();
157 sb.append("[chunk-coded; completed: ");
158 sb.append(isCompleted());
159 sb.append("]");
160 return sb.toString();
161 }
162
163 }