/* * * Copyright (c) 2013 - 2017 Lijun Liao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3 * as published by the Free Software Foundation with the addition of the * following permission added to Section 15 as permitted in Section 7(a): * * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY * THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT * OF THIRD PARTY RIGHTS. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License. * * You can be released from the requirements of the license by purchasing * a commercial license. Buying such a license is mandatory as soon as you * develop commercial activities involving the XiPKI software without * disclosing the source code of your own applications. * * For more information, please contact Lijun Liao at this * address: lijun.liao@gmail.com */ package org.xipki.commons.console.karaf.command; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import org.apache.karaf.shell.api.action.Argument; import org.apache.karaf.shell.api.action.Command; import org.apache.karaf.shell.api.action.Completion; import org.apache.karaf.shell.api.action.Option; import org.apache.karaf.shell.api.action.lifecycle.Service; import org.bouncycastle.util.encoders.Base64; import org.xipki.commons.common.util.IoUtil; import org.xipki.commons.common.util.StringUtil; import org.xipki.commons.console.karaf.IllegalCmdParamException; import org.xipki.commons.console.karaf.XipkiCommandSupport; import org.xipki.commons.console.karaf.completer.FilePathCompleter; /** * @author Lijun Liao * @since 2.1.0 */ @Command(scope = "xipki-cmd", name = "curl", description = "transfer a URL") @Service public class CurlCmd extends XipkiCommandSupport { @Argument(index = 0, name = "url", required = true, description = "URL\n" + "(required)") private String url; @Option(name = "--verbose", aliases = "-v", description = "show request and response verbosely") private Boolean verbose = Boolean.FALSE; @Option(name = "--post", aliases = "-p", description = "send the request via HTTP POST") private Boolean usePost = Boolean.FALSE; @Option(name = "--data", aliases = "-d", description = "data to be sent in a POST request") private String postData; @Option(name = "--data-charset", aliases = "-c", description = "charset of data") private String postDataCharSet = "UTF-8"; @Option(name = "--data-file", description = "file contains the data to be sent in a POST request") private String postDataFile; @Option(name = "--out", description = "where to save the response") @Completion(FilePathCompleter.class) private String outFile; @Option(name = "--header", aliases = "-h", multiValued = true, description = "header in request") @Completion(FilePathCompleter.class) private List<String> headers; @Option(name = "--user", aliases = "-u", description = "User and password of the form user:password") private String userPassword; @Override protected Object doExecute() throws Exception { byte[] dataBytes = null; if (postData != null) { dataBytes = postData.getBytes(postDataCharSet); } else if (postDataFile != null) { dataBytes = IoUtil.read(postDataFile); } if (dataBytes != null) { usePost = Boolean.TRUE; } URL newUrl = new URL(url); HttpURLConnection httpConn = IoUtil.openHttpConn(newUrl); try { httpConn.setRequestMethod(usePost ? "POST" : "GET"); httpConn.setUseCaches(false); if (headers != null) { for (String header : headers) { int idx = header.indexOf(':'); if (idx == -1 || idx == header.length() - 1) { throw new IllegalCmdParamException("invalid HTTP header: '" + header + "'"); } String key = header.substring(0, idx); String value = header.substring(idx + 1).trim(); httpConn.setRequestProperty(key, value); } } if (userPassword != null) { int idx = userPassword.indexOf(':'); if (idx == -1 || idx == userPassword.length() - 1) { throw new IllegalCmdParamException("invalid user"); } httpConn.setRequestProperty("Authorization", "Basic " + Base64.toBase64String(userPassword.getBytes())); } Map<String, List<String>> properties; if (dataBytes == null) { properties = httpConn.getRequestProperties(); } else { httpConn.setDoOutput(true); httpConn.setRequestProperty("Content-Length", Integer.toString(dataBytes.length)); properties = httpConn.getRequestProperties(); OutputStream outputstream = httpConn.getOutputStream(); outputstream.write(dataBytes); outputstream.flush(); } // show the request headers if (verbose) { println("=====request====="); println(" HTTP method: " + httpConn.getRequestMethod()); for (String key : properties.keySet()) { List<String> values = properties.get(key); for (String value : values) { println(" " + key + ": " + value); } } } // read the response int respCode = httpConn.getResponseCode(); if (verbose) { println("=====response====="); println(" response code: " + respCode + " " + httpConn.getResponseMessage()); properties = httpConn.getHeaderFields(); for (String key : properties.keySet()) { if (key == null) { continue; } List<String> values = properties.get(key); for (String value : values) { println(" " + key + ": " + value); } } println("=====response content====="); } else { if (respCode != HttpURLConnection.HTTP_OK) { println("ERROR: bad response: " + httpConn.getResponseCode() + " " + httpConn.getResponseMessage()); } } InputStream inputStream = null; InputStream errorStream = null; try { inputStream = httpConn.getInputStream(); } catch (IOException ex) { errorStream = httpConn.getErrorStream(); } byte[] respContentBytes; if (inputStream != null) { respContentBytes = IoUtil.read(inputStream); } else if (errorStream != null) { respContentBytes = IoUtil.read(errorStream); } else { respContentBytes = null; } if (respContentBytes == null || respContentBytes.length == 0) { println("NO response content"); return null; } if (outFile != null) { String fn = (errorStream != null) ? "error-" + outFile : outFile; saveVerbose("saved response to", new File(fn), respContentBytes); } else { String ct = httpConn.getHeaderField("Content-Type"); String charset = getCharset(ct); if (charset == null) { charset = "UTF-8"; } if (errorStream != null) { println("ERROR: "); } println(new String(respContentBytes, charset)); } } finally { httpConn.disconnect(); } return null; } private static String getCharset(final String contentType) { if (StringUtil.isBlank(contentType) || contentType.indexOf(';') == -1) { return null; } StringTokenizer st = new StringTokenizer(contentType, ";"); st.nextToken(); while (st.hasMoreTokens()) { String token = st.nextToken(); int idx = token.indexOf('='); if (idx == -1) { continue; } String paramName = token.substring(0, idx).trim(); if ("charset".equalsIgnoreCase(paramName)) { return token.substring(idx + 1, token.length()); } } return null; } }