com.tangosol.coherence.http.AbstractHttpServer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of coherence Show documentation
Show all versions of coherence Show documentation
Oracle Coherence Community Edition
/*
* Copyright (c) 2000, 2020, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
package com.tangosol.coherence.http;
import com.oracle.coherence.common.net.SSLSocketProvider;
import com.oracle.coherence.common.net.SocketProvider;
import com.tangosol.net.CacheFactory;
import com.tangosol.net.Service;
import com.tangosol.net.Session;
import com.tangosol.net.options.WithClassLoader;
import com.tangosol.net.security.IdentityAsserter;
import com.tangosol.net.security.JAASIdentityAsserter;
import com.tangosol.net.security.UsernameAndPassword;
import com.tangosol.util.Base;
import com.tangosol.util.ClassHelper;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.security.auth.Subject;
import javax.ws.rs.core.SecurityContext;
import org.glassfish.hk2.api.DynamicConfiguration;
import org.glassfish.hk2.api.DynamicConfigurationService;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.ServiceLocatorFactory;
import org.glassfish.hk2.utilities.BuilderHelper;
import org.glassfish.jersey.server.ApplicationHandler;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
/**
* Abstract base class for {@link HttpServer} implementations.
*
* @author as 2011.06.16
*/
public abstract class AbstractHttpServer
implements HttpServer, HttpServerStats
{
// ----- HttpServer interface ------------------------------------------
/**
* {@inheritDoc}
*/
public void setAuthMethod(String sMethod)
{
if (AUTH_BASIC.equalsIgnoreCase(sMethod) ||
AUTH_CERT.equalsIgnoreCase(sMethod) ||
AUTH_CERT_BASIC.equalsIgnoreCase(sMethod) ||
AUTH_NONE.equalsIgnoreCase(sMethod))
{
m_sAuthMethod = sMethod;
}
else
{
throw new IllegalArgumentException("unsupported method: " + sMethod);
}
}
/**
* {@inheritDoc}
*/
public void setSession(Session session)
{
m_session = session;
}
/**
* {@inheritDoc}
*/
public void setLocalAddress(String sAddr)
{
m_sAddr = sAddr;
}
/**
* {@inheritDoc}
*/
public String getListenAddress()
{
return getLocalAddress();
}
/**
* {@inheritDoc}
*/
public int getListenPort()
{
return m_nPort;
}
/**
* {@inheritDoc}
*/
public void setLocalPort(int nPort)
{
m_nPort = nPort;
}
/**
* {@inheritDoc}
*/
public void setParentService(Service service)
{
m_serviceParent = service;
}
/**
* {@inheritDoc}
*/
public void setResourceConfig(ResourceConfig config)
{
setResourceConfig(Collections.singletonMap("/", config));
}
/**
* {@inheritDoc}
*/
public void setResourceConfig(Map mapConfig)
{
m_mapResourceConfig.clear();
m_mapResourceConfig.putAll(mapConfig);
}
/**
* {@inheritDoc}
*/
public void setSocketProvider(SocketProvider provider)
{
m_socketProvider = provider;
}
/**
* {@inheritDoc}
*/
public synchronized void start()
throws IOException
{
if (!m_fStarted)
{
startInternal();
m_fStarted = true;
}
}
/**
* {@inheritDoc}
*/
public synchronized void stop()
throws IOException
{
if (m_fStarted)
{
stopInternal();
m_fStarted = false;
}
}
// ----- HttpServerStats interface --------------------------------------
@Override
public long getRequestCount()
{
return m_cRequestCount;
}
@Override
public float getAverageRequestTime()
{
return m_cRequestCount == 0 ? 0 : (float) m_ltdTotalRequestTime / m_cRequestCount;
}
@Override
public float getRequestsPerSecond()
{
return m_cRequestCount == 0 ? 0 :
(m_cRequestCount * 1.0f) /
((float) (Base.getSafeTimeMillis() - m_ldtResetTime) / 1000.0f);
}
@Override
public long getErrorCount()
{
return m_cErrors;
}
@Override
public long getHttpStatusCount(int nPrefix)
{
validatePrefix(nPrefix);
return f_aStatusCodes[nPrefix - 1];
}
@Override
public void resetStats ()
{
m_cRequestCount = 0L;
m_ltdTotalRequestTime = 0L;
m_cErrors = 0L;
m_ldtResetTime = Base.getSafeTimeMillis();
for (int i = 0; i < f_aStatusCodes.length ; i++)
{
f_aStatusCodes[i] = 0L;
}
}
/**
* Increment the request count.
*/
protected void incrementRequestCount ()
{
m_cRequestCount++;
}
/**
* Add to the total request time.
*
* @param ltdStartTime the start of the request
*/
protected void logRequestTime(long ltdStartTime)
{
m_ltdTotalRequestTime += Base.getLastSafeTimeMillis() - ltdStartTime;
}
/**
* Increment the number of errors.
*/
protected void incrementErrors()
{
m_cErrors++;
}
/**
* Add to the total of status codes.
*
* @param nStatusCode the status code to add
*/
protected void logStatusCount(int nStatusCode)
{
if (nStatusCode >= 100 && nStatusCode <= 600)
{
f_aStatusCodes[nStatusCode / 100 - 1]++;
}
}
/**
* Validate that the prefix is a valid value.
*
* @param nPrefix the prefix to validate
*/
private void validatePrefix(int nPrefix)
{
if (nPrefix < 0 || nPrefix > f_aStatusCodes.length)
{
throw new IllegalArgumentException("Prefix must be between 0 and " + f_aStatusCodes.length);
}
}
/**
* Helper to dump current stats.
*/
protected void dumpStats()
{
StringBuilder sb = new StringBuilder("HTTP statistics: ")
.append(new Date())
.append('\n').append(" Start time: ").append(new Date(m_ldtResetTime))
.append('\n')
.append("Request Count=").append(getRequestCount())
.append(", Avg Req Time=").append(getAverageRequestTime())
.append(", Req/sec=").append(getRequestsPerSecond())
.append(", Errors=").append(getErrorCount())
.append("\nStatus counts: ");
for (int i = 0; i < f_aStatusCodes.length; i++)
{
sb.append("Status ")
.append((i + 1) * 100)
.append('=')
.append(f_aStatusCodes[i])
.append(' ');
}
CacheFactory.log(sb.toString(), CacheFactory.LOG_INFO);
}
// ----- abstract methods -----------------------------------------------
/**
* Start the server.
*
* @throws IOException if an error occurs
*/
protected abstract void startInternal()
throws IOException;
/**
* Stop the server.
*
* @throws IOException if an error occurs
*/
protected abstract void stopInternal()
throws IOException;
/**
* Factory method for Jersey container instances.
*
* @param config the resource configuration
* @param locator the parent service locator
*
* @return container instance
*/
protected abstract Object instantiateContainer(ResourceConfig config, ServiceLocator locator);
// ----- helpers --------------------------------------------------------
/**
* Create and configure a Jersey container that will process HTTP requests.
*
* @param resourceConfig resource configuration
*
* @return the container
*/
protected Object createContainer(ResourceConfig resourceConfig)
{
ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
ServiceLocator locator = factory.create(getClass().getName());
DynamicConfiguration config = locator
.getService(DynamicConfigurationService.class)
.createDynamicConfiguration();
if (getParentService() != null)
{
config.bind(BuilderHelper.createConstantDescriptor(getParentService(), null, Service.class));
}
config.bind(BuilderHelper.createConstantDescriptor(getSession(), null, Session.class));
config.commit();
return instantiateContainer(resourceConfig, locator);
}
/**
* Perform HTTP Basic authentication and return authenticated Subject.
*
* @param sAuth the value of Authorization header from the request
* @return authenticated Subject if successful, null otherwise
*/
protected Subject authenticate(String sAuth)
{
if (sAuth != null && sAuth.startsWith("Basic "))
{
sAuth = sAuth.substring("Basic ".length());
String[] values = fromBase64(sAuth).split(":");
if (values.length == 2)
{
String sUsername = values[0];
String sPassword = values[1];
try
{
return getIdentityAsserter().assertIdentity(
new UsernameAndPassword(sUsername, sPassword),
getParentService());
}
catch (SecurityException ignore)
{
// fall through and return null
}
}
}
return null;
}
/**
* Creates Subject instance using principal and credentials from the
* SSL session.
*
* @param session SSL session
*
* @return Subject for the client
*
* @throws SSLPeerUnverifiedException if the client is not authenticated
*/
protected Subject getSubjectFromSession(SSLSession session)
throws SSLPeerUnverifiedException
{
Set setPrincipals =
Collections.singleton(session.getPeerPrincipal());
Set setCredentials = new HashSet<>(
Arrays.asList(session.getPeerCertificates()));
return new Subject(true, setPrincipals, setCredentials, Collections.emptySet());
}
/**
* Handle HTTP(S) request.
*
* @param app web application that should handle request
* @param request the request
* @param subject the subject, can be null
*
* @throws IOException if an error occurs
*/
protected void handleRequest(final ApplicationHandler app,
final ContainerRequest request,
final Subject subject)
throws IOException
{
if (subject == null)
{
app.handle(request);
}
else
{
try
{
Subject.doAs(subject, new PrivilegedExceptionAction