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/io
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • 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}
Note: See TracChangeset for help on using the changeset viewer.