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.client5.http.impl.classic;
29
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.net.SocketException;
34 import java.util.Arrays;
35 import java.util.List;
36
37 import org.apache.hc.client5.http.classic.ExecRuntime;
38 import org.apache.hc.core5.function.Supplier;
39 import org.apache.hc.core5.http.ClassicHttpResponse;
40 import org.apache.hc.core5.http.Header;
41 import org.apache.hc.core5.http.HttpEntity;
42 import org.apache.hc.core5.http.impl.io.ChunkedInputStream;
43 import org.apache.hc.core5.http.io.EofSensorInputStream;
44 import org.apache.hc.core5.http.io.EofSensorWatcher;
45 import org.apache.hc.core5.http.io.entity.HttpEntityWrapper;
46
47 class ResponseEntityProxy extends HttpEntityWrapper implements EofSensorWatcher {
48
49 private final ExecRuntime execRuntime;
50
51 public static void enhance(final ClassicHttpResponse response, final ExecRuntime execRuntime) {
52 final HttpEntity entity = response.getEntity();
53 if (entity != null && entity.isStreaming() && execRuntime != null) {
54 response.setEntity(new ResponseEntityProxy(entity, execRuntime));
55 }
56 }
57
58 ResponseEntityProxy(final HttpEntity entity, final ExecRuntime execRuntime) {
59 super(entity);
60 this.execRuntime = execRuntime;
61 }
62
63 private void cleanup() throws IOException {
64 if (this.execRuntime != null) {
65 if (this.execRuntime.isEndpointConnected()) {
66 this.execRuntime.disconnectEndpoint();
67 }
68 this.execRuntime.discardEndpoint();
69 }
70 }
71
72 private void discardConnection() {
73 if (this.execRuntime != null) {
74 this.execRuntime.discardEndpoint();
75 }
76 }
77
78 public void releaseConnection() {
79 if (this.execRuntime != null) {
80 this.execRuntime.releaseEndpoint();
81 }
82 }
83
84 @Override
85 public boolean isRepeatable() {
86 return false;
87 }
88
89 @Override
90 public InputStream getContent() throws IOException {
91 return new EofSensorInputStream(super.getContent(), this);
92 }
93
94 @Override
95 public void writeTo(final OutputStream outStream) throws IOException {
96 try {
97 if (outStream != null) {
98 super.writeTo(outStream);
99 }
100 releaseConnection();
101 } catch (final IOException | RuntimeException ex) {
102 discardConnection();
103 throw ex;
104 } finally {
105 cleanup();
106 }
107 }
108
109 @Override
110 public boolean eofDetected(final InputStream wrapped) throws IOException {
111 try {
112
113
114 if (wrapped != null) {
115 wrapped.close();
116 }
117 releaseConnection();
118 } catch (final IOException | RuntimeException ex) {
119 discardConnection();
120 throw ex;
121 } finally {
122 cleanup();
123 }
124 return false;
125 }
126
127 @Override
128 public boolean streamClosed(final InputStream wrapped) throws IOException {
129 try {
130 final boolean open = execRuntime != null && execRuntime.isEndpointAcquired();
131
132
133 try {
134 if (wrapped != null) {
135 wrapped.close();
136 }
137 releaseConnection();
138 } catch (final SocketException ex) {
139 if (open) {
140 throw ex;
141 }
142 }
143 } catch (final IOException | RuntimeException ex) {
144 discardConnection();
145 throw ex;
146 } finally {
147 cleanup();
148 }
149 return false;
150 }
151
152 @Override
153 public boolean streamAbort(final InputStream wrapped) throws IOException {
154 cleanup();
155 return false;
156 }
157
158 @Override
159 public Supplier<List<? extends Header>> getTrailers() {
160 try {
161 final InputStream underlyingStream = super.getContent();
162 return new Supplier<List<? extends Header>>() {
163 @Override
164 public List<? extends Header> get() {
165 final Header[] footers;
166 if (underlyingStream instanceof ChunkedInputStream) {
167 final ChunkedInputStream chunkedInputStream = (ChunkedInputStream) underlyingStream;
168 footers = chunkedInputStream.getFooters();
169 } else {
170 footers = new Header[0];
171 }
172 return Arrays.asList(footers);
173 }
174 };
175 } catch (final IOException e) {
176 throw new IllegalStateException("Unable to retrieve input stream", e);
177 }
178 }
179
180 }