blob: b1c1618e63d44d9c4ba33b9b285b7388770fa5a1 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.server;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.io.ByteArrayEndPoint;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.Scheduler;
public class LocalConnector extends AbstractConnector
{
private final BlockingQueue<LocalEndPoint> _connects = new LinkedBlockingQueue<>();
public LocalConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, ConnectionFactory... factories)
{
super(server,executor,scheduler,pool,acceptors,factories);
setIdleTimeout(30000);
}
public LocalConnector(Server server)
{
this(server, null, null, null, -1, new HttpConnectionFactory());
}
public LocalConnector(Server server, SslContextFactory sslContextFactory)
{
this(server, null, null, null, -1,AbstractConnectionFactory.getFactories(sslContextFactory,new HttpConnectionFactory()));
}
public LocalConnector(Server server, ConnectionFactory connectionFactory)
{
this(server, null, null, null, -1, connectionFactory);
}
public LocalConnector(Server server, ConnectionFactory connectionFactory, SslContextFactory sslContextFactory)
{
this(server, null, null, null, -1, AbstractConnectionFactory.getFactories(sslContextFactory,connectionFactory));
}
@Override
public Object getTransport()
{
return this;
}
/** Sends requests and get responses based on thread activity.
* Returns all the responses received once the thread activity has
* returned to the level it was before the requests.
* <p>
* This methods waits until the connection is closed or
* is idle for 1s before returning the responses.
* @param requests the requests
* @return the responses
* @throws Exception if the requests fail
*/
public String getResponses(String requests) throws Exception
{
return getResponses(requests, 5, TimeUnit.SECONDS);
}
/** Sends requests and get responses based on thread activity.
* Returns all the responses received once the thread activity has
* returned to the level it was before the requests.
* <p>
* This methods waits until the connection is closed or
* an idle period before returning the responses.
* @param requests the requests
* @param idleFor The time the response stream must be idle for before returning
* @param units The units of idleFor
* @return the responses
* @throws Exception if the requests fail
*/
public String getResponses(String requests,long idleFor,TimeUnit units) throws Exception
{
ByteBuffer result = getResponses(BufferUtil.toBuffer(requests,StandardCharsets.UTF_8),idleFor,units);
return result==null?null:BufferUtil.toString(result,StandardCharsets.UTF_8);
}
/** Sends requests and get's responses based on thread activity.
* Returns all the responses received once the thread activity has
* returned to the level it was before the requests.
* <p>
* This methods waits until the connection is closed or
* is idle for 1s before returning the responses.
* @param requestsBuffer the requests
* @return the responses
* @throws Exception if the requests fail
*/
public ByteBuffer getResponses(ByteBuffer requestsBuffer) throws Exception
{
return getResponses(requestsBuffer, 5, TimeUnit.SECONDS);
}
/** Sends requests and get's responses based on thread activity.
* Returns all the responses received once the thread activity has
* returned to the level it was before the requests.
* <p>
* This methods waits until the connection is closed or
* an idle period before returning the responses.
* @param requestsBuffer the requests
* @param idleFor The time the response stream must be idle for before returning
* @param units The units of idleFor
* @return the responses
* @throws Exception if the requests fail
*/
public ByteBuffer getResponses(ByteBuffer requestsBuffer,long idleFor,TimeUnit units) throws Exception
{
if (LOG.isDebugEnabled())
LOG.debug("requests {}", BufferUtil.toUTF8String(requestsBuffer));
LocalEndPoint endp = executeRequest(requestsBuffer);
endp.waitUntilClosedOrIdleFor(idleFor,units);
ByteBuffer responses = endp.takeOutput();
if (endp.isOutputShutdown())
endp.close();
if (LOG.isDebugEnabled())
LOG.debug("responses {}", BufferUtil.toUTF8String(responses));
return responses;
}
/**
* Execute a request and return the EndPoint through which
* responses can be received.
* @param rawRequest the request
* @return the local endpoint
*/
public LocalEndPoint executeRequest(String rawRequest)
{
return executeRequest(BufferUtil.toBuffer(rawRequest, StandardCharsets.UTF_8));
}
private LocalEndPoint executeRequest(ByteBuffer rawRequest)
{
if (!isStarted())
throw new IllegalStateException("!STARTED");
LocalEndPoint endp = new LocalEndPoint();
endp.addInput(rawRequest);
_connects.add(endp);
return endp;
}
@Override
protected void accept(int acceptorID) throws IOException, InterruptedException
{
if (LOG.isDebugEnabled())
LOG.debug("accepting {}", acceptorID);
LocalEndPoint endPoint = _connects.take();
endPoint.onOpen();
onEndPointOpened(endPoint);
Connection connection = getDefaultConnectionFactory().newConnection(this, endPoint);
endPoint.setConnection(connection);
connection.onOpen();
}
public class LocalEndPoint extends ByteArrayEndPoint
{
private final CountDownLatch _closed = new CountDownLatch(1);
public LocalEndPoint()
{
super(LocalConnector.this.getScheduler(), LocalConnector.this.getIdleTimeout());
setGrowOutput(true);
}
protected void execute(Runnable task)
{
getExecutor().execute(task);
}
@Override
public void onClose()
{
getConnection().onClose();
LocalConnector.this.onEndPointClosed(this);
super.onClose();
_closed.countDown();
}
@Override
public void doShutdownOutput()
{
super.shutdownOutput();
close();
}
public void waitUntilClosed()
{
while (isOpen())
{
try
{
if (!_closed.await(10,TimeUnit.SECONDS))
break;
}
catch(Exception e)
{
LOG.warn(e);
}
}
}
public void waitUntilClosedOrIdleFor(long idleFor,TimeUnit units)
{
Thread.yield();
int size=getOutput().remaining();
while (isOpen())
{
try
{
if (!_closed.await(idleFor,units))
{
if (size==getOutput().remaining())
{
if (LOG.isDebugEnabled())
LOG.debug("idle for {} {}",idleFor,units);
return;
}
size=getOutput().remaining();
}
}
catch(Exception e)
{
LOG.warn(e);
}
}
}
}
}