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.ReadableByteChannel;
34
35 import org.apache.http.ConnectionClosedException;
36 import org.apache.http.annotation.NotThreadSafe;
37 import org.apache.http.impl.io.HttpTransportMetricsImpl;
38 import org.apache.http.nio.FileContentDecoder;
39 import org.apache.http.nio.reactor.SessionInputBuffer;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 @NotThreadSafe
55 public class LengthDelimitedDecoder extends AbstractContentDecoder
56 implements FileContentDecoder {
57
58 private final long contentLength;
59
60 private long len;
61
62 public LengthDelimitedDecoder(
63 final ReadableByteChannel channel,
64 final SessionInputBuffer buffer,
65 final HttpTransportMetricsImpl metrics,
66 long contentLength) {
67 super(channel, buffer, metrics);
68 if (contentLength < 0) {
69 throw new IllegalArgumentException("Content length may not be negative");
70 }
71 this.contentLength = contentLength;
72 }
73
74 public int read(final ByteBuffer dst) throws IOException {
75 if (dst == null) {
76 throw new IllegalArgumentException("Byte buffer may not be null");
77 }
78 if (this.completed) {
79 return -1;
80 }
81 int chunk = (int) Math.min((this.contentLength - this.len), Integer.MAX_VALUE);
82
83 int bytesRead;
84 if (this.buffer.hasData()) {
85 int maxLen = Math.min(chunk, this.buffer.length());
86 bytesRead = this.buffer.read(dst, maxLen);
87 } else {
88 if (dst.remaining() > chunk) {
89 int oldLimit = dst.limit();
90 int newLimit = oldLimit - (dst.remaining() - chunk);
91 dst.limit(newLimit);
92 bytesRead = this.channel.read(dst);
93 dst.limit(oldLimit);
94 } else {
95 bytesRead = this.channel.read(dst);
96 }
97 if (bytesRead > 0) {
98 this.metrics.incrementBytesTransferred(bytesRead);
99 }
100 }
101 if (bytesRead == -1) {
102 this.completed = true;
103 if (this.len < this.contentLength) {
104 throw new ConnectionClosedException(
105 "Premature end of Content-Length delimited message body (expected: "
106 + this.contentLength + "; received: " + this.len);
107 }
108 }
109 this.len += bytesRead;
110 if (this.len >= this.contentLength) {
111 this.completed = true;
112 }
113 if (this.completed && bytesRead == 0) {
114 return -1;
115 } else {
116 return bytesRead;
117 }
118 }
119
120 public long transfer(
121 final FileChannel dst,
122 long position,
123 long count) throws IOException {
124
125 if (dst == null) {
126 return 0;
127 }
128 if (this.completed) {
129 return -1;
130 }
131
132 int chunk = (int) Math.min((this.contentLength - this.len), Integer.MAX_VALUE);
133
134 long bytesRead;
135 if (this.buffer.hasData()) {
136 int maxLen = Math.min(chunk, this.buffer.length());
137 dst.position(position);
138 bytesRead = this.buffer.read(dst, maxLen);
139 } else {
140 if (count > chunk) {
141 count = chunk;
142 }
143 if (this.channel.isOpen()) {
144 if (position > dst.size()) {
145 throw new IOException("Position past end of file [" + position +
146 " > " + dst.size() + "]");
147 }
148 bytesRead = dst.transferFrom(this.channel, position, count);
149 } else {
150 bytesRead = -1;
151 }
152 if (bytesRead > 0) {
153 this.metrics.incrementBytesTransferred(bytesRead);
154 }
155 }
156 if (bytesRead == -1) {
157 this.completed = true;
158 if (this.len < this.contentLength) {
159 throw new ConnectionClosedException(
160 "Premature end of Content-Length delimited message body (expected: "
161 + this.contentLength + "; received: " + this.len);
162 }
163 }
164 this.len += bytesRead;
165 if (this.len >= this.contentLength) {
166 this.completed = true;
167 }
168 return bytesRead;
169 }
170
171 @Override
172 public String toString() {
173 StringBuilder buffer = new StringBuilder();
174 buffer.append("[content length: ");
175 buffer.append(this.contentLength);
176 buffer.append("; pos: ");
177 buffer.append(this.len);
178 buffer.append("; completed: ");
179 buffer.append(this.completed);
180 buffer.append("]");
181 return buffer.toString();
182 }
183 }