mirror of https://github.com/apache/jclouds.git
Issue 191: corrected multipart form and added cookbook signing to chef client
This commit is contained in:
parent
306bb0ebde
commit
f25100fe9b
|
@ -24,6 +24,7 @@ import org.jclouds.blobstore.domain.Blob;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.MultipartForm;
|
import org.jclouds.http.MultipartForm;
|
||||||
import org.jclouds.http.MultipartForm.Part;
|
import org.jclouds.http.MultipartForm.Part;
|
||||||
|
import org.jclouds.http.MultipartForm.Part.PartOptions;
|
||||||
import org.jclouds.rest.Binder;
|
import org.jclouds.rest.Binder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,10 +37,10 @@ public class BindBlobToMultipartForm implements Binder {
|
||||||
|
|
||||||
public void bindToRequest(HttpRequest request, Object payload) {
|
public void bindToRequest(HttpRequest request, Object payload) {
|
||||||
Blob object = (Blob) payload;
|
Blob object = (Blob) payload;
|
||||||
|
|
||||||
Part part = Part.create(object.getMetadata().getName(), object.getPayload(), object
|
Part part = Part.create(object.getMetadata().getName(), object.getPayload(),
|
||||||
.getMetadata().getContentType());
|
new PartOptions().contentType(object.getMetadata().getContentType()));
|
||||||
|
|
||||||
MultipartForm form = new MultipartForm(BOUNDARY, part);
|
MultipartForm form = new MultipartForm(BOUNDARY, part);
|
||||||
request.setPayload(form.getInput());
|
request.setPayload(form.getInput());
|
||||||
request.getHeaders().put(HttpHeaders.CONTENT_TYPE,
|
request.getHeaders().put(HttpHeaders.CONTENT_TYPE,
|
||||||
|
|
|
@ -70,14 +70,15 @@ public interface ChefAsyncClient {
|
||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Path("cookbooks")
|
@Path("cookbooks")
|
||||||
ListenableFuture<String> listCookbooks();
|
@ResponseParser(ParseKeySetFromJson.class)
|
||||||
|
ListenableFuture<Set<String>> listCookbooks();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see ChefClient#createCookbook(String,File)
|
* @see ChefClient#createCookbook(String,File)
|
||||||
*/
|
*/
|
||||||
@POST
|
@POST
|
||||||
@Path("cookbooks")
|
@Path("name")
|
||||||
ListenableFuture<String> createCookbook(@FormParam("name") String name,
|
ListenableFuture<Void> createCookbook(@FormParam("name") String cookbookName,
|
||||||
@PartParam(name = "file", contentType = MediaType.APPLICATION_OCTET_STREAM) File content);
|
@PartParam(name = "file", contentType = MediaType.APPLICATION_OCTET_STREAM) File content);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,8 +86,44 @@ public interface ChefAsyncClient {
|
||||||
*/
|
*/
|
||||||
@POST
|
@POST
|
||||||
@Path("cookbooks")
|
@Path("cookbooks")
|
||||||
ListenableFuture<String> createCookbook(@FormParam("name") String name,
|
ListenableFuture<Void> createCookbook(
|
||||||
@PartParam(name = "file", contentType = MediaType.APPLICATION_OCTET_STREAM) byte[] content);
|
@FormParam("name") String cookbookName,
|
||||||
|
@PartParam(name = "file", contentType = MediaType.APPLICATION_OCTET_STREAM, filename = "{name}.tar.gz") byte[] content);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see ChefClient#updateCookbook(String,File)
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@Path("cookbooks/{cookbookname}/_content")
|
||||||
|
ListenableFuture<Void> updateCookbook(
|
||||||
|
@PathParam("cookbookname") @FormParam("name") String cookbookName,
|
||||||
|
@PartParam(name = "file", contentType = MediaType.APPLICATION_OCTET_STREAM) File content);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see ChefClient#updateCookbook(String,byte[])
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@Path("cookbooks/{cookbookname}/_content")
|
||||||
|
ListenableFuture<Void> updateCookbook(
|
||||||
|
@PathParam("cookbookname") @FormParam("name") String cookbookName,
|
||||||
|
@PartParam(name = "file", contentType = MediaType.APPLICATION_OCTET_STREAM, filename = "{name}.tar.gz") byte[] content);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see ChefCookbook#deleteCookbook
|
||||||
|
*/
|
||||||
|
@DELETE
|
||||||
|
@Path("cookbooks/{cookbookname}")
|
||||||
|
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
||||||
|
ListenableFuture<Void> deleteCookbook(@PathParam("cookbookname") String cookbookName);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see ChefCookbook#getCookbook
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("cookbooks/{cookbookname}")
|
||||||
|
ListenableFuture<String> getCookbook(@PathParam("cookbookname") String cookbookName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see ChefClient#createClient
|
* @see ChefClient#createClient
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
package org.jclouds.chef;
|
package org.jclouds.chef;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@ -60,11 +59,23 @@ import org.jclouds.rest.AuthorizationException;
|
||||||
*/
|
*/
|
||||||
@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
|
@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
|
||||||
public interface ChefClient {
|
public interface ChefClient {
|
||||||
String listCookbooks();
|
Set<String> listCookbooks();
|
||||||
|
|
||||||
String createCookbook(String name, File content);
|
@Timeout(duration = 10, timeUnit = TimeUnit.MINUTES)
|
||||||
|
void createCookbook(String cookbookName, File content);
|
||||||
|
|
||||||
String createCookbook(String name, byte[] content);
|
@Timeout(duration = 10, timeUnit = TimeUnit.MINUTES)
|
||||||
|
void createCookbook(String cookbookName, byte[] content);
|
||||||
|
|
||||||
|
@Timeout(duration = 10, timeUnit = TimeUnit.MINUTES)
|
||||||
|
void updateCookbook(String cookbookName, File content);
|
||||||
|
|
||||||
|
@Timeout(duration = 10, timeUnit = TimeUnit.MINUTES)
|
||||||
|
void updateCookbook(String cookbookName, byte[] content);
|
||||||
|
|
||||||
|
void deleteCookbook(String cookbookName);
|
||||||
|
|
||||||
|
String getCookbook(String cookbookName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates a new client
|
* creates a new client
|
||||||
|
|
|
@ -27,6 +27,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
@ -42,13 +43,16 @@ import org.jclouds.http.HttpException;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpRequestFilter;
|
import org.jclouds.http.HttpRequestFilter;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
|
import org.jclouds.http.MultipartForm;
|
||||||
import org.jclouds.http.Payload;
|
import org.jclouds.http.Payload;
|
||||||
import org.jclouds.http.Payloads;
|
import org.jclouds.http.Payloads;
|
||||||
|
import org.jclouds.http.MultipartForm.Part;
|
||||||
import org.jclouds.http.internal.SignatureWire;
|
import org.jclouds.http.internal.SignatureWire;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.util.Utils;
|
import org.jclouds.util.Utils;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
@ -130,7 +134,7 @@ public class SignedHeaderAuth implements HttpRequestFilter {
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
String hashPath(String path) {
|
String hashPath(String path) {
|
||||||
try {
|
try {
|
||||||
return encryptionService.sha1Base64(canonicalPath(path));
|
return encryptionService.sha1Base64(Utils.toInputStream(canonicalPath(path)));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Throwables.propagateIfPossible(e);
|
Throwables.propagateIfPossible(e);
|
||||||
throw new HttpException("error creating sigature for path: " + path, e);
|
throw new HttpException("error creating sigature for path: " + path, e);
|
||||||
|
@ -151,16 +155,36 @@ public class SignedHeaderAuth implements HttpRequestFilter {
|
||||||
String hashBody(Payload payload) {
|
String hashBody(Payload payload) {
|
||||||
if (payload == null)
|
if (payload == null)
|
||||||
return emptyStringHash;
|
return emptyStringHash;
|
||||||
|
payload = useTheFilePartIfForm(payload);
|
||||||
checkArgument(payload != null, "payload was null");
|
checkArgument(payload != null, "payload was null");
|
||||||
checkArgument(payload.isRepeatable(), "payload must be repeatable: " + payload);
|
checkArgument(payload.isRepeatable(), "payload must be repeatable: " + payload);
|
||||||
try {
|
try {
|
||||||
return encryptionService.sha1Base64(Utils.toStringAndClose(payload.getInput()));
|
return encryptionService.sha1Base64(payload.getInput());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Throwables.propagateIfPossible(e);
|
Throwables.propagateIfPossible(e);
|
||||||
throw new HttpException("error creating sigature for payload: " + payload, e);
|
throw new HttpException("error creating sigature for payload: " + payload, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Payload useTheFilePartIfForm(Payload payload) {
|
||||||
|
if (payload instanceof MultipartForm) {
|
||||||
|
Iterable<? extends Part> parts = MultipartForm.class.cast(payload).getParts();
|
||||||
|
try {
|
||||||
|
payload = Iterables.find(parts, new Predicate<Part>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Part input) {
|
||||||
|
return "file".equals(input.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
public String sign(String toSign) {
|
public String sign(String toSign) {
|
||||||
try {
|
try {
|
||||||
byte[] encrypted = encryptionService.rsaPrivateEncrypt(toSign, privateKey);
|
byte[] encrypted = encryptionService.rsaPrivateEncrypt(toSign, privateKey);
|
||||||
|
|
|
@ -41,8 +41,8 @@ import com.google.common.base.Throwables;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ParseErrorFromJsonOrNull implements Function<HttpResponse, String> {
|
public class ParseErrorFromJsonOrReturnBody implements Function<HttpResponse, String> {
|
||||||
Pattern pattern = Pattern.compile(".*error\": *\"([^\"]+)\".*");
|
Pattern pattern = Pattern.compile(".*\\[\"([^\"]+)\"\\].*");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String apply(HttpResponse response) {
|
public String apply(HttpResponse response) {
|
||||||
|
@ -63,9 +63,9 @@ public class ParseErrorFromJsonOrNull implements Function<HttpResponse, String>
|
||||||
|
|
||||||
public String parse(String in) {
|
public String parse(String in) {
|
||||||
Matcher matcher = pattern.matcher(in);
|
Matcher matcher = pattern.matcher(in);
|
||||||
while (matcher.find()) {
|
if (matcher.find()) {
|
||||||
return matcher.group(1);
|
return matcher.group(1);
|
||||||
}
|
}
|
||||||
return null;
|
return in;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -22,7 +22,7 @@ import javax.annotation.Resource;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.chef.functions.ParseErrorFromJsonOrNull;
|
import org.jclouds.chef.functions.ParseErrorFromJsonOrReturnBody;
|
||||||
import org.jclouds.http.HttpCommand;
|
import org.jclouds.http.HttpCommand;
|
||||||
import org.jclouds.http.HttpErrorHandler;
|
import org.jclouds.http.HttpErrorHandler;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
|
@ -43,17 +43,16 @@ import com.google.common.io.Closeables;
|
||||||
public class ChefErrorHandler implements HttpErrorHandler {
|
public class ChefErrorHandler implements HttpErrorHandler {
|
||||||
@Resource
|
@Resource
|
||||||
protected Logger logger = Logger.NULL;
|
protected Logger logger = Logger.NULL;
|
||||||
private final ParseErrorFromJsonOrNull errorParser;
|
private final ParseErrorFromJsonOrReturnBody errorParser;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ChefErrorHandler(ParseErrorFromJsonOrNull errorParser) {
|
ChefErrorHandler(ParseErrorFromJsonOrReturnBody errorParser) {
|
||||||
this.errorParser = errorParser;
|
this.errorParser = errorParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleError(HttpCommand command, HttpResponse response) {
|
public void handleError(HttpCommand command, HttpResponse response) {
|
||||||
String message = errorParser.apply(response);
|
String message = errorParser.apply(response);
|
||||||
Exception exception = message != null ? new HttpResponseException(command, response, message)
|
Exception exception = new HttpResponseException(command, response, message);
|
||||||
: new HttpResponseException(command, response);
|
|
||||||
try {
|
try {
|
||||||
message = message != null ? message : String.format("%s -> %s", command.getRequest()
|
message = message != null ? message : String.format("%s -> %s", command.getRequest()
|
||||||
.getRequestLine(), response.getStatusLine());
|
.getRequestLine(), response.getStatusLine());
|
||||||
|
|
|
@ -26,7 +26,6 @@ package org.jclouds.chef;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static org.testng.Assert.assertNotNull;
|
import static org.testng.Assert.assertNotNull;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -51,26 +50,37 @@ import com.google.common.io.Files;
|
||||||
@Test(groups = "live", testName = "chef.ChefClientLiveTest")
|
@Test(groups = "live", testName = "chef.ChefClientLiveTest")
|
||||||
public class ChefClientLiveTest {
|
public class ChefClientLiveTest {
|
||||||
|
|
||||||
|
private static final String COOKBOOK_NAME = "mysql";
|
||||||
|
private static final String COOKBOOK_URI = "https://s3.amazonaws.com/opscode-community/cookbook_versions/tarballs/212/original/mysql.tar.gz";
|
||||||
private RestContext<ChefClient, ChefAsyncClient> validatorConnection;
|
private RestContext<ChefClient, ChefAsyncClient> validatorConnection;
|
||||||
private RestContext<ChefClient, ChefAsyncClient> clientConnection;
|
private RestContext<ChefClient, ChefAsyncClient> clientConnection;
|
||||||
|
private RestContext<ChefClient, ChefAsyncClient> adminConnection;
|
||||||
|
|
||||||
private String clientKey;
|
private String clientKey;
|
||||||
private String endpoint;
|
private String endpoint;
|
||||||
private String validator;
|
private String validator;
|
||||||
|
private String user;
|
||||||
|
private byte[] cookbookContent;
|
||||||
|
private File cookbookFile;
|
||||||
|
|
||||||
public static final String PREFIX = System.getProperty("user.name") + "-jcloudstest";
|
public static final String PREFIX = System.getProperty("user.name") + "-jcloudstest";
|
||||||
|
|
||||||
@BeforeClass(groups = { "live" })
|
@BeforeClass(groups = { "live" })
|
||||||
public void setupClient() throws IOException {
|
public void setupClient() throws IOException {
|
||||||
endpoint = checkNotNull(System.getProperty("jclouds.test.endpoint"), "jclouds.test.endpoint");
|
endpoint = checkNotNull(System.getProperty("jclouds.test.endpoint"), "jclouds.test.endpoint");
|
||||||
validator = System.getProperty("jclouds.test.user");
|
validator = System.getProperty("jclouds.test.validator");
|
||||||
if (validator == null || validator.equals(""))
|
if (validator == null || validator.equals(""))
|
||||||
validator = "chef-validator";
|
validator = "chef-validator";
|
||||||
|
String validatorKey = System.getProperty("jclouds.test.validator.key");
|
||||||
|
if (validatorKey == null || validatorKey.equals(""))
|
||||||
|
validatorKey = "/etc/chef/validation.pem";
|
||||||
|
user = checkNotNull(System.getProperty("jclouds.test.user"));
|
||||||
String keyfile = System.getProperty("jclouds.test.key");
|
String keyfile = System.getProperty("jclouds.test.key");
|
||||||
if (keyfile == null || keyfile.equals(""))
|
if (keyfile == null || keyfile.equals(""))
|
||||||
keyfile = "/etc/chef/validation.pem";
|
keyfile = System.getProperty("user.home") + "/chef/" + user + ".pem";
|
||||||
validatorConnection = createConnection(validator, Files.toString(new File(keyfile),
|
validatorConnection = createConnection(validator, Files.toString(new File(validatorKey),
|
||||||
Charsets.UTF_8));
|
Charsets.UTF_8));
|
||||||
|
adminConnection = createConnection(user, Files.toString(new File(keyfile), Charsets.UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
private RestContext<ChefClient, ChefAsyncClient> createConnection(String identity, String key)
|
private RestContext<ChefClient, ChefAsyncClient> createConnection(String identity, String key)
|
||||||
|
@ -110,24 +120,22 @@ public class ChefClientLiveTest {
|
||||||
assertNotNull(validatorConnection.getApi().clientExists(PREFIX));
|
assertNotNull(validatorConnection.getApi().clientExists(PREFIX));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(dependsOnMethods = "testGenerateKeyForClient")
|
@Test
|
||||||
public void testCreateCookbooks() throws Exception {
|
public void testCreateCookbook() throws Exception {
|
||||||
|
adminConnection.getApi().deleteCookbook(COOKBOOK_NAME);
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
try {
|
try {
|
||||||
in = URI
|
in = URI.create(COOKBOOK_URI).toURL().openStream();
|
||||||
.create(
|
|
||||||
"https://s3.amazonaws.com/opscode-community/cookbook_versions/tarballs/194/original/java.tar.gz")
|
|
||||||
.toURL().openStream();
|
|
||||||
|
|
||||||
byte[] content = ByteStreams.toByteArray(in);
|
cookbookContent = ByteStreams.toByteArray(in);
|
||||||
|
|
||||||
System.err.println(clientConnection.getApi().createCookbook("java-bytearray", content));
|
cookbookFile = File.createTempFile("foo", ".tar.gz");
|
||||||
|
Files.write(cookbookContent, cookbookFile);
|
||||||
|
cookbookFile.deleteOnExit();
|
||||||
|
|
||||||
File file = File.createTempFile("foo", "bar");
|
adminConnection.getApi().createCookbook(COOKBOOK_NAME, cookbookFile);
|
||||||
Files.write(content, file);
|
adminConnection.getApi().deleteCookbook(COOKBOOK_NAME);
|
||||||
file.deleteOnExit();
|
adminConnection.getApi().createCookbook(COOKBOOK_NAME, cookbookContent);
|
||||||
|
|
||||||
System.err.println(clientConnection.getApi().createCookbook("java-file", file));
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
if (in != null)
|
if (in != null)
|
||||||
|
@ -135,9 +143,17 @@ public class ChefClientLiveTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(dependsOnMethods = "testCreateCookbooks")
|
@Test(dependsOnMethods = "testCreateCookbook")
|
||||||
|
public void testUpdateCookbook() throws Exception {
|
||||||
|
adminConnection.getApi().updateCookbook(COOKBOOK_NAME, cookbookFile);
|
||||||
|
// TODO verify timestamp or something
|
||||||
|
adminConnection.getApi().updateCookbook(COOKBOOK_NAME, cookbookContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(dependsOnMethods = "testUpdateCookbook")
|
||||||
public void testListCookbooks() throws Exception {
|
public void testListCookbooks() throws Exception {
|
||||||
System.err.println(clientConnection.getApi().listCookbooks());
|
for (String cookbook : adminConnection.getApi().listCookbooks())
|
||||||
|
System.err.println(adminConnection.getApi().getCookbook(cookbook));
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass(groups = { "live" })
|
@AfterClass(groups = { "live" })
|
||||||
|
@ -146,5 +162,7 @@ public class ChefClientLiveTest {
|
||||||
clientConnection.close();
|
clientConnection.close();
|
||||||
if (validatorConnection != null)
|
if (validatorConnection != null)
|
||||||
validatorConnection.close();
|
validatorConnection.close();
|
||||||
|
if (adminConnection != null)
|
||||||
|
adminConnection.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
* ====================================================================
|
||||||
|
*/
|
||||||
|
package org.jclouds.chef.functions;
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
import org.jclouds.http.HttpResponse;
|
||||||
|
import org.jclouds.util.Utils;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adrian Cole
|
||||||
|
*/
|
||||||
|
@Test(groups = "unit", testName = "chef.ParseErrorFromJsonOrReturnBodyTest")
|
||||||
|
public class ParseErrorFromJsonOrReturnBodyTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testApplyInputStreamDetails() throws UnknownHostException {
|
||||||
|
InputStream is = Utils
|
||||||
|
.toInputStream("{\"error\":[\"invalid tarball: tarball root must contain java-bytearray\"]}");
|
||||||
|
|
||||||
|
ParseErrorFromJsonOrReturnBody parser = new ParseErrorFromJsonOrReturnBody();
|
||||||
|
String response = parser.apply(new HttpResponse(is));
|
||||||
|
assertEquals(response, "invalid tarball: tarball root must contain java-bytearray");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -52,7 +52,7 @@ public interface EncryptionService {
|
||||||
String hmacSha256Base64(String toEncode, byte[] key) throws NoSuchAlgorithmException,
|
String hmacSha256Base64(String toEncode, byte[] key) throws NoSuchAlgorithmException,
|
||||||
NoSuchProviderException, InvalidKeyException;
|
NoSuchProviderException, InvalidKeyException;
|
||||||
|
|
||||||
String sha1Base64(String toEncode) throws NoSuchAlgorithmException, NoSuchProviderException,
|
String sha1Base64(InputStream toEncode) throws NoSuchAlgorithmException, NoSuchProviderException,
|
||||||
InvalidKeyException;
|
InvalidKeyException;
|
||||||
|
|
||||||
String hmacSha1Base64(String toEncode, byte[] key) throws NoSuchAlgorithmException,
|
String hmacSha1Base64(String toEncode, byte[] key) throws NoSuchAlgorithmException,
|
||||||
|
|
|
@ -161,11 +161,27 @@ public class JCEEncryptionService extends BaseEncryptionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String sha1Base64(String toEncode) throws NoSuchAlgorithmException,
|
public String sha1Base64(InputStream plainBytes) throws NoSuchAlgorithmException,
|
||||||
NoSuchProviderException, InvalidKeyException {
|
NoSuchProviderException, InvalidKeyException {
|
||||||
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
|
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
|
||||||
byte[] digest = sha1.digest(toEncode.getBytes());
|
byte[] buffer = new byte[1024];
|
||||||
return toBase64String(digest);
|
long length = 0;
|
||||||
|
int numRead = -1;
|
||||||
|
try {
|
||||||
|
do {
|
||||||
|
numRead = plainBytes.read(buffer);
|
||||||
|
if (numRead > 0) {
|
||||||
|
length += numRead;
|
||||||
|
sha1.update(buffer, 0, numRead);
|
||||||
|
}
|
||||||
|
} while (numRead != -1);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
Closeables.closeQuietly(plainBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toBase64String(sha1.digest());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -53,6 +53,7 @@ public class MultipartForm implements Payload {
|
||||||
private long size;
|
private long size;
|
||||||
private boolean isRepeatable;
|
private boolean isRepeatable;
|
||||||
private boolean written;
|
private boolean written;
|
||||||
|
private final Iterable<? extends Part> parts;
|
||||||
|
|
||||||
public MultipartForm(String boundary, Part... parts) {
|
public MultipartForm(String boundary, Part... parts) {
|
||||||
this(boundary, Lists.newArrayList(parts));
|
this(boundary, Lists.newArrayList(parts));
|
||||||
|
@ -60,6 +61,7 @@ public class MultipartForm implements Payload {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public MultipartForm(String boundary, Iterable<? extends Part> parts) {
|
public MultipartForm(String boundary, Iterable<? extends Part> parts) {
|
||||||
|
this.parts = parts;
|
||||||
String boundaryrn = boundary + rn;
|
String boundaryrn = boundary + rn;
|
||||||
isRepeatable = true;
|
isRepeatable = true;
|
||||||
InputSupplier<? extends InputStream> chain = ByteStreams.join();
|
InputSupplier<? extends InputStream> chain = ByteStreams.join();
|
||||||
|
@ -102,6 +104,7 @@ public class MultipartForm implements Payload {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Part implements Payload {
|
public static class Part implements Payload {
|
||||||
|
private final String name;
|
||||||
private final Multimap<String, String> headers;
|
private final Multimap<String, String> headers;
|
||||||
private final Payload delegate;
|
private final Payload delegate;
|
||||||
|
|
||||||
|
@ -129,24 +132,65 @@ public class MultipartForm implements Payload {
|
||||||
put(HttpHeaders.CONTENT_TYPE, checkNotNull(type, "type"));
|
put(HttpHeaders.CONTENT_TYPE, checkNotNull(type, "type"));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static PartMap create(String name, Payload delegate, PartOptions options) {
|
||||||
|
String filename = options != null ? options.getFilename() : null;
|
||||||
|
if (delegate instanceof FilePayload)
|
||||||
|
filename = FilePayload.class.cast(delegate).getRawContent().getName();
|
||||||
|
PartMap returnVal;
|
||||||
|
returnVal = (filename != null) ? create(name, filename) : create(name);
|
||||||
|
if (options != null)
|
||||||
|
returnVal.contentType(options.getContentType());
|
||||||
|
return returnVal;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Part(PartMap map, Payload delegate) {
|
private Part(String name, PartMap map, Payload delegate) {
|
||||||
|
this.name = name;
|
||||||
this.delegate = checkNotNull(delegate, "delegate");
|
this.delegate = checkNotNull(delegate, "delegate");
|
||||||
this.headers = ImmutableMultimap.copyOf(Multimaps.forMap((checkNotNull(map, "headers"))));
|
this.headers = ImmutableMultimap.copyOf(Multimaps.forMap((checkNotNull(map, "headers"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class PartOptions {
|
||||||
|
private String contentType;
|
||||||
|
private String filename;
|
||||||
|
|
||||||
|
public PartOptions contentType(String contentType) {
|
||||||
|
this.contentType = checkNotNull(contentType, "contentType");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PartOptions filename(String filename) {
|
||||||
|
this.filename = checkNotNull(filename, "filename");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
public static PartOptions contentType(String contentType) {
|
||||||
|
return new PartOptions().contentType(contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PartOptions filename(String filename) {
|
||||||
|
return new PartOptions().filename(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Part create(String name, String value) {
|
public static Part create(String name, String value) {
|
||||||
return new Part(PartMap.create(name), Payloads.newStringPayload(value));
|
return new Part(name, PartMap.create(name), Payloads.newStringPayload(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Part create(String name, Payload delegate, String contentType) {
|
public static Part create(String name, Payload delegate, PartOptions options) {
|
||||||
return new Part(PartMap.create(name).contentType(contentType), delegate);
|
return new Part(name, PartMap.create(name, delegate, options), delegate);
|
||||||
}
|
|
||||||
|
|
||||||
public static Part create(String name, FilePayload delegate, String contentType) {
|
|
||||||
return new Part(PartMap.create(name, delegate.getRawContent().getName()).contentType(
|
|
||||||
contentType), delegate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Multimap<String, String> getHeaders() {
|
public Multimap<String, String> getHeaders() {
|
||||||
|
@ -184,6 +228,7 @@ public class MultipartForm implements Payload {
|
||||||
int result = 1;
|
int result = 1;
|
||||||
result = prime * result + ((delegate == null) ? 0 : delegate.hashCode());
|
result = prime * result + ((delegate == null) ? 0 : delegate.hashCode());
|
||||||
result = prime * result + ((headers == null) ? 0 : headers.hashCode());
|
result = prime * result + ((headers == null) ? 0 : headers.hashCode());
|
||||||
|
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,8 +251,17 @@ public class MultipartForm implements Payload {
|
||||||
return false;
|
return false;
|
||||||
} else if (!headers.equals(other.headers))
|
} else if (!headers.equals(other.headers))
|
||||||
return false;
|
return false;
|
||||||
|
if (name == null) {
|
||||||
|
if (other.name != null)
|
||||||
|
return false;
|
||||||
|
} else if (!name.equals(other.name))
|
||||||
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -253,4 +307,8 @@ public class MultipartForm implements Payload {
|
||||||
return "MultipartForm [chain=" + chain + ", isRepeatable=" + isRepeatable + ", size=" + size
|
return "MultipartForm [chain=" + chain + ", isRepeatable=" + isRepeatable + ", size=" + size
|
||||||
+ ", written=" + written + "]";
|
+ ", written=" + written + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Iterable<? extends Part> getParts() {
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,24 +23,27 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import org.jclouds.http.Payload;
|
import org.jclouds.http.Payload;
|
||||||
|
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.io.Closeables;
|
import com.google.common.io.Closeables;
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
|
import com.google.common.io.InputSupplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class FilePayload implements Payload {
|
public class FilePayload implements Payload {
|
||||||
private final File content;
|
private final File content;
|
||||||
|
private final InputSupplier<FileInputStream> delegate;
|
||||||
|
|
||||||
public FilePayload(File content) {
|
public FilePayload(File content) {
|
||||||
checkArgument(checkNotNull(content, "content").exists(), "file must exist: " + content);
|
checkArgument(checkNotNull(content, "content").exists(), "file must exist: " + content);
|
||||||
|
this.delegate = Files.newInputStreamSupplier(content);
|
||||||
this.content = content;
|
this.content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,9 +57,10 @@ public class FilePayload implements Payload {
|
||||||
@Override
|
@Override
|
||||||
public InputStream getInput() {
|
public InputStream getInput() {
|
||||||
try {
|
try {
|
||||||
return new FileInputStream(content);
|
return delegate.getInput();
|
||||||
} catch (FileNotFoundException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException("file " + content + " does not exist", e);
|
Throwables.propagate(e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Designates that this parameter will be bound to a multipart form.
|
* Designates that this parameter will be bound to a multipart form.
|
||||||
*
|
*
|
||||||
|
@ -34,7 +32,13 @@ import javax.ws.rs.core.MediaType;
|
||||||
@Target(PARAMETER)
|
@Target(PARAMETER)
|
||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
public @interface PartParam {
|
public @interface PartParam {
|
||||||
|
// hacks as nulls are not allowed as default values
|
||||||
|
public static String NO_FILENAME = "---NO_FILENAME---";
|
||||||
|
public static String NO_CONTENT_TYPE = "---NO_CONTENT_TYPE---";
|
||||||
|
|
||||||
String name();
|
String name();
|
||||||
|
|
||||||
String contentType() default MediaType.TEXT_PLAIN;
|
String contentType() default NO_CONTENT_TYPE;
|
||||||
|
|
||||||
|
String filename() default NO_FILENAME;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.MultipartForm;
|
import org.jclouds.http.MultipartForm;
|
||||||
import org.jclouds.http.Payloads;
|
import org.jclouds.http.Payloads;
|
||||||
import org.jclouds.http.MultipartForm.Part;
|
import org.jclouds.http.MultipartForm.Part;
|
||||||
|
import org.jclouds.http.MultipartForm.Part.PartOptions;
|
||||||
import org.jclouds.http.functions.CloseContentAndReturn;
|
import org.jclouds.http.functions.CloseContentAndReturn;
|
||||||
import org.jclouds.http.functions.ParseSax;
|
import org.jclouds.http.functions.ParseSax;
|
||||||
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
|
||||||
|
@ -417,7 +418,8 @@ public class RestAnnotationProcessor<T> {
|
||||||
addHostHeaderIfAnnotatedWithVirtualHost(headers, request.getEndpoint().getHost(), method);
|
addHostHeaderIfAnnotatedWithVirtualHost(headers, request.getEndpoint().getHost(), method);
|
||||||
addFiltersIfAnnotated(method, request);
|
addFiltersIfAnnotated(method, request);
|
||||||
|
|
||||||
List<? extends Part> parts = getParts(method, args);
|
List<? extends Part> parts = getParts(method, args, Iterables.concat(tokenValues.entries(),
|
||||||
|
formParams.entries()));
|
||||||
if (parts.size() > 0) {
|
if (parts.size() > 0) {
|
||||||
if (formParams.size() > 0) {
|
if (formParams.size() > 0) {
|
||||||
parts = Lists.newLinkedList(Iterables.concat(Iterables
|
parts = Lists.newLinkedList(Iterables.concat(Iterables
|
||||||
|
@ -1005,15 +1007,21 @@ public class RestAnnotationProcessor<T> {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<? extends Part> getParts(Method method, Object... args) {
|
List<? extends Part> getParts(Method method, Object[] args,
|
||||||
|
Iterable<Entry<String, String>> iterable) {
|
||||||
List<Part> parts = Lists.newLinkedList();
|
List<Part> parts = Lists.newLinkedList();
|
||||||
Map<Integer, Set<Annotation>> indexToPartParam = methodToIndexOfParamToPartParamAnnotations
|
Map<Integer, Set<Annotation>> indexToPartParam = methodToIndexOfParamToPartParamAnnotations
|
||||||
.get(method);
|
.get(method);
|
||||||
for (Entry<Integer, Set<Annotation>> entry : indexToPartParam.entrySet()) {
|
for (Entry<Integer, Set<Annotation>> entry : indexToPartParam.entrySet()) {
|
||||||
for (Annotation key : entry.getValue()) {
|
for (Annotation key : entry.getValue()) {
|
||||||
PartParam param = (PartParam) key;
|
PartParam param = (PartParam) key;
|
||||||
Part part = Part.create(param.name(), Payloads.newPayload(args[entry.getKey()]), param
|
PartOptions options = new PartOptions();
|
||||||
.contentType());
|
if (!PartParam.NO_CONTENT_TYPE.equals(param.contentType()))
|
||||||
|
options.contentType(param.contentType());
|
||||||
|
if (!PartParam.NO_FILENAME.equals(param.filename()))
|
||||||
|
options.filename(replaceTokens(param.filename(), iterable));
|
||||||
|
Part part = Part.create(param.name(), Payloads.newPayload(args[entry.getKey()]),
|
||||||
|
options);
|
||||||
parts.add(part);
|
parts.add(part);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -117,7 +116,7 @@ public class Utils {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String replaceTokens(String value, Collection<Entry<String, String>> tokenValues) {
|
public static String replaceTokens(String value, Iterable<Entry<String, String>> tokenValues) {
|
||||||
for (Entry<String, String> tokenValue : tokenValues) {
|
for (Entry<String, String> tokenValue : tokenValues) {
|
||||||
value = replaceAll(value, TOKEN_TO_PATTERN.get(tokenValue.getKey()), tokenValue.getValue());
|
value = replaceAll(value, TOKEN_TO_PATTERN.get(tokenValue.getKey()), tokenValue.getValue());
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import java.io.OutputStream;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
import org.jclouds.http.MultipartForm.Part;
|
import org.jclouds.http.MultipartForm.Part;
|
||||||
|
import org.jclouds.http.MultipartForm.Part.PartOptions;
|
||||||
import org.jclouds.http.payloads.FilePayload;
|
import org.jclouds.http.payloads.FilePayload;
|
||||||
import org.jclouds.http.payloads.StringPayload;
|
import org.jclouds.http.payloads.StringPayload;
|
||||||
import org.jclouds.util.Utils;
|
import org.jclouds.util.Utils;
|
||||||
|
@ -99,7 +100,8 @@ public class MultipartFormTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Part newPart(String data) {
|
private Part newPart(String data) {
|
||||||
return Part.create("file", new MockFilePayload(data), MediaType.TEXT_PLAIN);
|
return Part.create("file", new MockFilePayload(data), new PartOptions()
|
||||||
|
.contentType(MediaType.TEXT_PLAIN));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addData(String boundary, String data, StringBuilder builder) {
|
private void addData(String boundary, String data, StringBuilder builder) {
|
||||||
|
|
|
@ -619,19 +619,22 @@ public class RestAnnotationProcessorTest {
|
||||||
@Endpoint(Localhost.class)
|
@Endpoint(Localhost.class)
|
||||||
static interface TestMultipartForm {
|
static interface TestMultipartForm {
|
||||||
@POST
|
@POST
|
||||||
public void withStringPart(@PartParam(name = "fooble") String path);
|
void withStringPart(@PartParam(name = "fooble") String path);
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
public void withParamStringPart(@FormParam("name") String name,
|
void withParamStringPart(@FormParam("name") String name, @PartParam(name = "file") String path);
|
||||||
@PartParam(name = "file") String path);
|
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
public void withParamFilePart(@FormParam("name") String name,
|
void withParamFilePart(@FormParam("name") String name, @PartParam(name = "file") File path);
|
||||||
@PartParam(name = "file") File path);
|
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
public void withParamFileBinaryPart(@FormParam("name") String name,
|
void withParamFileBinaryPart(@FormParam("name") String name,
|
||||||
@PartParam(name = "file", contentType = MediaType.APPLICATION_OCTET_STREAM) File path);
|
@PartParam(name = "file", contentType = MediaType.APPLICATION_OCTET_STREAM) File path);
|
||||||
|
|
||||||
|
@POST
|
||||||
|
void withParamByteArrayBinaryPart(
|
||||||
|
@FormParam("name") String name,
|
||||||
|
@PartParam(name = "file", contentType = MediaType.APPLICATION_OCTET_STREAM, filename = "{name}.tar.gz") byte[] content);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultipartWithStringPart() throws SecurityException, NoSuchMethodException,
|
public void testMultipartWithStringPart() throws SecurityException, NoSuchMethodException,
|
||||||
|
@ -641,11 +644,10 @@ public class RestAnnotationProcessorTest {
|
||||||
.createRequest(method, "foobledata");
|
.createRequest(method, "foobledata");
|
||||||
assertRequestLineEquals(httpRequest, "POST http://localhost:9999 HTTP/1.1");
|
assertRequestLineEquals(httpRequest, "POST http://localhost:9999 HTTP/1.1");
|
||||||
assertHeadersEqual(httpRequest,
|
assertHeadersEqual(httpRequest,
|
||||||
"Content-Length: 119\nContent-Type: multipart/form-data; boundary=--JCLOUDS--\n");
|
"Content-Length: 93\nContent-Type: multipart/form-data; boundary=--JCLOUDS--\n");
|
||||||
assertPayloadEquals(httpRequest,//
|
assertPayloadEquals(httpRequest,//
|
||||||
"----JCLOUDS--\r\n" + //
|
"----JCLOUDS--\r\n" + //
|
||||||
"Content-Disposition: form-data; name=\"fooble\"\r\n" + //
|
"Content-Disposition: form-data; name=\"fooble\"\r\n" + //
|
||||||
"Content-Type: text/plain\r\n" + //
|
|
||||||
"\r\n" + //
|
"\r\n" + //
|
||||||
"foobledata\r\n" + //
|
"foobledata\r\n" + //
|
||||||
"----JCLOUDS----\r\n");
|
"----JCLOUDS----\r\n");
|
||||||
|
@ -659,7 +661,7 @@ public class RestAnnotationProcessorTest {
|
||||||
.createRequest(method, "name", "foobledata");
|
.createRequest(method, "name", "foobledata");
|
||||||
assertRequestLineEquals(httpRequest, "POST http://localhost:9999 HTTP/1.1");
|
assertRequestLineEquals(httpRequest, "POST http://localhost:9999 HTTP/1.1");
|
||||||
assertHeadersEqual(httpRequest,
|
assertHeadersEqual(httpRequest,
|
||||||
"Content-Length: 185\nContent-Type: multipart/form-data; boundary=--JCLOUDS--\n");
|
"Content-Length: 159\nContent-Type: multipart/form-data; boundary=--JCLOUDS--\n");
|
||||||
assertPayloadEquals(httpRequest,//
|
assertPayloadEquals(httpRequest,//
|
||||||
"----JCLOUDS--\r\n" + //
|
"----JCLOUDS--\r\n" + //
|
||||||
"Content-Disposition: form-data; name=\"name\"\r\n" + //
|
"Content-Disposition: form-data; name=\"name\"\r\n" + //
|
||||||
|
@ -667,7 +669,6 @@ public class RestAnnotationProcessorTest {
|
||||||
"name\r\n" + // /
|
"name\r\n" + // /
|
||||||
"----JCLOUDS--\r\n" + //
|
"----JCLOUDS--\r\n" + //
|
||||||
"Content-Disposition: form-data; name=\"file\"\r\n" + //
|
"Content-Disposition: form-data; name=\"file\"\r\n" + //
|
||||||
"Content-Type: text/plain\r\n" + //
|
|
||||||
"\r\n" + //
|
"\r\n" + //
|
||||||
"foobledata\r\n" + //
|
"foobledata\r\n" + //
|
||||||
"----JCLOUDS----\r\n");
|
"----JCLOUDS----\r\n");
|
||||||
|
@ -685,20 +686,49 @@ public class RestAnnotationProcessorTest {
|
||||||
.createRequest(method, "name", file);
|
.createRequest(method, "name", file);
|
||||||
assertRequestLineEquals(httpRequest, "POST http://localhost:9999 HTTP/1.1");
|
assertRequestLineEquals(httpRequest, "POST http://localhost:9999 HTTP/1.1");
|
||||||
assertHeadersEqual(httpRequest,
|
assertHeadersEqual(httpRequest,
|
||||||
"Content-Length: 185\nContent-Type: multipart/form-data; boundary=--JCLOUDS--\n");
|
"Content-Length: " + (172 + file.getName().length())
|
||||||
|
+ "\nContent-Type: multipart/form-data; boundary=--JCLOUDS--\n");
|
||||||
assertPayloadEquals(httpRequest,//
|
assertPayloadEquals(httpRequest,//
|
||||||
"----JCLOUDS--\r\n" + //
|
"----JCLOUDS--\r\n" + //
|
||||||
"Content-Disposition: form-data; name=\"name\"\r\n" + //
|
"Content-Disposition: form-data; name=\"name\"\r\n" + //
|
||||||
"\r\n" + //
|
"\r\n" + //
|
||||||
"name\r\n" + // /
|
"name\r\n" + // /
|
||||||
"----JCLOUDS--\r\n" + //
|
"----JCLOUDS--\r\n" + //
|
||||||
"Content-Disposition: form-data; name=\"file\"\r\n" + //
|
"Content-Disposition: form-data; name=\"file\"; filename=\""
|
||||||
"Content-Type: text/plain\r\n" + //
|
+ file.getName() + "\"\r\n" + //
|
||||||
"\r\n" + //
|
"\r\n" + //
|
||||||
"foobledata\r\n" + //
|
"foobledata\r\n" + //
|
||||||
"----JCLOUDS----\r\n");
|
"----JCLOUDS----\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testMultipartWithParamByteArrayPart() throws SecurityException,
|
||||||
|
NoSuchMethodException, IOException {
|
||||||
|
Method method = TestMultipartForm.class.getMethod("withParamByteArrayBinaryPart",
|
||||||
|
String.class, byte[].class);
|
||||||
|
GeneratedHttpRequest<TestMultipartForm> httpRequest = factory(TestMultipartForm.class)
|
||||||
|
.createRequest(method, "name", "goo".getBytes());
|
||||||
|
assertRequestLineEquals(httpRequest, "POST http://localhost:9999 HTTP/1.1");
|
||||||
|
assertHeadersEqual(httpRequest,
|
||||||
|
"Content-Length: 216\nContent-Type: multipart/form-data; boundary=--JCLOUDS--\n");
|
||||||
|
assertPayloadEquals(httpRequest,//
|
||||||
|
"----JCLOUDS--\r\n"
|
||||||
|
+ //
|
||||||
|
"Content-Disposition: form-data; name=\"name\"\r\n"
|
||||||
|
+ //
|
||||||
|
"\r\n"
|
||||||
|
+ //
|
||||||
|
"name\r\n"
|
||||||
|
+ // /
|
||||||
|
"----JCLOUDS--\r\n"
|
||||||
|
+ //
|
||||||
|
"Content-Disposition: form-data; name=\"file\"; filename=\"name.tar.gz\"\r\n"
|
||||||
|
+ //
|
||||||
|
"Content-Type: application/octet-stream\r\n" + //
|
||||||
|
"\r\n" + //
|
||||||
|
"goo\r\n" + //
|
||||||
|
"----JCLOUDS----\r\n");
|
||||||
|
};
|
||||||
|
|
||||||
public void testMultipartWithParamFileBinaryPart() throws SecurityException,
|
public void testMultipartWithParamFileBinaryPart() throws SecurityException,
|
||||||
NoSuchMethodException, IOException {
|
NoSuchMethodException, IOException {
|
||||||
Method method = TestMultipartForm.class.getMethod("withParamFileBinaryPart", String.class,
|
Method method = TestMultipartForm.class.getMethod("withParamFileBinaryPart", String.class,
|
||||||
|
@ -710,15 +740,21 @@ public class RestAnnotationProcessorTest {
|
||||||
GeneratedHttpRequest<TestMultipartForm> httpRequest = factory(TestMultipartForm.class)
|
GeneratedHttpRequest<TestMultipartForm> httpRequest = factory(TestMultipartForm.class)
|
||||||
.createRequest(method, "name", file);
|
.createRequest(method, "name", file);
|
||||||
assertRequestLineEquals(httpRequest, "POST http://localhost:9999 HTTP/1.1");
|
assertRequestLineEquals(httpRequest, "POST http://localhost:9999 HTTP/1.1");
|
||||||
assertHeadersEqual(httpRequest,
|
assertHeadersEqual(httpRequest, "Content-Length: " + (207 + file.getName().length())
|
||||||
"Content-Length: 194\nContent-Type: multipart/form-data; boundary=--JCLOUDS--\n");
|
+ "\nContent-Type: multipart/form-data; boundary=--JCLOUDS--\n");
|
||||||
assertPayloadEquals(httpRequest,//
|
assertPayloadEquals(httpRequest,//
|
||||||
"----JCLOUDS--\r\n" + //
|
"----JCLOUDS--\r\n"
|
||||||
"Content-Disposition: form-data; name=\"name\"\r\n" + //
|
+ //
|
||||||
"\r\n" + //
|
"Content-Disposition: form-data; name=\"name\"\r\n"
|
||||||
"name\r\n" + // /
|
+ //
|
||||||
"----JCLOUDS--\r\n" + //
|
"\r\n"
|
||||||
"Content-Disposition: form-data; name=\"file\"\r\n" + //
|
+ //
|
||||||
|
"name\r\n"
|
||||||
|
+ // /
|
||||||
|
"----JCLOUDS--\r\n"
|
||||||
|
+ //
|
||||||
|
"Content-Disposition: form-data; name=\"file\"; filename=\""
|
||||||
|
+ file.getName() + "\"\r\n" + //
|
||||||
"Content-Type: application/octet-stream\r\n" + //
|
"Content-Type: application/octet-stream\r\n" + //
|
||||||
"\r\n" + //
|
"\r\n" + //
|
||||||
"'(2\r\n" + //
|
"'(2\r\n" + //
|
||||||
|
|
|
@ -163,12 +163,26 @@ public class BouncyCastleEncryptionService extends BaseEncryptionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String sha1Base64(String toEncode) throws NoSuchAlgorithmException,
|
public String sha1Base64(InputStream plainBytes) throws NoSuchAlgorithmException,
|
||||||
NoSuchProviderException, InvalidKeyException {
|
NoSuchProviderException, InvalidKeyException {
|
||||||
byte[] plainBytes = toEncode.getBytes();
|
|
||||||
Digest digest = new SHA1Digest();
|
Digest digest = new SHA1Digest();
|
||||||
byte[] resBuf = new byte[digest.getDigestSize()];
|
byte[] resBuf = new byte[digest.getDigestSize()];
|
||||||
digest.update(plainBytes, 0, plainBytes.length);
|
byte[] buffer = new byte[1024];
|
||||||
|
long length = 0;
|
||||||
|
int numRead = -1;
|
||||||
|
try {
|
||||||
|
do {
|
||||||
|
numRead = plainBytes.read(buffer);
|
||||||
|
if (numRead > 0) {
|
||||||
|
length += numRead;
|
||||||
|
digest.update(buffer, 0, numRead);
|
||||||
|
}
|
||||||
|
} while (numRead != -1);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
Closeables.closeQuietly(plainBytes);
|
||||||
|
}
|
||||||
digest.doFinal(resBuf, 0);
|
digest.doFinal(resBuf, 0);
|
||||||
return toBase64String(resBuf);
|
return toBase64String(resBuf);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue