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 package org.apache.hc.core5.http.nio.entity;
28
29 import java.io.IOException;
30 import java.nio.ByteBuffer;
31 import java.security.MessageDigest;
32 import java.security.NoSuchAlgorithmException;
33 import java.util.ArrayList;
34 import java.util.LinkedHashSet;
35 import java.util.List;
36 import java.util.Set;
37
38 import org.apache.hc.core5.http.Header;
39 import org.apache.hc.core5.http.message.BasicHeader;
40 import org.apache.hc.core5.http.nio.AsyncEntityProducer;
41 import org.apache.hc.core5.http.nio.DataStreamChannel;
42 import org.apache.hc.core5.util.Args;
43 import org.apache.hc.core5.util.TextUtils;
44
45
46
47
48
49
50
51 public class DigestingEntityProducer implements AsyncEntityProducer {
52
53 private final AsyncEntityProducer wrapped;
54 private final MessageDigest digester;
55
56 private volatile byte[] digest;
57
58 public DigestingEntityProducer(
59 final String algo,
60 final AsyncEntityProducer wrapped) {
61 this.wrapped = Args.notNull(wrapped, "Entity consumer");
62 try {
63 this.digester = MessageDigest.getInstance(algo);
64 } catch (final NoSuchAlgorithmException ex) {
65 throw new IllegalArgumentException("Unsupported digest algorithm: " + algo);
66 }
67 }
68
69 @Override
70 public boolean isRepeatable() {
71 return wrapped.isRepeatable();
72 }
73
74 @Override
75 public long getContentLength() {
76 return wrapped.getContentLength();
77 }
78
79 @Override
80 public String getContentType() {
81 return wrapped.getContentType();
82 }
83
84 @Override
85 public String getContentEncoding() {
86 return wrapped.getContentEncoding();
87 }
88
89 @Override
90 public boolean isChunked() {
91 return wrapped.isChunked();
92 }
93
94 @Override
95 public Set<String> getTrailerNames() {
96 final Set<String> allNames = new LinkedHashSet<>();
97 final Set<String> names = wrapped.getTrailerNames();
98 if (names != null) {
99 allNames.addAll(names);
100 }
101 allNames.add("digest-algo");
102 allNames.add("digest");
103 return allNames;
104 }
105
106 @Override
107 public int available() {
108 return wrapped.available();
109 }
110
111 @Override
112 public void produce(final DataStreamChannel channel) throws IOException {
113 wrapped.produce(new DataStreamChannel() {
114
115 @Override
116 public void requestOutput() {
117 channel.requestOutput();
118 }
119
120 @Override
121 public int write(final ByteBuffer src) throws IOException {
122 final ByteBuffer dup = src.duplicate();
123 final int writtenBytes = channel.write(src);
124 if (writtenBytes > 0) {
125 dup.limit(dup.position() + writtenBytes);
126 digester.update(dup);
127 }
128 return writtenBytes;
129 }
130
131 @Override
132 public void endStream(final List<? extends Header> trailers) throws IOException {
133 digest = digester.digest();
134 final List<Header> allTrailers = new ArrayList<>();
135 if (trailers != null) {
136 allTrailers.addAll(trailers);
137 }
138 allTrailers.add(new BasicHeader("digest-algo", digester.getAlgorithm()));
139 allTrailers.add(new BasicHeader("digest", TextUtils.toHexString(digest)));
140 channel.endStream(allTrailers);
141 }
142
143 @Override
144 public void endStream() throws IOException {
145 endStream(null);
146 }
147
148 });
149 }
150
151 @Override
152 public void failed(final Exception cause) {
153 wrapped.failed(cause);
154 }
155
156 @Override
157 public void releaseResources() {
158 wrapped.releaseResources();
159 }
160
161
162
163
164
165
166 public byte[] getDigest() {
167 return digest;
168 }
169
170 }