@@ -11,8 +11,9 @@ import lombok.extern.slf4j.Slf4j; | |||||
import org.apache.http.entity.ContentType; | import org.apache.http.entity.ContentType; | ||||
import org.kohsuke.args4j.Option; | import org.kohsuke.args4j.Option; | ||||
import static org.cobbzilla.util.daemon.ZillaRuntime.readStdin; | |||||
import static org.cobbzilla.util.daemon.ZillaRuntime.shortError; | |||||
import java.io.InputStream; | |||||
import static org.cobbzilla.util.daemon.ZillaRuntime.*; | |||||
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | ||||
import static org.cobbzilla.util.json.JsonUtil.*; | import static org.cobbzilla.util.json.JsonUtil.*; | ||||
@@ -26,12 +27,22 @@ public class BubbleHttpEntityOptions extends BubbleHttpOptions { | |||||
try { | try { | ||||
return json(json(data, JsonNode.class, FULL_MAPPER_ALLOW_COMMENTS), COMPACT_MAPPER); | return json(json(data, JsonNode.class, FULL_MAPPER_ALLOW_COMMENTS), COMPACT_MAPPER); | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
log.warn("getRequestJson: error scrubbing comments from JSON, sending as-is: "+shortError(e)); | |||||
log.warn("getRequestJson: error scrubbing comments from JSON, sending as-is: " + shortError(e)); | |||||
} | } | ||||
} | } | ||||
return data; | return data; | ||||
} | } | ||||
public InputStream getRequestStream() { return System.in; } | |||||
public static final String USAGE_MULTIPART = "Send PUT or POST as a multipart-encoded file upload with this file name"; | |||||
public static final String OPT_MULTIPART = "-M"; | |||||
public static final String LONGOPT_MULTIPART= "--multipart"; | |||||
@Option(name=OPT_MULTIPART, aliases=LONGOPT_MULTIPART, usage=USAGE_MULTIPART) | |||||
@Getter @Setter private String multipartFileName = null; | |||||
public boolean hasMultipartFileName() { return !empty(multipartFileName); } | |||||
public static final String USAGE_CONTENT_TYPE = "Content-Type to send. Default is application/json"; | public static final String USAGE_CONTENT_TYPE = "Content-Type to send. Default is application/json"; | ||||
public static final String OPT_CONTENT_TYPE = "-C"; | public static final String OPT_CONTENT_TYPE = "-C"; | ||||
public static final String LONGOPT_CONTENT_TYPE= "--content-type"; | public static final String LONGOPT_CONTENT_TYPE= "--content-type"; | ||||
@@ -6,6 +6,7 @@ package bubble.main.http; | |||||
import bubble.main.BubbleApiMain; | import bubble.main.BubbleApiMain; | ||||
import org.apache.commons.io.IOUtils; | import org.apache.commons.io.IOUtils; | ||||
import org.cobbzilla.util.collection.NameAndValue; | |||||
import org.cobbzilla.util.http.HttpRequestBean; | import org.cobbzilla.util.http.HttpRequestBean; | ||||
import org.cobbzilla.util.string.Base64; | import org.cobbzilla.util.string.Base64; | ||||
import org.cobbzilla.wizard.api.ApiException; | import org.cobbzilla.wizard.api.ApiException; | ||||
@@ -43,14 +44,25 @@ public abstract class BubbleHttpMain<OPT extends BubbleHttpOptions> extends Bubb | |||||
final String requestUrl = url.startsWith("/") ? url : "/" + url; | final String requestUrl = url.startsWith("/") ? url : "/" + url; | ||||
if (options.isRaw()) { | if (options.isRaw()) { | ||||
final String entity = options instanceof BubbleHttpEntityOptions | |||||
? ((BubbleHttpEntityOptions) options).getRequestJson() | |||||
: null; | |||||
final HttpRequestBean request = new HttpRequestBean(getMethod(), requestUrl, entity); | |||||
final HttpRequestBean request; | |||||
final BubbleHttpEntityOptions entityOptions = (options instanceof BubbleHttpEntityOptions) ? (BubbleHttpEntityOptions) options : null; | |||||
if (entityOptions != null) { | |||||
if (entityOptions.hasMultipartFileName()) { | |||||
request = new HttpRequestBean(getMethod(), requestUrl, entityOptions.getRequestStream(), entityOptions.getMultipartFileName(), NameAndValue.EMPTY_ARRAY); | |||||
} else { | |||||
request = new HttpRequestBean(getMethod(), requestUrl, entityOptions.getRequestJson()); | |||||
} | |||||
} else { | |||||
request = new HttpRequestBean(getMethod(), requestUrl); | |||||
} | |||||
if (options.hasHttpBasicUser()) request.setAuthUsername(options.getHttpBasicUser()); | if (options.hasHttpBasicUser()) request.setAuthUsername(options.getHttpBasicUser()); | ||||
if (options.hasHttpBasicPassword()) request.setAuthPassword(options.getHttpBasicPassword()); | if (options.hasHttpBasicPassword()) request.setAuthPassword(options.getHttpBasicPassword()); | ||||
IOUtils.copyLarge(getApiClient().getStream(request), System.out); | |||||
if (entityOptions != null && entityOptions.hasMultipartFileName()) { | |||||
IOUtils.copyLarge(getApiClient().uploadMultipartStream(request, entityOptions.getMultipartFileName()), System.out); | |||||
} else { | |||||
IOUtils.copyLarge(getApiClient().getStream(request), System.out); | |||||
} | |||||
} else { | } else { | ||||
RestResponse response = null; | RestResponse response = null; | ||||
try { | try { | ||||
@@ -9,6 +9,7 @@ import lombok.Getter; | |||||
import lombok.Setter; | import lombok.Setter; | ||||
import org.kohsuke.args4j.Option; | import org.kohsuke.args4j.Option; | ||||
import static org.cobbzilla.util.daemon.ZillaRuntime.die; | |||||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | ||||
public class BubbleHttpOptions extends BubbleApiOptionsBase { | public class BubbleHttpOptions extends BubbleApiOptionsBase { | ||||
@@ -26,11 +27,21 @@ public class BubbleHttpOptions extends BubbleApiOptionsBase { | |||||
@Getter @Setter private String httpBasicUser; | @Getter @Setter private String httpBasicUser; | ||||
public boolean hasHttpBasicUser () { return !empty(httpBasicUser); } | public boolean hasHttpBasicUser () { return !empty(httpBasicUser); } | ||||
public static final String USAGE_HTTP_PASS = "HTTP Basic Auth username"; | |||||
public static final String USAGE_HTTP_PASS = "HTTP Basic Auth username. Use @ENV_VAR_NAME to read an env var"; | |||||
public static final String OPT_HTTP_PASS = "-W"; | public static final String OPT_HTTP_PASS = "-W"; | ||||
public static final String LONGOPT_HTTP_PASS= "--password"; | public static final String LONGOPT_HTTP_PASS= "--password"; | ||||
@Option(name=OPT_HTTP_PASS, aliases=LONGOPT_HTTP_PASS, usage=USAGE_HTTP_PASS) | @Option(name=OPT_HTTP_PASS, aliases=LONGOPT_HTTP_PASS, usage=USAGE_HTTP_PASS) | ||||
@Getter @Setter private String httpBasicPassword; | |||||
@Setter private String httpBasicPassword; | |||||
public String getHttpBasicPassword () { | |||||
if (!hasHttpBasicPassword()) return null; | |||||
if (httpBasicPassword.startsWith("@")) { | |||||
final String envVarName = httpBasicPassword.substring(1); | |||||
final String pass = System.getenv(envVarName); | |||||
if (empty(pass)) return die("getHttpBasicPassword: env var not defined: "+ envVarName); | |||||
return pass; | |||||
} | |||||
return httpBasicPassword; | |||||
} | |||||
public boolean hasHttpBasicPassword () { return !empty(httpBasicPassword); } | public boolean hasHttpBasicPassword () { return !empty(httpBasicPassword); } | ||||
public static final String USAGE_RAW = "Raw response: do not parse as JSON"; | public static final String USAGE_RAW = "Raw response: do not parse as JSON"; | ||||
@@ -12,9 +12,11 @@ import lombok.extern.slf4j.Slf4j; | |||||
import net.lingala.zip4j.ZipFile; | import net.lingala.zip4j.ZipFile; | ||||
import net.lingala.zip4j.exception.ZipException; | import net.lingala.zip4j.exception.ZipException; | ||||
import org.cobbzilla.util.http.*; | import org.cobbzilla.util.http.*; | ||||
import org.cobbzilla.util.io.ByteLimitedInputStream; | |||||
import org.cobbzilla.util.io.FileUtil; | import org.cobbzilla.util.io.FileUtil; | ||||
import org.cobbzilla.util.io.TempDir; | import org.cobbzilla.util.io.TempDir; | ||||
import org.cobbzilla.util.string.Base64; | import org.cobbzilla.util.string.Base64; | ||||
import org.cobbzilla.util.system.Bytes; | |||||
import org.cobbzilla.wizard.auth.LoginRequest; | import org.cobbzilla.wizard.auth.LoginRequest; | ||||
import org.glassfish.jersey.media.multipart.FormDataParam; | import org.glassfish.jersey.media.multipart.FormDataParam; | ||||
import org.glassfish.jersey.server.ContainerRequest; | import org.glassfish.jersey.server.ContainerRequest; | ||||
@@ -47,7 +49,10 @@ public class NodeManagerResource { | |||||
public static final String ROOT_DIR_PREFIX = "root_dir/"; | public static final String ROOT_DIR_PREFIX = "root_dir/"; | ||||
public static final String COMPONENT_ROOT = "root"; | public static final String COMPONENT_ROOT = "root"; | ||||
public static final Set<String> PATCH_COMPONENTS = new HashSet<>(Arrays.asList(new String[]{COMPONENT_ROOT, "bubble", "mitmproxy"})); | public static final Set<String> PATCH_COMPONENTS = new HashSet<>(Arrays.asList(new String[]{COMPONENT_ROOT, "bubble", "mitmproxy"})); | ||||
// other constants | |||||
public static final String AUTH_BASIC_PREFIX = "Basic "; | public static final String AUTH_BASIC_PREFIX = "Basic "; | ||||
public static final long MAX_PATCH_SIZE = 200 * Bytes.MB; | |||||
private BubbleNode node; | private BubbleNode node; | ||||
@@ -170,7 +175,7 @@ public class NodeManagerResource { | |||||
.setMethod(HttpMethods.POST); | .setMethod(HttpMethods.POST); | ||||
// create a zipfile containing the file at the proper path | // create a zipfile containing the file at the proper path | ||||
final File zipFile = buildPatchZip(component, path, in); | |||||
final File zipFile = buildPatchZip(component, path, new ByteLimitedInputStream(in, MAX_PATCH_SIZE)); | |||||
// register patch with NodeManagerService, receive URL | // register patch with NodeManagerService, receive URL | ||||
final String url = nodeManagerService.registerPatch(zipFile); | final String url = nodeManagerService.registerPatch(zipFile); | ||||
@@ -186,10 +191,12 @@ public class NodeManagerResource { | |||||
} | } | ||||
if (path.startsWith("/")) path = path.substring(1); | if (path.startsWith("/")) path = path.substring(1); | ||||
if (empty(path)) throw invalidEx("err.nodemanager.invalidPath"); | if (empty(path)) throw invalidEx("err.nodemanager.invalidPath"); | ||||
final String fullPath = abs(tempDir)+"/"+path; | |||||
final File parentDir = mkdirOrDie(dirname(fullPath)); | |||||
FileUtil.toFileOrDie(new File(parentDir, basename(path)), in); | |||||
final File dest = new File(abs(tempDir)+"/"+path); | |||||
mkdirOrDie(dirname(abs(dest))); | |||||
FileUtil.toFileOrDie(dest, in); | |||||
log.info("buildPatchZip: wrote temp file: "+abs(dest)); | |||||
final File zipFile = FileUtil.temp(".zip"); | final File zipFile = FileUtil.temp(".zip"); | ||||
log.info("buildPatchZip: zipping into zipFile: "+abs(zipFile)); | |||||
try { | try { | ||||
new ZipFile(zipFile).addFolder(tempDir); | new ZipFile(zipFile).addFolder(tempDir); | ||||
} catch (ZipException e) { | } catch (ZipException e) { | ||||
@@ -1 +1 @@ | |||||
Subproject commit 2b3bd69e5b3abf9644c975539beb8ba64c0c414c | |||||
Subproject commit a168473a8855ea2b05e4afffdce84ae7f54f7dbe |
@@ -1 +1 @@ | |||||
Subproject commit eb4581a2a654748b28ebf37f34e45b77967bc753 | |||||
Subproject commit 073dbbc3f8401e946b731e2269f6e253b04bee68 |