org.eclipse.jetty.server.LocalConnector Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// 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.http.HttpField;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.http.HttpVersion;
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.ByteArrayOutputStream2;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.Scheduler;
/**
* A local connector, mostly for testing purposes.
*
* HttpTester.Request request = HttpTester.newRequest();
* request.setURI("/some/resource");
* HttpTester.Response response =
* HttpTester.parseResponse(HttpTester.from(localConnector.getResponse(request.generate())));
*
*
* @deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
*/
@Deprecated(since = "2021-05-27")
public class LocalConnector extends AbstractConnector {
private final BlockingQueue _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.
*
* This methods waits until the connection is closed or
* is idle for 5s before returning the responses.
*
Use {@link #getResponse(String)} for an alternative that does not wait for idle.
*
* @param requests the requests
* @return the responses
* @throws Exception if the requests fail
* @deprecated Use {@link #getResponse(String)}
*/
@Deprecated
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.
*
* This methods waits until the connection is closed or
* an idle period before returning the responses.
*
Use {@link #getResponse(String)} for an alternative that does not wait for idle.
*
* @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
* @deprecated Use {@link #getResponse(String, boolean, long, TimeUnit)}
*/
@Deprecated
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.
*
* This methods waits until the connection is closed or
* is idle for 5s before returning the responses.
*
Use {@link #getResponse(ByteBuffer)} for an alternative that does not wait for idle.
*
* @param requestsBuffer the requests
* @return the responses
* @throws Exception if the requests fail
* @deprecated Use {@link #getResponse(ByteBuffer)}
*/
@Deprecated
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.
*
* 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
* @deprecated Use {@link #getResponse(ByteBuffer, boolean, long, TimeUnit)}
*/
@Deprecated
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
* multiple responses can be received or more input provided.
*
* @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;
}
public LocalEndPoint connect() {
LocalEndPoint endp = new LocalEndPoint();
_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();
Connection connection = getDefaultConnectionFactory().newConnection(this, endPoint);
endPoint.setConnection(connection);
endPoint.onOpen();
onEndPointOpened(endPoint);
connection.onOpen();
}
/**
* Get a single response using a parser to search for the end of the message.
*
* @param requestsBuffer The request to send
* @return ByteBuffer containing response or null.
* @throws Exception If there is a problem
*/
public ByteBuffer getResponse(ByteBuffer requestsBuffer) throws Exception {
return getResponse(requestsBuffer, false, 10, TimeUnit.SECONDS);
}
/**
* Get a single response using a parser to search for the end of the message.
*
* @param requestBuffer The request to send
* @param time The time to wait
* @param unit The units of the wait
* @return ByteBuffer containing response or null.
* @throws Exception If there is a problem
*/
public ByteBuffer getResponse(ByteBuffer requestBuffer, long time, TimeUnit unit) throws Exception {
boolean head = BufferUtil.toString(requestBuffer).toLowerCase().startsWith("head ");
if (LOG.isDebugEnabled())
LOG.debug("requests {}", BufferUtil.toUTF8String(requestBuffer));
LocalEndPoint endp = executeRequest(requestBuffer);
return endp.waitForResponse(head, time, unit);
}
/**
* Get a single response using a parser to search for the end of the message.
*
* @param requestBuffer The request to send
* @param head True if the response is for a head request
* @param time The time to wait
* @param unit The units of the wait
* @return ByteBuffer containing response or null.
* @throws Exception If there is a problem
*/
public ByteBuffer getResponse(ByteBuffer requestBuffer, boolean head, long time, TimeUnit unit) throws Exception {
if (LOG.isDebugEnabled())
LOG.debug("requests {}", BufferUtil.toUTF8String(requestBuffer));
LocalEndPoint endp = executeRequest(requestBuffer);
return endp.waitForResponse(head, time, unit);
}
/**
* Get a single response using a parser to search for the end of the message.
*
* @param rawRequest The request to send
* @return ByteBuffer containing response or null.
* @throws Exception If there is a problem
*/
public String getResponse(String rawRequest) throws Exception {
return getResponse(rawRequest, false, 30, TimeUnit.SECONDS);
}
/**
* Get a single response using a parser to search for the end of the message.
*
* @param rawRequest The request to send
* @param time The time to wait
* @param unit The units of the wait
* @return ByteBuffer containing response or null.
* @throws Exception If there is a problem
*/
public String getResponse(String rawRequest, long time, TimeUnit unit) throws Exception {
boolean head = rawRequest.toLowerCase().startsWith("head ");
ByteBuffer requestsBuffer = BufferUtil.toBuffer(rawRequest, StandardCharsets.ISO_8859_1);
if (LOG.isDebugEnabled())
LOG.debug("request {}", BufferUtil.toUTF8String(requestsBuffer));
LocalEndPoint endp = executeRequest(requestsBuffer);
return BufferUtil.toString(endp.waitForResponse(head, time, unit), StandardCharsets.ISO_8859_1);
}
/**
* Get a single response using a parser to search for the end of the message.
*
* @param rawRequest The request to send
* @param head True if the response is for a head request
* @param time The time to wait
* @param unit The units of the wait
* @return ByteBuffer containing response or null.
* @throws Exception If there is a problem
*/
public String getResponse(String rawRequest, boolean head, long time, TimeUnit unit) throws Exception {
ByteBuffer requestsBuffer = BufferUtil.toBuffer(rawRequest, StandardCharsets.ISO_8859_1);
if (LOG.isDebugEnabled())
LOG.debug("request {}", BufferUtil.toUTF8String(requestsBuffer));
LocalEndPoint endp = executeRequest(requestsBuffer);
return BufferUtil.toString(endp.waitForResponse(head, time, unit), StandardCharsets.ISO_8859_1);
}
/**
* Local EndPoint
*
* @deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
*/
@Deprecated(since = "2021-05-27")
public class LocalEndPoint extends ByteArrayEndPoint {
private final CountDownLatch _closed = new CountDownLatch(1);
private ByteBuffer _responseData;
public LocalEndPoint() {
super(LocalConnector.this.getScheduler(), LocalConnector.this.getIdleTimeout());
setGrowOutput(true);
}
@Override
protected void execute(Runnable task) {
getExecutor().execute(task);
}
@Override
public void onClose() {
Connection connection = getConnection();
if (connection != null)
connection.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);
}
}
}
/**
* Remaining output ByteBuffer after calls to {@link #getResponse()} or {@link #waitForResponse(boolean, long, TimeUnit)}
*
* @return the remaining response data buffer
*/
public ByteBuffer getResponseData() {
return _responseData;
}
/**
* Wait for a response using a parser to detect the end of message
*
* @return Buffer containing full response or null for EOF;
* @throws Exception if the response cannot be parsed
*/
public String getResponse() throws Exception {
return getResponse(false, 30, TimeUnit.SECONDS);
}
/**
* Wait for a response using a parser to detect the end of message
*
* @param head whether the request is a HEAD request
* @param time the maximum time to wait
* @param unit the time unit of the {@code timeout} argument
* @return Buffer containing full response or null for EOF;
* @throws Exception if the response cannot be parsed
*/
public String getResponse(boolean head, long time, TimeUnit unit) throws Exception {
ByteBuffer response = waitForResponse(head, time, unit);
if (response != null)
return BufferUtil.toString(response);
return null;
}
/**
* Wait for a response using a parser to detect the end of message
*
* @param head whether the request is a HEAD request
* @param time the maximum time to wait
* @param unit the time unit of the {@code timeout} argument
* @return Buffer containing full response or null for EOF;
* @throws Exception if the response cannot be parsed
*/
public ByteBuffer waitForResponse(boolean head, long time, TimeUnit unit) throws Exception {
HttpParser.ResponseHandler handler = new HttpParser.ResponseHandler() {
@Override
public void parsedHeader(HttpField field) {
}
@Override
public boolean contentComplete() {
return false;
}
@Override
public boolean messageComplete() {
return true;
}
@Override
public boolean headerComplete() {
return false;
}
@Override
public int getHeaderCacheSize() {
return 0;
}
@Override
public void earlyEOF() {
}
@Override
public boolean content(ByteBuffer item) {
return false;
}
@Override
public boolean startResponse(HttpVersion version, int status, String reason) {
return false;
}
};
HttpParser parser = new HttpParser(handler);
parser.setHeadResponse(head);
try (ByteArrayOutputStream2 bout = new ByteArrayOutputStream2()) {
loop: while (true) {
// read a chunk of response
ByteBuffer chunk;
if (BufferUtil.hasContent(_responseData))
chunk = _responseData;
else {
chunk = waitForOutput(time, unit);
if (BufferUtil.isEmpty(chunk) && (!isOpen() || isOutputShutdown())) {
parser.atEOF();
parser.parseNext(BufferUtil.EMPTY_BUFFER);
break loop;
}
}
// Parse the content of this chunk
while (BufferUtil.hasContent(chunk)) {
int pos = chunk.position();
boolean complete = parser.parseNext(chunk);
if (chunk.position() == pos) {
// Nothing consumed
if (BufferUtil.isEmpty(chunk))
break;
return null;
}
// Add all consumed bytes to the output stream
bout.write(chunk.array(), chunk.arrayOffset() + pos, chunk.position() - pos);
// If we are complete then break the outer loop
if (complete) {
if (BufferUtil.hasContent(chunk))
_responseData = chunk;
break loop;
}
}
}
if (bout.getCount() == 0 && isOutputShutdown())
return null;
return ByteBuffer.wrap(bout.getBuf(), 0, bout.getCount());
}
}
}
}