-
Notifications
You must be signed in to change notification settings - Fork 113
Add support for apache http-client 4.5 #234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
d5c420a
Add support for apache-httpclient 4.5.
nitzanj cc32f0d
Add apache http-client 4.5 to travis.
nitzanj 7c5c8a1
Remove accidental comment copy-paste.
nitzanj b7952a2
Add support for apache-httpclient 4.5.
nitzanj 83c2a07
Add apache http-client 4.5 to travis.
nitzanj 4f25cd2
Remove accidental comment copy-paste.
nitzanj c289bd2
Add `oauth` support to the apache-http-client 4.5 strategy.
nitzanj c141094
Merge remote-tracking branch 'origin/feature/apache-httpclient45-supp…
nitzanj File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ env: | |
| - MODULE=http42 | ||
| - MODULE=http43 | ||
| - MODULE=http44 | ||
| - MODULE=http45 | ||
|
|
||
| branches: | ||
| except: | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| plugins { | ||
| id 'java-library' | ||
| id 'signing' | ||
| id 'maven-publish' | ||
| id 'io.codearte.nexus-staging' version '0.21.1' | ||
| id "de.marcphilipp.nexus-publish" version "0.4.0" | ||
| } | ||
|
|
||
| apply from: "../java_shared.gradle" | ||
|
|
||
| task ciTest( type: Test ) { | ||
| useJUnit { | ||
| excludeCategories 'com.cloudinary.test.TimeoutTest' | ||
| } | ||
| } | ||
|
|
||
| dependencies { | ||
| compile project(':cloudinary-core') | ||
| compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1' | ||
| compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.13' | ||
| compile group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.5.13' | ||
| testCompile project(':cloudinary-test-common') | ||
| testCompile group: 'org.hamcrest', name: 'java-hamcrest', version: '2.0.0.0' | ||
| testCompile group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.5' | ||
| testCompile group: 'junit', name: 'junit', version: '4.12' | ||
| } | ||
|
|
||
| if (hasProperty("ossrhPassword")) { | ||
|
|
||
| signing { | ||
| sign configurations.archives | ||
| } | ||
|
|
||
| nexusStaging { | ||
| packageGroup = group | ||
| username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" | ||
| password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" | ||
| } | ||
|
|
||
| publishing { | ||
| publications { | ||
| mavenJava(MavenPublication) { | ||
| from components.java | ||
| artifact sourcesJar | ||
| artifact javadocJar | ||
| pom { | ||
| name = 'Cloudinary Apache HTTP 4.5 Library' | ||
| packaging = 'jar' | ||
| groupId = publishGroupId | ||
| artifactId = 'cloudinary-http45' | ||
| description = publishDescription | ||
| url = githubUrl | ||
| licenses { | ||
| license { | ||
| name = licenseName | ||
| url = licenseUrl | ||
| } | ||
| } | ||
|
|
||
| developers { | ||
| developer { | ||
| id = developerId | ||
| name = developerName | ||
| email = developerEmail | ||
| } | ||
| } | ||
| scm { | ||
| connection = scmConnection | ||
| developerConnection = scmDeveloperConnection | ||
| url = scmUrl | ||
| } | ||
| } | ||
|
|
||
| pom.withXml { | ||
| def pomFile = file("${project.buildDir}/generated-pom.xml") | ||
| writeTo(pomFile) | ||
| def pomAscFile = signing.sign(pomFile).signatureFiles[0] | ||
| artifact(pomAscFile) { | ||
| classifier = null | ||
| extension = 'pom.asc' | ||
| } | ||
| } | ||
|
|
||
| // create the signed artifacts | ||
| project.tasks.signArchives.signatureFiles.each { | ||
| artifact(it) { | ||
| def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/ | ||
| if (matcher.find()) { | ||
| classifier = matcher.group(1) | ||
| } else { | ||
| classifier = null | ||
| } | ||
| extension = 'jar.asc' | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| nexusPublishing { | ||
| repositories { | ||
| sonatype { | ||
| username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" | ||
| password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" | ||
| } | ||
| } | ||
| } | ||
|
|
||
| model { | ||
| tasks.generatePomFileForMavenJavaPublication { | ||
| destination = file("$buildDir/generated-pom.xml") | ||
| } | ||
| tasks.publishMavenJavaPublicationToMavenLocal { | ||
| dependsOn project.tasks.signArchives | ||
| } | ||
| tasks.publishMavenJavaPublicationToSonatypeRepository { | ||
| dependsOn project.tasks.signArchives | ||
| } | ||
| } | ||
| } | ||
| } | ||
205 changes: 205 additions & 0 deletions
205
cloudinary-http45/src/main/java/com/cloudinary/http45/ApiStrategy.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,205 @@ | ||
| package com.cloudinary.http45; | ||
|
|
||
| import com.cloudinary.Api; | ||
| import com.cloudinary.Api.HttpMethod; | ||
| import com.cloudinary.Cloudinary; | ||
| import com.cloudinary.api.ApiResponse; | ||
| import com.cloudinary.api.exceptions.GeneralError; | ||
| import com.cloudinary.http45.api.Response; | ||
| import com.cloudinary.utils.Base64Coder; | ||
| import com.cloudinary.utils.ObjectUtils; | ||
| import com.cloudinary.utils.StringUtils; | ||
| import org.apache.http.Consts; | ||
| import org.apache.http.HttpHost; | ||
| import org.apache.http.NameValuePair; | ||
| import org.apache.http.client.config.RequestConfig; | ||
| import org.apache.http.client.entity.UrlEncodedFormEntity; | ||
| import org.apache.http.client.methods.*; | ||
| import org.apache.http.client.utils.URIBuilder; | ||
| import org.apache.http.conn.HttpClientConnectionManager; | ||
| import org.apache.http.entity.ContentType; | ||
| import org.apache.http.entity.StringEntity; | ||
| import org.apache.http.impl.client.CloseableHttpClient; | ||
| import org.apache.http.impl.client.HttpClientBuilder; | ||
| import org.apache.http.impl.client.HttpClients; | ||
| import org.cloudinary.json.JSONException; | ||
| import org.cloudinary.json.JSONObject; | ||
|
|
||
| import java.io.InputStream; | ||
| import java.io.UnsupportedEncodingException; | ||
| import java.lang.reflect.Constructor; | ||
| import java.net.URI; | ||
| import java.net.URISyntaxException; | ||
| import java.util.Arrays; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| import static com.cloudinary.http45.ApiUtils.prepareParams; | ||
| import static com.cloudinary.http45.ApiUtils.setTimeouts; | ||
|
|
||
| public class ApiStrategy extends com.cloudinary.strategies.AbstractApiStrategy { | ||
|
|
||
| private CloseableHttpClient client = null; | ||
|
|
||
| @Override | ||
| public void init(Api api) { | ||
| super.init(api); | ||
|
|
||
| HttpClientBuilder clientBuilder = HttpClients.custom(); | ||
| clientBuilder.useSystemProperties().setUserAgent(Cloudinary.USER_AGENT + " ApacheHTTPComponents/4.5"); | ||
|
|
||
| // If the configuration specifies a proxy then apply it to the client | ||
| if (api.cloudinary.config.proxyHost != null && api.cloudinary.config.proxyPort != 0) { | ||
| HttpHost proxy = new HttpHost(api.cloudinary.config.proxyHost, api.cloudinary.config.proxyPort); | ||
| clientBuilder.setProxy(proxy); | ||
| } | ||
|
|
||
| HttpClientConnectionManager connectionManager = (HttpClientConnectionManager) api.cloudinary.config.properties.get("connectionManager"); | ||
| if (connectionManager != null) { | ||
| clientBuilder.setConnectionManager(connectionManager); | ||
| } | ||
|
|
||
| int timeout = this.api.cloudinary.config.timeout; | ||
| if (timeout > 0) { | ||
| RequestConfig config = RequestConfig.custom() | ||
| .setSocketTimeout(timeout * 1000) | ||
| .setConnectTimeout(timeout * 1000) | ||
| .build(); | ||
| clientBuilder.setDefaultRequestConfig(config); | ||
| } | ||
|
|
||
| this.client = clientBuilder.build(); | ||
| } | ||
|
|
||
| @SuppressWarnings({"rawtypes", "unchecked"}) | ||
| public ApiResponse callApi(HttpMethod method, Iterable<String> uri, Map<String, ?> params, Map options) throws Exception { | ||
| if (options == null) | ||
| options = ObjectUtils.emptyMap(); | ||
|
|
||
| String prefix = ObjectUtils.asString(options.get("upload_prefix"), ObjectUtils.asString(this.api.cloudinary.config.uploadPrefix, "https://api.cloudinary.com")); | ||
| String cloudName = ObjectUtils.asString(options.get("cloud_name"), this.api.cloudinary.config.cloudName); | ||
| if (cloudName == null) throw new IllegalArgumentException("Must supply cloud_name"); | ||
| String apiKey = ObjectUtils.asString(options.get("api_key"), this.api.cloudinary.config.apiKey); | ||
| String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.config.apiSecret); | ||
| String oauthToken = ObjectUtils.asString(options.get("oauth_token"), this.api.cloudinary.config.oauthToken); | ||
|
|
||
| validateAuthorization(apiKey, apiSecret, oauthToken); | ||
|
|
||
|
|
||
| String apiUrl = createApiUrl(uri, prefix, cloudName); | ||
| HttpUriRequest request = prepareRequest(method, apiUrl, params, options); | ||
|
|
||
| request.setHeader("Authorization", getAuthorizationHeaderValue(apiKey, apiSecret, oauthToken)); | ||
|
|
||
| return getApiResponse(request); | ||
| } | ||
|
|
||
| private ApiResponse getApiResponse(HttpUriRequest request) throws Exception { | ||
| String responseData = null; | ||
| int code = 0; | ||
| CloseableHttpResponse response = client.execute(request); | ||
| try { | ||
| code = response.getStatusLine().getStatusCode(); | ||
| InputStream responseStream = response.getEntity().getContent(); | ||
| responseData = StringUtils.read(responseStream); | ||
| } finally { | ||
| response.close(); | ||
| } | ||
|
|
||
| Class<? extends Exception> exceptionClass = Api.CLOUDINARY_API_ERROR_CLASSES.get(code); | ||
| if (code != 200 && exceptionClass == null) { | ||
| throw new GeneralError("Server returned unexpected status code - " + code + " - " + responseData); | ||
| } | ||
| Map result; | ||
|
|
||
| try { | ||
| JSONObject responseJSON = new JSONObject(responseData); | ||
| result = ObjectUtils.toMap(responseJSON); | ||
| } catch (JSONException e) { | ||
| throw new RuntimeException("Invalid JSON response from server " + e.getMessage()); | ||
| } | ||
|
|
||
| if (code == 200) { | ||
| return new Response(response, result); | ||
| } else { | ||
| String message = (String) ((Map) result.get("error")).get("message"); | ||
| Constructor<? extends Exception> exceptionConstructor = exceptionClass.getConstructor(String.class); | ||
| throw exceptionConstructor.newInstance(message); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public ApiResponse callAccountApi(HttpMethod method, Iterable<String> uri, Map<String, ?> params, Map options) throws Exception { | ||
| if (options == null) | ||
| options = ObjectUtils.emptyMap(); | ||
|
|
||
| String prefix = ObjectUtils.asString(options.get("upload_prefix"), "https://api.cloudinary.com"); | ||
| String apiKey = ObjectUtils.asString(options.get("provisioning_api_key")); | ||
| if (apiKey == null) throw new IllegalArgumentException("Must supply provisioning_api_key"); | ||
| String apiSecret = ObjectUtils.asString(options.get("provisioning_api_secret")); | ||
| if (apiSecret == null) throw new IllegalArgumentException("Must supply provisioning_api_secret"); | ||
|
|
||
| String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1"), "/"); | ||
| for (String component : uri) { | ||
| apiUrl = apiUrl + "/" + component; | ||
| } | ||
|
|
||
| HttpUriRequest request = prepareRequest(method, apiUrl, params, options); | ||
|
|
||
| request.setHeader("Authorization", getAuthorizationHeaderValue(apiKey, apiSecret, null)); | ||
|
|
||
| return getApiResponse(request); | ||
| } | ||
|
|
||
| /** | ||
| * Prepare a request with the URL and parameters based on the HTTP method used | ||
| * | ||
| * @param method the HTTP method: GET, PUT, POST, DELETE | ||
| * @param apiUrl the cloudinary API URI | ||
| * @param params the parameters to pass to the server | ||
| * @return an HTTP request | ||
| * @throws URISyntaxException | ||
| * @throws UnsupportedEncodingException | ||
| */ | ||
| private HttpUriRequest prepareRequest(HttpMethod method, String apiUrl, Map<String, ?> params, Map options) throws URISyntaxException, UnsupportedEncodingException { | ||
| URI apiUri; | ||
| HttpRequestBase request; | ||
|
|
||
| String contentType = ObjectUtils.asString(options.get("content_type"), "urlencoded"); | ||
| URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); | ||
| List<NameValuePair> urlEncodedParams = prepareParams(params); | ||
|
|
||
| if (method == HttpMethod.GET) { | ||
| apiUrlBuilder.setParameters(prepareParams(params)); | ||
| apiUri = apiUrlBuilder.build(); | ||
| request = new HttpGet(apiUri); | ||
| } else { | ||
| Map<String,Object> paramsCopy = new HashMap<String, Object>((Map<String,Object>) params); | ||
| apiUri = apiUrlBuilder.build(); | ||
| switch (method) { | ||
| case PUT: | ||
| request = new HttpPut(apiUri); | ||
| break; | ||
| case DELETE: //uses HttpPost instead of HttpDelete | ||
| paramsCopy.put("_method", "delete"); | ||
| //continue with POST | ||
| case POST: | ||
| request = new HttpPost(apiUri); | ||
| break; | ||
| default: | ||
| throw new IllegalArgumentException("Unknown HTTP method"); | ||
| } | ||
| if (contentType.equals("json")) { | ||
| JSONObject asJSON = ObjectUtils.toJSON(paramsCopy); | ||
| StringEntity requestEntity = new StringEntity(asJSON.toString(), ContentType.APPLICATION_JSON); | ||
| ((HttpEntityEnclosingRequestBase) request).setEntity(requestEntity); | ||
| } else { | ||
| ((HttpEntityEnclosingRequestBase) request).setEntity(new UrlEncodedFormEntity(prepareParams(paramsCopy), Consts.UTF_8)); | ||
| } | ||
| } | ||
|
|
||
| setTimeouts(request, options); | ||
| return request; | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't want to bump that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can bump in a separate PR, it's not really related to the client and may require changes in other modules as well.