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.http.impl.nio.codecs;
29
30 import java.io.IOException;
31 import java.nio.ByteBuffer;
32 import java.nio.channels.FileChannel;
33 import java.nio.channels.WritableByteChannel;
34
35 import org.apache.http.annotation.NotThreadSafe;
36 import org.apache.http.impl.io.HttpTransportMetricsImpl;
37 import org.apache.http.nio.FileContentEncoder;
38 import org.apache.http.nio.reactor.SessionOutputBuffer;
39 import org.apache.http.util.Args;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 @NotThreadSafe
55 public class LengthDelimitedEncoder extends AbstractContentEncoder
56 implements FileContentEncoder {
57
58 private final long contentLength;
59 private final int fragHint;
60
61 private long remaining;
62
63
64
65
66
67
68
69
70
71
72
73
74 public LengthDelimitedEncoder(
75 final WritableByteChannel channel,
76 final SessionOutputBuffer buffer,
77 final HttpTransportMetricsImpl metrics,
78 final long contentLength,
79 final int fragementSizeHint) {
80 super(channel, buffer, metrics);
81 Args.notNegative(contentLength, "Content length");
82 this.contentLength = contentLength;
83 this.fragHint = fragementSizeHint > 0 ? fragementSizeHint : 0;
84 this.remaining = contentLength;
85 }
86
87 public LengthDelimitedEncoder(
88 final WritableByteChannel channel,
89 final SessionOutputBuffer buffer,
90 final HttpTransportMetricsImpl metrics,
91 final long contentLength) {
92 this(channel, buffer, metrics, contentLength, 0);
93 }
94
95 private int nextChunk(final ByteBuffer src) {
96 return (int) Math.min(Math.min(this.remaining, Integer.MAX_VALUE), src.remaining());
97 }
98
99 public int write(final ByteBuffer src) throws IOException {
100 if (src == null) {
101 return 0;
102 }
103 assertNotCompleted();
104
105 int total = 0;
106 while (src.hasRemaining() && this.remaining > 0) {
107 if (this.buffer.hasData() || this.fragHint > 0) {
108 final int chunk = nextChunk(src);
109 if (chunk <= this.fragHint) {
110 final int capacity = this.fragHint - this.buffer.length();
111 if (capacity > 0) {
112 final int limit = Math.min(capacity, chunk);
113 final int bytesWritten = writeToBuffer(src, limit);
114 this.remaining -= bytesWritten;
115 total += bytesWritten;
116 }
117 }
118 }
119 if (this.buffer.hasData()) {
120 final int chunk = nextChunk(src);
121 if (this.buffer.length() >= this.fragHint || chunk > 0) {
122 final int bytesWritten = flushToChannel();
123 if (bytesWritten == 0) {
124 break;
125 }
126 }
127 }
128 if (!this.buffer.hasData()) {
129 final int chunk = nextChunk(src);
130 if (chunk > this.fragHint) {
131 final int limit = chunk;
132 final int bytesWritten = writeToChannel(src, limit);
133 this.remaining -= bytesWritten;
134 total += bytesWritten;
135 if (bytesWritten == 0) {
136 break;
137 }
138 }
139 }
140 }
141 if (this.remaining <= 0) {
142 this.completed = true;
143 }
144 return total;
145 }
146
147 public long transfer(
148 final FileChannel src,
149 final long position,
150 final long count) throws IOException {
151
152 if (src == null) {
153 return 0;
154 }
155 assertNotCompleted();
156
157 flushToChannel();
158 if (this.buffer.hasData()) {
159 return 0;
160 }
161
162 final long chunk = Math.min(this.remaining, count);
163 final long bytesWritten = src.transferTo(position, chunk, this.channel);
164 if (bytesWritten > 0) {
165 this.metrics.incrementBytesTransferred(bytesWritten);
166 }
167 this.remaining -= bytesWritten;
168 if (this.remaining <= 0) {
169 this.completed = true;
170 }
171 return bytesWritten;
172 }
173
174 @Override
175 public String toString() {
176 final StringBuilder buffer = new StringBuilder();
177 buffer.append("[content length: ");
178 buffer.append(this.contentLength);
179 buffer.append("; pos: ");
180 buffer.append(this.contentLength - this.remaining);
181 buffer.append("; completed: ");
182 buffer.append(this.completed);
183 buffer.append("]");
184 return buffer.toString();
185 }
186
187 }