Changeset 9172 in josm


Ignore:
Timestamp:
2015-12-26T23:42:03+01:00 (4 years ago)
Author:
simon04
Message:

see #12231 - Use HttpClient for OSM API calls

This requires adaptors to the OAuth library: SignpostAdapters

Location:
trunk/src/org/openstreetmap/josm
Files:
1 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/oauth/OAuthParameters.java

    r8513 r9172  
    77import oauth.signpost.OAuthConsumer;
    88import oauth.signpost.OAuthProvider;
    9 import oauth.signpost.basic.DefaultOAuthConsumer;
    10 import oauth.signpost.basic.DefaultOAuthProvider;
    119
    1210import org.openstreetmap.josm.Main;
     
    220218     */
    221219    public OAuthConsumer buildConsumer() {
    222         return new DefaultOAuthConsumer(consumerKey, consumerSecret);
     220        return new SignpostAdapters.OAuthConsumer(consumerKey, consumerSecret);
    223221    }
    224222
     
    232230    public OAuthProvider buildProvider(OAuthConsumer consumer) {
    233231        CheckParameterUtil.ensureParameterNotNull(consumer, "consumer");
    234         return new DefaultOAuthProvider(
     232        return new SignpostAdapters.OAuthProvider(
    235233                requestTokenUrl,
    236234                accessTokenUrl,
  • trunk/src/org/openstreetmap/josm/gui/oauth/OsmOAuthAuthorizationClient.java

    r8540 r9172  
    55
    66import java.io.BufferedReader;
    7 import java.io.DataOutputStream;
    87import java.io.IOException;
    9 import java.io.InputStream;
    10 import java.io.InputStreamReader;
    118import java.lang.reflect.Field;
    129import java.net.HttpURLConnection;
     
    2522import oauth.signpost.OAuthConsumer;
    2623import oauth.signpost.OAuthProvider;
    27 import oauth.signpost.basic.DefaultOAuthProvider;
    2824import oauth.signpost.exception.OAuthException;
    2925
     
    3632import org.openstreetmap.josm.io.OsmTransferCanceledException;
    3733import org.openstreetmap.josm.tools.CheckParameterUtil;
     34import org.openstreetmap.josm.tools.HttpClient;
    3835import org.openstreetmap.josm.tools.Utils;
    3936
     
    4744    private final OAuthProvider provider;
    4845    private boolean canceled;
    49     private HttpURLConnection connection;
     46    private HttpClient.Response connection;
    5047
    5148    private static class SessionId {
     
    9996     */
    10097    public void cancel() {
    101         DefaultOAuthProvider p  = (DefaultOAuthProvider) provider;
    10298        canceled = true;
    103         if (p != null) {
     99        if (provider != null) {
    104100            try {
    105                 Field f =  p.getClass().getDeclaredField("connection");
     101                // TODO
     102                Field f =  provider.getClass().getDeclaredField("connection");
    106103                f.setAccessible(true);
    107                 HttpURLConnection con = (HttpURLConnection) f.get(p);
     104                HttpURLConnection con = (HttpURLConnection) f.get(provider);
    108105                if (con != null) {
    109106                    con.disconnect();
     
    195192    }
    196193
    197     protected String extractToken(HttpURLConnection connection) {
    198         try (
    199             InputStream is = connection.getInputStream();
    200             BufferedReader r = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))
    201         ) {
     194    protected String extractToken() {
     195        try (BufferedReader r = connection.getContentReader()) {
    202196            String c;
    203197            Pattern p = Pattern.compile(".*authenticity_token.*value=\"([^\"]+)\".*");
     
    215209    }
    216210
    217     protected SessionId extractOsmSession(HttpURLConnection connection) {
    218         List<String> setCookies = connection.getHeaderFields().get("Set-Cookie");
     211    protected SessionId extractOsmSession() {
     212        List<String> setCookies = connection.getHeaderFields("Set-Cookie");
    219213        if (setCookies == null)
    220214            // no cookies set
     
    234228                if ("_osm_session".equals(kv[0])) {
    235229                    // osm session cookie found
    236                     String token = extractToken(connection);
     230                    String token = extractToken();
    237231                    if (token == null)
    238232                        return null;
     
    309303            URL url = new URL(sb.toString());
    310304            synchronized (this) {
    311                 connection = Utils.openHttpConnection(url);
    312             }
    313             connection.setRequestMethod("GET");
    314             connection.setDoInput(true);
    315             connection.setDoOutput(false);
    316             connection.connect();
    317             SessionId sessionId = extractOsmSession(connection);
     305                connection = HttpClient.create(url).connect();
     306            }
     307            SessionId sessionId = extractOsmSession();
    318308            if (sessionId == null)
    319309                throw new OsmOAuthAuthorizationException(
     
    339329            URL url = new URL(getAuthoriseUrl(requestToken));
    340330            synchronized (this) {
    341                 connection = Utils.openHttpConnection(url);
    342             }
    343             connection.setRequestMethod("GET");
    344             connection.setDoInput(true);
    345             connection.setDoOutput(false);
    346             connection.setRequestProperty("Cookie", "_osm_session=" + sessionId.id + "; _osm_username=" + sessionId.userName);
    347             connection.connect();
    348             sessionId.token = extractToken(connection);
     331                connection = HttpClient.create(url)
     332                        .setHeader("Cookie", "_osm_session=" + sessionId.id + "; _osm_username=" + sessionId.userName)
     333                        .connect();
     334            }
     335            sessionId.token = extractToken();
    349336            if (sessionId.token == null)
    350337                throw new OsmOAuthAuthorizationException(tr("OSM website did not return a session cookie in response to ''{0}'',",
     
    362349        try {
    363350            URL url = new URL(buildOsmLoginUrl());
    364             synchronized (this) {
    365                 connection = Utils.openHttpConnection(url);
    366             }
    367             connection.setRequestMethod("POST");
    368             connection.setDoInput(true);
    369             connection.setDoOutput(true);
    370             connection.setUseCaches(false);
     351            final HttpClient client = HttpClient.create(url, "POST").useCache(false);
    371352
    372353            Map<String, String> parameters = new HashMap<>();
     
    376357            parameters.put("commit", "Login");
    377358            parameters.put("authenticity_token", sessionId.token);
    378 
    379             String request = buildPostRequest(parameters);
    380 
    381             connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    382             connection.setRequestProperty("Content-Length", Integer.toString(request.length()));
    383             connection.setRequestProperty("Cookie", "_osm_session=" + sessionId.id);
     359            client.setRequestBody(buildPostRequest(parameters).getBytes(StandardCharsets.UTF_8));
     360
     361            client.setHeader("Content-Type", "application/x-www-form-urlencoded");
     362            client.setHeader("Cookie", "_osm_session=" + sessionId.id);
    384363            // make sure we can catch 302 Moved Temporarily below
    385             connection.setInstanceFollowRedirects(false);
    386 
    387             connection.connect();
    388 
    389             try (DataOutputStream dout = new DataOutputStream(connection.getOutputStream())) {
    390                 dout.writeBytes(request);
    391                 dout.flush();
     364            client.setMaxRedirects(-1);
     365
     366            synchronized (this) {
     367                connection = client.connect();
    392368            }
    393369
     
    415391            URL url = new URL(buildOsmLogoutUrl());
    416392            synchronized (this) {
    417                 connection = Utils.openHttpConnection(url);
    418             }
    419             connection.setRequestMethod("GET");
    420             connection.setDoInput(true);
    421             connection.setDoOutput(false);
    422             connection.connect();
     393                connection = HttpClient.create(url).connect();
     394            }
    423395        } catch (IOException e) {
    424396            throw new OsmOAuthAuthorizationException(e);
     
    461433        try {
    462434            URL url = new URL(oauthProviderParameters.getAuthoriseUrl());
    463             synchronized (this) {
    464                 connection = Utils.openHttpConnection(url);
    465             }
    466             connection.setRequestMethod("POST");
    467             connection.setDoInput(true);
    468             connection.setDoOutput(true);
    469             connection.setUseCaches(false);
    470             connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    471             connection.setRequestProperty("Content-Length", Integer.toString(request.length()));
    472             connection.setRequestProperty("Cookie", "_osm_session=" + sessionId.id + "; _osm_username=" + sessionId.userName);
    473             connection.setInstanceFollowRedirects(false);
    474 
    475             connection.connect();
    476 
    477             try (DataOutputStream dout = new DataOutputStream(connection.getOutputStream())) {
    478                 dout.writeBytes(request);
    479                 dout.flush();
     435            final HttpClient client = HttpClient.create(url, "POST").useCache(false);
     436            client.setHeader("Content-Type", "application/x-www-form-urlencoded");
     437            client.setHeader("Cookie", "_osm_session=" + sessionId.id + "; _osm_username=" + sessionId.userName);
     438            client.setMaxRedirects(-1);
     439            client.setRequestBody(request.getBytes(StandardCharsets.UTF_8));
     440
     441            synchronized (this) {
     442                connection = client.connect();
    480443            }
    481444
  • trunk/src/org/openstreetmap/josm/gui/oauth/TestAccessTokenTask.java

    r9078 r9172  
    2525import org.openstreetmap.josm.io.auth.DefaultAuthenticator;
    2626import org.openstreetmap.josm.tools.CheckParameterUtil;
    27 import org.openstreetmap.josm.tools.Utils;
     27import org.openstreetmap.josm.tools.HttpClient;
    2828import org.openstreetmap.josm.tools.XmlParsingException;
    2929import org.w3c.dom.Document;
     
    4646    private final Component parent;
    4747    private final String apiUrl;
    48     private HttpURLConnection connection;
     48    private HttpClient.Response connection;
    4949
    5050    /**
     
    8080    protected void finish() {}
    8181
    82     protected void sign(HttpURLConnection con) throws OAuthException {
     82    protected void sign(HttpClient con) throws OAuthException {
    8383        OAuthConsumer consumer = oauthParameters.buildConsumer();
    8484        consumer.setTokenWithSecret(token.getKey(), token.getSecret());
     
    103103            authenticatorEnabled = DefaultAuthenticator.getInstance().isEnabled();
    104104            DefaultAuthenticator.getInstance().setEnabled(false);
     105
     106            final HttpClient client = HttpClient.create(url);
     107            sign(client);
    105108            synchronized (this) {
    106                 connection = Utils.openHttpConnection(url);
     109                connection = client.connect();
    107110            }
    108 
    109             connection.setDoOutput(true);
    110             connection.setRequestMethod("GET");
    111             sign(connection);
    112             connection.connect();
    113111
    114112            if (connection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED)
     
    122120            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
    123121                throw new OsmApiException(connection.getResponseCode(), connection.getHeaderField("Error"), null);
    124             Document d = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(connection.getInputStream());
     122            Document d = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(connection.getContent());
    125123            return OsmServerUserInfoReader.buildFromXML(d);
    126124        } catch (SAXException | ParserConfigurationException e) {
  • trunk/src/org/openstreetmap/josm/gui/preferences/server/ApiUrlTestTask.java

    r8510 r9172  
    2020import org.openstreetmap.josm.io.OsmTransferException;
    2121import org.openstreetmap.josm.tools.CheckParameterUtil;
    22 import org.openstreetmap.josm.tools.Utils;
     22import org.openstreetmap.josm.tools.HttpClient;
    2323import org.xml.sax.InputSource;
    2424import org.xml.sax.SAXException;
     
    3636    private boolean success;
    3737    private final Component parent;
    38     private HttpURLConnection connection;
     38    private HttpClient.Response connection;
    3939
    4040    /**
     
    177177
    178178            synchronized (this) {
    179                 connection = Utils.openHttpConnection(capabilitiesUrl);
    180             }
    181             connection.setDoInput(true);
    182             connection.setDoOutput(false);
    183             connection.setRequestMethod("GET");
    184             connection.connect();
     179                connection = HttpClient.create(capabilitiesUrl).connect();
     180            }
    185181
    186182            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
     
    190186
    191187            try {
    192                 Capabilities.CapabilitiesParser.parse(new InputSource(connection.getInputStream()));
     188                Capabilities.CapabilitiesParser.parse(new InputSource(connection.getContent()));
    193189            } catch (SAXException | ParserConfigurationException e) {
    194190                Main.warn(e.getMessage());
  • trunk/src/org/openstreetmap/josm/io/OsmApi.java

    r9078 r9172  
    55import static org.openstreetmap.josm.tools.I18n.trn;
    66
    7 import java.io.BufferedReader;
    8 import java.io.BufferedWriter;
    97import java.io.IOException;
    10 import java.io.InputStream;
    11 import java.io.InputStreamReader;
    12 import java.io.OutputStream;
    13 import java.io.OutputStreamWriter;
    148import java.io.PrintWriter;
    159import java.io.StringReader;
     
    4236import org.openstreetmap.josm.io.Capabilities.CapabilitiesParser;
    4337import org.openstreetmap.josm.tools.CheckParameterUtil;
     38import org.openstreetmap.josm.tools.HttpClient;
    4439import org.openstreetmap.josm.tools.Utils;
    4540import org.openstreetmap.josm.tools.XmlParsingException;
     
    617612    protected final String sendRequest(String requestMethod, String urlSuffix, String requestBody, ProgressMonitor monitor,
    618613            boolean doAuthenticate, boolean fastFail) throws OsmTransferException {
    619         StringBuilder responseBody = new StringBuilder();
    620614        int retries = fastFail ? 0 : getMaxRetries();
    621615
     
    623617            try {
    624618                url = new URL(new URL(getBaseUrl()), urlSuffix);
    625                 Main.info(requestMethod + ' ' + url + "... ");
    626                 Main.debug(requestBody);
    627                 // fix #5369, see http://www.tikalk.com/java/forums/httpurlconnection-disable-keep-alive
    628                 activeConnection = Utils.openHttpConnection(url, false);
    629                 activeConnection.setConnectTimeout(fastFail ? 1000 : Main.pref.getInteger("socket.timeout.connect", 15)*1000);
     619                final HttpClient client = HttpClient.create(url, requestMethod).keepAlive(false);
    630620                if (fastFail) {
    631                     activeConnection.setReadTimeout(1000);
     621                    client.setReadTimeout(1000);
    632622                }
    633                 activeConnection.setRequestMethod(requestMethod);
    634623                if (doAuthenticate) {
    635                     addAuth(activeConnection);
     624                    addAuth(client);
    636625                }
    637626
    638627                if ("PUT".equals(requestMethod) || "POST".equals(requestMethod) || "DELETE".equals(requestMethod)) {
    639                     activeConnection.setDoOutput(true);
    640                     activeConnection.setRequestProperty("Content-type", "text/xml");
    641                     try (OutputStream out = activeConnection.getOutputStream()) {
    642                         // It seems that certain bits of the Ruby API are very unhappy upon
    643                         // receipt of a PUT/POST message without a Content-length header,
    644                         // even if the request has no payload.
    645                         // Since Java will not generate a Content-length header unless
    646                         // we use the output stream, we create an output stream for PUT/POST
    647                         // even if there is no payload.
    648                         if (requestBody != null) {
    649                             try (BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8))) {
    650                                 bwr.write(requestBody);
    651                                 bwr.flush();
    652                             }
    653                         }
    654                     }
     628                    // It seems that certain bits of the Ruby API are very unhappy upon
     629                    // receipt of a PUT/POST message without a Content-length header,
     630                    // even if the request has no payload.
     631                    // Since Java will not generate a Content-length header unless
     632                    // we use the output stream, we create an output stream for PUT/POST
     633                    // even if there is no payload.
     634                    client.setRequestBody(requestBody.getBytes(StandardCharsets.UTF_8));
    655635                }
    656636
    657                 activeConnection.connect();
     637                activeConnection = client.connect();
    658638                Main.info(activeConnection.getResponseMessage());
    659639                int retCode = activeConnection.getResponseCode();
     
    667647                }
    668648
    669                 // populate return fields.
    670                 responseBody.setLength(0);
    671 
    672                 // If the API returned an error code like 403 forbidden, getInputStream will fail with an IOException.
    673                 InputStream i = getConnectionStream();
    674                 if (i != null) {
    675                     // the input stream can be null if both the input and the error stream
    676                     // are null. Seems to be the case if the OSM server replies a 401 Unauthorized, see #3887.
    677                     String s;
    678                     try (BufferedReader in = new BufferedReader(new InputStreamReader(i, StandardCharsets.UTF_8))) {
    679                         while ((s = in.readLine()) != null) {
    680                             responseBody.append(s);
    681                             responseBody.append('\n');
    682                         }
    683                     }
    684                 }
     649                final String responseBody = activeConnection.fetchContent();
     650
    685651                String errorHeader = null;
    686652                // Look for a detailed error message from the server
     
    693659                activeConnection.disconnect();
    694660
    695                 if (Main.isDebugEnabled()) {
    696                     Main.debug("RESPONSE: "+ activeConnection.getHeaderFields());
    697                 }
    698 
    699661                errorHeader = errorHeader == null ? null : errorHeader.trim();
    700                 String errorBody = responseBody.length() == 0 ? null : responseBody.toString().trim();
     662                String errorBody = responseBody.length() == 0 ? null : responseBody.trim();
    701663                switch(retCode) {
    702664                case HttpURLConnection.HTTP_OK:
    703                     return responseBody.toString();
     665                    return responseBody;
    704666                case HttpURLConnection.HTTP_GONE:
    705667                    throw new OsmApiPrimitiveGoneException(errorHeader, errorBody);
     
    729691    }
    730692
    731     private InputStream getConnectionStream() {
    732         try {
    733             return activeConnection.getInputStream();
    734         } catch (IOException ioe) {
    735             Main.warn(ioe);
    736             return activeConnection.getErrorStream();
    737         }
    738     }
    739 
    740693    /**
    741694     * Replies the API capabilities.
  • trunk/src/org/openstreetmap/josm/io/OsmConnection.java

    r8846 r9172  
    55
    66import java.net.Authenticator.RequestorType;
    7 import java.net.HttpURLConnection;
    87import java.nio.ByteBuffer;
    98import java.nio.CharBuffer;
     
    1918import org.openstreetmap.josm.io.auth.CredentialsManager;
    2019import org.openstreetmap.josm.tools.Base64;
     20import org.openstreetmap.josm.tools.HttpClient;
    2121
    2222import oauth.signpost.OAuthConsumer;
     
    3131public class OsmConnection {
    3232    protected boolean cancel;
    33     protected HttpURLConnection activeConnection;
     33    protected HttpClient.Response activeConnection;
    3434    protected OAuthParameters oauthParameters;
    35 
    36     /**
    37      * Initialize the http defaults and the authenticator.
    38      */
    39     static {
    40         try {
    41             HttpURLConnection.setFollowRedirects(true);
    42         } catch (SecurityException e) {
    43             Main.error(e);
    44         }
    45     }
    4635
    4736    /**
     
    5039    public void cancel() {
    5140        cancel = true;
    52         synchronized (this) {
    53             if (activeConnection != null) {
    54                 activeConnection.setConnectTimeout(100);
    55                 activeConnection.setReadTimeout(100);
    56             }
    57         }
    58         try {
    59             Thread.sleep(100);
    60         } catch (InterruptedException ex) {
    61             Main.warn("InterruptedException in "+getClass().getSimpleName()+" during cancel");
    62         }
    63 
    6441        synchronized (this) {
    6542            if (activeConnection != null) {
     
    7552     * @throws OsmTransferException if something went wrong. Check for nested exceptions
    7653     */
    77     protected void addBasicAuthorizationHeader(HttpURLConnection con) throws OsmTransferException {
     54    protected void addBasicAuthorizationHeader(HttpClient con) throws OsmTransferException {
    7855        CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
    7956        CredentialsAgentResponse response;
     
    9875            try {
    9976                ByteBuffer bytes = encoder.encode(CharBuffer.wrap(token));
    100                 con.addRequestProperty("Authorization", "Basic "+Base64.encode(bytes));
     77                con.setHeader("Authorization", "Basic "+Base64.encode(bytes));
    10178            } catch (CharacterCodingException e) {
    10279                throw new OsmTransferException(e);
     
    11390     * @throws OsmTransferException if signing fails
    11491     */
    115     protected void addOAuthAuthorizationHeader(HttpURLConnection connection) throws OsmTransferException {
     92    protected void addOAuthAuthorizationHeader(HttpClient connection) throws OsmTransferException {
    11693        if (oauthParameters == null) {
    11794            oauthParameters = OAuthParameters.createFromPreferences(Main.pref);
     
    129106    }
    130107
    131     protected void addAuth(HttpURLConnection connection) throws OsmTransferException {
     108    protected void addAuth(HttpClient connection) throws OsmTransferException {
    132109        String authMethod = Main.pref.get("osm-server.auth-method", "basic");
    133110        if ("basic".equals(authMethod)) {
  • trunk/src/org/openstreetmap/josm/io/OsmServerReader.java

    r9078 r9172  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    6 import java.io.BufferedReader;
    7 import java.io.IOException;
    86import java.io.InputStream;
    9 import java.io.InputStreamReader;
    107import java.net.HttpURLConnection;
    118import java.net.MalformedURLException;
    129import java.net.URL;
    13 import java.nio.charset.StandardCharsets;
    1410import java.util.List;
    15 import java.util.Map;
    16 import java.util.zip.GZIPInputStream;
    17 import java.util.zip.Inflater;
    18 import java.util.zip.InflaterInputStream;
    1911
    2012import org.openstreetmap.josm.Main;
     
    2315import org.openstreetmap.josm.data.osm.DataSet;
    2416import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    25 import org.openstreetmap.josm.tools.Utils;
     17import org.openstreetmap.josm.tools.HttpClient;
    2618
    2719/**
     
    129121                throw new OsmTransferException(e);
    130122            }
    131             try {
    132                 // fix #7640, see http://www.tikalk.com/java/forums/httpurlconnection-disable-keep-alive
    133                 activeConnection = Utils.openHttpConnection(url, false);
    134             } catch (Exception e) {
    135                 throw new OsmTransferException(tr("Failed to open connection to API {0}.", url.toExternalForm()), e);
    136             }
    137             Utils.setupURLConnection(activeConnection);
    138             if (cancel) {
    139                 activeConnection.disconnect();
    140                 return null;
    141             }
    142 
     123
     124            final HttpClient client = HttpClient.create(url);
     125            client.setReasonForRequest(reason);
    143126            if (doAuthenticate) {
    144                 addAuth(activeConnection);
     127                addAuth(client);
    145128            }
    146129            if (cancel)
    147130                throw new OsmTransferCanceledException("Operation canceled");
    148             if (Main.pref.getBoolean("osm-server.use-compression", true)) {
    149                 activeConnection.setRequestProperty("Accept-Encoding", "gzip, deflate");
    150             }
    151131
    152132            try {
    153                 if (reason != null && !reason.isEmpty()) {
    154                     Main.info("GET " + url + " (" + reason + ')');
    155                 } else {
    156                     Main.info("GET " + url);
    157                 }
    158                 activeConnection.connect();
     133                activeConnection = client.connect();
    159134            } catch (Exception e) {
    160135                Main.error(e);
     
    165140            }
    166141            try {
    167                 if (Main.isDebugEnabled()) {
    168                     Main.debug("RESPONSE: "+activeConnection.getHeaderFields());
    169                 }
    170142                if (activeConnection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED)
    171143                    throw new OsmApiException(HttpURLConnection.HTTP_UNAUTHORIZED, null, null);
     
    174146                    throw new OsmTransferCanceledException("Proxy Authentication Required");
    175147
    176                 String encoding = activeConnection.getContentEncoding();
    177148                if (activeConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
    178149                    String errorHeader = activeConnection.getHeaderField("Error");
    179                     StringBuilder errorBody = new StringBuilder();
    180                     try {
    181                         InputStream i = fixEncoding(activeConnection.getErrorStream(), encoding);
    182                         if (i != null) {
    183                             BufferedReader in = new BufferedReader(new InputStreamReader(i, StandardCharsets.UTF_8));
    184                             String s;
    185                             while ((s = in.readLine()) != null) {
    186                                 errorBody.append(s);
    187                                 errorBody.append('\n');
    188                             }
    189                         }
    190                     } catch (Exception e) {
    191                         errorBody.append(tr("Reading error text failed."));
    192                     }
    193 
    194                     throw new OsmApiException(activeConnection.getResponseCode(), errorHeader, errorBody.toString(), url.toString());
     150                    final String errorBody = activeConnection.fetchContent();
     151                    throw new OsmApiException(activeConnection.getResponseCode(), errorHeader, errorBody, url.toString());
    195152                }
    196153
    197154                InputStream in = new ProgressInputStream(activeConnection, progressMonitor);
    198155                if (uncompressAccordingToContentDisposition) {
    199                     in = uncompressAccordingToContentDisposition(in, activeConnection.getHeaderFields());
     156                    activeConnection.uncompressAccordingToContentDisposition(true);
    200157                }
    201                 return fixEncoding(in, encoding);
     158                return in;
    202159            } catch (OsmTransferException e) {
    203160                throw e;
     
    210167    }
    211168
    212     private static InputStream fixEncoding(InputStream stream, String encoding) throws IOException {
    213         if ("gzip".equalsIgnoreCase(encoding)) {
    214             stream = new GZIPInputStream(stream);
    215         } else if ("deflate".equalsIgnoreCase(encoding)) {
    216             stream = new InflaterInputStream(stream, new Inflater(true));
    217         }
    218         return stream;
    219     }
    220 
    221     private InputStream uncompressAccordingToContentDisposition(InputStream stream, Map<String, List<String>> headerFields) throws IOException {
    222         List<String> field = headerFields.get("Content-Disposition");
    223         if (field != null && field.toString().contains(".gz\"")) {
    224             return Compression.GZIP.getUncompressedInputStream(stream);
    225         } else if (field != null && field.toString().contains(".bz2\"")) {
    226             return Compression.BZIP2.getUncompressedInputStream(stream);
    227         } else {
    228             return stream;
    229         }
    230     }
    231 
    232169    /**
    233170     * Download OSM files from somewhere
  • trunk/src/org/openstreetmap/josm/io/ProgressInputStream.java

    r8840 r9172  
    1010import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
    1111import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     12import org.openstreetmap.josm.tools.HttpClient;
    1213
    1314/**
     
    1819
    1920    private final InputStream in;
     21    private final long size;
    2022    private int readSoFar;
    2123    private int lastDialogUpdate;
    22     private boolean sizeKnown;
    23     private final URLConnection connection;
    2424    private final ProgressMonitor progressMonitor;
    2525
     26    public ProgressInputStream(InputStream in, long size, ProgressMonitor progressMonitor) {
     27        if (progressMonitor == null) {
     28            progressMonitor = NullProgressMonitor.INSTANCE;
     29        }
     30        this.in = in;
     31        this.size = size;
     32        this.progressMonitor = progressMonitor;
     33        progressMonitor.beginTask(tr("Contacting OSM Server..."), 1);
     34        progressMonitor.indeterminateSubTask(null);
     35        initProgressMonitor();
     36    }
     37
     38    public ProgressInputStream(HttpClient.Response response, ProgressMonitor progressMonitor) throws IOException {
     39        this(response.getContent(), response.getContentLength(), progressMonitor);
     40    }
     41
    2642    public ProgressInputStream(URLConnection con, ProgressMonitor progressMonitor) throws OsmTransferException {
    27         this.connection = con;
    2843        if (progressMonitor == null) {
    2944            progressMonitor = NullProgressMonitor.INSTANCE;
     
    3550        try {
    3651            this.in = con.getInputStream();
     52            this.size = con.getContentLength();
    3753        } catch (IOException e) {
    3854            progressMonitor.finishTask();
     
    4157            throw new OsmTransferException(e);
    4258        }
     59        initProgressMonitor();
     60    }
    4361
    44         updateSize();
    45         if (!sizeKnown) {
     62    protected void initProgressMonitor() {
     63        if (size > 0) {
     64            progressMonitor.subTask(tr("Downloading OSM data..."));
     65            progressMonitor.setTicksCount((int) size);
     66        } else {
    4667            progressMonitor.indeterminateSubTask(tr("Downloading OSM data..."));
    4768        }
     
    82103    private void advanceTicker(int amount) {
    83104        readSoFar += amount;
    84         updateSize();
    85105
    86106        if (readSoFar / 1024 != lastDialogUpdate) {
    87107            lastDialogUpdate++;
    88             if (sizeKnown) {
     108            if (size > 0) {
    89109                progressMonitor.setTicks(readSoFar);
    90110            }
     
    92112        }
    93113    }
    94 
    95     private void updateSize() {
    96         if (!sizeKnown && connection.getContentLength() > 0) {
    97             sizeKnown = true;
    98             progressMonitor.subTask(tr("Downloading OSM data..."));
    99             progressMonitor.setTicksCount(connection.getContentLength());
    100         }
    101     }
    102114}
  • trunk/src/org/openstreetmap/josm/tools/HttpClient.java

    r9171 r9172  
    1111import java.net.HttpURLConnection;
    1212import java.net.URL;
     13import java.util.List;
    1314import java.util.Map;
    1415import java.util.Scanner;
    15 import java.util.concurrent.ConcurrentHashMap;
     16import java.util.TreeMap;
     17import java.util.regex.Matcher;
     18import java.util.regex.Pattern;
    1619import java.util.zip.GZIPInputStream;
    1720
     
    3033    private int connectTimeout = Main.pref.getInteger("socket.timeout.connect", 15) * 1000;
    3134    private int readTimeout = Main.pref.getInteger("socket.timeout.read", 30) * 1000;
    32     private String accept;
    33     private String contentType;
    34     private String acceptEncoding = "gzip";
    35     private long contentLength;
    3635    private byte[] requestBody;
    3736    private long ifModifiedSince;
    38     private final Map<String, String> headers = new ConcurrentHashMap<>();
     37    private final Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
    3938    private int maxRedirects = Main.pref.getInteger("socket.maxredirects", 5);
    4039    private boolean useCache;
    41     private boolean keepAlive;
     40    private String reasonForRequest;
    4241
    4342    private HttpClient(URL url, String requestMethod) {
    4443        this.url = url;
    4544        this.requestMethod = requestMethod;
     45        this.headers.put("Accept-Encoding", "gzip");
    4646    }
    4747
    4848    public Response connect() throws IOException {
    4949        final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
     50        connection.setRequestMethod(requestMethod);
    5051        connection.setRequestProperty("User-Agent", Version.getInstance().getFullAgentString());
    5152        connection.setConnectTimeout(connectTimeout);
    5253        connection.setReadTimeout(readTimeout);
    53         if (accept != null) {
    54             connection.setRequestProperty("Accept", accept);
    55         }
    56         if (contentType != null) {
    57             connection.setRequestProperty("Content-Type", contentType);
    58         }
    59         if (acceptEncoding != null) {
    60             connection.setRequestProperty("Accept-Encoding", acceptEncoding);
    61         }
    62         if (contentLength > 0) {
    63             connection.setRequestProperty("Content-Length", String.valueOf(contentLength));
    64         }
     54        connection.setInstanceFollowRedirects(maxRedirects > 0);
     55        if (ifModifiedSince > 0) {
     56            connection.setIfModifiedSince(ifModifiedSince);
     57        }
     58        connection.setUseCaches(useCache);
     59        if (!useCache) {
     60            connection.setRequestProperty("Cache-Control", "no-cache");
     61        }
     62        for (Map.Entry<String, String> header : headers.entrySet()) {
     63            if (header.getValue() != null) {
     64                connection.setRequestProperty(header.getKey(), header.getValue());
     65            }
     66        }
     67
    6568        if ("PUT".equals(requestMethod) || "POST".equals(requestMethod) || "DELETE".equals(requestMethod)) {
     69            headers.put("Content-Length", String.valueOf(requestBody.length));
    6670            connection.setDoOutput(true);
    6771            try (OutputStream out = new BufferedOutputStream(connection.getOutputStream())) {
    6872                out.write(requestBody);
    6973            }
    70         }
    71         if (ifModifiedSince > 0) {
    72             connection.setIfModifiedSince(ifModifiedSince);
    73         }
    74         connection.setUseCaches(useCache);
    75         if (!useCache) {
    76             connection.setRequestProperty("Cache-Control", "no-cache");
    77         }
    78         if (!keepAlive) {
    79             connection.setRequestProperty("Connection", "close");
    80         }
    81         for (Map.Entry<String, String> header : headers.entrySet()) {
    82             connection.setRequestProperty(header.getKey(), header.getValue());
    8374        }
    8475
     
    8778            try {
    8879                connection.connect();
    89                 Main.info("{0} {1} => {2}", requestMethod, url, connection.getResponseCode());
     80                if (reasonForRequest != null && "".equalsIgnoreCase(reasonForRequest)) {
     81                    Main.info("{0} {1} ({2}) -> {3}", requestMethod, url, reasonForRequest, connection.getResponseCode());
     82                } else {
     83                    Main.info("{0} {1} -> {2}", requestMethod, url, connection.getResponseCode());
     84                }
     85                if (Main.isDebugEnabled()) {
     86                    Main.debug("RESPONSE: " + connection.getHeaderFields());
     87                }
    9088            } catch (IOException e) {
    9189                //noinspection ThrowableResultOfMethodCallIgnored
     
    105103                    Main.info(tr("Download redirected to ''{0}''", redirectLocation));
    106104                    return connect();
    107                 } else {
     105                } else if (maxRedirects == 0) {
    108106                    String msg = tr("Too many redirects to the download URL detected. Aborting.");
    109107                    throw new IOException(msg);
     
    126124        private final HttpURLConnection connection;
    127125        private final int responseCode;
     126        private final String responseMessage;
    128127        private boolean uncompress;
     128        private boolean uncompressAccordingToContentDisposition;
    129129
    130130        private Response(HttpURLConnection connection) throws IOException {
     131            CheckParameterUtil.ensureParameterNotNull(connection, "connection");
    131132            this.connection = connection;
    132133            this.responseCode = connection.getResponseCode();
     134            this.responseMessage = connection.getResponseMessage();
    133135        }
    134136
     
    144146        }
    145147
     148        public Response uncompressAccordingToContentDisposition(boolean uncompressAccordingToContentDisposition) {
     149            this.uncompressAccordingToContentDisposition = uncompressAccordingToContentDisposition;
     150            return this;
     151        }
     152
     153        /**
     154         * @see HttpURLConnection#getURL()
     155         */
     156        public URL getURL() {
     157            return connection.getURL();
     158        }
     159
     160        /**
     161         * @see HttpURLConnection#getRequestMethod()
     162         */
     163        public String getRequestMethod() {
     164            return connection.getRequestMethod();
     165        }
     166
    146167        /**
    147168         * Returns an input stream that reads from this HTTP connection, or,
    148169         * error stream if the connection failed but the server sent useful data.
     170         *
     171         * Note: the return value can be null, if both the input and the error stream are null.
     172         * Seems to be the case if the OSM server replies a 401 Unauthorized, see #3887
    149173         *
    150174         * @see HttpURLConnection#getInputStream()
     
    160184            in = "gzip".equalsIgnoreCase(getContentEncoding()) ? new GZIPInputStream(in) : in;
    161185            if (uncompress) {
    162                 return Compression.forContentType(getContentType()).getUncompressedInputStream(in);
    163             } else {
    164                 return in;
    165             }
     186                final String contentType = getContentType();
     187                Main.debug("Uncompressing input stream according to Content-Type header: {0}", contentType);
     188                in = Compression.forContentType(contentType).getUncompressedInputStream(in);
     189            }
     190            if (uncompressAccordingToContentDisposition) {
     191                final String contentDisposition = getHeaderField("Content-Disposition");
     192                final Matcher matcher = Pattern.compile("filename=\"([^\"]+)\"").matcher(contentDisposition);
     193                if (matcher.find()) {
     194                    Main.debug("Uncompressing input stream according to Content-Disposition header: {0}", contentDisposition);
     195                    in = Compression.byExtension(matcher.group(1)).getUncompressedInputStream(in);
     196                }
     197            }
     198            return in;
    166199        }
    167200
     
    183216         */
    184217        public String fetchContent() throws IOException {
    185             try (Scanner scanner = new Scanner(getContentReader())) {
    186                 return scanner.useDelimiter("\\A").next();
     218            try (Scanner scanner = new Scanner(getContentReader()).useDelimiter("\\A")) {
     219                return scanner.hasNext() ? scanner.next() : "";
    187220            }
    188221        }
     
    198231
    199232        /**
     233         * Gets the response message from this HTTP connection.
     234         *
     235         * @see HttpURLConnection#getResponseMessage()
     236         */
     237        public String getResponseMessage() {
     238            return responseMessage;
     239        }
     240
     241        /**
    200242         * Returns the {@code Content-Encoding} header.
    201243         */
     
    219261
    220262        /**
     263         * @see HttpURLConnection#getHeaderField(String)
     264         */
     265        public String getHeaderField(String name) {
     266            return connection.getHeaderField(name);
     267        }
     268
     269        /**
     270         * @see HttpURLConnection#getHeaderFields()
     271         */
     272        public List<String> getHeaderFields(String name) {
     273            return connection.getHeaderFields().get(name);
     274        }
     275
     276        /**
    221277         * @see HttpURLConnection#disconnect()
    222278         */
    223279        public void disconnect() {
     280            // TODO is this block necessary for disconnecting?
     281            // Fix upload aborts - see #263
     282            connection.setConnectTimeout(100);
     283            connection.setReadTimeout(100);
     284            try {
     285                Thread.sleep(100);
     286            } catch (InterruptedException ex) {
     287                Main.warn("InterruptedException in " + getClass().getSimpleName() + " during cancel");
     288            }
     289
    224290            connection.disconnect();
    225291        }
     
    239305     * Creates a new instance for the given URL and a {@code GET} request
    240306     *
    241      * @param url           the URL
     307     * @param url the URL
    242308     * @param requestMethod the HTTP request method to perform when calling
    243309     * @return a new instance
     
    245311    public static HttpClient create(URL url, String requestMethod) {
    246312        return new HttpClient(url, requestMethod);
     313    }
     314
     315    /**
     316     * Returns the URL set for this connection.
     317     * @see #create(URL)
     318     * @see #create(URL, String)
     319     */
     320    public URL getURL() {
     321        return url;
     322    }
     323
     324    /**
     325     * Returns the request method set for this connection.
     326     * @see #create(URL, String)
     327     */
     328    public String getRequestMethod() {
     329        return requestMethod;
     330    }
     331
     332    /**
     333     * Returns the set value for the given {@code header}.
     334     */
     335    public String getRequestHeader(String header) {
     336        return headers.get(header);
    247337    }
    248338
     
    268358     */
    269359    public HttpClient keepAlive(boolean keepAlive) {
    270         this.keepAlive = keepAlive;
    271         return this;
     360        return setHeader("Connection", keepAlive ? null : "close");
    272361    }
    273362
     
    297386     */
    298387    public HttpClient setAccept(String accept) {
    299         this.accept = accept;
    300         return this;
    301     }
    302 
    303     /**
    304      * Sets the {@code Content-Type} header.
    305      *
    306      * @return {@code this}
    307      */
    308     public HttpClient setContentType(String contentType) {
    309         this.contentType = contentType;
    310         return this;
    311     }
    312 
    313     /**
    314      * Sets the {@code Accept-Encoding} header.
    315      *
    316      * @return {@code this}
    317      */
    318     public HttpClient setAcceptEncoding(String acceptEncoding) {
    319         this.acceptEncoding = acceptEncoding;
    320         return this;
    321     }
    322 
    323     /**
    324      * Sets the {@code Content-Length} header for {@code PUT}/{@code POST} requests.
    325      *
    326      * @return {@code this}
    327      */
    328     public HttpClient setContentLength(long contentLength) {
    329         this.contentLength = contentLength;
    330         return this;
     388        return setHeader("Accept", accept);
    331389    }
    332390
     
    354412     * Sets the maximum number of redirections to follow.
    355413     *
     414     * Set {@code maxRedirects} to {@code -1} in order to ignore redirects, i.e.,
     415     * to not throw an {@link IOException} in {@link #connect()}.
     416     *
    356417     * @return {@code this}
    357418     */
     
    378439    public HttpClient setHeaders(Map<String, String> headers) {
    379440        this.headers.putAll(headers);
     441        return this;
     442    }
     443
     444    /**
     445     * Sets a reason to show on console. Can be {@code null} if no reason is given.
     446     */
     447    public HttpClient setReasonForRequest(String reasonForRequest) {
     448        this.reasonForRequest = reasonForRequest;
    380449        return this;
    381450    }
Note: See TracChangeset for help on using the changeset viewer.