<commit_before>/**
 * Copyright 2007-2016, Kaazing Corporation. All rights reserved.
 *
 * Licensed 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.kaazing.gateway.service.http.proxy;

import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_CONNECTION;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_LOCATION;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_SET_COOKIE;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_UPGRADE;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_VIA;
import static org.kaazing.gateway.transport.http.HttpStatus.CLIENT_NOT_FOUND;
import static org.kaazing.gateway.transport.http.HttpStatus.INFO_SWITCHING_PROTOCOLS;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_FORWARDED;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_X_FORWARDED_FOR;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_X_FORWARDED_HOST;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_X_FORWARDED_PROTO;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_X_FORWARDED_SERVER;
import static java.lang.String.format;

import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.apache.mina.core.future.CloseFuture;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.core.session.IoSessionInitializer;
import org.kaazing.gateway.resource.address.ResourceAddress;
import org.kaazing.gateway.resource.address.http.HttpResourceAddress;
import org.kaazing.gateway.resource.address.uri.URIUtils;
import org.kaazing.gateway.service.ServiceContext;
import org.kaazing.gateway.service.ServiceProperties;
import org.kaazing.gateway.service.proxy.AbstractProxyAcceptHandler;
import org.kaazing.gateway.service.proxy.AbstractProxyHandler;
import org.kaazing.gateway.transport.BridgeSession;
import org.kaazing.gateway.transport.IoHandlerAdapter;
import org.kaazing.gateway.transport.http.DefaultHttpSession;
import org.kaazing.gateway.transport.http.HttpAcceptSession;
import org.kaazing.gateway.transport.http.HttpConnectSession;
import org.kaazing.gateway.transport.http.HttpSession;
import org.kaazing.gateway.transport.http.HttpStatus;
import org.kaazing.mina.core.session.IoSessionEx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class HttpProxyServiceHandler extends AbstractProxyAcceptHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger("service.http.proxy");
    private static final String VIA_HEADER_FORMATTER = "1.1 kaazing-%s";
    private static final String FORWARDED_INJECT = "inject";
    private static final String FORWARDED_EXCLUDE = "exclude";
    private static final String FORWARDED_IGNORE = "ignore";
    private static final String FORWARDED_FOR = "for";
    private static final String FORWARDED_BY = "by";
    private static final String FORWARDED_PROTO = "proto";
    private static final String FORWARDED_HOST = "host";

    private static final Set KNOWN_SIMPLE_PROPERTIES;
    static {
        Set<String> set = new HashSet<>();
        set.add("rewrite-cookie-domain");
        set.add("rewrite-cookie-path");
        set.add("rewrite-location");
        set.add("use-forwarded");
        KNOWN_SIMPLE_PROPERTIES = Collections.unmodifiableSet(set);
    }
    private static final Set KNOWN_NESTED_PROPERTIES;
    static {
        Set<String> set = new HashSet<>();
        set.add("cookie-domain-mapping");
        set.add("cookie-path-mapping");
        set.add("location-mapping");
        KNOWN_NESTED_PROPERTIES = Collections.unmodifiableSet(set);
    }

    private final String viaHeader;
    private static final Set<String> USE_FORWARDED_VALUES;
    static {
        Set<String> set = new HashSet<>();
        set.add(FORWARDED_INJECT);
        set.add(FORWARDED_EXCLUDE);
        set.add(FORWARDED_IGNORE);
        USE_FORWARDED_VALUES = Collections.unmodifiableSet(set);
    }

    private String connectURI;
    private String useForwarded;
    private int remoteClientPort;
    private boolean rewriteCookieDomain;
    private boolean rewriteCookiePath;
    private boolean rewriteLocation;
    private Map<String, String> cookieDomainMap;
    private Map<String, String> cookiePathMap;
    private Map<String, String> locationMap;

    public HttpProxyServiceHandler() {
        viaHeader = String.format(VIA_HEADER_FORMATTER, UUID.randomUUID());
    }

    void init() {
        ServiceContext serviceContext = getServiceContext();

        Collection<String> acceptURIs = serviceContext.getAccepts();
        Collection<String> connectURIs = serviceContext.getConnects();

        String acceptURI = acceptURIs.iterator().next();
        connectURI = connectURIs.iterator().next();

        validateProperties(serviceContext);

        ServiceProperties properties = serviceContext.getProperties();

        rewriteCookieDomain = "enabled".equals(properties.get("rewrite-cookie-domain"));
        rewriteCookiePath = "enabled".equals(properties.get("rewrite-cookie-path"));
        rewriteLocation = !"disabled".equals(properties.get("rewrite-location"));

        cookieDomainMap = new HashMap<>();
        if (rewriteCookieDomain) {
            List<ServiceProperties> cookieDomainProperties = properties.getNested("cookie-domain-mapping");
            for (ServiceProperties sp : cookieDomainProperties) {
                cookieDomainMap.put(sp.get("from"), sp.get("to"));
            }
        }

        cookiePathMap = new HashMap<>();
        if (rewriteCookiePath) {
            List<ServiceProperties> cookiePathProperties = properties.getNested("cookie-path-mapping");
            for (ServiceProperties sp : cookiePathProperties) {
                cookiePathMap.put(sp.get("from"), sp.get("to"));
            }
        }

        locationMap = new HashMap<>();
        if (rewriteLocation) {
            List<ServiceProperties> locationProperties = properties.getNested("location-mapping");
            for (ServiceProperties sp : locationProperties) {
                locationMap.put(sp.get("from"), sp.get("to"));
            }
            locationMap.put(connectURI, acceptURI);
        }

        useForwarded = properties.get("use-forwarded");
        if (useForwarded == null) {
            useForwarded = FORWARDED_IGNORE;
        }
        if (!USE_FORWARDED_VALUES.contains(useForwarded)) {
            throw new IllegalArgumentException(serviceContext.getServiceName()
                    + " http.proxy service specifies unknown property value : " + useForwarded + " for use-forwarded");
        }
    }

    private void validateProperties(ServiceContext serviceContext) {
        ServiceProperties properties = serviceContext.getProperties();

        // validate all properties: rewrite-cookie-domain, rewrite-cookie-path, rewrite-location
        Iterable<String> simpleProperties = properties.simplePropertyNames();
        Set<String> unknownProperties = StreamSupport.stream(simpleProperties.spliterator(), false)
                .filter(p -> !KNOWN_SIMPLE_PROPERTIES.contains(p))
                .collect(Collectors.toSet());
        Iterable<String> nestedProperties = properties.nestedPropertyNames();
        StreamSupport.stream(nestedProperties.spliterator(), false)
                .filter(p -> !KNOWN_NESTED_PROPERTIES.contains(p))
                .forEach(unknownProperties::add);
        if (!unknownProperties.isEmpty()) {
            throw new IllegalArgumentException(serviceContext.getServiceName() +
                    " http.proxy service specifies unknown properties : " + unknownProperties);
        }
    }

    

    @Override
    protected AbstractProxyHandler createConnectHandler() {
        return new ConnectHandler();
    }

    @Override
    public void sessionOpened(IoSession session) {
    	// get the port number of the remote client
    	BridgeSession bridgeSession = (BridgeSession) session;
    	remoteClientPort = BridgeSession.REMOTE_ADDRESS.get(bridgeSession).getTransport().getResource().getPort();
        if (!session.isClosing()) {
            final DefaultHttpSession acceptSession = (DefaultHttpSession) session;
            // final Subject subject = ((IoSessionEx) acceptSession).getSubject();

            if (!validateRequestPath(acceptSession)) {
                acceptSession.setStatus(CLIENT_NOT_FOUND);
                acceptSession.close(false);
                return;
            }
            if (!validateNoLoopDetected(acceptSession)) {
                return;
            }

            ConnectSessionInitializer sessionInitializer = new ConnectSessionInitializer(acceptSession);
            ConnectFuture future = getServiceContext().connect(connectURI, getConnectHandler(), sessionInitializer);
            future.addListener(new ConnectListener(acceptSession));
            super.sessionOpened(acceptSession);
        }
    }

    private boolean validateRequestPath(DefaultHttpSession acceptSession) {
        URI requestURI = acceptSession.getRequestURI();<commit_msg>Added Log warning first time we see Http 1.0.<commit_after>/**
 * Copyright 2007-2016, Kaazing Corporation. All rights reserved.
 *
 * Licensed 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.kaazing.gateway.service.http.proxy;

import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_CONNECTION;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_LOCATION;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_SET_COOKIE;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_UPGRADE;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_VIA;
import static org.kaazing.gateway.transport.http.HttpStatus.CLIENT_NOT_FOUND;
import static org.kaazing.gateway.transport.http.HttpStatus.INFO_SWITCHING_PROTOCOLS;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_FORWARDED;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_X_FORWARDED_FOR;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_X_FORWARDED_HOST;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_X_FORWARDED_PROTO;
import static org.kaazing.gateway.transport.http.HttpHeaders.HEADER_X_FORWARDED_SERVER;
import static java.lang.String.format;

import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.apache.mina.core.future.CloseFuture;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.core.session.IoSessionInitializer;
import org.kaazing.gateway.resource.address.ResourceAddress;
import org.kaazing.gateway.resource.address.http.HttpResourceAddress;
import org.kaazing.gateway.resource.address.uri.URIUtils;
import org.kaazing.gateway.service.ServiceContext;
import org.kaazing.gateway.service.ServiceProperties;
import org.kaazing.gateway.service.proxy.AbstractProxyAcceptHandler;
import org.kaazing.gateway.service.proxy.AbstractProxyHandler;
import org.kaazing.gateway.transport.BridgeSession;
import org.kaazing.gateway.transport.IoHandlerAdapter;
import org.kaazing.gateway.transport.http.DefaultHttpSession;
import org.kaazing.gateway.transport.http.HttpAcceptSession;
import org.kaazing.gateway.transport.http.HttpConnectSession;
import org.kaazing.gateway.transport.http.HttpSession;
import org.kaazing.gateway.transport.http.HttpStatus;
import org.kaazing.mina.core.session.IoSessionEx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class HttpProxyServiceHandler extends AbstractProxyAcceptHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger("service.http.proxy");
    private static final String VIA_HEADER_FORMATTER = "1.1 kaazing-%s";
    private static final String FORWARDED_INJECT = "inject";
    private static final String FORWARDED_EXCLUDE = "exclude";
    private static final String FORWARDED_IGNORE = "ignore";
    private static final String FORWARDED_FOR = "for";
    private static final String FORWARDED_BY = "by";
    private static final String FORWARDED_PROTO = "proto";
    private static final String FORWARDED_HOST = "host";

    private static final Set KNOWN_SIMPLE_PROPERTIES;
    static {
        Set<String> set = new HashSet<>();
        set.add("rewrite-cookie-domain");
        set.add("rewrite-cookie-path");
        set.add("rewrite-location");
        set.add("use-forwarded");
        KNOWN_SIMPLE_PROPERTIES = Collections.unmodifiableSet(set);
    }
    private static final Set KNOWN_NESTED_PROPERTIES;
    static {
        Set<String> set = new HashSet<>();
        set.add("cookie-domain-mapping");
        set.add("cookie-path-mapping");
        set.add("location-mapping");
        KNOWN_NESTED_PROPERTIES = Collections.unmodifiableSet(set);
    }

    private final String viaHeader;
    private static final Set<String> USE_FORWARDED_VALUES;
    static {
        Set<String> set = new HashSet<>();
        set.add(FORWARDED_INJECT);
        set.add(FORWARDED_EXCLUDE);
        set.add(FORWARDED_IGNORE);
        USE_FORWARDED_VALUES = Collections.unmodifiableSet(set);
    }

    private String connectURI;
    private String useForwarded;
    private int remoteClientPort;
    private boolean rewriteCookieDomain;
    private boolean rewriteCookiePath;
    private boolean rewriteLocation;
    private Map<String, String> cookieDomainMap;
    private Map<String, String> cookiePathMap;
    private Map<String, String> locationMap;

    public HttpProxyServiceHandler() {
        viaHeader = String.format(VIA_HEADER_FORMATTER, UUID.randomUUID());
    }

    void init() {
        ServiceContext serviceContext = getServiceContext();

        Collection<String> acceptURIs = serviceContext.getAccepts();
        Collection<String> connectURIs = serviceContext.getConnects();

        String acceptURI = acceptURIs.iterator().next();
        connectURI = connectURIs.iterator().next();

        validateProperties(serviceContext);

        ServiceProperties properties = serviceContext.getProperties();

        rewriteCookieDomain = "enabled".equals(properties.get("rewrite-cookie-domain"));
        rewriteCookiePath = "enabled".equals(properties.get("rewrite-cookie-path"));
        rewriteLocation = !"disabled".equals(properties.get("rewrite-location"));

        cookieDomainMap = new HashMap<>();
        if (rewriteCookieDomain) {
            List<ServiceProperties> cookieDomainProperties = properties.getNested("cookie-domain-mapping");
            for (ServiceProperties sp : cookieDomainProperties) {
                cookieDomainMap.put(sp.get("from"), sp.get("to"));
            }
        }

        cookiePathMap = new HashMap<>();
        if (rewriteCookiePath) {
            List<ServiceProperties> cookiePathProperties = properties.getNested("cookie-path-mapping");
            for (ServiceProperties sp : cookiePathProperties) {
                cookiePathMap.put(sp.get("from"), sp.get("to"));
            }
        }

        locationMap = new HashMap<>();
        if (rewriteLocation) {
            List<ServiceProperties> locationProperties = properties.getNested("location-mapping");
            for (ServiceProperties sp : locationProperties) {
                locationMap.put(sp.get("from"), sp.get("to"));
            }
            locationMap.put(connectURI, acceptURI);
        }

        useForwarded = properties.get("use-forwarded");
        if (useForwarded == null) {
            useForwarded = FORWARDED_IGNORE;
        }
        if (!USE_FORWARDED_VALUES.contains(useForwarded)) {
            throw new IllegalArgumentException(serviceContext.getServiceName()
                    + " http.proxy service specifies unknown property value : " + useForwarded + " for use-forwarded");
        }
    }

    private void validateProperties(ServiceContext serviceContext) {
        ServiceProperties properties = serviceContext.getProperties();

        // validate all properties: rewrite-cookie-domain, rewrite-cookie-path, rewrite-location
        Iterable<String> simpleProperties = properties.simplePropertyNames();
        Set<String> unknownProperties = StreamSupport.stream(simpleProperties.spliterator(), false)
                .filter(p -> !KNOWN_SIMPLE_PROPERTIES.contains(p))
                .collect(Collectors.toSet());
        Iterable<String> nestedProperties = properties.nestedPropertyNames();
        StreamSupport.stream(nestedProperties.spliterator(), false)
                .filter(p -> !KNOWN_NESTED_PROPERTIES.contains(p))
                .forEach(unknownProperties::add);
        if (!unknownProperties.isEmpty()) {
            throw new IllegalArgumentException(serviceContext.getServiceName() +
                    " http.proxy service specifies unknown properties : " + unknownProperties);
        }
    }

    

    @Override
    protected AbstractProxyHandler createConnectHandler() {
        return new ConnectHandler();
    }

    @Override
    public void sessionOpened(IoSession session) {
    	// get the port number of the remote client
    	BridgeSession bridgeSession = (BridgeSession) session;
    	remoteClientPort = BridgeSession.REMOTE_ADDRESS.get(bridgeSession).getTransport().getResource().getPort();
        if (!session.isClosing()) {
            final DefaultHttpSession acceptSession = (DefaultHttpSession) session;
            // final Subject subject = ((IoSessionEx) acceptSession).getSubject();

            // log warning first time we see Http 1.0 request
            if (acceptSession.getVersion().toString().equals("HTTP/1.0")){
                LOGGER.warn(String.format("http.proxy service %s received an HTTP 1.0 request. HTTP 1.0 is not"
                         + " explicitly supported.", acceptSession.getService().toString()));
            }

            if (!validateRequestPath(acceptSession)) {
                acceptSession.setStatus(CLIENT_NOT_FOUND);
                acceptSession.close(false);
                return;
            }
            if (!validateNoLoopDetected(acceptSession)) {
                return;
            }

            ConnectSessionInitializer sessionInitializer = new ConnectSessionInitializer(acceptSession);
            ConnectFuture future = getServiceContext().connect(connectURI, getConnectHandler(), sessionInitializer);
            future.addListener(new ConnectListener(acceptSession));
            super.sessionOpened(acceptSession);
        }
    }

    private boolean validateRequestPath(DefaultHttpSession acceptSession) {
        URI requestURI = acceptSession.getRequestURI();
{'fraction_non_alphanumeric': 0.09799960583366181, 'fraction_numerical': 0.0020693732755222706, 'mean_word_length': 3.5879294755877034, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7156057', 'n_tokens_mistral': 5525, 'n_tokens_neox': 5328, 'n_words': 1111}