source: josm/trunk/src/org/openstreetmap/josm/tools/Http1Client.java

Last change on this file was 15798, checked in by simon04, 4 years ago

Http1Client: determine actual HTTP version

File size: 7.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import java.io.BufferedOutputStream;
5import java.io.ByteArrayInputStream;
6import java.io.IOException;
7import java.io.InputStream;
8import java.io.OutputStream;
9import java.net.HttpURLConnection;
10import java.net.URL;
11import java.util.Collections;
12import java.util.List;
13import java.util.Map;
14import java.util.Map.Entry;
15import java.util.Optional;
16import java.util.TreeMap;
17
18import org.openstreetmap.josm.data.Version;
19import org.openstreetmap.josm.gui.progress.ProgressMonitor;
20import org.openstreetmap.josm.io.ProgressOutputStream;
21
22/**
23 * Provides a uniform access for a HTTP/HTTPS 1.0/1.1 server.
24 * @since 15229
25 */
26public final class Http1Client extends HttpClient {
27
28 private HttpURLConnection connection; // to allow disconnecting before `response` is set
29
30 /**
31 * Constructs a new {@code Http1Client}.
32 * @param url URL to access
33 * @param requestMethod HTTP request method (GET, POST, PUT, DELETE...)
34 */
35 public Http1Client(URL url, String requestMethod) {
36 super(url, requestMethod);
37 }
38
39 @Override
40 protected void setupConnection(ProgressMonitor progressMonitor) throws IOException {
41 connection = (HttpURLConnection) getURL().openConnection();
42 connection.setRequestMethod(getRequestMethod());
43 connection.setRequestProperty("User-Agent", Version.getInstance().getFullAgentString());
44 connection.setConnectTimeout(getConnectTimeout());
45 connection.setReadTimeout(getReadTimeout());
46 connection.setInstanceFollowRedirects(false); // we do that ourselves
47 if (getIfModifiedSince() > 0) {
48 connection.setIfModifiedSince(getIfModifiedSince());
49 }
50 connection.setUseCaches(isUseCache());
51 if (!isUseCache()) {
52 connection.setRequestProperty("Cache-Control", "no-cache");
53 }
54 for (Map.Entry<String, String> header : getHeaders().entrySet()) {
55 if (header.getValue() != null) {
56 connection.setRequestProperty(header.getKey(), header.getValue());
57 }
58 }
59
60 notifyConnect(progressMonitor);
61
62 if (requiresBody()) {
63 logRequestBody();
64 byte[] body = getRequestBody();
65 connection.setFixedLengthStreamingMode(body.length);
66 connection.setDoOutput(true);
67 try (OutputStream out = new BufferedOutputStream(
68 new ProgressOutputStream(connection.getOutputStream(), body.length,
69 progressMonitor, getOutputMessage(), isFinishOnCloseOutput()))) {
70 out.write(body);
71 }
72 }
73 }
74
75 @Override
76 protected ConnectionResponse performConnection() throws IOException {
77 try {
78 connection.connect();
79 } catch (RuntimeException e) {
80 throw new IOException(e);
81 }
82 return new ConnectionResponse() {
83 @Override
84 public String getResponseVersion() {
85 String headerField = connection.getHeaderField(0);
86 if (headerField != null && headerField.startsWith("HTTP")) {
87 return headerField.replaceFirst(" .*", "");
88 }
89 return "HTTP/1";
90 }
91
92 @Override
93 public int getResponseCode() throws IOException {
94 return connection.getResponseCode();
95 }
96
97 @Override
98 public String getHeaderField(String name) {
99 return connection.getHeaderField(name);
100 }
101
102 @Override
103 public long getContentLengthLong() {
104 return connection.getContentLengthLong();
105 }
106
107 @Override
108 public Map<String, List<String>> getHeaderFields() {
109 return connection.getHeaderFields();
110 }
111 };
112 }
113
114 @Override
115 protected void performDisconnection() throws IOException {
116 connection.disconnect();
117 }
118
119 @Override
120 protected Response buildResponse(ProgressMonitor progressMonitor) throws IOException {
121 return new Http1Response(connection, progressMonitor);
122 }
123
124 /**
125 * A wrapper for the HTTP 1.x response.
126 */
127 public static final class Http1Response extends Response {
128 private final HttpURLConnection connection;
129
130 private Http1Response(HttpURLConnection connection, ProgressMonitor progressMonitor) throws IOException {
131 super(progressMonitor, connection.getResponseCode(), connection.getResponseMessage());
132 this.connection = connection;
133 debugRedirect();
134 }
135
136 @Override
137 public URL getURL() {
138 return connection.getURL();
139 }
140
141 @Override
142 public String getRequestMethod() {
143 return connection.getRequestMethod();
144 }
145
146 @Override
147 public InputStream getInputStream() throws IOException {
148 InputStream in;
149 try {
150 in = connection.getInputStream();
151 } catch (IOException ioe) {
152 Logging.debug(ioe);
153 in = Optional.ofNullable(connection.getErrorStream()).orElseGet(() -> new ByteArrayInputStream(new byte[]{}));
154 }
155 return in;
156 }
157
158 @Override
159 public String getContentEncoding() {
160 return connection.getContentEncoding();
161 }
162
163 @Override
164 public String getContentType() {
165 return connection.getHeaderField("Content-Type");
166 }
167
168 @Override
169 public long getExpiration() {
170 return connection.getExpiration();
171 }
172
173 @Override
174 public long getLastModified() {
175 return connection.getLastModified();
176 }
177
178 @Override
179 public long getContentLength() {
180 return connection.getContentLengthLong();
181 }
182
183 @Override
184 public String getHeaderField(String name) {
185 return connection.getHeaderField(name);
186 }
187
188 @Override
189 public Map<String, List<String>> getHeaderFields() {
190 // returned map from HttpUrlConnection is case sensitive, use case insensitive TreeMap to conform to RFC 2616
191 Map<String, List<String>> ret = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
192 for (Entry<String, List<String>> e: connection.getHeaderFields().entrySet()) {
193 if (e.getKey() != null) {
194 ret.put(e.getKey(), e.getValue());
195 }
196 }
197 return Collections.unmodifiableMap(ret);
198 }
199
200 @Override
201 public void disconnect() {
202 Http1Client.disconnect(connection);
203 }
204 }
205
206 /**
207 * @see HttpURLConnection#disconnect()
208 */
209 @Override
210 public void disconnect() {
211 Http1Client.disconnect(connection);
212 }
213
214 private static void disconnect(final HttpURLConnection connection) {
215 if (connection != null) {
216 // Fix upload aborts - see #263
217 connection.setConnectTimeout(100);
218 connection.setReadTimeout(100);
219 try {
220 Thread.sleep(100);
221 } catch (InterruptedException ex) {
222 Logging.warn("InterruptedException in " + Http1Client.class + " during cancel");
223 Thread.currentThread().interrupt();
224 }
225 connection.disconnect();
226 }
227 }
228}
Note: See TracBrowser for help on using the repository browser.