diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/io/NetworkUtils.java b/OsmAnd-java/src/main/java/net/osmand/osm/io/NetworkUtils.java index 8880184f96..d684b148a4 100644 --- a/OsmAnd-java/src/main/java/net/osmand/osm/io/NetworkUtils.java +++ b/OsmAnd-java/src/main/java/net/osmand/osm/io/NetworkUtils.java @@ -4,6 +4,7 @@ import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Response; import com.github.scribejava.core.model.Verb; import net.osmand.PlatformUtil; +import net.osmand.osm.oauth.IInputStreamHttpClient; import net.osmand.osm.oauth.OsmOAuthAuthorizationClient; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; @@ -72,12 +73,13 @@ public class NetworkUtils { HttpURLConnection conn; if (client != null && client.isValidToken()){ OAuthRequest req = new OAuthRequest(Verb.POST, urlText); - req.setPayload(prepareStream(formName,fileToUpload,gzip)); client.getService().signRequest(client.getAccessToken(), req); req.addHeader("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); try { + IInputStreamHttpClient service = (IInputStreamHttpClient) client.getService(); + service.execute(service.getUserAgent(), req.getHeaders(), req.getVerb(), req.getCompleteUrl(), fileToUpload); Response r = client.getService().execute(req); - if(r.getCode() != 200){ + if (r.getCode() != 200) { return r.getBody(); } return null; @@ -100,7 +102,29 @@ public class NetworkUtils { conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); //$NON-NLS-1$ //$NON-NLS-2$ conn.setRequestProperty("User-Agent", "OsmAnd"); //$NON-NLS-1$ //$NON-NLS-2$ OutputStream ous = conn.getOutputStream(); - ous.write(prepareStream(formName,fileToUpload,gzip)); + ous.write(("--" + BOUNDARY + "\r\n").getBytes()); + String filename = fileToUpload.getName(); + if (gzip) { + filename += ".gz"; + } + ous.write(("content-disposition: form-data; name=\"" + formName + "\"; filename=\"" + filename + "\"\r\n").getBytes()); //$NON-NLS-1$ //$NON-NLS-2$ + ous.write(("Content-Type: application/octet-stream\r\n\r\n").getBytes()); //$NON-NLS-1$ + InputStream fis = new FileInputStream(fileToUpload); + BufferedInputStream bis = new BufferedInputStream(fis, 20 * 1024); + ous.flush(); + if (gzip) { + GZIPOutputStream gous = new GZIPOutputStream(ous, 1024); + Algorithms.streamCopy(bis, gous); + gous.flush(); + gous.finish(); + } else { + Algorithms.streamCopy(bis, ous); + } + ous.write(("\r\n--" + BOUNDARY + "--\r\n").getBytes()); //$NON-NLS-1$ //$NON-NLS-2$ + ous.flush(); + Algorithms.closeStream(bis); + Algorithms.closeStream(ous); + log.info("Finish uploading file " + fileToUpload.getName()); log.info("Response code and message : " + conn.getResponseCode() + " " + conn.getResponseMessage()); if(conn.getResponseCode() != 200){ @@ -131,46 +155,6 @@ public class NetworkUtils { } } - private static byte[] prepareStream(String formName, File fileToUpload, boolean gzip) { - try { - ByteArrayOutputStream ous = new ByteArrayOutputStream(); -// for (String key : additionalMapData.keySet()) { -// ous.write(("--" + BOUNDARY + "\r\n").getBytes()); -// ous.write(("content-disposition: form-data; name=\"" + key + "\"\r\n").getBytes()); //$NON-NLS-1$ //$NON-NLS-2$ -// ous.write((additionalMapData.get(key) + "\r\n").getBytes()); -// } - ous.write(("--" + BOUNDARY + "\r\n").getBytes()); - - String filename = fileToUpload.getName(); - if (gzip) { - filename += ".gz"; - } - ous.write(("content-disposition: form-data; name=\"" + formName + "\"; filename=\"" + filename + "\"\r\n").getBytes()); //$NON-NLS-1$ //$NON-NLS-2$ - ous.write(("Content-Type: application/octet-stream\r\n\r\n").getBytes()); //$NON-NLS-1$ - InputStream fis = new FileInputStream(fileToUpload); - BufferedInputStream bis = new BufferedInputStream(fis, 20 * 1024); - ous.flush(); - if (gzip) { - GZIPOutputStream gous = new GZIPOutputStream(ous, 1024); - Algorithms.streamCopy(bis, gous); - gous.flush(); - gous.finish(); - gous.close(); - } else { - Algorithms.streamCopy(bis, ous); - } - - ous.write(("\r\n--" + BOUNDARY + "--\r\n").getBytes()); //$NON-NLS-1$ //$NON-NLS-2$ - ous.flush(); - Algorithms.closeStream(bis); - Algorithms.closeStream(ous); - return ous.toByteArray(); - } catch (IOException e) { - log.error(e); - } - return new byte[0]; - } - public static void setProxy(String host, int port) { if(host != null && port > 0) { InetSocketAddress isa = new InetSocketAddress(host, port); @@ -179,7 +163,6 @@ public class NetworkUtils { proxy = null; } } - public static Proxy getProxy() { return proxy; } diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/oauth/IInputStreamHttpClient.java b/OsmAnd-java/src/main/java/net/osmand/osm/oauth/IInputStreamHttpClient.java new file mode 100644 index 0000000000..c42182fdd7 --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/osm/oauth/IInputStreamHttpClient.java @@ -0,0 +1,7 @@ +package net.osmand.osm.oauth; + +import com.github.scribejava.core.httpclient.HttpClient; + +public interface IInputStreamHttpClient extends HttpClient { + String getUserAgent(); +} diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/oauth/OsmAndJDKHttpClient.java b/OsmAnd-java/src/main/java/net/osmand/osm/oauth/OsmAndJDKHttpClient.java new file mode 100644 index 0000000000..c7d8f88446 --- /dev/null +++ b/OsmAnd-java/src/main/java/net/osmand/osm/oauth/OsmAndJDKHttpClient.java @@ -0,0 +1,260 @@ +package net.osmand.osm.oauth; + +import com.github.scribejava.core.exceptions.OAuthException; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.jdk.JDKHttpClientConfig; +import com.github.scribejava.core.httpclient.jdk.JDKHttpFuture; +import com.github.scribejava.core.httpclient.multipart.MultipartPayload; +import com.github.scribejava.core.httpclient.multipart.MultipartUtils; +import com.github.scribejava.core.model.*; +import net.osmand.util.Algorithms; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class OsmAndJDKHttpClient implements IInputStreamHttpClient { + private static final String BOUNDARY = "CowMooCowMooCowCowCow"; + private final JDKHttpClientConfig config; + + public OsmAndJDKHttpClient() { + this(JDKHttpClientConfig.defaultConfig()); + } + + public OsmAndJDKHttpClient(JDKHttpClientConfig clientConfig) { + config = clientConfig; + } + + @Override + public void close() { + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + byte[] bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { + + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodyType.BYTE_ARRAY, bodyContents, callback, + converter); + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + MultipartPayload bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodyType.MULTIPART, bodyContents, callback, + converter); + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + String bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { + + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodyType.STRING, bodyContents, callback, + converter); + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + File bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodyType.STREAM, bodyContents, callback, + converter); + } + + private Future doExecuteAsync(String userAgent, Map headers, Verb httpVerb, + String completeUrl, BodyType bodyType, Object bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + try { + final Response response = doExecute(userAgent, headers, httpVerb, completeUrl, bodyType, bodyContents); + @SuppressWarnings("unchecked") + final T t = converter == null ? (T) response : converter.convert(response); + if (callback != null) { + callback.onCompleted(t); + } + return new JDKHttpFuture<>(t); + } catch (IOException | RuntimeException e) { + if (callback != null) { + callback.onThrowable(e); + } + return new JDKHttpFuture<>(e); + } + } + + @Override + public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, + byte[] bodyContents) throws InterruptedException, ExecutionException, IOException { + return doExecute(userAgent, headers, httpVerb, completeUrl, BodyType.BYTE_ARRAY, bodyContents); + } + + @Override + public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, + MultipartPayload multipartPayloads) throws InterruptedException, ExecutionException, IOException { + return doExecute(userAgent, headers, httpVerb, completeUrl, BodyType.MULTIPART, multipartPayloads); + } + + @Override + public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, + String bodyContents) throws InterruptedException, ExecutionException, IOException { + return doExecute(userAgent, headers, httpVerb, completeUrl, BodyType.STRING, bodyContents); + } + + @Override + public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, + File bodyContents) throws InterruptedException, ExecutionException, IOException { + return doExecute(userAgent, headers, httpVerb, completeUrl, BodyType.STREAM, bodyContents); + } + + private Response doExecute(String userAgent, Map headers, Verb httpVerb, String completeUrl, + BodyType bodyType, Object bodyContents) throws IOException { + final URL url = new URL(completeUrl); + final HttpURLConnection connection; + if (config.getProxy() == null) { + connection = (HttpURLConnection) url.openConnection(); + } else { + connection = (HttpURLConnection) url.openConnection(config.getProxy()); + } + connection.setInstanceFollowRedirects(config.isFollowRedirects()); + connection.setRequestMethod(httpVerb.name()); + if (config.getConnectTimeout() != null) { + connection.setConnectTimeout(config.getConnectTimeout()); + } + if (config.getReadTimeout() != null) { + connection.setReadTimeout(config.getReadTimeout()); + } + addHeaders(connection, headers, userAgent); + if (httpVerb.isPermitBody()) { + bodyType.setBody(connection, bodyContents, httpVerb.isRequiresBody()); + } + + try { + connection.connect(); + final int responseCode = connection.getResponseCode(); + return new Response(responseCode, connection.getResponseMessage(), parseHeaders(connection), + responseCode >= 200 && responseCode < 400 ? connection.getInputStream() + : connection.getErrorStream()); + } catch (UnknownHostException e) { + throw new OAuthException("The IP address of a host could not be determined.", e); + } + } + + @Override + public String getUserAgent() { + return "OsmandUserAgent"; + } + + private enum BodyType { + BYTE_ARRAY { + @Override + void setBody(HttpURLConnection connection, Object bodyContents, boolean requiresBody) throws IOException { + addBody(connection, (byte[]) bodyContents, requiresBody); + } + }, + STREAM { + @Override + void setBody(HttpURLConnection connection, Object bodyContents, boolean requiresBody) throws IOException { + addBody(connection, (File) bodyContents, requiresBody); + } + }, + MULTIPART { + @Override + void setBody(HttpURLConnection connection, Object bodyContents, boolean requiresBody) throws IOException { + addBody(connection, (MultipartPayload) bodyContents, requiresBody); + } + }, + STRING { + @Override + void setBody(HttpURLConnection connection, Object bodyContents, boolean requiresBody) throws IOException { + addBody(connection, ((String) bodyContents).getBytes(), requiresBody); + } + }; + abstract void setBody(HttpURLConnection connection, Object bodyContents, boolean requiresBody) + throws IOException; + } + + private static Map parseHeaders(HttpURLConnection conn) { + final Map headers = new HashMap<>(); + + for (Map.Entry> headerField : conn.getHeaderFields().entrySet()) { + final String key = headerField.getKey(); + final String value = headerField.getValue().get(0); + if ("Content-Encoding".equalsIgnoreCase(key)) { + headers.put("Content-Encoding", value); + } else { + headers.put(key, value); + } + } + return headers; + } + + private static void addHeaders(HttpURLConnection connection, Map headers, String userAgent) { + for (Map.Entry header : headers.entrySet()) { + connection.setRequestProperty(header.getKey(), header.getValue()); + } + + if (userAgent != null) { + connection.setRequestProperty(OAuthConstants.USER_AGENT_HEADER_NAME, userAgent); + } + } + + private static void addBody(HttpURLConnection connection, File file, boolean requiresBody) throws IOException { + if (requiresBody) { + String filename = file.getName(); + String formName = "file"; + final OutputStream ous = prepareConnectionForBodyAndGetOutputStream(connection, 20 * 1024); + ous.write(("--" + BOUNDARY+"\r\n").getBytes()); + InputStream stream = new FileInputStream(file); + ous.write(("content-disposition: form-data; name=\""+formName+"\"; filename=\"" + filename + "\"\r\n").getBytes()); //$NON-NLS-1$ //$NON-NLS-2$ + ous.write(("Content-Type: application/octet-stream\r\n\r\n").getBytes()); //$NON-NLS-1$ + BufferedInputStream bis = new BufferedInputStream(stream, 20 * 1024); + ous.flush(); + Algorithms.streamCopy(bis, ous); + ous.write(("\r\n--" + BOUNDARY + "--\r\n").getBytes()); //$NON-NLS-1$ //$NON-NLS-2$ + ous.flush(); + Algorithms.closeStream(bis); + Algorithms.closeStream(ous); + } + } + + private static void addBody(HttpURLConnection connection, byte[] content, boolean requiresBody) throws IOException { + final int contentLength = content.length; + if (requiresBody || contentLength > 0) { + final OutputStream outputStream = prepareConnectionForBodyAndGetOutputStream(connection, contentLength); + if (contentLength > 0) { + outputStream.write(content); + } + } + } + + private static void addBody(HttpURLConnection connection, MultipartPayload multipartPayload, boolean requiresBody) + throws IOException { + + for (Map.Entry header : multipartPayload.getHeaders().entrySet()) { + connection.setRequestProperty(header.getKey(), header.getValue()); + } + + if (requiresBody) { + final ByteArrayOutputStream os = MultipartUtils.getPayload(multipartPayload); + final int contentLength = os.size(); + final OutputStream outputStream = prepareConnectionForBodyAndGetOutputStream(connection, contentLength); + if (contentLength > 0) { + os.writeTo(outputStream); + } + } + } + + private static OutputStream prepareConnectionForBodyAndGetOutputStream(HttpURLConnection connection, + int contentLength) throws IOException { + connection.setRequestProperty(CONTENT_LENGTH, String.valueOf(contentLength)); + if (connection.getRequestProperty(CONTENT_TYPE) == null) { + connection.setRequestProperty(CONTENT_TYPE, DEFAULT_CONTENT_TYPE); + } + connection.setDoOutput(true); + return connection.getOutputStream(); + } +} \ No newline at end of file diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/oauth/OsmOAuthAuthorizationClient.java b/OsmAnd-java/src/main/java/net/osmand/osm/oauth/OsmOAuthAuthorizationClient.java index 5726855f72..709dbaa275 100644 --- a/OsmAnd-java/src/main/java/net/osmand/osm/oauth/OsmOAuthAuthorizationClient.java +++ b/OsmAnd-java/src/main/java/net/osmand/osm/oauth/OsmOAuthAuthorizationClient.java @@ -4,6 +4,8 @@ package net.osmand.osm.oauth; import com.github.scribejava.core.builder.ServiceBuilder; import com.github.scribejava.core.builder.api.DefaultApi10a; import com.github.scribejava.core.builder.api.OAuth1SignatureType; +import com.github.scribejava.core.httpclient.jdk.JDKHttpClient; +import com.github.scribejava.core.httpclient.jdk.JDKHttpClientConfig; import com.github.scribejava.core.model.*; import com.github.scribejava.core.oauth.OAuth10aService; import net.osmand.PlatformUtil; @@ -26,6 +28,7 @@ public class OsmOAuthAuthorizationClient { public OsmOAuthAuthorizationClient(String key, String secret) { service = new ServiceBuilder(key) .apiSecret(secret) + .httpClient(new OsmAndJDKHttpClient(JDKHttpClientConfig.defaultConfig())) .callback("osmand-oauth://example.com/oauth") .build(new OsmApi()); }