source: josm/trunk/src/org/openstreetmap/josm/io/OsmServerReader.java@ 7282

Last change on this file since 7282 was 7267, checked in by Don-vip, 10 years ago

fix #10172 - Cannot open GPX from URL without Content-Disposition http header

  • Property svn:eol-style set to native
File size: 14.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.BufferedReader;
7import java.io.IOException;
8import java.io.InputStream;
9import java.io.InputStreamReader;
10import java.net.HttpURLConnection;
11import java.net.MalformedURLException;
12import java.net.URL;
13import java.nio.charset.StandardCharsets;
14import java.util.List;
15import java.util.Map;
16import java.util.zip.GZIPInputStream;
17import java.util.zip.Inflater;
18import java.util.zip.InflaterInputStream;
19
20import org.openstreetmap.josm.Main;
21import org.openstreetmap.josm.data.gpx.GpxData;
22import org.openstreetmap.josm.data.osm.DataSet;
23import org.openstreetmap.josm.gui.progress.ProgressMonitor;
24import org.openstreetmap.josm.tools.Utils;
25
26/**
27 * This DataReader reads directly from the REST API of the osm server.
28 *
29 * It supports plain text transfer as well as gzip or deflate encoded transfers;
30 * if compressed transfers are unwanted, set property osm-server.use-compression
31 * to false.
32 *
33 * @author imi
34 */
35public abstract class OsmServerReader extends OsmConnection {
36 private OsmApi api = OsmApi.getOsmApi();
37 private boolean doAuthenticate = false;
38 protected boolean gpxParsedProperly;
39
40 /**
41 * Open a connection to the given url and return a reader on the input stream
42 * from that connection. In case of user cancel, return <code>null</code>.
43 * Relative URL's are directed to API base URL.
44 * @param urlStr The url to connect to.
45 * @param progressMonitor progress monitoring and abort handler
46 * @return A reader reading the input stream (servers answer) or <code>null</code>.
47 * @throws OsmTransferException thrown if data transfer errors occur
48 */
49 protected InputStream getInputStream(String urlStr, ProgressMonitor progressMonitor) throws OsmTransferException {
50 return getInputStream(urlStr, progressMonitor, null);
51 }
52
53 /**
54 * Open a connection to the given url and return a reader on the input stream
55 * from that connection. In case of user cancel, return <code>null</code>.
56 * Relative URL's are directed to API base URL.
57 * @param urlStr The url to connect to.
58 * @param progressMonitor progress monitoring and abort handler
59 * @param reason The reason to show on console. Can be {@code null} if no reason is given
60 * @return A reader reading the input stream (servers answer) or <code>null</code>.
61 * @throws OsmTransferException thrown if data transfer errors occur
62 */
63 protected InputStream getInputStream(String urlStr, ProgressMonitor progressMonitor, String reason) throws OsmTransferException {
64 try {
65 api.initialize(progressMonitor);
66 String url = urlStr.startsWith("http") ? urlStr : (getBaseUrl() + urlStr);
67 return getInputStreamRaw(url, progressMonitor, reason);
68 } finally {
69 progressMonitor.invalidate();
70 }
71 }
72
73 /**
74 * Return the base URL for relative URL requests
75 * @return base url of API
76 */
77 protected String getBaseUrl() {
78 return api.getBaseUrl();
79 }
80
81 /**
82 * Open a connection to the given url and return a reader on the input stream
83 * from that connection. In case of user cancel, return <code>null</code>.
84 * @param urlStr The exact url to connect to.
85 * @param progressMonitor progress monitoring and abort handler
86 * @return An reader reading the input stream (servers answer) or <code>null</code>.
87 * @throws OsmTransferException thrown if data transfer errors occur
88 */
89 protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor) throws OsmTransferException {
90 return getInputStreamRaw(urlStr, progressMonitor, null);
91 }
92
93 /**
94 * Open a connection to the given url and return a reader on the input stream
95 * from that connection. In case of user cancel, return <code>null</code>.
96 * @param urlStr The exact url to connect to.
97 * @param progressMonitor progress monitoring and abort handler
98 * @param reason The reason to show on console. Can be {@code null} if no reason is given
99 * @return An reader reading the input stream (servers answer) or <code>null</code>.
100 * @throws OsmTransferException thrown if data transfer errors occur
101 */
102 protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor, String reason) throws OsmTransferException {
103 return getInputStreamRaw(urlStr, progressMonitor, reason, false);
104 }
105
106 /**
107 * Open a connection to the given url and return a reader on the input stream
108 * from that connection. In case of user cancel, return <code>null</code>.
109 * @param urlStr The exact url to connect to.
110 * @param progressMonitor progress monitoring and abort handler
111 * @param reason The reason to show on console. Can be {@code null} if no reason is given
112 * @param uncompressAccordingToContentDisposition Whether to inspect the HTTP header {@code Content-Disposition}
113 * for {@code filename} and uncompress a gzip/bzip2 stream.
114 * @return An reader reading the input stream (servers answer) or <code>null</code>.
115 * @throws OsmTransferException thrown if data transfer errors occur
116 */
117 @SuppressWarnings("resource")
118 protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor, String reason, boolean uncompressAccordingToContentDisposition) throws OsmTransferException {
119 try {
120 URL url = null;
121 try {
122 url = new URL(urlStr.replace(" ", "%20"));
123 } catch(MalformedURLException e) {
124 throw new OsmTransferException(e);
125 }
126 try {
127 // fix #7640, see http://www.tikalk.com/java/forums/httpurlconnection-disable-keep-alive
128 activeConnection = Utils.openHttpConnection(url, false);
129 } catch(Exception e) {
130 throw new OsmTransferException(tr("Failed to open connection to API {0}.", url.toExternalForm()), e);
131 }
132 if (cancel) {
133 activeConnection.disconnect();
134 return null;
135 }
136
137 if (doAuthenticate) {
138 addAuth(activeConnection);
139 }
140 if (cancel)
141 throw new OsmTransferCanceledException();
142 if (Main.pref.getBoolean("osm-server.use-compression", true)) {
143 activeConnection.setRequestProperty("Accept-Encoding", "gzip, deflate");
144 }
145
146 activeConnection.setConnectTimeout(Main.pref.getInteger("socket.timeout.connect",15)*1000);
147
148 try {
149 if (reason != null && !reason.isEmpty()) {
150 Main.info("GET " + url + " (" + reason + ")");
151 } else {
152 Main.info("GET " + url);
153 }
154 activeConnection.connect();
155 } catch (Exception e) {
156 Main.error(e);
157 OsmTransferException ote = new OsmTransferException(tr("Could not connect to the OSM server. Please check your internet connection."), e);
158 ote.setUrl(url.toString());
159 throw ote;
160 }
161 try {
162 if (Main.isDebugEnabled()) {
163 Main.debug("RESPONSE: "+activeConnection.getHeaderFields());
164 }
165 if (activeConnection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED)
166 throw new OsmApiException(HttpURLConnection.HTTP_UNAUTHORIZED,null,null);
167
168 if (activeConnection.getResponseCode() == HttpURLConnection.HTTP_PROXY_AUTH)
169 throw new OsmTransferCanceledException();
170
171 String encoding = activeConnection.getContentEncoding();
172 if (activeConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
173 String errorHeader = activeConnection.getHeaderField("Error");
174 StringBuilder errorBody = new StringBuilder();
175 try {
176 InputStream i = fixEncoding(activeConnection.getErrorStream(), encoding);
177 if (i != null) {
178 BufferedReader in = new BufferedReader(new InputStreamReader(i, StandardCharsets.UTF_8));
179 String s;
180 while((s = in.readLine()) != null) {
181 errorBody.append(s);
182 errorBody.append("\n");
183 }
184 }
185 }
186 catch(Exception e) {
187 errorBody.append(tr("Reading error text failed."));
188 }
189
190 throw new OsmApiException(activeConnection.getResponseCode(), errorHeader, errorBody.toString(), url.toString());
191 }
192
193 InputStream in = new ProgressInputStream(activeConnection, progressMonitor);
194 if (uncompressAccordingToContentDisposition) {
195 in = uncompressAccordingToContentDisposition(in, activeConnection.getHeaderFields());
196 }
197 return fixEncoding(in, encoding);
198 } catch (OsmTransferException e) {
199 throw e;
200 } catch (Exception e) {
201 throw new OsmTransferException(e);
202 }
203 } finally {
204 progressMonitor.invalidate();
205 }
206 }
207
208 private InputStream fixEncoding(InputStream stream, String encoding) throws IOException {
209 if ("gzip".equalsIgnoreCase(encoding)) {
210 stream = new GZIPInputStream(stream);
211 } else if ("deflate".equalsIgnoreCase(encoding)) {
212 stream = new InflaterInputStream(stream, new Inflater(true));
213 }
214 return stream;
215 }
216
217 private InputStream uncompressAccordingToContentDisposition(InputStream stream, Map<String, List<String>> headerFields) throws IOException {
218 List<String> field = headerFields.get("Content-Disposition");
219 if (field != null && field.toString().contains(".gz\"")) {
220 return Compression.GZIP.getUncompressedInputStream(stream);
221 } else if (field != null && field.toString().contains(".bz2\"")) {
222 return Compression.BZIP2.getUncompressedInputStream(stream);
223 } else {
224 return stream;
225 }
226 }
227
228 /**
229 * Download OSM files from somewhere
230 * @param progressMonitor The progress monitor
231 * @return The corresponding dataset
232 * @throws OsmTransferException if any error occurs
233 */
234 public abstract DataSet parseOsm(final ProgressMonitor progressMonitor) throws OsmTransferException;
235
236 /**
237 * Download OSM Change files from somewhere
238 * @param progressMonitor The progress monitor
239 * @return The corresponding dataset
240 * @throws OsmTransferException if any error occurs
241 */
242 public DataSet parseOsmChange(final ProgressMonitor progressMonitor) throws OsmTransferException {
243 return null;
244 }
245
246 /**
247 * Download BZip2-compressed OSM Change files from somewhere
248 * @param progressMonitor The progress monitor
249 * @return The corresponding dataset
250 * @throws OsmTransferException if any error occurs
251 */
252 public DataSet parseOsmChangeBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
253 return null;
254 }
255
256 /**
257 * Download GZip-compressed OSM Change files from somewhere
258 * @param progressMonitor The progress monitor
259 * @return The corresponding dataset
260 * @throws OsmTransferException if any error occurs
261 */
262 public DataSet parseOsmChangeGzip(final ProgressMonitor progressMonitor) throws OsmTransferException {
263 return null;
264 }
265
266 /**
267 * Retrieve raw gps waypoints from the server API.
268 * @param progressMonitor The progress monitor
269 * @return The corresponding GPX tracks
270 * @throws OsmTransferException if any error occurs
271 */
272 public GpxData parseRawGps(final ProgressMonitor progressMonitor) throws OsmTransferException {
273 return null;
274 }
275
276 /**
277 * Retrieve BZip2-compressed GPX files from somewhere.
278 * @param progressMonitor The progress monitor
279 * @return The corresponding GPX tracks
280 * @throws OsmTransferException if any error occurs
281 * @since 6244
282 */
283 public GpxData parseRawGpsBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
284 return null;
285 }
286
287 /**
288 * Download BZip2-compressed OSM files from somewhere
289 * @param progressMonitor The progress monitor
290 * @return The corresponding dataset
291 * @throws OsmTransferException if any error occurs
292 */
293 public DataSet parseOsmBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
294 return null;
295 }
296
297 /**
298 * Download GZip-compressed OSM files from somewhere
299 * @param progressMonitor The progress monitor
300 * @return The corresponding dataset
301 * @throws OsmTransferException if any error occurs
302 */
303 public DataSet parseOsmGzip(final ProgressMonitor progressMonitor) throws OsmTransferException {
304 return null;
305 }
306
307 /**
308 * Download Zip-compressed OSM files from somewhere
309 * @param progressMonitor The progress monitor
310 * @return The corresponding dataset
311 * @throws OsmTransferException if any error occurs
312 * @since 6882
313 */
314 public DataSet parseOsmZip(final ProgressMonitor progressMonitor) throws OsmTransferException {
315 return null;
316 }
317
318 /**
319 * Returns true if this reader is adding authentication credentials to the read
320 * request sent to the server.
321 *
322 * @return true if this reader is adding authentication credentials to the read
323 * request sent to the server
324 */
325 public boolean isDoAuthenticate() {
326 return doAuthenticate;
327 }
328
329 /**
330 * Sets whether this reader adds authentication credentials to the read
331 * request sent to the server.
332 *
333 * @param doAuthenticate true if this reader adds authentication credentials to the read
334 * request sent to the server
335 */
336 public void setDoAuthenticate(boolean doAuthenticate) {
337 this.doAuthenticate = doAuthenticate;
338 }
339
340 /**
341 * Determines if the GPX data has been parsed properly.
342 * @return true if the GPX data has been parsed properly, false otherwise
343 * @see GpxReader#parse
344 */
345 public final boolean isGpxParsedProperly() {
346 return gpxParsedProperly;
347 }
348}
Note: See TracBrowser for help on using the repository browser.