Merge remote-tracking branch 'origin/master' into jetty-8
This commit is contained in:
commit
3141b50fa0
|
@ -29,6 +29,7 @@ import org.eclipse.jetty.server.handler.DefaultHandler;
|
|||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.RequestLogHandler;
|
||||
import org.eclipse.jetty.server.handler.StatisticsHandler;
|
||||
import org.eclipse.jetty.server.nio.BlockingChannelConnector;
|
||||
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
|
||||
import org.eclipse.jetty.server.ssl.SslSocketConnector;
|
||||
|
@ -68,6 +69,13 @@ public class LikeJettyXml
|
|||
|
||||
server.setConnectors(new Connector[]
|
||||
{ connector });
|
||||
|
||||
BlockingChannelConnector bConnector = new BlockingChannelConnector();
|
||||
bConnector.setPort(8888);
|
||||
bConnector.setMaxIdleTime(30000);
|
||||
bConnector.setConfidentialPort(8443);
|
||||
bConnector.setAcceptors(1);
|
||||
server.addConnector(bConnector);
|
||||
|
||||
SslSelectChannelConnector ssl_connector = new SslSelectChannelConnector();
|
||||
ssl_connector.setPort(8443);
|
||||
|
|
|
@ -179,7 +179,7 @@ public abstract class AbstractHttpConnection extends AbstractConnection implemen
|
|||
_generator.setVersion(_exchange.getVersion());
|
||||
|
||||
String method=_exchange.getMethod();
|
||||
String uri = _exchange.getURI();
|
||||
String uri = _exchange.getRequestURI();
|
||||
if (_destination.isProxied() && !HttpMethods.CONNECT.equals(method) && uri.startsWith("/"))
|
||||
{
|
||||
boolean secure = _destination.isSecure();
|
||||
|
@ -394,7 +394,11 @@ public abstract class AbstractHttpConnection extends AbstractConnection implemen
|
|||
}
|
||||
}
|
||||
|
||||
_endp.close();
|
||||
if (_endp.isOpen())
|
||||
{
|
||||
_endp.close();
|
||||
_destination.returnConnection(this, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void setIdleTimeout()
|
||||
|
|
|
@ -14,15 +14,8 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -36,18 +29,22 @@ import org.eclipse.jetty.server.Server;
|
|||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.log.StdErrLog;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
*/
|
||||
public abstract class AbstractHttpExchangeCancelTest
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(AbstractHttpExchangeCancelTest.TestHttpExchange.class);
|
||||
|
||||
private Server server;
|
||||
private Connector connector;
|
||||
|
||||
|
@ -362,7 +359,7 @@ public abstract class AbstractHttpExchangeCancelTest
|
|||
|
||||
int status = exchange.waitForDone();
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
|
||||
assertTrue(HttpExchange.STATUS_EXPIRED==status||HttpExchange.STATUS_EXCEPTED==status);
|
||||
assertFalse(exchange.isResponseCompleted());
|
||||
assertTrue(end-start<4000);
|
||||
|
@ -371,6 +368,26 @@ public abstract class AbstractHttpExchangeCancelTest
|
|||
assertFalse(exchange.isAssociated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpExchangeCancelReturnsConnection() throws Exception
|
||||
{
|
||||
TestHttpExchange exchange = new TestHttpExchange();
|
||||
Address address = newAddress();
|
||||
exchange.setAddress(address);
|
||||
long delay = 5000;
|
||||
exchange.setRequestURI("/?action=wait" + delay);
|
||||
|
||||
HttpClient httpClient = getHttpClient();
|
||||
HttpDestination destination = httpClient.getDestination(address, false);
|
||||
int connections = destination.getConnections();
|
||||
httpClient.send(exchange);
|
||||
Thread.sleep(delay / 2);
|
||||
Assert.assertEquals(connections + 1, destination.getConnections());
|
||||
|
||||
exchange.cancel();
|
||||
Assert.assertEquals(connections, destination.getConnections());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected abstract HttpClient getHttpClient();
|
||||
|
||||
|
|
|
@ -110,6 +110,13 @@ public class BlockingHttpConnection extends AbstractHttpConnection
|
|||
_endp.shutdownOutput();
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have a committed response and we are not suspended
|
||||
if (_endp.isInputShutdown() && _generator.isIdle() && !_request.getAsyncContinuation().isSuspended())
|
||||
{
|
||||
// then no more can happen, so close.
|
||||
_endp.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -112,6 +112,14 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* to avoid reparsing headers and cookies that are likely to be the same for
|
||||
* requests from the same connection.
|
||||
*
|
||||
* <p>
|
||||
* The form content that a request can process is limited to protect from Denial of Service
|
||||
* attacks. The size in bytes is limited by {@link ContextHandler#getMaxFormContentSize()} or if there is no
|
||||
* context then the "org.eclipse.jetty.server.Request.maxFormContentSize" {@link Server} attribute.
|
||||
* The number of parameters keys is limited by {@link ContextHandler#getMaxFormKeys()} or if there is no
|
||||
* context then the "org.eclipse.jetty.server.Request.maxFormKeys" {@link Server} attribute.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class Request implements HttpServletRequest
|
||||
{
|
||||
|
@ -247,7 +255,7 @@ public class Request implements HttpServletRequest
|
|||
if (content_type != null && content_type.length() > 0)
|
||||
{
|
||||
content_type = HttpFields.valueParameters(content_type, null);
|
||||
|
||||
|
||||
if (MimeTypes.FORM_ENCODED.equalsIgnoreCase(content_type) && _inputState==__NONE &&
|
||||
(HttpMethods.POST.equals(getMethod()) || HttpMethods.PUT.equals(getMethod())))
|
||||
{
|
||||
|
@ -257,16 +265,21 @@ public class Request implements HttpServletRequest
|
|||
try
|
||||
{
|
||||
int maxFormContentSize=-1;
|
||||
int maxFormKeys=-1;
|
||||
|
||||
if (_context!=null)
|
||||
{
|
||||
maxFormContentSize=_context.getContextHandler().getMaxFormContentSize();
|
||||
maxFormKeys=_context.getContextHandler().getMaxFormKeys();
|
||||
}
|
||||
else
|
||||
{
|
||||
Integer size = (Integer)_connection.getConnector().getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormContentSize");
|
||||
if (size!=null)
|
||||
maxFormContentSize =size.intValue();
|
||||
Number size = (Number)_connection.getConnector().getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormContentSize");
|
||||
maxFormContentSize=size==null?200000:size.intValue();
|
||||
Number keys = (Number)_connection.getConnector().getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormKeys");
|
||||
maxFormKeys =keys==null?1000:keys.intValue();
|
||||
}
|
||||
|
||||
|
||||
if (content_length>maxFormContentSize && maxFormContentSize > 0)
|
||||
{
|
||||
throw new IllegalStateException("Form too large"+content_length+">"+maxFormContentSize);
|
||||
|
@ -274,7 +287,7 @@ public class Request implements HttpServletRequest
|
|||
InputStream in = getInputStream();
|
||||
|
||||
// Add form params to query params
|
||||
UrlEncoded.decodeTo(in, _baseParameters, encoding,content_length<0?maxFormContentSize:-1);
|
||||
UrlEncoded.decodeTo(in, _baseParameters, encoding,content_length<0?maxFormContentSize:-1,maxFormKeys);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
|
|
|
@ -130,6 +130,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
|
|||
private EventListener[] _eventListeners;
|
||||
private Logger _logger;
|
||||
private boolean _allowNullPathInfo;
|
||||
private int _maxFormKeys = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormKeys",1000).intValue();
|
||||
private int _maxFormContentSize = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormContentSize",200000).intValue();
|
||||
private boolean _compactPath = false;
|
||||
private boolean _aliases = false;
|
||||
|
@ -1382,11 +1383,31 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set the maximum size of a form post, to protect against DOS attacks from large forms.
|
||||
* @param maxSize
|
||||
*/
|
||||
public void setMaxFormContentSize(int maxSize)
|
||||
{
|
||||
_maxFormContentSize = maxSize;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public int getMaxFormKeys()
|
||||
{
|
||||
return _maxFormKeys;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Set the maximum number of form Keys to protect against DOS attack from crafted hash keys.
|
||||
* @param max
|
||||
*/
|
||||
public void setMaxFormKeys(int max)
|
||||
{
|
||||
_maxFormKeys = max;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return True if URLs are compacted to replace multiple '/'s with a single '/'
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.io.IOException;
|
|||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.channels.ByteChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.Set;
|
||||
|
@ -341,5 +342,20 @@ public class BlockingChannelConnector extends AbstractNIOConnector
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("BCEP@%x{l(%s)<->r(%s),open=%b,ishut=%b,oshut=%b}-{%s}",
|
||||
hashCode(),
|
||||
_socket.getRemoteSocketAddress(),
|
||||
_socket.getLocalSocketAddress(),
|
||||
isOpen(),
|
||||
isInputShutdown(),
|
||||
isOutputShutdown(),
|
||||
_connection);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,12 +19,16 @@ import static org.junit.Assert.assertNotSame;
|
|||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -34,9 +38,11 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -742,6 +748,56 @@ public class RequestTest
|
|||
assertEquals(null,cookie[1]);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testHashDOS() throws Exception
|
||||
{
|
||||
_server.setAttribute("org.eclipse.jetty.server.Request.maxFormContentSize",-1);
|
||||
_server.setAttribute("org.eclipse.jetty.server.Request.maxFormKeys",1000);
|
||||
|
||||
// This file is not distributed - as it is dangerous
|
||||
File evil_keys = new File("/tmp/keys_mapping_to_zero_2m");
|
||||
if (!evil_keys.exists())
|
||||
{
|
||||
Log.info("testHashDOS skipped");
|
||||
return;
|
||||
}
|
||||
|
||||
BufferedReader in = new BufferedReader(new FileReader(evil_keys));
|
||||
StringBuilder buf = new StringBuilder(4000000);
|
||||
|
||||
String key=null;
|
||||
buf.append("a=b");
|
||||
while((key=in.readLine())!=null)
|
||||
{
|
||||
buf.append("&").append(key).append("=").append("x");
|
||||
}
|
||||
buf.append("&c=d");
|
||||
|
||||
_handler._checker = new RequestTester()
|
||||
{
|
||||
public boolean check(HttpServletRequest request,HttpServletResponse response)
|
||||
{
|
||||
return "b".equals(request.getParameter("a")) && request.getParameter("c")==null;
|
||||
}
|
||||
};
|
||||
|
||||
String request="POST / HTTP/1.1\r\n"+
|
||||
"Host: whatever\r\n"+
|
||||
"Content-Type: "+MimeTypes.FORM_ENCODED+"\r\n"+
|
||||
"Content-Length: "+buf.length()+"\r\n"+
|
||||
"Connection: close\r\n"+
|
||||
"\r\n"+
|
||||
buf;
|
||||
|
||||
long start=System.currentTimeMillis();
|
||||
String response = _connector.getResponses(request);
|
||||
assertTrue(response.contains("200 OK"));
|
||||
long now=System.currentTimeMillis();
|
||||
assertTrue((now-start)<5000);
|
||||
}
|
||||
|
||||
|
||||
interface RequestTester
|
||||
{
|
||||
boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException;
|
||||
|
@ -756,13 +812,15 @@ public class RequestTest
|
|||
{
|
||||
((Request)request).setHandled(true);
|
||||
|
||||
if (request.getContentLength()>0)
|
||||
if (request.getContentLength()>0 && !MimeTypes.FORM_ENCODED.equals(request.getContentType()))
|
||||
_content=IO.toString(request.getInputStream());
|
||||
|
||||
|
||||
if (_checker!=null && _checker.check(request,response))
|
||||
response.setStatus(200);
|
||||
else
|
||||
response.sendError(500);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.io.FileInputStream;
|
|||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
@ -30,7 +31,6 @@ import java.lang.reflect.Method;
|
|||
import java.net.ConnectException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.security.Policy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -40,17 +40,15 @@ import java.util.List;
|
|||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/*-------------------------------------------*/
|
||||
/**
|
||||
* <p>
|
||||
* Main start class. This class is intended to be the main class listed in the MANIFEST.MF of the start.jar archive. It
|
||||
* allows an application to be started with the command "java -jar start.jar".
|
||||
* Main start class. This class is intended to be the main class listed in the MANIFEST.MF of the start.jar archive. It allows an application to be started with
|
||||
* the command "java -jar start.jar".
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* The behaviour of Main is controlled by the parsing of the {@link Config} "org/eclipse/start/start.config" file
|
||||
* obtained as a resource or file.
|
||||
* The behaviour of Main is controlled by the parsing of the {@link Config} "org/eclipse/start/start.config" file obtained as a resource or file.
|
||||
* </p>
|
||||
*/
|
||||
public class Main
|
||||
|
@ -67,257 +65,240 @@ public class Main
|
|||
private boolean _dryRun = false;
|
||||
private boolean _exec = false;
|
||||
private final Config _config = new Config();
|
||||
private Set<String> _sysProps = new HashSet<String>();
|
||||
private List<String> _jvmArgs = new ArrayList<String>();
|
||||
private final Set<String> _sysProps = new HashSet<String>();
|
||||
private final List<String> _jvmArgs = new ArrayList<String>();
|
||||
private String _startConfig = null;
|
||||
|
||||
private String _jettyHome;
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
Main main = new Main();
|
||||
main.parseCommandLine(args);
|
||||
}
|
||||
|
||||
public void parseCommandLine(String[] args)
|
||||
public static void main(String[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<String> arguments = new ArrayList<String>();
|
||||
|
||||
// add the command line args and look for start.ini args
|
||||
boolean ini=false;
|
||||
for (String arg : args)
|
||||
{
|
||||
if (arg.startsWith("--ini=")||arg.equals("--ini"))
|
||||
{
|
||||
ini=true;
|
||||
if (arg.length()>6)
|
||||
{
|
||||
arguments.addAll(loadStartIni(arg.substring(6)));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (arg.startsWith("--config="))
|
||||
{
|
||||
_startConfig=arg.substring(9);
|
||||
}
|
||||
else
|
||||
{
|
||||
arguments.add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
// if no non-option inis, add the start.ini
|
||||
if (!ini)
|
||||
{
|
||||
arguments.addAll(0,loadStartIni(null));
|
||||
}
|
||||
|
||||
// The XML Configuration Files to initialize with
|
||||
List<String> xmls = new ArrayList<String>();
|
||||
|
||||
// Process the arguments
|
||||
int startup=0;
|
||||
for (String arg : arguments)
|
||||
{
|
||||
if ("--help".equals(arg) || "-?".equals(arg))
|
||||
{
|
||||
_showUsage = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--stop".equals(arg))
|
||||
{
|
||||
int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1"));
|
||||
String key = Config.getProperty("STOP.KEY",null);
|
||||
stop(port,key);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("--version".equals(arg) || "-v".equals(arg) || "--info".equals(arg))
|
||||
{
|
||||
_dumpVersions = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--list-modes".equals(arg) || "--list-options".equals(arg))
|
||||
{
|
||||
_listOptions = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--list-config".equals(arg))
|
||||
{
|
||||
_listConfig=true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--exec-print".equals(arg)||"--dry-run".equals(arg))
|
||||
{
|
||||
_dryRun = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--exec".equals(arg))
|
||||
{
|
||||
_exec = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Special internal indicator that jetty was started by the jetty.sh Daemon
|
||||
if ("--daemon".equals(arg))
|
||||
{
|
||||
File startDir = new File(System.getProperty("jetty.logs","logs"));
|
||||
if (!startDir.exists() || !startDir.canWrite() )
|
||||
startDir = new File(".");
|
||||
File startLog = new File(startDir,"start.log");
|
||||
if (!startLog.exists() && !startLog.createNewFile())
|
||||
{
|
||||
// Output about error is lost in majority of cases.
|
||||
System.err.println("Unable to create: " + startLog.getAbsolutePath());
|
||||
// Toss a unique exit code indicating this failure.
|
||||
usageExit(ERR_LOGGING);
|
||||
}
|
||||
|
||||
if (!startLog.canWrite())
|
||||
{
|
||||
// Output about error is lost in majority of cases.
|
||||
System.err.println("Unable to write to: " + startLog.getAbsolutePath());
|
||||
// Toss a unique exit code indicating this failure.
|
||||
usageExit(ERR_LOGGING);
|
||||
}
|
||||
PrintStream logger = new PrintStream(new FileOutputStream(startLog,false));
|
||||
System.setOut(logger);
|
||||
System.setErr(logger);
|
||||
System.out.println("Establishing start.log on " + new Date());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.startsWith("--pre="))
|
||||
{
|
||||
xmls.add(startup++,arg.substring(6));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.startsWith("-D"))
|
||||
{
|
||||
String[] assign = arg.substring(2).split("=",2);
|
||||
_sysProps.add(assign[0]);
|
||||
switch(assign.length)
|
||||
{
|
||||
case 2:
|
||||
System.setProperty(assign[0],assign[1]);
|
||||
break;
|
||||
case 1:
|
||||
System.setProperty(assign[0],"");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.startsWith("-"))
|
||||
{
|
||||
_jvmArgs.add(arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is this a Property?
|
||||
if (arg.indexOf('=') >= 0)
|
||||
{
|
||||
String[] assign = arg.split("=",2);
|
||||
|
||||
switch(assign.length)
|
||||
{
|
||||
case 2:
|
||||
if ("OPTIONS".equals(assign[0]))
|
||||
{
|
||||
String opts[] = assign[1].split(",");
|
||||
for (String opt : opts)
|
||||
_config.addActiveOption(opt);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._config.setProperty(assign[0],assign[1]);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
this._config.setProperty(assign[0],null);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Anything else is considered an XML file.
|
||||
if (xmls.contains(arg))
|
||||
{
|
||||
System.out.println("WARN: Argument '"+arg+"' specified multiple times. Check start.ini?");
|
||||
System.out.println("Use \"java -jar start.jar --help\" for more information.");
|
||||
}
|
||||
xmls.add(arg);
|
||||
}
|
||||
|
||||
start(xmls);
|
||||
Main main = new Main();
|
||||
List<String> arguments = main.expandCommandLine(args);
|
||||
List<String> xmls = main.processCommandLine(arguments);
|
||||
if (xmls!=null)
|
||||
main.start(xmls);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
usageExit(t,ERR_UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a start.ini is present in the CWD, then load it into the argument list.
|
||||
*/
|
||||
private List<String> loadStartIni(String ini)
|
||||
{
|
||||
String jettyHome=System.getProperty("jetty.home");
|
||||
File startIniFile = ini==null?((jettyHome!=null)? new File(jettyHome,"start.ini"):new File("start.ini")):new File(ini);
|
||||
if (!startIniFile.exists())
|
||||
{
|
||||
if (ini != null)
|
||||
{
|
||||
System.err.println("Warning - can't find ini file: " + ini);
|
||||
}
|
||||
// No start.ini found, skip load.
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<String> args = new ArrayList<String>();
|
||||
|
||||
FileReader reader = null;
|
||||
BufferedReader buf = null;
|
||||
try
|
||||
{
|
||||
reader = new FileReader(startIniFile);
|
||||
buf = new BufferedReader(reader);
|
||||
|
||||
String arg;
|
||||
while ((arg = buf.readLine()) != null)
|
||||
{
|
||||
arg = arg.trim();
|
||||
if (arg.length() == 0 || arg.startsWith("#"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
args.add(arg);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (Throwable e)
|
||||
{
|
||||
usageExit(e,ERR_UNKNOWN);
|
||||
}
|
||||
finally
|
||||
}
|
||||
|
||||
Main() throws IOException
|
||||
{
|
||||
_jettyHome = System.getProperty("jetty.home",".");
|
||||
_jettyHome = new File(_jettyHome).getCanonicalPath();
|
||||
}
|
||||
|
||||
public List<String> expandCommandLine(String[] args) throws Exception
|
||||
{
|
||||
List<String> arguments = new ArrayList<String>();
|
||||
|
||||
// add the command line args and look for start.ini args
|
||||
boolean ini = false;
|
||||
for (String arg : args)
|
||||
{
|
||||
close(buf);
|
||||
close(reader);
|
||||
if (arg.startsWith("--ini=") || arg.equals("--ini"))
|
||||
{
|
||||
ini = true;
|
||||
if (arg.length() > 6)
|
||||
{
|
||||
arguments.addAll(loadStartIni(new File(arg.substring(6))));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (arg.startsWith("--config="))
|
||||
{
|
||||
_startConfig = arg.substring(9);
|
||||
}
|
||||
else
|
||||
{
|
||||
arguments.add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
// if no non-option inis, add the start.ini and start.d
|
||||
if (!ini)
|
||||
{
|
||||
List<String> ini_args=new ArrayList<String>();
|
||||
File start_ini = new File(_jettyHome,"start.ini");
|
||||
if (start_ini.exists())
|
||||
ini_args.addAll(loadStartIni(start_ini));
|
||||
|
||||
File start_d = new File(_jettyHome,"start.d");
|
||||
if (start_d.isDirectory())
|
||||
{
|
||||
File[] inis = start_d.listFiles(new FilenameFilter()
|
||||
{
|
||||
public boolean accept(File dir, String name)
|
||||
{
|
||||
return name.toLowerCase().endsWith(".ini");
|
||||
}
|
||||
});
|
||||
Arrays.sort(inis);
|
||||
for (File i : inis)
|
||||
ini_args.addAll(loadStartIni(i));
|
||||
}
|
||||
arguments.addAll(0,ini_args);
|
||||
}
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
public List<String> processCommandLine(List<String> arguments) throws Exception
|
||||
{
|
||||
// The XML Configuration Files to initialize with
|
||||
List<String> xmls = new ArrayList<String>();
|
||||
|
||||
// Process the arguments
|
||||
int startup = 0;
|
||||
for (String arg : arguments)
|
||||
{
|
||||
if ("--help".equals(arg) || "-?".equals(arg))
|
||||
{
|
||||
_showUsage = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--stop".equals(arg))
|
||||
{
|
||||
int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1"));
|
||||
String key = Config.getProperty("STOP.KEY",null);
|
||||
stop(port,key);
|
||||
return null;
|
||||
}
|
||||
|
||||
if ("--version".equals(arg) || "-v".equals(arg) || "--info".equals(arg))
|
||||
{
|
||||
_dumpVersions = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--list-modes".equals(arg) || "--list-options".equals(arg))
|
||||
{
|
||||
_listOptions = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--list-config".equals(arg))
|
||||
{
|
||||
_listConfig = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--exec-print".equals(arg) || "--dry-run".equals(arg))
|
||||
{
|
||||
_dryRun = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("--exec".equals(arg))
|
||||
{
|
||||
_exec = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Special internal indicator that jetty was started by the jetty.sh Daemon
|
||||
if ("--daemon".equals(arg))
|
||||
{
|
||||
File startDir = new File(System.getProperty("jetty.logs","logs"));
|
||||
if (!startDir.exists() || !startDir.canWrite())
|
||||
startDir = new File(".");
|
||||
File startLog = new File(startDir,"start.log");
|
||||
if (!startLog.exists() && !startLog.createNewFile())
|
||||
{
|
||||
// Output about error is lost in majority of cases.
|
||||
System.err.println("Unable to create: " + startLog.getAbsolutePath());
|
||||
// Toss a unique exit code indicating this failure.
|
||||
usageExit(ERR_LOGGING);
|
||||
}
|
||||
|
||||
if (!startLog.canWrite())
|
||||
{
|
||||
// Output about error is lost in majority of cases.
|
||||
System.err.println("Unable to write to: " + startLog.getAbsolutePath());
|
||||
// Toss a unique exit code indicating this failure.
|
||||
usageExit(ERR_LOGGING);
|
||||
}
|
||||
PrintStream logger = new PrintStream(new FileOutputStream(startLog,false));
|
||||
System.setOut(logger);
|
||||
System.setErr(logger);
|
||||
System.out.println("Establishing start.log on " + new Date());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.startsWith("--pre="))
|
||||
{
|
||||
xmls.add(startup++,arg.substring(6));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.startsWith("-D"))
|
||||
{
|
||||
String[] assign = arg.substring(2).split("=",2);
|
||||
_sysProps.add(assign[0]);
|
||||
switch (assign.length)
|
||||
{
|
||||
case 2:
|
||||
System.setProperty(assign[0],assign[1]);
|
||||
break;
|
||||
case 1:
|
||||
System.setProperty(assign[0],"");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.startsWith("-"))
|
||||
{
|
||||
_jvmArgs.add(arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is this a Property?
|
||||
if (arg.indexOf('=') >= 0)
|
||||
{
|
||||
String[] assign = arg.split("=",2);
|
||||
|
||||
switch (assign.length)
|
||||
{
|
||||
case 2:
|
||||
if ("OPTIONS".equals(assign[0]))
|
||||
{
|
||||
String opts[] = assign[1].split(",");
|
||||
for (String opt : opts)
|
||||
_config.addActiveOption(opt);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._config.setProperty(assign[0],assign[1]);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
this._config.setProperty(assign[0],null);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Anything else is considered an XML file.
|
||||
if (xmls.contains(arg))
|
||||
{
|
||||
System.out.println("WARN: Argument '" + arg + "' specified multiple times. Check start.ini?");
|
||||
System.out.println("Use \"java -jar start.jar --help\" for more information.");
|
||||
}
|
||||
xmls.add(arg);
|
||||
}
|
||||
|
||||
return xmls;
|
||||
}
|
||||
|
||||
private void usage()
|
||||
|
@ -339,10 +320,10 @@ public class Main
|
|||
|
||||
while ((line = buf.readLine()) != null)
|
||||
{
|
||||
if (line.endsWith("@") && line.indexOf('@')!=line.lastIndexOf('@'))
|
||||
if (line.endsWith("@") && line.indexOf('@') != line.lastIndexOf('@'))
|
||||
{
|
||||
String indent=line.substring(0,line.indexOf("@"));
|
||||
String info=line.substring(line.indexOf('@'),line.lastIndexOf('@'));
|
||||
String indent = line.substring(0,line.indexOf("@"));
|
||||
String info = line.substring(line.indexOf('@'),line.lastIndexOf('@'));
|
||||
|
||||
if (info.equals("@OPTIONS"))
|
||||
{
|
||||
|
@ -352,7 +333,7 @@ public class Main
|
|||
|
||||
for (String option : sortedOptions)
|
||||
{
|
||||
if ("*".equals(option) || option.trim().length()==0)
|
||||
if ("*".equals(option) || option.trim().length() == 0)
|
||||
continue;
|
||||
System.out.print(indent);
|
||||
System.out.println(option);
|
||||
|
@ -396,7 +377,7 @@ public class Main
|
|||
else if (info.equals("@STARTINI"))
|
||||
{
|
||||
List<String> ini = loadStartIni(null);
|
||||
if (ini!=null && ini.size()>0)
|
||||
if (ini != null && ini.size() > 0)
|
||||
{
|
||||
for (String a : ini)
|
||||
{
|
||||
|
@ -429,7 +410,7 @@ public class Main
|
|||
}
|
||||
|
||||
public void invokeMain(ClassLoader classloader, String classname, List<String> args) throws IllegalAccessException, InvocationTargetException,
|
||||
NoSuchMethodException, ClassNotFoundException
|
||||
NoSuchMethodException, ClassNotFoundException
|
||||
{
|
||||
Class<?> invoked_class = null;
|
||||
|
||||
|
@ -462,10 +443,12 @@ public class Main
|
|||
|
||||
String argArray[] = args.toArray(new String[0]);
|
||||
|
||||
Class<?>[] method_param_types = new Class[] { argArray.getClass() };
|
||||
Class<?>[] method_param_types = new Class[]
|
||||
{ argArray.getClass() };
|
||||
|
||||
Method main = invoked_class.getDeclaredMethod("main",method_param_types);
|
||||
Object[] method_params = new Object[] { argArray };
|
||||
Object[] method_params = new Object[]
|
||||
{ argArray };
|
||||
main.invoke(null,method_params);
|
||||
}
|
||||
|
||||
|
@ -492,13 +475,12 @@ public class Main
|
|||
// Setup Start / Stop Monitoring
|
||||
int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1"));
|
||||
String key = Config.getProperty("STOP.KEY",null);
|
||||
Monitor monitor=new Monitor(port,key);
|
||||
|
||||
Monitor monitor = new Monitor(port,key);
|
||||
|
||||
// Load potential Config (start.config)
|
||||
List<String> configuredXmls = loadConfig(xmls);
|
||||
|
||||
// No XML defined in start.config or command line. Can't execute.
|
||||
// No XML defined in start.config or command line. Can't execute.
|
||||
if (configuredXmls.isEmpty())
|
||||
{
|
||||
throw new FileNotFoundException("No XML configuration files specified in start.config or command line.");
|
||||
|
@ -523,7 +505,7 @@ public class Main
|
|||
System.err.println("classloader.parent=" + cl.getParent());
|
||||
System.err.println("properties=" + Config.getProperties());
|
||||
}
|
||||
|
||||
|
||||
// Show the usage information and return
|
||||
if (_showUsage)
|
||||
{
|
||||
|
@ -544,7 +526,7 @@ public class Main
|
|||
showAllOptionsWithVersions(classpath);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (_listConfig)
|
||||
{
|
||||
listConfig();
|
||||
|
@ -557,7 +539,7 @@ public class Main
|
|||
System.out.println(buildCommandLine(classpath,configuredXmls));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// execute Jetty in another JVM
|
||||
if (_exec)
|
||||
{
|
||||
|
@ -565,11 +547,12 @@ public class Main
|
|||
final Process process = Runtime.getRuntime().exec(cmd);
|
||||
Runtime.getRuntime().addShutdownHook(new Thread()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
Config.debug("Destroying " + process);
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
});
|
||||
copyInThread(process.getErrorStream(),System.err);
|
||||
copyInThread(process.getInputStream(),System.out);
|
||||
|
@ -578,7 +561,7 @@ public class Main
|
|||
process.waitFor();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (_jvmArgs.size() > 0 || _sysProps.size() > 0)
|
||||
{
|
||||
System.err.println("WARNING: System properties and/or JVM args set. Consider using --dry-run or --exec");
|
||||
|
@ -617,7 +600,7 @@ public class Main
|
|||
}
|
||||
}
|
||||
|
||||
private void copyInThread(final InputStream in,final OutputStream out)
|
||||
private void copyInThread(final InputStream in, final OutputStream out)
|
||||
{
|
||||
new Thread(new Runnable()
|
||||
{
|
||||
|
@ -625,23 +608,23 @@ public class Main
|
|||
{
|
||||
try
|
||||
{
|
||||
byte[] buf=new byte[1024];
|
||||
int len=in.read(buf);
|
||||
while(len>0)
|
||||
byte[] buf = new byte[1024];
|
||||
int len = in.read(buf);
|
||||
while (len > 0)
|
||||
{
|
||||
out.write(buf,0,len);
|
||||
len=in.read(buf);
|
||||
len = in.read(buf);
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
catch (IOException e)
|
||||
{
|
||||
// e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
private String resolveXmlConfig(String xmlFilename) throws FileNotFoundException
|
||||
{
|
||||
if (!xmlFilename.toLowerCase().endsWith(".xml"))
|
||||
|
@ -675,22 +658,22 @@ public class Main
|
|||
{
|
||||
StringBuilder cmd = new StringBuilder();
|
||||
cmd.append(findJavaBin());
|
||||
for (String x:_jvmArgs)
|
||||
for (String x : _jvmArgs)
|
||||
cmd.append(' ').append(x);
|
||||
cmd.append(" -Djetty.home=").append(_jettyHome);
|
||||
for (String p:_sysProps)
|
||||
for (String p : _sysProps)
|
||||
{
|
||||
cmd.append(" -D").append(p);
|
||||
String v=System.getProperty(p);
|
||||
if (v!=null && v.length()>0)
|
||||
String v = System.getProperty(p);
|
||||
if (v != null && v.length() > 0)
|
||||
cmd.append('=').append(v);
|
||||
}
|
||||
cmd.append(" -cp ").append(classpath.toString());
|
||||
cmd.append(" ").append(_config.getMainClassname());
|
||||
|
||||
|
||||
// Check if we need to pass properties as a file
|
||||
Properties properties = Config.getProperties();
|
||||
if (properties.size()>0)
|
||||
if (properties.size() > 0)
|
||||
{
|
||||
File prop_file = File.createTempFile("start",".properties");
|
||||
if (!_dryRun)
|
||||
|
@ -698,7 +681,7 @@ public class Main
|
|||
properties.store(new FileOutputStream(prop_file),"start.jar properties");
|
||||
cmd.append(" ").append(prop_file.getAbsolutePath());
|
||||
}
|
||||
|
||||
|
||||
for (String xml : xmls)
|
||||
{
|
||||
cmd.append(' ').append(xml);
|
||||
|
@ -875,11 +858,10 @@ public class Main
|
|||
|
||||
private String getZipVersion(File element)
|
||||
{
|
||||
// TODO - find version in zip file. Look for META-INF/MANIFEST.MF ?
|
||||
// TODO - find version in zip file. Look for META-INF/MANIFEST.MF ?
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
private List<String> resolveXmlConfigs(List<String> xmls) throws FileNotFoundException
|
||||
{
|
||||
List<String> ret = new ArrayList<String>();
|
||||
|
@ -896,15 +878,15 @@ public class Main
|
|||
InputStream cfgstream = null;
|
||||
try
|
||||
{
|
||||
cfgstream=getConfigStream();
|
||||
byte[] buf=new byte[4096];
|
||||
|
||||
int len=0;
|
||||
|
||||
while (len>=0)
|
||||
cfgstream = getConfigStream();
|
||||
byte[] buf = new byte[4096];
|
||||
|
||||
int len = 0;
|
||||
|
||||
while (len >= 0)
|
||||
{
|
||||
len=cfgstream.read(buf);
|
||||
if (len>0)
|
||||
len = cfgstream.read(buf);
|
||||
if (len > 0)
|
||||
System.out.write(buf,0,len);
|
||||
}
|
||||
}
|
||||
|
@ -917,13 +899,13 @@ public class Main
|
|||
close(cfgstream);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load Configuration.
|
||||
*
|
||||
* No specific configuration is real until a {@link Config#getCombinedClasspath(java.util.Collection)} is used to
|
||||
* execute the {@link Class} specified by {@link Config#getMainClassname()} is executed.
|
||||
*
|
||||
*
|
||||
* No specific configuration is real until a {@link Config#getCombinedClasspath(java.util.Collection)} is used to execute the {@link Class} specified by
|
||||
* {@link Config#getMainClassname()} is executed.
|
||||
*
|
||||
* @param xmls
|
||||
* the command line specified xml configuration options.
|
||||
* @return the list of xml configurations arriving via command line and start.config choices.
|
||||
|
@ -935,13 +917,13 @@ public class Main
|
|||
{
|
||||
// Pass in xmls.size into Config so that conditions based on "nargs" work.
|
||||
_config.setArgCount(xmls.size());
|
||||
|
||||
cfgstream=getConfigStream();
|
||||
|
||||
|
||||
cfgstream = getConfigStream();
|
||||
|
||||
// parse the config
|
||||
_config.parse(cfgstream);
|
||||
|
||||
_jettyHome = Config.getProperty("jetty.home");
|
||||
|
||||
_jettyHome = Config.getProperty("jetty.home",_jettyHome);
|
||||
if (_jettyHome != null)
|
||||
{
|
||||
_jettyHome = new File(_jettyHome).getCanonicalPath();
|
||||
|
@ -975,12 +957,12 @@ public class Main
|
|||
|
||||
private InputStream getConfigStream() throws FileNotFoundException
|
||||
{
|
||||
String config=_startConfig;
|
||||
String config = _startConfig;
|
||||
if (config == null || config.length() == 0)
|
||||
{
|
||||
config = System.getProperty("START","org/eclipse/jetty/start/start.config");
|
||||
}
|
||||
|
||||
|
||||
Config.debug("config=" + config);
|
||||
|
||||
// Look up config as resource first.
|
||||
|
@ -991,11 +973,10 @@ public class Main
|
|||
{
|
||||
cfgstream = new FileInputStream(config);
|
||||
}
|
||||
|
||||
|
||||
return cfgstream;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stop a running jetty instance.
|
||||
*/
|
||||
|
@ -1038,7 +1019,6 @@ public class Main
|
|||
usageExit(e,ERR_UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void usageExit(Throwable t, int exit)
|
||||
{
|
||||
|
@ -1048,6 +1028,7 @@ public class Main
|
|||
System.err.println(" java -jar start.jar --help # for more information");
|
||||
System.exit(exit);
|
||||
}
|
||||
|
||||
static void usageExit(int exit)
|
||||
{
|
||||
System.err.println();
|
||||
|
@ -1055,4 +1036,53 @@ public class Main
|
|||
System.err.println(" java -jar start.jar --help # for more information");
|
||||
System.exit(exit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a start.ini format file into an argument list.
|
||||
*/
|
||||
static List<String> loadStartIni(File ini)
|
||||
{
|
||||
File startIniFile = ini;
|
||||
if (!startIniFile.exists())
|
||||
{
|
||||
if (ini != null)
|
||||
{
|
||||
System.err.println("Warning - can't find ini file: " + ini);
|
||||
}
|
||||
// No start.ini found, skip load.
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<String> args = new ArrayList<String>();
|
||||
|
||||
FileReader reader = null;
|
||||
BufferedReader buf = null;
|
||||
try
|
||||
{
|
||||
reader = new FileReader(ini);
|
||||
buf = new BufferedReader(reader);
|
||||
|
||||
String arg;
|
||||
while ((arg = buf.readLine()) != null)
|
||||
{
|
||||
arg = arg.trim();
|
||||
if (arg.length() == 0 || arg.startsWith("#"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
args.add(arg);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// usageExit(e,ERR_UNKNOWN);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Main.close(buf);
|
||||
Main.close(reader);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,8 +116,11 @@ Available Configurations:
|
|||
Defaults:
|
||||
A start.ini file may be used to specify default arguments to start.jar,
|
||||
which are used if no command line arguments are provided and override
|
||||
the defaults in the start.config file. If --ini options are provided on
|
||||
the command line, then start.ini will no be read. The current start.ini
|
||||
arguments are:
|
||||
the defaults in the start.config file. If the directory start.d exists,
|
||||
then multiple *.ini files will be read from that directory in alphabetical
|
||||
order. If --ini options are provided on the command line, then start.ini
|
||||
and start.d will not be read.
|
||||
|
||||
The current start.ini arguments are:
|
||||
|
||||
@STARTINI@
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 2009-2009 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.start;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
*/
|
||||
public class MainTest
|
||||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
System.setProperty("jetty.home",this.getClass().getResource("/jetty.home").getFile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link org.eclipse.jetty.start.StartIniParser#loadStartIni(java.lang.String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testLoadStartIni()
|
||||
{
|
||||
URL startIni = this.getClass().getResource("/jetty.home/start.ini");
|
||||
String startIniFileName = startIni.getFile();
|
||||
List<String> args = Main.loadStartIni(new File(startIniFileName));
|
||||
assertEquals("Expected 5 uncommented lines in start.ini",5,args.size());
|
||||
assertEquals("First uncommented line in start.ini doesn't match expected result","OPTIONS=Server,jsp,resources,websocket,ext",args.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandCommandLine() throws Exception
|
||||
{
|
||||
Main main = new Main();
|
||||
List<String> args = main.expandCommandLine(new String[]{});
|
||||
assertEquals("start.ini OPTIONS","OPTIONS=Server,jsp,resources,websocket,ext",args.get(0));
|
||||
assertEquals("start.d/jmx OPTIONS","OPTIONS=jmx",args.get(5));
|
||||
assertEquals("start.d/jmx XML","--pre=etc/jetty-jmx.xml",args.get(6));
|
||||
assertEquals("start.d/websocket OPTIONS","OPTIONS=websocket",args.get(7));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessCommandLine() throws Exception
|
||||
{
|
||||
Main main = new Main();
|
||||
List<String> args = main.expandCommandLine(new String[]{});
|
||||
List<String> xmls = main.processCommandLine(args);
|
||||
|
||||
assertEquals("jmx --pre","etc/jetty-jmx.xml",xmls.get(0));
|
||||
assertEquals("start.ini","etc/jetty.xml",xmls.get(1));
|
||||
assertEquals("start.d","etc/jetty-testrealm.xml",xmls.get(5));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#===========================================================
|
||||
# Additional Jetty start.jar arguments
|
||||
# Each line of this file is prepended to the command line
|
||||
# arguments # of a call to:
|
||||
# java -jar start.jar [arg...]
|
||||
#===========================================================
|
||||
|
||||
|
||||
|
||||
#===========================================================
|
||||
#-----------------------------------------------------------
|
||||
OPTIONS=jmx
|
||||
#-----------------------------------------------------------
|
||||
|
||||
|
||||
#===========================================================
|
||||
# Configuration files.
|
||||
# For a full list of available configuration files do
|
||||
# java -jar start.jar --help
|
||||
#-----------------------------------------------------------
|
||||
--pre=etc/jetty-jmx.xml
|
||||
#===========================================================
|
|
@ -0,0 +1,13 @@
|
|||
#===========================================================
|
||||
# Additional Jetty start.jar arguments
|
||||
# Each line of this file is prepended to the command line
|
||||
# arguments # of a call to:
|
||||
# java -jar start.jar [arg...]
|
||||
#===========================================================
|
||||
|
||||
|
||||
|
||||
#===========================================================
|
||||
#-----------------------------------------------------------
|
||||
OPTIONS=websocket
|
||||
#-----------------------------------------------------------
|
|
@ -0,0 +1 @@
|
|||
etc/jetty-testrealm.xml
|
|
@ -0,0 +1,65 @@
|
|||
#===========================================================
|
||||
# Jetty start.jar arguments
|
||||
# Each line of this file is prepended to the command line
|
||||
# arguments # of a call to:
|
||||
# java -jar start.jar [arg...]
|
||||
#===========================================================
|
||||
|
||||
|
||||
|
||||
#===========================================================
|
||||
# If the arguements in this file include JVM arguments
|
||||
# (eg -Xmx512m) or JVM System properties (eg com.sun.???),
|
||||
# then these will not take affect unless the --exec
|
||||
# parameter is included or if the output from --dry-run
|
||||
# is executed like:
|
||||
# eval $(java -jar start.jar --dry-run)
|
||||
#
|
||||
# Below are some recommended options for Sun's JRE
|
||||
#-----------------------------------------------------------
|
||||
# --exec
|
||||
# -Dorg.apache.jasper.compiler.disablejsr199=true
|
||||
# -Dcom.sun.management.jmxremote
|
||||
# -Dorg.eclipse.jetty.util.log.IGNORED=true
|
||||
# -Dorg.eclipse.jetty.util.log.stderr.DEBUG=true
|
||||
# -Dorg.eclipse.jetty.util.log.stderr.SOURCE=true
|
||||
# -Xmx2000m
|
||||
# -Xmn512m
|
||||
# -verbose:gc
|
||||
# -XX:+PrintGCDateStamps
|
||||
# -XX:+PrintGCTimeStamps
|
||||
# -XX:+PrintGCDetails
|
||||
# -XX:+PrintTenuringDistribution
|
||||
# -XX:+PrintCommandLineFlags
|
||||
# -XX:+DisableExplicitGC
|
||||
# -XX:+UseConcMarkSweepGC
|
||||
# -XX:ParallelCMSThreads=2
|
||||
# -XX:+CMSClassUnloadingEnabled
|
||||
# -XX:+UseCMSCompactAtFullCollection
|
||||
# -XX:CMSInitiatingOccupancyFraction=80
|
||||
#-----------------------------------------------------------
|
||||
|
||||
|
||||
#===========================================================
|
||||
# Start classpath OPTIONS.
|
||||
# These control what classes are on the classpath
|
||||
# for a full listing do
|
||||
# java -jar start.jar --list-options
|
||||
#-----------------------------------------------------------
|
||||
OPTIONS=Server,jsp,resources,websocket,ext
|
||||
#-----------------------------------------------------------
|
||||
|
||||
|
||||
#===========================================================
|
||||
# Configuration files.
|
||||
# For a full list of available configuration files do
|
||||
# java -jar start.jar --help
|
||||
#-----------------------------------------------------------
|
||||
etc/jetty.xml
|
||||
# etc/jetty-ssl.xml
|
||||
# etc/jetty-requestlog.xml
|
||||
etc/jetty-deploy.xml
|
||||
#etc/jetty-overlay.xml
|
||||
etc/jetty-webapps.xml
|
||||
etc/jetty-contexts.xml
|
||||
#===========================================================
|
|
@ -16,6 +16,7 @@ package org.eclipse.jetty.util;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
@ -78,13 +79,13 @@ public class UrlEncoded extends MultiMap implements Cloneable
|
|||
/* ----------------------------------------------------------------- */
|
||||
public void decode(String query)
|
||||
{
|
||||
decodeTo(query,this,ENCODING);
|
||||
decodeTo(query,this,ENCODING,-1);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
public void decode(String query,String charset)
|
||||
{
|
||||
decodeTo(query,this,charset);
|
||||
decodeTo(query,this,charset,-1);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -177,6 +178,15 @@ public class UrlEncoded extends MultiMap implements Cloneable
|
|||
* @param content the string containing the encoded parameters
|
||||
*/
|
||||
public static void decodeTo(String content, MultiMap map, String charset)
|
||||
{
|
||||
decodeTo(content,map,charset,-1);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/** Decoded parameters to Map.
|
||||
* @param content the string containing the encoded parameters
|
||||
*/
|
||||
public static void decodeTo(String content, MultiMap map, String charset, int maxKeys)
|
||||
{
|
||||
if (charset==null)
|
||||
charset=ENCODING;
|
||||
|
@ -208,6 +218,11 @@ public class UrlEncoded extends MultiMap implements Cloneable
|
|||
}
|
||||
key = null;
|
||||
value=null;
|
||||
if (maxKeys>0 && map.size()>maxKeys)
|
||||
{
|
||||
LOG.warn("maxFormKeys limit exceeded keys>{}",maxKeys);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case '=':
|
||||
if (key!=null)
|
||||
|
@ -343,9 +358,10 @@ public class UrlEncoded extends MultiMap implements Cloneable
|
|||
/** Decoded parameters to Map.
|
||||
* @param in InputSteam to read
|
||||
* @param map MultiMap to add parameters to
|
||||
* @param maxLength maximum length of content to read 0r -1 for no limit
|
||||
* @param maxLength maximum length of content to read or -1 for no limit
|
||||
* @param maxLength maximum number of keys to read or -1 for no limit
|
||||
*/
|
||||
public static void decode88591To(InputStream in, MultiMap map, int maxLength)
|
||||
public static void decode88591To(InputStream in, MultiMap map, int maxLength, int maxKeys)
|
||||
throws IOException
|
||||
{
|
||||
synchronized(map)
|
||||
|
@ -375,6 +391,11 @@ public class UrlEncoded extends MultiMap implements Cloneable
|
|||
}
|
||||
key = null;
|
||||
value=null;
|
||||
if (maxKeys>0 && map.size()>maxKeys)
|
||||
{
|
||||
LOG.warn("maxFormKeys limit exceeded keys>{}",maxKeys);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case '=':
|
||||
|
@ -423,9 +444,10 @@ public class UrlEncoded extends MultiMap implements Cloneable
|
|||
/** Decoded parameters to Map.
|
||||
* @param in InputSteam to read
|
||||
* @param map MultiMap to add parameters to
|
||||
* @param maxLength maximum length of content to read 0r -1 for no limit
|
||||
* @param maxLength maximum length of content to read or -1 for no limit
|
||||
* @param maxLength maximum number of keys to read or -1 for no limit
|
||||
*/
|
||||
public static void decodeUtf8To(InputStream in, MultiMap map, int maxLength)
|
||||
public static void decodeUtf8To(InputStream in, MultiMap map, int maxLength, int maxKeys)
|
||||
throws IOException
|
||||
{
|
||||
synchronized(map)
|
||||
|
@ -455,6 +477,11 @@ public class UrlEncoded extends MultiMap implements Cloneable
|
|||
}
|
||||
key = null;
|
||||
value=null;
|
||||
if (maxKeys>0 && map.size()>maxKeys)
|
||||
{
|
||||
LOG.warn("maxFormKeys limit exceeded keys>{}",maxKeys);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case '=':
|
||||
|
@ -500,25 +527,20 @@ public class UrlEncoded extends MultiMap implements Cloneable
|
|||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
public static void decodeUtf16To(InputStream in, MultiMap map, int maxLength) throws IOException
|
||||
public static void decodeUtf16To(InputStream in, MultiMap map, int maxLength, int maxKeys) throws IOException
|
||||
{
|
||||
InputStreamReader input = new InputStreamReader(in,StringUtil.__UTF16);
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
int c;
|
||||
int length=0;
|
||||
if (maxLength<0)
|
||||
maxLength=Integer.MAX_VALUE;
|
||||
while ((c=input.read())>0 && length++<maxLength)
|
||||
buf.append((char)c);
|
||||
decodeTo(buf.toString(),map,ENCODING);
|
||||
StringWriter buf = new StringWriter(8192);
|
||||
IO.copy(input,buf,maxLength);
|
||||
|
||||
decodeTo(buf.getBuffer().toString(),map,ENCODING,maxKeys);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
/** Decoded parameters to Map.
|
||||
* @param in the stream containing the encoded parameters
|
||||
*/
|
||||
public static void decodeTo(InputStream in, MultiMap map, String charset, int maxLength)
|
||||
public static void decodeTo(InputStream in, MultiMap map, String charset, int maxLength, int maxKeys)
|
||||
throws IOException
|
||||
{
|
||||
//no charset present, use the configured default
|
||||
|
@ -527,22 +549,21 @@ public class UrlEncoded extends MultiMap implements Cloneable
|
|||
charset=ENCODING;
|
||||
}
|
||||
|
||||
|
||||
if (StringUtil.__UTF8.equalsIgnoreCase(charset))
|
||||
{
|
||||
decodeUtf8To(in,map,maxLength);
|
||||
decodeUtf8To(in,map,maxLength,maxKeys);
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtil.__ISO_8859_1.equals(charset))
|
||||
{
|
||||
decode88591To(in,map,maxLength);
|
||||
decode88591To(in,map,maxLength,maxKeys);
|
||||
return;
|
||||
}
|
||||
|
||||
if (StringUtil.__UTF16.equalsIgnoreCase(charset)) // Should be all 2 byte encodings
|
||||
{
|
||||
decodeUtf16To(in,map,maxLength);
|
||||
decodeUtf16To(in,map,maxLength,maxKeys);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -178,7 +178,7 @@ public class URLEncodedTest
|
|||
{
|
||||
ByteArrayInputStream in = new ByteArrayInputStream("name\n=value+%30&name1=&name2&n\u00e3me3=value+3".getBytes(charsets[i][0]));
|
||||
MultiMap m = new MultiMap();
|
||||
UrlEncoded.decodeTo(in, m, charsets[i][1], -1);
|
||||
UrlEncoded.decodeTo(in, m, charsets[i][1], -1,-1);
|
||||
System.err.println(m);
|
||||
assertEquals(i+" stream length",4,m.size());
|
||||
assertEquals(i+" stream name\\n","value 0",m.getString("name\n"));
|
||||
|
@ -192,7 +192,7 @@ public class URLEncodedTest
|
|||
{
|
||||
ByteArrayInputStream in2 = new ByteArrayInputStream ("name=%83e%83X%83g".getBytes());
|
||||
MultiMap m2 = new MultiMap();
|
||||
UrlEncoded.decodeTo(in2, m2, "Shift_JIS", -1);
|
||||
UrlEncoded.decodeTo(in2, m2, "Shift_JIS", -1,-1);
|
||||
assertEquals("stream length",1,m2.size());
|
||||
assertEquals("stream name","\u30c6\u30b9\u30c8",m2.getString("name"));
|
||||
}
|
||||
|
|
|
@ -348,8 +348,8 @@ public class WebSocketClient
|
|||
|
||||
return holder;
|
||||
}
|
||||
|
||||
public static final InetSocketAddress toSocketAddress(URI uri)
|
||||
|
||||
public static InetSocketAddress toSocketAddress(URI uri)
|
||||
{
|
||||
String scheme = uri.getScheme();
|
||||
if (!("ws".equalsIgnoreCase(scheme) || "wss".equalsIgnoreCase(scheme)))
|
||||
|
@ -360,8 +360,7 @@ public class WebSocketClient
|
|||
if (port < 0)
|
||||
port = "ws".equals(scheme) ? 80 : 443;
|
||||
|
||||
InetSocketAddress address = new InetSocketAddress(uri.getHost(), port);
|
||||
return address;
|
||||
return new InetSocketAddress(uri.getHost(), port);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -371,16 +370,8 @@ public class WebSocketClient
|
|||
{
|
||||
final WebSocket _websocket;
|
||||
final URI _uri;
|
||||
final String _protocol;
|
||||
final String _origin;
|
||||
final MaskGen _maskGen;
|
||||
final int _maxIdleTime;
|
||||
final int _maxTextMessageSize;
|
||||
final int _maxBinaryMessageSize;
|
||||
final Map<String,String> _cookies;
|
||||
final List<String> _extensions;
|
||||
final WebSocketClient _client;
|
||||
final CountDownLatch _done = new CountDownLatch(1);
|
||||
|
||||
ByteChannel _channel;
|
||||
WebSocketConnection _connection;
|
||||
Throwable _exception;
|
||||
|
@ -389,14 +380,7 @@ public class WebSocketClient
|
|||
{
|
||||
_websocket=websocket;
|
||||
_uri=uri;
|
||||
_protocol=client._protocol;
|
||||
_origin=client._origin;
|
||||
_maskGen=client._maskGen;
|
||||
_maxIdleTime=client._maxIdleTime;
|
||||
_maxTextMessageSize=client._maxTextMessageSize;
|
||||
_maxBinaryMessageSize=client._maxBinaryMessageSize;
|
||||
_cookies=client._cookies;
|
||||
_extensions=client._extensions;
|
||||
_client=client;
|
||||
_channel=channel;
|
||||
}
|
||||
|
||||
|
@ -404,8 +388,10 @@ public class WebSocketClient
|
|||
{
|
||||
try
|
||||
{
|
||||
connection.getConnection().setMaxTextMessageSize(_maxTextMessageSize);
|
||||
connection.getConnection().setMaxBinaryMessageSize(_maxBinaryMessageSize);
|
||||
_client.getFactory().addConnection(connection);
|
||||
|
||||
connection.getConnection().setMaxTextMessageSize(_client.getMaxTextMessageSize());
|
||||
connection.getConnection().setMaxBinaryMessageSize(_client.getMaxBinaryMessageSize());
|
||||
|
||||
WebSocketConnection con;
|
||||
synchronized (this)
|
||||
|
@ -460,12 +446,12 @@ public class WebSocketClient
|
|||
|
||||
public Map<String,String> getCookies()
|
||||
{
|
||||
return _cookies;
|
||||
return _client.getCookies();
|
||||
}
|
||||
|
||||
public String getProtocol()
|
||||
{
|
||||
return _protocol;
|
||||
return _client.getProtocol();
|
||||
}
|
||||
|
||||
public WebSocket getWebSocket()
|
||||
|
@ -480,17 +466,17 @@ public class WebSocketClient
|
|||
|
||||
public int getMaxIdleTime()
|
||||
{
|
||||
return _maxIdleTime;
|
||||
return _client.getMaxIdleTime();
|
||||
}
|
||||
|
||||
public String getOrigin()
|
||||
{
|
||||
return _origin;
|
||||
return _client.getOrigin();
|
||||
}
|
||||
|
||||
public MaskGen getMaskGen()
|
||||
{
|
||||
return _maskGen;
|
||||
return _client.getMaskGen();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,8 +20,11 @@ import java.io.IOException;
|
|||
import java.net.ProtocolException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
|
@ -33,6 +36,7 @@ import org.eclipse.jetty.io.Buffers;
|
|||
import org.eclipse.jetty.io.ByteArrayBuffer;
|
||||
import org.eclipse.jetty.io.ConnectedEndPoint;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.SimpleBuffers;
|
||||
import org.eclipse.jetty.io.nio.AsyncConnection;
|
||||
import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
|
||||
|
@ -60,8 +64,8 @@ public class WebSocketClientFactory extends AggregateLifeCycle
|
|||
{
|
||||
private final static Logger __log = org.eclipse.jetty.util.log.Log.getLogger(WebSocketClientFactory.class.getName());
|
||||
private final static ByteArrayBuffer __ACCEPT = new ByteArrayBuffer.CaseInsensitive("Sec-WebSocket-Accept");
|
||||
|
||||
private SslContextFactory _sslContextFactory = new SslContextFactory();
|
||||
private final Queue<WebSocketConnection> connections = new ConcurrentLinkedQueue<WebSocketConnection>();
|
||||
private final SslContextFactory _sslContextFactory = new SslContextFactory();
|
||||
private final ThreadPool _threadPool;
|
||||
private final WebSocketClientSelector _selector;
|
||||
private MaskGen _maskGen;
|
||||
|
@ -200,6 +204,12 @@ public class WebSocketClientFactory extends AggregateLifeCycle
|
|||
return _buffers.getBufferSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
closeConnections();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* <p>Creates and returns a new instance of a {@link WebSocketClient}, configured with this
|
||||
|
@ -231,6 +241,22 @@ public class WebSocketClientFactory extends AggregateLifeCycle
|
|||
return sslEngine;
|
||||
}
|
||||
|
||||
protected boolean addConnection(WebSocketConnection connection)
|
||||
{
|
||||
return isRunning() && connections.add(connection);
|
||||
}
|
||||
|
||||
protected boolean removeConnection(WebSocketConnection connection)
|
||||
{
|
||||
return connections.remove(connection);
|
||||
}
|
||||
|
||||
protected void closeConnections()
|
||||
{
|
||||
for (WebSocketConnection connection : connections)
|
||||
connection.shutdown();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* WebSocket Client Selector Manager
|
||||
|
@ -457,18 +483,9 @@ public class WebSocketClientFactory extends AggregateLifeCycle
|
|||
}
|
||||
else
|
||||
{
|
||||
Buffer header = _parser.getHeaderBuffer();
|
||||
MaskGen maskGen = _future.getMaskGen();
|
||||
WebSocketConnectionRFC6455 connection =
|
||||
new WebSocketConnectionRFC6455(_future.getWebSocket(),
|
||||
_endp,
|
||||
_buffers, System.currentTimeMillis(),
|
||||
_future.getMaxIdleTime(),
|
||||
_future.getProtocol(),
|
||||
null,
|
||||
WebSocketConnectionRFC6455.VERSION,
|
||||
maskGen);
|
||||
WebSocketConnection connection = newWebSocketConnection();
|
||||
|
||||
Buffer header = _parser.getHeaderBuffer();
|
||||
if (header.hasContent())
|
||||
connection.fillBuffersFrom(header);
|
||||
_buffers.returnBuffer(header);
|
||||
|
@ -483,6 +500,21 @@ public class WebSocketClientFactory extends AggregateLifeCycle
|
|||
return this;
|
||||
}
|
||||
|
||||
private WebSocketConnection newWebSocketConnection() throws IOException
|
||||
{
|
||||
return new WebSocketClientConnection(
|
||||
_future._client.getFactory(),
|
||||
_future.getWebSocket(),
|
||||
_endp,
|
||||
_buffers,
|
||||
System.currentTimeMillis(),
|
||||
_future.getMaxIdleTime(),
|
||||
_future.getProtocol(),
|
||||
null,
|
||||
WebSocketConnectionRFC6455.VERSION,
|
||||
_future.getMaskGen());
|
||||
}
|
||||
|
||||
public void onInputShutdown() throws IOException
|
||||
{
|
||||
_endp.close();
|
||||
|
@ -506,4 +538,22 @@ public class WebSocketClientFactory extends AggregateLifeCycle
|
|||
_future.handshakeFailed(new EOFException());
|
||||
}
|
||||
}
|
||||
|
||||
private static class WebSocketClientConnection extends WebSocketConnectionRFC6455
|
||||
{
|
||||
private final WebSocketClientFactory factory;
|
||||
|
||||
public WebSocketClientConnection(WebSocketClientFactory factory, WebSocket webSocket, EndPoint endPoint, WebSocketBuffers buffers, long timeStamp, int maxIdleTime, String protocol, List<Extension> extensions, int draftVersion, MaskGen maskGen) throws IOException
|
||||
{
|
||||
super(webSocket, endPoint, buffers, timeStamp, maxIdleTime, protocol, extensions, draftVersion, maskGen);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
super.onClose();
|
||||
factory.removeConnection(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,8 +24,10 @@ import org.eclipse.jetty.io.nio.AsyncConnection;
|
|||
public interface WebSocketConnection extends AsyncConnection
|
||||
{
|
||||
void fillBuffersFrom(Buffer buffer);
|
||||
|
||||
|
||||
List<Extension> getExtensions();
|
||||
|
||||
|
||||
WebSocket.Connection getConnection();
|
||||
}
|
||||
|
||||
void shutdown();
|
||||
}
|
||||
|
|
|
@ -293,6 +293,11 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc
|
|||
}
|
||||
}
|
||||
|
||||
public void shutdown()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void fillBuffersFrom(Buffer buffer)
|
||||
{
|
||||
|
@ -389,7 +394,7 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc
|
|||
{
|
||||
return _protocol;
|
||||
}
|
||||
|
||||
|
||||
protected void onFrameHandshake()
|
||||
{
|
||||
if (_websocket instanceof OnFrame)
|
||||
|
|
|
@ -274,6 +274,13 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc
|
|||
}
|
||||
}
|
||||
|
||||
public void shutdown()
|
||||
{
|
||||
final WebSocket.Connection connection = _connection;
|
||||
if (connection != null)
|
||||
connection.close(CLOSE_SHUTDOWN, null);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void fillBuffersFrom(Buffer buffer)
|
||||
{
|
||||
|
@ -294,7 +301,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc
|
|||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
protected void onFrameHandshake()
|
||||
{
|
||||
if (_onFrame!=null)
|
||||
|
@ -302,7 +309,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc
|
|||
_onFrame.onHandshake(_connection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void onWebSocketOpen()
|
||||
{
|
||||
_webSocket.onOpen(_connection);
|
||||
|
|
|
@ -370,6 +370,13 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc
|
|||
}
|
||||
}
|
||||
|
||||
public void shutdown()
|
||||
{
|
||||
final WebSocket.Connection connection = _connection;
|
||||
if (connection != null)
|
||||
connection.close(CLOSE_SHUTDOWN, null);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void fillBuffersFrom(Buffer buffer)
|
||||
{
|
||||
|
|
|
@ -400,6 +400,13 @@ public class WebSocketConnectionRFC6455 extends AbstractConnection implements We
|
|||
}
|
||||
}
|
||||
|
||||
public void shutdown()
|
||||
{
|
||||
final WebSocket.Connection connection = _connection;
|
||||
if (connection != null)
|
||||
connection.close(CLOSE_SHUTDOWN, null);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void fillBuffersFrom(Buffer buffer)
|
||||
{
|
||||
|
@ -431,7 +438,7 @@ public class WebSocketConnectionRFC6455 extends AbstractConnection implements We
|
|||
/* ------------------------------------------------------------ */
|
||||
private class WSFrameConnection implements WebSocket.FrameConnection
|
||||
{
|
||||
volatile boolean _disconnecting;
|
||||
private volatile boolean _disconnecting;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void sendMessage(String content) throws IOException
|
||||
|
|
|
@ -30,12 +30,12 @@ package org.eclipse.jetty.websocket;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
@ -45,15 +45,17 @@ import org.eclipse.jetty.io.ConnectedEndPoint;
|
|||
import org.eclipse.jetty.server.AbstractHttpConnection;
|
||||
import org.eclipse.jetty.server.BlockingHttpConnection;
|
||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* Factory to create WebSocket connections
|
||||
*/
|
||||
public class WebSocketFactory
|
||||
public class WebSocketFactory extends AbstractLifeCycle
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(WebSocketFactory.class);
|
||||
private final Queue<WebSocketServletConnection> connections = new ConcurrentLinkedQueue<WebSocketServletConnection>();
|
||||
|
||||
public interface Acceptor
|
||||
{
|
||||
|
@ -87,7 +89,7 @@ public class WebSocketFactory
|
|||
private final Acceptor _acceptor;
|
||||
private WebSocketBuffers _buffers;
|
||||
private int _maxIdleTime = 300000;
|
||||
private int _maxTextMessageSize = 16*1024;
|
||||
private int _maxTextMessageSize = 16 * 1024;
|
||||
private int _maxBinaryMessageSize = -1;
|
||||
|
||||
public WebSocketFactory(Acceptor acceptor)
|
||||
|
@ -101,7 +103,6 @@ public class WebSocketFactory
|
|||
_acceptor = acceptor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return A modifiable map of extension name to extension class
|
||||
*/
|
||||
|
@ -187,6 +188,12 @@ public class WebSocketFactory
|
|||
_maxBinaryMessageSize = maxBinaryMessageSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
closeConnections();
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade the request/response to a WebSocket Connection.
|
||||
* <p>This method will not normally return, but will instead throw a
|
||||
|
@ -230,40 +237,49 @@ public class WebSocketFactory
|
|||
}
|
||||
|
||||
final WebSocketServletConnection connection;
|
||||
final List<Extension> extensions;
|
||||
switch (draft)
|
||||
{
|
||||
case -1: // unspecified draft/version
|
||||
case 0: // Old school draft/version
|
||||
extensions=Collections.emptyList();
|
||||
connection = new WebSocketServletConnectionD00(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol);
|
||||
{
|
||||
connection = new WebSocketServletConnectionD00(this, websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
extensions=Collections.emptyList();
|
||||
connection = new WebSocketServletConnectionD06(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol);
|
||||
{
|
||||
connection = new WebSocketServletConnectionD06(this, websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol);
|
||||
break;
|
||||
}
|
||||
case 7:
|
||||
case 8:
|
||||
extensions= initExtensions(extensions_requested,8-WebSocketConnectionD08.OP_EXT_DATA, 16-WebSocketConnectionD08.OP_EXT_CTRL,3);
|
||||
connection = new WebSocketServletConnectionD08(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft);
|
||||
{
|
||||
List<Extension> extensions = initExtensions(extensions_requested, 8 - WebSocketConnectionD08.OP_EXT_DATA, 16 - WebSocketConnectionD08.OP_EXT_CTRL, 3);
|
||||
connection = new WebSocketServletConnectionD08(this, websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol, extensions, draft);
|
||||
break;
|
||||
}
|
||||
case WebSocketConnectionRFC6455.VERSION: // RFC 6455 Version
|
||||
extensions= initExtensions(extensions_requested,8-WebSocketConnectionRFC6455.OP_EXT_DATA, 16-WebSocketConnectionRFC6455.OP_EXT_CTRL,3);
|
||||
connection = new WebSocketServletConnectionRFC6455(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft);
|
||||
{
|
||||
List<Extension> extensions = initExtensions(extensions_requested, 8 - WebSocketConnectionRFC6455.OP_EXT_DATA, 16 - WebSocketConnectionRFC6455.OP_EXT_CTRL, 3);
|
||||
connection = new WebSocketServletConnectionRFC6455(this, websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol, extensions, draft);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG.warn("Unsupported Websocket version: "+draft);
|
||||
{
|
||||
LOG.warn("Unsupported Websocket version: " + draft);
|
||||
// Per RFC 6455 - 4.4 - Supporting Multiple Versions of WebSocket Protocol
|
||||
// Using the examples as outlined
|
||||
response.setHeader("Sec-WebSocket-Version","13, 8, 6, 0");
|
||||
response.setHeader("Sec-WebSocket-Version", "13, 8, 6, 0");
|
||||
throw new HttpException(400, "Unsupported websocket version specification: " + draft);
|
||||
}
|
||||
}
|
||||
|
||||
addConnection(connection);
|
||||
|
||||
// Set the defaults
|
||||
connection.getConnection().setMaxBinaryMessageSize(_maxBinaryMessageSize);
|
||||
connection.getConnection().setMaxTextMessageSize(_maxTextMessageSize);
|
||||
|
@ -281,8 +297,6 @@ public class WebSocketFactory
|
|||
request.setAttribute("org.eclipse.jetty.io.Connection", connection);
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
protected String[] parseProtocols(String protocol)
|
||||
{
|
||||
if (protocol == null)
|
||||
|
@ -296,8 +310,6 @@ public class WebSocketFactory
|
|||
return protocols;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException
|
||||
{
|
||||
|
@ -353,8 +365,6 @@ public class WebSocketFactory
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public List<Extension> initExtensions(List<String> requested,int maxDataOpcodes,int maxControlOpcodes,int maxReservedBits)
|
||||
{
|
||||
List<Extension> extensions = new ArrayList<Extension>();
|
||||
|
@ -386,8 +396,6 @@ public class WebSocketFactory
|
|||
return extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
private Extension newExtension(String name)
|
||||
{
|
||||
try
|
||||
|
@ -403,4 +411,20 @@ public class WebSocketFactory
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean addConnection(WebSocketServletConnection connection)
|
||||
{
|
||||
return isRunning() && connections.add(connection);
|
||||
}
|
||||
|
||||
protected boolean removeConnection(WebSocketServletConnection connection)
|
||||
{
|
||||
return connections.remove(connection);
|
||||
}
|
||||
|
||||
protected void closeConnections()
|
||||
{
|
||||
for (WebSocketServletConnection connection : connections)
|
||||
connection.shutdown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,31 +34,33 @@ import javax.servlet.http.HttpServlet;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Servlet to upgrade connections to WebSocket
|
||||
* <p>
|
||||
* <p/>
|
||||
* The request must have the correct upgrade headers, else it is
|
||||
* handled as a normal servlet request.
|
||||
* <p>
|
||||
* <p/>
|
||||
* The initParameter "bufferSize" can be used to set the buffer size,
|
||||
* which is also the max frame byte size (default 8192).
|
||||
* <p>
|
||||
* <p/>
|
||||
* The initParameter "maxIdleTime" can be used to set the time in ms
|
||||
* that a websocket may be idle before closing.
|
||||
* <p>
|
||||
* <p/>
|
||||
* The initParameter "maxTextMessagesSize" can be used to set the size in characters
|
||||
* that a websocket may be accept before closing.
|
||||
* <p>
|
||||
* <p/>
|
||||
* The initParameter "maxBinaryMessagesSize" can be used to set the size in bytes
|
||||
* that a websocket may be accept before closing.
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class WebSocketServlet extends HttpServlet implements WebSocketFactory.Acceptor
|
||||
{
|
||||
WebSocketFactory _webSocketFactory;
|
||||
private final Logger LOG = Log.getLogger(getClass());
|
||||
private WebSocketFactory _webSocketFactory;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
|
@ -67,20 +69,32 @@ public abstract class WebSocketServlet extends HttpServlet implements WebSocketF
|
|||
@Override
|
||||
public void init() throws ServletException
|
||||
{
|
||||
String bs=getInitParameter("bufferSize");
|
||||
_webSocketFactory = new WebSocketFactory(this,bs==null?8192:Integer.parseInt(bs));
|
||||
String max=getInitParameter("maxIdleTime");
|
||||
if (max!=null)
|
||||
_webSocketFactory.setMaxIdleTime(Integer.parseInt(max));
|
||||
try
|
||||
{
|
||||
String bs = getInitParameter("bufferSize");
|
||||
_webSocketFactory = new WebSocketFactory(this, bs == null ? 8192 : Integer.parseInt(bs));
|
||||
_webSocketFactory.start();
|
||||
|
||||
max=getInitParameter("maxTextMessageSize");
|
||||
if (max!=null)
|
||||
_webSocketFactory.setMaxTextMessageSize(Integer.parseInt(max));
|
||||
String max = getInitParameter("maxIdleTime");
|
||||
if (max != null)
|
||||
_webSocketFactory.setMaxIdleTime(Integer.parseInt(max));
|
||||
|
||||
max=getInitParameter("maxBinaryMessageSize");
|
||||
if (max!=null)
|
||||
_webSocketFactory.setMaxBinaryMessageSize(Integer.parseInt(max));
|
||||
max = getInitParameter("maxTextMessageSize");
|
||||
if (max != null)
|
||||
_webSocketFactory.setMaxTextMessageSize(Integer.parseInt(max));
|
||||
|
||||
max = getInitParameter("maxBinaryMessageSize");
|
||||
if (max != null)
|
||||
_webSocketFactory.setMaxBinaryMessageSize(Integer.parseInt(max));
|
||||
}
|
||||
catch (ServletException x)
|
||||
{
|
||||
throw x;
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
throw new ServletException(x);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -90,9 +104,9 @@ public abstract class WebSocketServlet extends HttpServlet implements WebSocketF
|
|||
@Override
|
||||
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
if (_webSocketFactory.acceptWebSocket(request,response) || response.isCommitted())
|
||||
if (_webSocketFactory.acceptWebSocket(request, response) || response.isCommitted())
|
||||
return;
|
||||
super.service(request,response);
|
||||
super.service(request, response);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -101,6 +115,17 @@ public abstract class WebSocketServlet extends HttpServlet implements WebSocketF
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
try
|
||||
{
|
||||
_webSocketFactory.stop();
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.ignore(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,10 +25,13 @@ import org.eclipse.jetty.util.QuotedStringTokenizer;
|
|||
|
||||
public class WebSocketServletConnectionD00 extends WebSocketConnectionD00 implements WebSocketServletConnection
|
||||
{
|
||||
public WebSocketServletConnectionD00(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol)
|
||||
private final WebSocketFactory factory;
|
||||
|
||||
public WebSocketServletConnectionD00(WebSocketFactory factory, WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol)
|
||||
throws IOException
|
||||
{
|
||||
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
|
||||
|
@ -70,7 +73,7 @@ public class WebSocketServletConnectionD00 extends WebSocketConnectionD00 implem
|
|||
{
|
||||
response.addHeader("Sec-WebSocket-Protocol",subprotocol);
|
||||
}
|
||||
response.sendError(101,"WebSocket Protocol Handshake");
|
||||
response.sendError(101, "WebSocket Protocol Handshake");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -89,4 +92,11 @@ public class WebSocketServletConnectionD00 extends WebSocketConnectionD00 implem
|
|||
onWebsocketOpen();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
super.onClose();
|
||||
factory.removeConnection(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,13 @@ import org.eclipse.jetty.io.EndPoint;
|
|||
|
||||
public class WebSocketServletConnectionD06 extends WebSocketConnectionD06 implements WebSocketServletConnection
|
||||
{
|
||||
public WebSocketServletConnectionD06(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol)
|
||||
private final WebSocketFactory factory;
|
||||
|
||||
public WebSocketServletConnectionD06(WebSocketFactory factory, WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol)
|
||||
throws IOException
|
||||
{
|
||||
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -47,4 +50,11 @@ public class WebSocketServletConnectionD06 extends WebSocketConnectionD06 implem
|
|||
onFrameHandshake();
|
||||
onWebSocketOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
super.onClose();
|
||||
factory.removeConnection(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,16 +24,13 @@ import org.eclipse.jetty.io.EndPoint;
|
|||
|
||||
public class WebSocketServletConnectionD08 extends WebSocketConnectionD08 implements WebSocketServletConnection
|
||||
{
|
||||
public WebSocketServletConnectionD08(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol,
|
||||
List<Extension> extensions, int draft, MaskGen maskgen) throws IOException
|
||||
{
|
||||
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol,extensions,draft,maskgen);
|
||||
}
|
||||
private final WebSocketFactory factory;
|
||||
|
||||
public WebSocketServletConnectionD08(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol,
|
||||
public WebSocketServletConnectionD08(WebSocketFactory factory, WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol,
|
||||
List<Extension> extensions, int draft) throws IOException
|
||||
{
|
||||
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol,extensions,draft);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -59,4 +56,11 @@ public class WebSocketServletConnectionD08 extends WebSocketConnectionD08 implem
|
|||
onFrameHandshake();
|
||||
onWebSocketOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
super.onClose();
|
||||
factory.removeConnection(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,16 +24,13 @@ import org.eclipse.jetty.io.EndPoint;
|
|||
|
||||
public class WebSocketServletConnectionRFC6455 extends WebSocketConnectionRFC6455 implements WebSocketServletConnection
|
||||
{
|
||||
public WebSocketServletConnectionRFC6455(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol,
|
||||
List<Extension> extensions, int draft, MaskGen maskgen) throws IOException
|
||||
{
|
||||
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol,extensions,draft,maskgen);
|
||||
}
|
||||
private final WebSocketFactory factory;
|
||||
|
||||
public WebSocketServletConnectionRFC6455(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol,
|
||||
public WebSocketServletConnectionRFC6455(WebSocketFactory factory, WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol,
|
||||
List<Extension> extensions, int draft) throws IOException
|
||||
{
|
||||
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol,extensions,draft);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -59,4 +56,11 @@ public class WebSocketServletConnectionRFC6455 extends WebSocketConnectionRFC645
|
|||
onFrameHandshake();
|
||||
onWebSocketOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
super.onClose();
|
||||
factory.removeConnection(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,57 +25,80 @@ import java.io.InputStream;
|
|||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.Utf8StringBuilder;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.StdErrLog;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class WebSocketMessageD00Test
|
||||
{
|
||||
private static Server _server;
|
||||
private static Connector _connector;
|
||||
private static TestWebSocket _serverWebSocket;
|
||||
private static Server __server;
|
||||
private static Connector __connector;
|
||||
private static TestWebSocket __serverWebSocket;
|
||||
private static CountDownLatch __latch;
|
||||
private static AtomicInteger __textCount = new AtomicInteger(0);
|
||||
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
{
|
||||
_server = new Server();
|
||||
_connector = new SelectChannelConnector();
|
||||
_server.addConnector(_connector);
|
||||
__server = new Server();
|
||||
__connector = new SelectChannelConnector();
|
||||
__server.addConnector(__connector);
|
||||
WebSocketHandler wsHandler = new WebSocketHandler()
|
||||
{
|
||||
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
|
||||
{
|
||||
_serverWebSocket = new TestWebSocket();
|
||||
_serverWebSocket.onConnect=("onConnect".equals(protocol));
|
||||
return _serverWebSocket;
|
||||
__serverWebSocket = new TestWebSocket();
|
||||
__serverWebSocket._onConnect=("onConnect".equals(protocol));
|
||||
__serverWebSocket._echo=("echo".equals(protocol));
|
||||
__serverWebSocket._latch=("latch".equals(protocol));
|
||||
if (__serverWebSocket._latch)
|
||||
__latch=new CountDownLatch(1);
|
||||
return __serverWebSocket;
|
||||
}
|
||||
};
|
||||
wsHandler.setHandler(new DefaultHandler());
|
||||
_server.setHandler(wsHandler);
|
||||
_server.start();
|
||||
__server.setHandler(wsHandler);
|
||||
__server.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
_server.stop();
|
||||
_server.join();
|
||||
__server.stop();
|
||||
__server.join();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void reset()
|
||||
{
|
||||
__textCount.set(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerSendBigStringMessage() throws Exception
|
||||
{
|
||||
Socket socket = new Socket("localhost", _connector.getLocalPort());
|
||||
Socket socket = new Socket("localhost", __connector.getLocalPort());
|
||||
OutputStream output = socket.getOutputStream();
|
||||
output.write(
|
||||
("GET /test HTTP/1.1\r\n" +
|
||||
|
@ -94,7 +117,6 @@ public class WebSocketMessageD00Test
|
|||
InputStream input = socket.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "ISO-8859-1"));
|
||||
String responseLine = reader.readLine();
|
||||
System.err.println(responseLine);
|
||||
assertTrue(responseLine.startsWith("HTTP/1.1 101 WebSocket Protocol Handshake"));
|
||||
// Read until we find an empty line, which signals the end of the http response
|
||||
String line;
|
||||
|
@ -102,8 +124,8 @@ public class WebSocketMessageD00Test
|
|||
if (line.length() == 0)
|
||||
break;
|
||||
|
||||
assertTrue(_serverWebSocket.awaitConnected(1000));
|
||||
assertNotNull(_serverWebSocket.outbound);
|
||||
assertTrue(__serverWebSocket.awaitConnected(1000));
|
||||
assertNotNull(__serverWebSocket.outbound);
|
||||
|
||||
// read the hixie bytes
|
||||
char[] hixie=new char[16]; // should be bytes, but we know this example is all ascii
|
||||
|
@ -125,7 +147,7 @@ public class WebSocketMessageD00Test
|
|||
String text = "0123456789ABCDEF";
|
||||
for (int i = 0; i < 64 * 1024 / text.length(); ++i)
|
||||
message.append(text);
|
||||
_serverWebSocket.outbound.sendMessage(message.toString());
|
||||
__serverWebSocket.outbound.sendMessage(message.toString());
|
||||
|
||||
// Read until we get 0xFF
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
@ -147,7 +169,7 @@ public class WebSocketMessageD00Test
|
|||
@Test
|
||||
public void testServerSendOnConnect() throws Exception
|
||||
{
|
||||
Socket socket = new Socket("localhost", _connector.getLocalPort());
|
||||
Socket socket = new Socket("localhost", __connector.getLocalPort());
|
||||
OutputStream output = socket.getOutputStream();
|
||||
output.write(
|
||||
("GET /test HTTP/1.1\r\n" +
|
||||
|
@ -200,8 +222,8 @@ public class WebSocketMessageD00Test
|
|||
}
|
||||
|
||||
|
||||
assertTrue(_serverWebSocket.awaitConnected(1000));
|
||||
assertNotNull(_serverWebSocket.outbound);
|
||||
assertTrue(__serverWebSocket.awaitConnected(1000));
|
||||
assertNotNull(__serverWebSocket.outbound);
|
||||
|
||||
looking_for="8jKS'y:G*Co,Wxa-";
|
||||
while(true)
|
||||
|
@ -232,17 +254,498 @@ public class WebSocketMessageD00Test
|
|||
assertEquals(0xff,input.read());
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static class TestWebSocket implements WebSocket
|
||||
@Test
|
||||
public void testServerEcho() throws Exception
|
||||
{
|
||||
// Log.getLogger("org.eclipse.jetty.websocket").setDebugEnabled(true);
|
||||
|
||||
Socket socket = new Socket("localhost", __connector.getLocalPort());
|
||||
socket.setSoTimeout(1000);
|
||||
OutputStream output = socket.getOutputStream();
|
||||
InputStream input = socket.getInputStream();
|
||||
output.write(
|
||||
("GET /test HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Upgrade: WebSocket\r\n" +
|
||||
"Connection: Upgrade\r\n" +
|
||||
"Sec-WebSocket-Protocol: echo\r\n" +
|
||||
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
|
||||
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
|
||||
"\r\n"+
|
||||
"^n:ds[4U").getBytes("ISO-8859-1"));
|
||||
output.flush();
|
||||
|
||||
lookFor("HTTP/1.1 101 WebSocket Protocol Handshake\r\n",input);
|
||||
skipTo("\r\n\r\n",input);
|
||||
lookFor("8jKS'y:G*Co,Wxa-",input);
|
||||
|
||||
assertTrue(__serverWebSocket.awaitConnected(1000));
|
||||
assertNotNull(__serverWebSocket.connection);
|
||||
|
||||
|
||||
output.write(0x00);
|
||||
byte[] bytes="this is an echo".getBytes(StringUtil.__ISO_8859_1);
|
||||
for (int i=0;i<bytes.length;i++)
|
||||
output.write(bytes[i]);
|
||||
output.write(0xff);
|
||||
output.flush();
|
||||
|
||||
assertEquals("00",TypeUtil.toHexString((byte)(0xff&input.read())));
|
||||
lookFor("this is an echo",input);
|
||||
assertEquals(0xff,input.read());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlockedConsumer() throws Exception
|
||||
{
|
||||
boolean onConnect=false;
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
private volatile Connection outbound;
|
||||
|
||||
// Log.getLogger("org.eclipse.jetty.websocket").setDebugEnabled(true);
|
||||
|
||||
Socket socket = new Socket("localhost", __connector.getLocalPort());
|
||||
socket.setSoTimeout(60000);
|
||||
OutputStream output = socket.getOutputStream();
|
||||
InputStream input = socket.getInputStream();
|
||||
output.write(
|
||||
("GET /test HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Upgrade: WebSocket\r\n" +
|
||||
"Connection: Upgrade\r\n" +
|
||||
"Sec-WebSocket-Protocol: latch\r\n" +
|
||||
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
|
||||
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
|
||||
"\r\n"+
|
||||
"^n:ds[4U").getBytes("ISO-8859-1"));
|
||||
output.flush();
|
||||
|
||||
lookFor("HTTP/1.1 101 WebSocket Protocol Handshake\r\n",input);
|
||||
skipTo("\r\n\r\n",input);
|
||||
lookFor("8jKS'y:G*Co,Wxa-",input);
|
||||
|
||||
assertTrue(__serverWebSocket.awaitConnected(1000));
|
||||
assertNotNull(__serverWebSocket.connection);
|
||||
__serverWebSocket.connection.setMaxIdleTime(60000);
|
||||
|
||||
|
||||
byte[] bytes="This is a long message of text that we will send again and again".getBytes(StringUtil.__ISO_8859_1);
|
||||
byte[] mesg=new byte[bytes.length+2];
|
||||
mesg[0]=(byte)0x00;
|
||||
for (int i=0;i<bytes.length;i++)
|
||||
mesg[i+1]=(byte)(bytes[i]);
|
||||
mesg[mesg.length-1]=(byte)0xFF;
|
||||
|
||||
final int count = 100000;
|
||||
|
||||
|
||||
// Send and receive 1 message
|
||||
output.write(mesg);
|
||||
output.flush();
|
||||
while(__textCount.get()==0)
|
||||
Thread.sleep(10);
|
||||
|
||||
// unblock the latch in 4s
|
||||
new Thread()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(4000);
|
||||
__latch.countDown();
|
||||
//System.err.println("latched");
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
|
||||
// Send enough messages to fill receive buffer
|
||||
long max=0;
|
||||
long start=System.currentTimeMillis();
|
||||
for (int i=0;i<count;i++)
|
||||
{
|
||||
output.write(mesg);
|
||||
if (i%100==0)
|
||||
{
|
||||
// System.err.println(">>> "+i);
|
||||
output.flush();
|
||||
|
||||
long now=System.currentTimeMillis();
|
||||
long duration=now-start;
|
||||
start=now;
|
||||
if (max<duration)
|
||||
max=duration;
|
||||
}
|
||||
}
|
||||
|
||||
Thread.sleep(50);
|
||||
while(__textCount.get()<count+1)
|
||||
{
|
||||
System.err.println(__textCount.get()+"<"+(count+1));
|
||||
Thread.sleep(10);
|
||||
}
|
||||
assertEquals(count+1,__textCount.get()); // all messages
|
||||
assertTrue(max>2000); // was blocked
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlockedProducer() throws Exception
|
||||
{
|
||||
// Log.getLogger("org.eclipse.jetty.websocket").setDebugEnabled(true);
|
||||
|
||||
final Socket socket = new Socket("localhost", __connector.getLocalPort());
|
||||
socket.setSoTimeout(60000);
|
||||
OutputStream output = socket.getOutputStream();
|
||||
InputStream input = socket.getInputStream();
|
||||
output.write(
|
||||
("GET /test HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Upgrade: WebSocket\r\n" +
|
||||
"Connection: Upgrade\r\n" +
|
||||
"Sec-WebSocket-Protocol: latch\r\n" +
|
||||
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
|
||||
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
|
||||
"\r\n"+
|
||||
"^n:ds[4U").getBytes("ISO-8859-1"));
|
||||
output.flush();
|
||||
|
||||
lookFor("HTTP/1.1 101 WebSocket Protocol Handshake\r\n",input);
|
||||
skipTo("\r\n\r\n",input);
|
||||
lookFor("8jKS'y:G*Co,Wxa-",input);
|
||||
|
||||
assertTrue(__serverWebSocket.awaitConnected(1000));
|
||||
assertNotNull(__serverWebSocket.connection);
|
||||
|
||||
|
||||
final int count = 100000;
|
||||
|
||||
__serverWebSocket.connection.setMaxIdleTime(60000);
|
||||
__latch.countDown();
|
||||
|
||||
// wait 2s and then consume messages
|
||||
final AtomicLong totalB=new AtomicLong();
|
||||
new Thread()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(2000);
|
||||
|
||||
byte[] recv = new byte[32*1024];
|
||||
|
||||
int len=0;
|
||||
while (len>=0)
|
||||
{
|
||||
totalB.addAndGet(len);
|
||||
len=socket.getInputStream().read(recv,0,recv.length);
|
||||
Thread.sleep(10);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
|
||||
|
||||
// Send enough messages to fill receive buffer
|
||||
long max=0;
|
||||
long start=System.currentTimeMillis();
|
||||
String mesg="How Now Brown Cow";
|
||||
for (int i=0;i<count;i++)
|
||||
{
|
||||
__serverWebSocket.connection.sendMessage(mesg);
|
||||
if (i%100==0)
|
||||
{
|
||||
output.flush();
|
||||
|
||||
long now=System.currentTimeMillis();
|
||||
long duration=now-start;
|
||||
start=now;
|
||||
if (max<duration)
|
||||
max=duration;
|
||||
}
|
||||
}
|
||||
|
||||
while(totalB.get()<(count*(mesg.length()+2)))
|
||||
Thread.sleep(100);
|
||||
|
||||
assertEquals(count*(mesg.length()+2),totalB.get()); // all messages
|
||||
assertTrue(max>1000); // was blocked
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testIdle() throws Exception
|
||||
{
|
||||
// Log.getLogger("org.eclipse.jetty.websocket").setDebugEnabled(true);
|
||||
|
||||
final Socket socket = new Socket("localhost", __connector.getLocalPort());
|
||||
socket.setSoTimeout(10000);
|
||||
OutputStream output = socket.getOutputStream();
|
||||
InputStream input = socket.getInputStream();
|
||||
output.write(
|
||||
("GET /test HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Upgrade: WebSocket\r\n" +
|
||||
"Connection: Upgrade\r\n" +
|
||||
"Sec-WebSocket-Protocol: onConnect\r\n" +
|
||||
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
|
||||
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
|
||||
"\r\n"+
|
||||
"^n:ds[4U").getBytes("ISO-8859-1"));
|
||||
output.flush();
|
||||
|
||||
lookFor("HTTP/1.1 101 WebSocket Protocol Handshake\r\n",input);
|
||||
skipTo("\r\n\r\n",input);
|
||||
lookFor("8jKS'y:G*Co,Wxa-",input);
|
||||
|
||||
assertTrue(__serverWebSocket.awaitConnected(1000));
|
||||
assertNotNull(__serverWebSocket.connection);
|
||||
__serverWebSocket.connection.setMaxIdleTime(250);
|
||||
assertEquals(0x00,input.read());
|
||||
lookFor("sent on connect",input);
|
||||
assertEquals(0xff,input.read());
|
||||
|
||||
assertEquals(-1,input.read());
|
||||
socket.close();
|
||||
|
||||
assertTrue(__serverWebSocket.awaitDisconnected(100));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIdleBadClient() throws Exception
|
||||
{
|
||||
// Log.getLogger("org.eclipse.jetty.websocket").setDebugEnabled(true);
|
||||
|
||||
final Socket socket = new Socket("localhost", __connector.getLocalPort());
|
||||
socket.setSoTimeout(10000);
|
||||
OutputStream output = socket.getOutputStream();
|
||||
InputStream input = socket.getInputStream();
|
||||
output.write(
|
||||
("GET /test HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Upgrade: WebSocket\r\n" +
|
||||
"Connection: Upgrade\r\n" +
|
||||
"Sec-WebSocket-Protocol: onConnect\r\n" +
|
||||
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
|
||||
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
|
||||
"\r\n"+
|
||||
"^n:ds[4U").getBytes("ISO-8859-1"));
|
||||
output.flush();
|
||||
|
||||
lookFor("HTTP/1.1 101 WebSocket Protocol Handshake\r\n",input);
|
||||
skipTo("\r\n\r\n",input);
|
||||
lookFor("8jKS'y:G*Co,Wxa-",input);
|
||||
|
||||
assertTrue(__serverWebSocket.awaitConnected(1000));
|
||||
assertNotNull(__serverWebSocket.connection);
|
||||
__serverWebSocket.connection.setMaxIdleTime(250);
|
||||
assertEquals(0x00,input.read());
|
||||
lookFor("sent on connect",input);
|
||||
assertEquals(0xff,input.read());
|
||||
|
||||
assertEquals(-1,input.read());
|
||||
|
||||
assertTrue(__serverWebSocket.disconnected.getCount()>0);
|
||||
assertTrue(__serverWebSocket.awaitDisconnected(1000));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTCPClose() throws Exception
|
||||
{
|
||||
// Log.getLogger("org.eclipse.jetty.websocket").setDebugEnabled(true);
|
||||
|
||||
final Socket socket = new Socket("localhost", __connector.getLocalPort());
|
||||
socket.setSoTimeout(10000);
|
||||
OutputStream output = socket.getOutputStream();
|
||||
InputStream input = socket.getInputStream();
|
||||
output.write(
|
||||
("GET /test HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Upgrade: WebSocket\r\n" +
|
||||
"Connection: Upgrade\r\n" +
|
||||
"Sec-WebSocket-Protocol: onConnect\r\n" +
|
||||
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
|
||||
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
|
||||
"\r\n"+
|
||||
"^n:ds[4U").getBytes("ISO-8859-1"));
|
||||
output.flush();
|
||||
|
||||
lookFor("HTTP/1.1 101 WebSocket Protocol Handshake\r\n",input);
|
||||
skipTo("\r\n\r\n",input);
|
||||
lookFor("8jKS'y:G*Co,Wxa-",input);
|
||||
|
||||
assertTrue(__serverWebSocket.awaitConnected(1000));
|
||||
assertNotNull(__serverWebSocket.connection);
|
||||
__serverWebSocket.connection.setMaxIdleTime(250);
|
||||
assertEquals(0x00,input.read());
|
||||
lookFor("sent on connect",input);
|
||||
assertEquals(0xff,input.read());
|
||||
|
||||
|
||||
|
||||
socket.close();
|
||||
|
||||
assertTrue(__serverWebSocket.awaitDisconnected(500));
|
||||
|
||||
try
|
||||
{
|
||||
__serverWebSocket.connection.sendMessage("Don't send");
|
||||
assertTrue(false);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTCPHalfClose() throws Exception
|
||||
{
|
||||
// Log.getLogger("org.eclipse.jetty.websocket").setDebugEnabled(true);
|
||||
|
||||
final Socket socket = new Socket("localhost", __connector.getLocalPort());
|
||||
socket.setSoTimeout(10000);
|
||||
OutputStream output = socket.getOutputStream();
|
||||
InputStream input = socket.getInputStream();
|
||||
output.write(
|
||||
("GET /test HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Upgrade: WebSocket\r\n" +
|
||||
"Connection: Upgrade\r\n" +
|
||||
"Sec-WebSocket-Protocol: onConnect\r\n" +
|
||||
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n" +
|
||||
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n" +
|
||||
"\r\n"+
|
||||
"^n:ds[4U").getBytes("ISO-8859-1"));
|
||||
output.flush();
|
||||
|
||||
lookFor("HTTP/1.1 101 WebSocket Protocol Handshake\r\n",input);
|
||||
skipTo("\r\n\r\n",input);
|
||||
lookFor("8jKS'y:G*Co,Wxa-",input);
|
||||
|
||||
assertTrue(__serverWebSocket.awaitConnected(1000));
|
||||
assertNotNull(__serverWebSocket.connection);
|
||||
__serverWebSocket.connection.setMaxIdleTime(250);
|
||||
assertEquals(0x00,input.read());
|
||||
lookFor("sent on connect",input);
|
||||
assertEquals(0xff,input.read());
|
||||
|
||||
|
||||
socket.shutdownOutput();
|
||||
|
||||
assertTrue(__serverWebSocket.awaitDisconnected(500));
|
||||
|
||||
assertEquals(-1,input.read());
|
||||
|
||||
// look for broken pipe
|
||||
try
|
||||
{
|
||||
for (int i=0;i<1000;i++)
|
||||
output.write(0);
|
||||
Assert.fail();
|
||||
}
|
||||
catch(SocketException e)
|
||||
{
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void lookFor(String string,InputStream in)
|
||||
throws IOException
|
||||
{
|
||||
String orig=string;
|
||||
Utf8StringBuilder scanned=new Utf8StringBuilder();
|
||||
try
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
int b = in.read();
|
||||
if (b<0)
|
||||
throw new EOFException();
|
||||
scanned.append((byte)b);
|
||||
assertEquals("looking for\""+orig+"\" in '"+scanned+"'",(int)string.charAt(0),b);
|
||||
if (string.length()==1)
|
||||
break;
|
||||
string=string.substring(1);
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
System.err.println("IOE while looking for \""+orig+"\" in '"+scanned+"'");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void skipTo(String string,InputStream in)
|
||||
throws IOException
|
||||
{
|
||||
int state=0;
|
||||
|
||||
while(true)
|
||||
{
|
||||
int b = in.read();
|
||||
if (b<0)
|
||||
throw new EOFException();
|
||||
|
||||
if (b==string.charAt(state))
|
||||
{
|
||||
state++;
|
||||
if (state==string.length())
|
||||
break;
|
||||
}
|
||||
else
|
||||
state=0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static class TestWebSocket implements WebSocket.OnFrame, WebSocket, WebSocket.OnTextMessage
|
||||
{
|
||||
protected boolean _latch;
|
||||
boolean _echo=true;
|
||||
boolean _onConnect=false;
|
||||
private volatile Connection outbound;
|
||||
private final CountDownLatch connected = new CountDownLatch(1);
|
||||
private final CountDownLatch disconnected = new CountDownLatch(1);
|
||||
private volatile FrameConnection connection;
|
||||
|
||||
public FrameConnection getConnection()
|
||||
{
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void onHandshake(FrameConnection connection)
|
||||
{
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
public void onOpen(Connection outbound)
|
||||
{
|
||||
this.outbound = outbound;
|
||||
if (onConnect)
|
||||
if (_onConnect)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -253,16 +756,55 @@ public class WebSocketMessageD00Test
|
|||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
latch.countDown();
|
||||
connected.countDown();
|
||||
}
|
||||
|
||||
private boolean awaitConnected(long time) throws InterruptedException
|
||||
{
|
||||
return latch.await(time, TimeUnit.MILLISECONDS);
|
||||
return connected.await(time, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private boolean awaitDisconnected(long time) throws InterruptedException
|
||||
{
|
||||
return disconnected.await(time, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void onClose(int code,String message)
|
||||
{
|
||||
disconnected.countDown();
|
||||
}
|
||||
|
||||
public boolean onFrame(byte flags, byte opcode, byte[] data, int offset, int length)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onMessage(String data)
|
||||
{
|
||||
__textCount.incrementAndGet();
|
||||
if (_latch)
|
||||
{
|
||||
try
|
||||
{
|
||||
__latch.await();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (_echo)
|
||||
{
|
||||
try
|
||||
{
|
||||
outbound.sendMessage(data);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2011 Intalio, Inc.
|
||||
* ======================================================================
|
||||
* 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.websocket;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class WebSocketRedeployTest
|
||||
{
|
||||
private Server server;
|
||||
private ServletContextHandler context;
|
||||
private String uri;
|
||||
private WebSocketClientFactory wsFactory;
|
||||
|
||||
public void init(final WebSocket webSocket) throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
SelectChannelConnector connector = new SelectChannelConnector();
|
||||
// connector.setPort(8080);
|
||||
server.addConnector(connector);
|
||||
|
||||
HandlerCollection handlers = new HandlerCollection();
|
||||
server.setHandler(handlers);
|
||||
|
||||
String contextPath = "/test_context";
|
||||
context = new ServletContextHandler(handlers, contextPath, ServletContextHandler.SESSIONS);
|
||||
|
||||
WebSocketServlet servlet = new WebSocketServlet()
|
||||
{
|
||||
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
|
||||
{
|
||||
return webSocket;
|
||||
}
|
||||
};
|
||||
String servletPath = "/websocket";
|
||||
context.addServlet(new ServletHolder(servlet), servletPath);
|
||||
|
||||
server.start();
|
||||
|
||||
uri = "ws://localhost:" + connector.getLocalPort() + contextPath + servletPath;
|
||||
|
||||
wsFactory = new WebSocketClientFactory();
|
||||
wsFactory.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void destroy() throws Exception
|
||||
{
|
||||
if (wsFactory != null)
|
||||
wsFactory.stop();
|
||||
if (server != null)
|
||||
{
|
||||
server.stop();
|
||||
server.join();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoppingContextClosesConnections() throws Exception
|
||||
{
|
||||
final CountDownLatch openLatch = new CountDownLatch(2);
|
||||
final CountDownLatch closeLatch = new CountDownLatch(2);
|
||||
init(new WebSocket.OnTextMessage()
|
||||
{
|
||||
public void onOpen(Connection connection)
|
||||
{
|
||||
openLatch.countDown();
|
||||
}
|
||||
|
||||
public void onMessage(String data)
|
||||
{
|
||||
}
|
||||
|
||||
public void onClose(int closeCode, String message)
|
||||
{
|
||||
closeLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
WebSocketClient client = wsFactory.newWebSocketClient();
|
||||
client.open(new URI(uri), new WebSocket.OnTextMessage()
|
||||
{
|
||||
public void onOpen(Connection connection)
|
||||
{
|
||||
openLatch.countDown();
|
||||
}
|
||||
|
||||
public void onMessage(String data)
|
||||
{
|
||||
}
|
||||
|
||||
public void onClose(int closeCode, String message)
|
||||
{
|
||||
closeLatch.countDown();
|
||||
}
|
||||
}, 5, TimeUnit.SECONDS);
|
||||
|
||||
Assert.assertTrue(openLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
context.stop();
|
||||
|
||||
Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoppingClientFactoryClosesConnections() throws Exception
|
||||
{
|
||||
final CountDownLatch openLatch = new CountDownLatch(2);
|
||||
final CountDownLatch closeLatch = new CountDownLatch(2);
|
||||
init(new WebSocket.OnTextMessage()
|
||||
{
|
||||
public void onOpen(Connection connection)
|
||||
{
|
||||
openLatch.countDown();
|
||||
}
|
||||
|
||||
public void onMessage(String data)
|
||||
{
|
||||
}
|
||||
|
||||
public void onClose(int closeCode, String message)
|
||||
{
|
||||
closeLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
WebSocketClient client = wsFactory.newWebSocketClient();
|
||||
client.open(new URI(uri), new WebSocket.OnTextMessage()
|
||||
{
|
||||
public void onOpen(Connection connection)
|
||||
{
|
||||
openLatch.countDown();
|
||||
}
|
||||
|
||||
public void onMessage(String data)
|
||||
{
|
||||
}
|
||||
|
||||
public void onClose(int closeCode, String message)
|
||||
{
|
||||
closeLatch.countDown();
|
||||
}
|
||||
}, 5, TimeUnit.SECONDS);
|
||||
|
||||
Assert.assertTrue(openLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
wsFactory.stop();
|
||||
|
||||
Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
|
@ -110,20 +110,22 @@ public class XmlConfiguration
|
|||
__parser = new XmlParser();
|
||||
try
|
||||
{
|
||||
URL configURL = Loader.getResource(XmlConfiguration.class,"org/eclipse/jetty/xml/configure_6_0.dtd",true);
|
||||
__parser.redirectEntity("configure.dtd",configURL);
|
||||
__parser.redirectEntity("configure_1_0.dtd",configURL);
|
||||
__parser.redirectEntity("configure_1_1.dtd",configURL);
|
||||
__parser.redirectEntity("configure_1_2.dtd",configURL);
|
||||
__parser.redirectEntity("configure_1_3.dtd",configURL);
|
||||
__parser.redirectEntity("configure_6_0.dtd",configURL);
|
||||
URL config60 = Loader.getResource(XmlConfiguration.class,"org/eclipse/jetty/xml/configure_6_0.dtd",true);
|
||||
URL config71 = Loader.getResource(XmlConfiguration.class,"org/eclipse/jetty/xml/configure_7_1.dtd",true);
|
||||
__parser.redirectEntity("configure.dtd",config71);
|
||||
__parser.redirectEntity("configure_1_0.dtd",config60);
|
||||
__parser.redirectEntity("configure_1_1.dtd",config60);
|
||||
__parser.redirectEntity("configure_1_2.dtd",config60);
|
||||
__parser.redirectEntity("configure_1_3.dtd",config60);
|
||||
__parser.redirectEntity("configure_6_0.dtd",config60);
|
||||
__parser.redirectEntity("configure_7_1.dtd",config71);
|
||||
|
||||
__parser.redirectEntity("http://jetty.mortbay.org/configure.dtd",configURL);
|
||||
__parser.redirectEntity("http://jetty.eclipse.org/configure.dtd",configURL);
|
||||
__parser.redirectEntity("http://www.eclipse.org/jetty/configure.dtd",configURL);
|
||||
__parser.redirectEntity("http://jetty.mortbay.org/configure.dtd",config71);
|
||||
__parser.redirectEntity("http://jetty.eclipse.org/configure.dtd",config71);
|
||||
__parser.redirectEntity("http://www.eclipse.org/jetty/configure.dtd",config71);
|
||||
|
||||
__parser.redirectEntity("-//Mort Bay Consulting//DTD Configure//EN",configURL);
|
||||
__parser.redirectEntity("-//Jetty//Configure//EN",configURL);
|
||||
__parser.redirectEntity("-//Mort Bay Consulting//DTD Configure//EN",config71);
|
||||
__parser.redirectEntity("-//Jetty//Configure//EN",config71);
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
|
@ -902,7 +904,7 @@ public class XmlConfiguration
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* Create a new value object.
|
||||
* Get a Property.
|
||||
*
|
||||
* @param obj @param node @return @exception Exception
|
||||
*/
|
||||
|
@ -922,6 +924,7 @@ public class XmlConfiguration
|
|||
configure(prop,node,0);
|
||||
return prop;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
|
@ -1091,6 +1094,14 @@ public class XmlConfiguration
|
|||
String defaultValue = node.getAttribute("default");
|
||||
return System.getProperty(name,defaultValue);
|
||||
}
|
||||
|
||||
if ("Env".equals(tag))
|
||||
{
|
||||
String name = node.getAttribute("name");
|
||||
String defaultValue = node.getAttribute("default");
|
||||
String value=System.getenv(name);
|
||||
return value==null?defaultValue:value;
|
||||
}
|
||||
|
||||
LOG.warn("Unknown value tag: " + node,new Throwable());
|
||||
return null;
|
||||
|
|
|
@ -19,7 +19,7 @@ my be specified if a match is not achieved.
|
|||
-->
|
||||
|
||||
<!ENTITY % CONFIG "Set|Get|Put|Call|New|Ref|Array|Map|Property">
|
||||
<!ENTITY % VALUE "#PCDATA|Get|Call|New|Ref|Array|Map|SystemProperty|Property">
|
||||
<!ENTITY % VALUE "#PCDATA|Get|Call|New|Ref|Array|Map|SystemProperty|Env|Property">
|
||||
|
||||
<!ENTITY % TYPEATTR "type CDATA #IMPLIED " > <!-- String|Character|Short|Byte|Integer|Long|Boolean|Float|Double|char|short|byte|int|long|boolean|float|double|URL|InetAddress|InetAddrPort| #classname -->
|
||||
<!ENTITY % IMPLIEDCLASSATTR "class NMTOKEN #IMPLIED" >
|
||||
|
@ -245,6 +245,24 @@ This is equivalent to:
|
|||
<!ELEMENT SystemProperty EMPTY>
|
||||
<!ATTLIST SystemProperty %NAMEATTR; %DEFAULTATTR; %IDATTR;>
|
||||
|
||||
<!--
|
||||
Environment variable Element.
|
||||
This element allows OS Environment variables to be retrieved as
|
||||
part of the value of elements such as Set, Put, Arg, etc.
|
||||
The name attribute specifies the env variable name and the optional
|
||||
default argument provides a default value.
|
||||
|
||||
<Env name="Test" default="value" />
|
||||
|
||||
This is equivalent to:
|
||||
|
||||
String v=System.getEnv("Test");
|
||||
if (v==null) v="value";
|
||||
|
||||
-->
|
||||
<!ELEMENT Env EMPTY>
|
||||
<!ATTLIST Env %NAMEATTR; %DEFAULTATTR; %IDATTR;>
|
||||
|
||||
|
||||
<!--
|
||||
Property Element.
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
|
||||
<!--
|
||||
This is the document type descriptor for the
|
||||
org.eclipse.XmlConfiguration class. It allows a java object to be
|
||||
configured by with a sequence of Set, Put and Call elements. These tags are
|
||||
mapped to methods on the object to be configured as follows:
|
||||
|
||||
<Set name="Test">value</Set> == obj.setTest("value");
|
||||
<Put name="Test">value</Put> == obj.put("Test","value");
|
||||
<Call name="test"><Arg>value</Arg></Call> == obj.test("value");
|
||||
|
||||
Values themselves may be configured objects that are created with the
|
||||
<New> tag or returned from a <Call> tag.
|
||||
|
||||
Values are matched to arguments on a best effort approach, but types
|
||||
my be specified if a match is not achieved.
|
||||
|
||||
-->
|
||||
|
||||
<!ENTITY % CONFIG "Set|Get|Put|Call|New|Ref|Array|Map|Property">
|
||||
<!ENTITY % VALUE "#PCDATA|Get|Call|New|Ref|Array|Map|SystemProperty|Property">
|
||||
|
||||
<!ENTITY % TYPEATTR "type CDATA #IMPLIED " > <!-- String|Character|Short|Byte|Integer|Long|Boolean|Float|Double|char|short|byte|int|long|boolean|float|double|URL|InetAddress|InetAddrPort| #classname -->
|
||||
<!ENTITY % IMPLIEDCLASSATTR "class NMTOKEN #IMPLIED" >
|
||||
<!ENTITY % CLASSATTR "class NMTOKEN #REQUIRED" >
|
||||
<!ENTITY % NAMEATTR "name NMTOKEN #REQUIRED" >
|
||||
<!ENTITY % IMPLIEDNAMEATTR "name NMTOKEN #IMPLIED" >
|
||||
<!ENTITY % DEFAULTATTR "default CDATA #IMPLIED" >
|
||||
<!ENTITY % IDATTR "id NMTOKEN #IMPLIED" >
|
||||
<!ENTITY % REQUIREDIDATTR "id NMTOKEN #REQUIRED" >
|
||||
|
||||
|
||||
<!--
|
||||
Configure Element.
|
||||
This is the root element that specifies the class of object that
|
||||
can be configured:
|
||||
|
||||
<Configure class="com.acme.MyClass"> ... </Configure>
|
||||
-->
|
||||
<!ELEMENT Configure (%CONFIG;)* >
|
||||
<!ATTLIST Configure %IMPLIEDCLASSATTR; %IDATTR; >
|
||||
|
||||
|
||||
<!--
|
||||
Set Element.
|
||||
This element maps to a call to a setter method or field on the current object.
|
||||
The name and optional type attributes are used to select the setter
|
||||
method. If the name given is xxx, then a setXxx method is used, or
|
||||
the xxx field is used of setXxx cannot be found.
|
||||
A Set element can contain value text and/or the value objects returned
|
||||
by other elements such as Call, New, SystemProperty, etc.
|
||||
If no value type is specified, then white
|
||||
space is trimmed out of the value. If it contains multiple value
|
||||
elements they are added as strings before being converted to any
|
||||
specified type.
|
||||
|
||||
A Set with a class attribute is treated as a static set method invocation.
|
||||
-->
|
||||
<!ELEMENT Set ( %VALUE; )* >
|
||||
<!ATTLIST Set %NAMEATTR; %TYPEATTR; %IMPLIEDCLASSATTR; >
|
||||
|
||||
|
||||
<!--
|
||||
Get Element.
|
||||
This element maps to a call to a getter method or field on the current object.
|
||||
The name attribute is used to select the get method.
|
||||
If the name given is xxx, then a getXxx method is used, or
|
||||
the xxx field is used if getXxx cannot be found.
|
||||
A Get element can contain other elements such as Set, Put, Call, etc.
|
||||
which act on the object returned by the get call.
|
||||
|
||||
A Get with a class attribute is treated as a static get method or field.
|
||||
-->
|
||||
<!ELEMENT Get (%CONFIG;)*>
|
||||
<!ATTLIST Get %NAMEATTR; %IMPLIEDCLASSATTR; %IDATTR; >
|
||||
|
||||
|
||||
<!--
|
||||
Put Element.
|
||||
This element maps to a call to a put method on the current object,
|
||||
which must implement the Map interface. The name attribute is used
|
||||
as the put key and the optional type attribute can force the type
|
||||
of the value.
|
||||
|
||||
A Put element can contain value text and/or value elements such as Call,
|
||||
New, SystemProperty, etc. If no value type is specified, then white
|
||||
space is trimmed out of the value. If it contains multiple value
|
||||
elements they are added as strings before being converted to any
|
||||
specified type.
|
||||
-->
|
||||
<!ELEMENT Put ( %VALUE; )* >
|
||||
<!ATTLIST Put %NAMEATTR; %TYPEATTR; >
|
||||
|
||||
|
||||
<!--
|
||||
Call Element.
|
||||
This element maps to an arbitrary call to a method on the current object,
|
||||
The name attribute and Arg elements are used to select the method.
|
||||
|
||||
A Call element can contain a sequence of Arg elements followed by
|
||||
a sequence of other elements such as Set, Put, Call, etc. which act on any object
|
||||
returned by the original call:
|
||||
|
||||
<Call id="o2" name="test">
|
||||
<Arg>value1</Arg>
|
||||
<Set name="Test">Value2</Set>
|
||||
</Call>
|
||||
|
||||
This is equivalent to:
|
||||
|
||||
Object o2 = o1.test("value1");
|
||||
o2.setTest("value2");
|
||||
|
||||
A Call with a class attribute is treated as a static call.
|
||||
-->
|
||||
<!ELEMENT Call (Arg*,(%CONFIG;)*)>
|
||||
<!ATTLIST Call %NAMEATTR; %IMPLIEDCLASSATTR; %IDATTR;>
|
||||
|
||||
|
||||
<!--
|
||||
Arg Element.
|
||||
This element defines a positional argument for the Call element.
|
||||
The optional type attribute can force the type of the value.
|
||||
|
||||
An Arg element can contain value text and/or value elements such as Call,
|
||||
New, SystemProperty, etc. If no value type is specified, then white
|
||||
space is trimmed out of the value. If it contains multiple value
|
||||
elements they are added as strings before being converted to any
|
||||
specified type.
|
||||
-->
|
||||
<!ELEMENT Arg ( %VALUE; )* >
|
||||
<!ATTLIST Arg %TYPEATTR; %IMPLIEDNAMEATTR; >
|
||||
|
||||
|
||||
<!--
|
||||
New Element.
|
||||
This element allows the creation of a new object as part of a
|
||||
value for elements such as Set, Put, Arg, etc. The class attribute determines
|
||||
the type of the new object and the contained Arg elements
|
||||
are used to select the constructor for the new object.
|
||||
|
||||
A New element can contain a sequence of Arg elements followed by
|
||||
a sequence of elements such as Set, Put, Call, etc. elements
|
||||
which act on the new object:
|
||||
|
||||
<New id="o" class="com.acme.MyClass">
|
||||
<Arg>value1</Arg>
|
||||
<Set name="test">Value2</Set>
|
||||
</New>
|
||||
|
||||
This is equivalent to:
|
||||
|
||||
Object o = new com.acme.MyClass("value1");
|
||||
o.setTest("Value2");
|
||||
-->
|
||||
<!ELEMENT New (Arg*,(%CONFIG;)*)>
|
||||
<!ATTLIST New %CLASSATTR; %IDATTR;>
|
||||
|
||||
|
||||
<!--
|
||||
Ref Element.
|
||||
This element allows a previously created object to be referenced by id.
|
||||
A Ref element can contain a sequence of elements such as Set, Put, Call, etc.
|
||||
which act on the referenced object:
|
||||
|
||||
<Ref id="myobject">
|
||||
<Set name="Test">Value2</Set>
|
||||
</New>
|
||||
-->
|
||||
<!ELEMENT Ref ((%CONFIG;)*)>
|
||||
<!ATTLIST Ref %REQUIREDIDATTR;>
|
||||
|
||||
|
||||
<!--
|
||||
Array Element.
|
||||
This element allows the creation of a new array as part of a
|
||||
value of elements such as Set, Put, Arg, etc. The type attribute determines
|
||||
the type of the new array and the contained Item elements
|
||||
are used for each element of the array:
|
||||
|
||||
<Array type="java.lang.String">
|
||||
<Item>value0</Item>
|
||||
<Item><New class="java.lang.String"><Arg>value1</Arg></New></Item>
|
||||
</Array>
|
||||
|
||||
This is equivalent to:
|
||||
String[] a = new String[] { "value0", new String("value1") };
|
||||
-->
|
||||
<!ELEMENT Array (Item*)>
|
||||
<!ATTLIST Array %TYPEATTR; %IDATTR; >
|
||||
|
||||
|
||||
<!--
|
||||
Map Element.
|
||||
This element allows the creation of a new map as part of a
|
||||
value of elements such as Set, Put, Arg, etc. The type attribute determines
|
||||
the type of the new array and the contained Item elements
|
||||
are used for each element of the array:
|
||||
|
||||
<Map>
|
||||
<Entry>
|
||||
<Item>keyName</Item>
|
||||
<Item><New class="java.lang.String"><Arg>value1</Arg></New></Item>
|
||||
</Entry>
|
||||
</Map>
|
||||
|
||||
This is equivalent to:
|
||||
Map m = new HashMap();
|
||||
m.put("keyName", new String("value1"));
|
||||
-->
|
||||
<!ELEMENT Map (Entry*)>
|
||||
<!ATTLIST Map %IDATTR; >
|
||||
<!ELEMENT Entry (Item,Item)>
|
||||
|
||||
|
||||
<!--
|
||||
Item Element.
|
||||
This element defines an entry for the Array or Map Entry elements.
|
||||
The optional type attribute can force the type of the value.
|
||||
|
||||
An Item element can contain value text and/or the value object of
|
||||
elements such as Call, New, SystemProperty, etc. If no value type
|
||||
is specified, then white space is trimmed out of the value.
|
||||
If it contains multiple value elements they are added as strings
|
||||
before being converted to any specified type.
|
||||
-->
|
||||
<!ELEMENT Item ( %VALUE; )* >
|
||||
<!ATTLIST Item %TYPEATTR; %IDATTR; >
|
||||
|
||||
|
||||
<!--
|
||||
System Property Element.
|
||||
This element allows JVM System properties to be retrieved as
|
||||
part of the value of elements such as Set, Put, Arg, etc.
|
||||
The name attribute specifies the property name and the optional
|
||||
default argument provides a default value.
|
||||
|
||||
<SystemProperty name="Test" default="value" />
|
||||
|
||||
This is equivalent to:
|
||||
|
||||
System.getProperty("Test","value");
|
||||
-->
|
||||
<!ELEMENT SystemProperty EMPTY>
|
||||
<!ATTLIST SystemProperty %NAMEATTR; %DEFAULTATTR; %IDATTR;>
|
||||
|
||||
|
||||
<!--
|
||||
Property Element.
|
||||
This element allows arbitrary properties to be retrieved by name.
|
||||
The name attribute specifies the property name and the optional
|
||||
default argument provides a default value.
|
||||
|
||||
A Property element can contain a sequence of elements such as Set, Put, Call, etc.
|
||||
which act on the retrieved object:
|
||||
|
||||
<Property name="Server">
|
||||
<Call id="jdbcIdMgr" name="getAttribute">
|
||||
<Arg>jdbcIdMgr</Arg>
|
||||
</Call>
|
||||
</Property>
|
||||
-->
|
||||
<!ELEMENT Property ((%CONFIG;)*)>
|
||||
<!ATTLIST Property %NAMEATTR; %DEFAULTATTR; %IDATTR;>
|
|
@ -63,6 +63,8 @@ public class XmlConfigurationTest
|
|||
assertEquals( "ObjectsWhiteString", "-1\n String",tc.get("ObjectsWhiteString"));
|
||||
|
||||
assertEquals( "SystemProperty", System.getProperty("user.dir")+"/stuff",tc.get("SystemProperty"));
|
||||
assertEquals( "Env", System.getenv("HOME"),tc.get("Env"));
|
||||
|
||||
assertEquals( "Property", "xxx", tc.get("Property"));
|
||||
|
||||
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
<Put name="SomethingElse"><SystemProperty name="floople" default="xxx"/></Put>
|
||||
<Put name="Boolean" type="Boolean">True</Put>
|
||||
<Put name="Float" type="Float">2.3</Put>
|
||||
<Put name="Env"><Env name="HOME"/></Put>
|
||||
|
||||
<Call name="call">
|
||||
</Call>
|
||||
|
|
Loading…
Reference in New Issue