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