All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.intuit.karate.http.HttpUtils Maven / Gradle / Ivy

The newest version!
/*
 * The MIT License
 *
 * Copyright 2022 Karate Labs Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.intuit.karate.http;

import com.intuit.karate.FileUtils;
import com.intuit.karate.StringUtils;
import com.intuit.karate.http.cert.SelfSignedCertGenerator;
import com.intuit.karate.shell.Command;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import java.io.File;
import java.io.FileInputStream;
import java.net.URI;

import java.nio.charset.Charset;
import java.security.KeyStore;
import java.security.Security;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author pthomas3
 */
public class HttpUtils {

    private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class);

    private HttpUtils() {
        // only static methods
    }

    public static Charset parseContentTypeCharset(String mimeType) {
        Map map = parseContentTypeParams(mimeType);
        if (map == null) {
            return null;
        }
        String cs = map.get("charset");
        if (cs == null) {
            return null;
        }
        return Charset.forName(cs);
    }

    public static Map parseContentTypeParams(String mimeType) {
        List items = StringUtils.split(mimeType, ';', false);
        int count = items.size();
        if (count <= 1) {
            return null;
        }
        Map map = new LinkedHashMap<>(count - 1);
        for (int i = 1; i < count; i++) {
            String item = items.get(i);
            int pos = item.indexOf('=');
            if (pos == -1) {
                continue;
            }
            String key = item.substring(0, pos).trim();
            String val = item.substring(pos + 1).trim();
            map.put(key, val);
        }
        return map;
    }

    public static Map parseUriPattern(String pattern, String url) {
        int qpos = url.indexOf('?');
        if (qpos != -1) {
            url = url.substring(0, qpos);
        }
        List leftList = StringUtils.split(pattern, '/', false);
        List rightList = StringUtils.split(url, '/', false);
        int leftSize = leftList.size();
        int rightSize = rightList.size();
        if (rightSize != leftSize) {
            return null;
        }
        Map map = new LinkedHashMap<>(leftSize);
        for (int i = 0; i < leftSize; i++) {
            String left = leftList.get(i);
            String right = rightList.get(i);
            if (left.equals(right)) {
                continue;
            }
            if (left.startsWith("{") && left.endsWith("}")) {
                left = left.substring(1, left.length() - 1);
                map.put(left, right);
            } else {
                return null; // match failed
            }
        }
        return map;
    }

    public static String normaliseUriPath(String uri) {
        uri = uri.indexOf('?') == -1 ? uri : uri.substring(0, uri.indexOf('?'));
        if (uri.endsWith("/")) {
            uri = uri.substring(0, uri.length() - 1);
        }
        if (!uri.startsWith("/")) {
            uri = "/" + uri;
        }
        return uri;
    }

    public static StringUtils.Pair parseUriIntoUrlBaseAndPath(String rawUri) {
        int pos = rawUri.indexOf('/');
        if (pos == -1) {
            return StringUtils.pair(null, "");
        }
        URI uri;
        try {
            uri = new URI(rawUri);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (uri.getHost() == null) {
            return StringUtils.pair(null, rawUri);
        }
        String path = uri.getRawPath();
        pos = rawUri.lastIndexOf(path); // edge case that path is just "/"
        String urlBase = rawUri.substring(0, pos);
        return StringUtils.pair(urlBase, rawUri.substring(pos));
    }

    //==========================================================================
    //
    public static void flushAndClose(Channel ch) {
        if (ch != null && ch.isActive()) {
            ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
        }
    }

    public static void createSelfSignedCertificate(File cert, File key) {
        try {
            SelfSignedCertGenerator ssc = new SelfSignedCertGenerator();
            FileUtils.copy(ssc.getCertificate(), cert);
            FileUtils.copy(ssc.getPrivateKey(), key);
        } catch (Exception e) {
            throw new RuntimeException();
        }
    }
    
    private static final String PROXY_ALIAS = "karate-proxy";
    private static final String KEYSTORE_PASSWORD = "karate-secret";
    private static final String KEYSTORE_FILENAME = PROXY_ALIAS + ".jks";    

    public static SSLContext getSslContext(File keyStoreFile) {
        keyStoreFile = initKeyStore(keyStoreFile);
        String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
        if (algorithm == null) {
            algorithm = "SunX509";
        }
        try {
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream(keyStoreFile), KEYSTORE_PASSWORD.toCharArray());
            TrustManager[] trustManagers = new TrustManager[]{LenientTrustManager.INSTANCE};
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
            kmf.init(ks, KEYSTORE_PASSWORD.toCharArray());
            KeyManager[] keyManagers = kmf.getKeyManagers();
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(keyManagers, trustManagers, null);
            return ctx;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static File initKeyStore(File keyStoreFile) {
        if (keyStoreFile == null) {
            keyStoreFile = new File(KEYSTORE_FILENAME);
        }
        if (keyStoreFile.exists()) {
            if (logger.isTraceEnabled()) {
                logger.trace("keystore file already exists: {}", keyStoreFile);
            }
            return keyStoreFile;
        }
        File parentFile = keyStoreFile.getParentFile();
        if (parentFile != null) {
            parentFile.mkdirs();
        }
        Command.exec(false, parentFile, "keytool", "-genkey", "-alias", PROXY_ALIAS, "-keysize",
                "4096", "-validity", "36500", "-keyalg", "RSA", "-dname",
                "CN=" + PROXY_ALIAS, "-keypass", KEYSTORE_PASSWORD, "-storepass",
                KEYSTORE_PASSWORD, "-keystore", keyStoreFile.getName());
        Command.exec(false, parentFile, "keytool", "-exportcert", "-alias", PROXY_ALIAS, "-keystore",
                keyStoreFile.getName(), "-storepass", KEYSTORE_PASSWORD, "-file", keyStoreFile.getName() + ".der");
        return keyStoreFile;
    }

    public static FullHttpResponse createResponse(int status, String body) {
        return createResponse(HttpResponseStatus.valueOf(status), body);
    }

    public static FullHttpResponse createResponse(HttpResponseStatus status, String body) {
        byte[] bytes = FileUtils.toBytes(body);
        ByteBuf bodyBuf = Unpooled.copiedBuffer(bytes);
        DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, bodyBuf);
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, bytes.length);
        return response;
    }

    public static FullHttpResponse transform(FullHttpResponse original, String body) {
        FullHttpResponse response = createResponse(original.status(), body);
        response.headers().set(original.headers());
        return response;
    }

    private static final HttpResponseStatus CONNECTION_ESTABLISHED = new HttpResponseStatus(200, "Connection established");

    public static FullHttpResponse connectionEstablished() {
        return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, CONNECTION_ESTABLISHED);
    }

    public static void fixHeadersForProxy(HttpRequest request) {
        String adjustedUri = ProxyContext.removeHostColonPort(request.uri());
        request.setUri(adjustedUri);
        request.headers().remove(HttpHeaderNames.CONNECTION);
        // addViaHeader(request, PROXY_ALIAS);
    }

    public static void addViaHeader(HttpMessage msg, String alias) {
        StringBuilder sb = new StringBuilder();
        sb.append(msg.protocolVersion().majorVersion()).append('.');
        sb.append(msg.protocolVersion().minorVersion()).append(' ');
        sb.append(alias);
        List list;
        if (msg.headers().contains(HttpHeaderNames.VIA)) {
            List existing = msg.headers().getAll(HttpHeaderNames.VIA);
            list = new ArrayList<>(existing);
            list.add(sb.toString());
        } else {
            list = Collections.singletonList(sb.toString());
        }
        msg.headers().set(HttpHeaderNames.VIA, list);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy