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