package org.eclipse.jetty.test;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket;
import java.security.MessageDigest;
import java.util.Collections;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.ContentExchange;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.security.Realm;
import org.eclipse.jetty.client.security.SimpleRealmResolver;
import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.security.authentication.DigestAuthenticator;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.security.Password;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
public class DigestPostTest
{
private static final String NC = "00000001";
public final static String __message =
"0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 \n"+
"9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 9876543210 \n"+
"1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 \n"+
"0987654321 0987654321 0987654321 0987654321 0987654321 0987654321 0987654321 0987654321 \n"+
"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz \n"+
"ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ \n"+
"Now is the time for all good men to come to the aid of the party.\n"+
"How now brown cow.\n"+
"The quick brown fox jumped over the lazy dog.\n";
public volatile static String _received = null;
private static Server _server;
@BeforeClass
public static void setUpServer()
{
try
{
_server = new Server();
_server.setConnectors(new Connector[]
{ new SelectChannelConnector() });
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SECURITY);
context.setContextPath("/test");
context.addServlet(PostServlet.class,"/");
HashLoginService realm = new HashLoginService("test");
realm.putUser("testuser",new Password("password"),new String[]{"test"});
_server.addBean(realm);
ConstraintSecurityHandler security=(ConstraintSecurityHandler)context.getSecurityHandler();
security.setAuthenticator(new DigestAuthenticator());
security.setLoginService(realm);
Constraint constraint = new Constraint("SecureTest","test");
constraint.setAuthenticate(true);
ConstraintMapping mapping = new ConstraintMapping();
mapping.setConstraint(constraint);
mapping.setPathSpec("/*");
security.setConstraintMappings(Collections.singletonList(mapping));
HandlerCollection handlers = new HandlerCollection();
handlers.setHandlers(new Handler[]
{ context, new DefaultHandler() });
_server.setHandler(handlers);
_server.start();
}
catch (final Exception e)
{
e.printStackTrace();
}
}
@AfterClass
public static void tearDownServer() throws Exception
{
_server.stop();
}
@Test
public void testServerDirectlyHTTP10() throws Exception
{
Socket socket = new Socket("127.0.0.1",_server.getConnectors()[0].getLocalPort());
byte[] bytes = __message.getBytes("UTF-8");
_received=null;
socket.getOutputStream().write(
("POST /test/ HTTP/1.0\r\n"+
"Host: 127.0.0.1:"+_server.getConnectors()[0].getLocalPort()+"\r\n"+
"Content-Length: "+bytes.length+"\r\n"+
"\r\n").getBytes("UTF-8"));
socket.getOutputStream().write(bytes);
socket.getOutputStream().flush();
String result = IO.toString(socket.getInputStream());
Assert.assertTrue(result.startsWith("HTTP/1.1 401 Unauthorized"));
Assert.assertEquals(null,_received);
int n=result.indexOf("nonce=");
String nonce=result.substring(n+7,result.indexOf('"',n+7));
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] b= md.digest(String.valueOf(System.currentTimeMillis()).getBytes(org.eclipse.jetty.util.StringUtil.__ISO_8859_1));
String cnonce=encode(b);
String digest="Digest username=\"testuser\" realm=\"test\" nonce=\""+nonce+"\" uri=\"/test/\" algorithm=MD5 response=\""+
newResponse("POST","/test/",cnonce,"testuser","test","password",nonce,"auth")+
"\" qop=auth nc="+NC+" cnonce=\""+cnonce+"\"";
socket = new Socket("127.0.0.1",_server.getConnectors()[0].getLocalPort());
_received=null;
socket.getOutputStream().write(
("POST /test/ HTTP/1.0\r\n"+
"Host: 127.0.0.1:"+_server.getConnectors()[0].getLocalPort()+"\r\n"+
"Content-Length: "+bytes.length+"\r\n"+
"Authorization: "+digest+"\r\n"+
"\r\n").getBytes("UTF-8"));
socket.getOutputStream().write(bytes);
socket.getOutputStream().flush();
result = IO.toString(socket.getInputStream());
Assert.assertTrue(result.startsWith("HTTP/1.1 200 OK"));
Assert.assertEquals(__message,_received);
}
@Test
public void testServerDirectlyHTTP11() throws Exception
{
Socket socket = new Socket("127.0.0.1",_server.getConnectors()[0].getLocalPort());
byte[] bytes = __message.getBytes("UTF-8");
_received=null;
socket.getOutputStream().write(
("POST /test/ HTTP/1.1\r\n"+
"Host: 127.0.0.1:"+_server.getConnectors()[0].getLocalPort()+"\r\n"+
"Content-Length: "+bytes.length+"\r\n"+
"\r\n").getBytes("UTF-8"));
socket.getOutputStream().write(bytes);
socket.getOutputStream().flush();
Thread.sleep(100);
byte[] buf=new byte[4096];
int len=socket.getInputStream().read(buf);
String result=new String(buf,0,len,"UTF-8");
Assert.assertTrue(result.startsWith("HTTP/1.1 401 Unauthorized"));
Assert.assertEquals(null,_received);
int n=result.indexOf("nonce=");
String nonce=result.substring(n+7,result.indexOf('"',n+7));
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] b= md.digest(String.valueOf(System.currentTimeMillis()).getBytes(StringUtil.__ISO_8859_1));
String cnonce=encode(b);
String digest="Digest username=\"testuser\" realm=\"test\" nonce=\""+nonce+"\" uri=\"/test/\" algorithm=MD5 response=\""+
newResponse("POST","/test/",cnonce,"testuser","test","password",nonce,"auth")+
"\" qop=auth nc="+NC+" cnonce=\""+cnonce+"\"";
_received=null;
socket.getOutputStream().write(
("POST /test/ HTTP/1.0\r\n"+
"Host: 127.0.0.1:"+_server.getConnectors()[0].getLocalPort()+"\r\n"+
"Content-Length: "+bytes.length+"\r\n"+
"Authorization: "+digest+"\r\n"+
"\r\n").getBytes("UTF-8"));
socket.getOutputStream().write(bytes);
socket.getOutputStream().flush();
result = IO.toString(socket.getInputStream());
Assert.assertTrue(result.startsWith("HTTP/1.1 200 OK"));
Assert.assertEquals(__message,_received);
}
@Test
public void testServerWithHttpClientStringContent() throws Exception
{
HttpClient client = new HttpClient();
client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
client.setRealmResolver(new SimpleRealmResolver(new TestRealm()));
client.start();
String srvUrl = "http://127.0.0.1:" + _server.getConnectors()[0].getLocalPort() + "/test/";
ContentExchange ex = new ContentExchange();
ex.setMethod(HttpMethods.POST);
ex.setURL(srvUrl);
ex.setRequestContent(new ByteArrayBuffer(__message,"UTF-8"));
_received=null;
client.send(ex);
ex.waitForDone();
Assert.assertEquals(__message,_received);
Assert.assertEquals(200,ex.getResponseStatus());
}
@Test
public void testServerWithHttpClientStreamContent() throws Exception
{
HttpClient client = new HttpClient();
client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
client.setRealmResolver(new SimpleRealmResolver(new TestRealm()));
client.start();
String srvUrl = "http://127.0.0.1:" + _server.getConnectors()[0].getLocalPort() + "/test/";
ContentExchange ex = new ContentExchange();
ex.setMethod(HttpMethods.POST);
ex.setURL(srvUrl);
ex.setRequestContentSource(new BufferedInputStream(new FileInputStream("src/test/resources/message.txt")));
_received=null;
client.send(ex);
ex.waitForDone();
String sent = IO.toString(new FileInputStream("src/test/resources/message.txt"));
Assert.assertEquals(sent,_received);
Assert.assertEquals(200,ex.getResponseStatus());
}
public static class TestRealm implements Realm
{
public String getPrincipal()
{
return "testuser";
}
public String getId()
{
return "test";
}
public String getCredentials()
{
return "password";
}
}
public static class PostServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException
{
String received = IO.toString(request.getInputStream());
_received = received;
response.setStatus(200);
response.getWriter().println("Received "+received.length()+" bytes");
}
}
protected String newResponse(String method, String uri, String cnonce, String principal, String realm, String credentials, String nonce, String qop)
throws Exception
{
MessageDigest md = MessageDigest.getInstance("MD5");
// calc A1 digest
md.update(principal.getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(realm.getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(credentials.getBytes(StringUtil.__ISO_8859_1));
byte[] ha1 = md.digest();
// calc A2 digest
md.reset();
md.update(method.getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(uri.getBytes(StringUtil.__ISO_8859_1));
byte[] ha2=md.digest();
md.update(TypeUtil.toString(ha1,16).getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(nonce.getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(NC.getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(cnonce.getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(qop.getBytes(StringUtil.__ISO_8859_1));
md.update((byte)':');
md.update(TypeUtil.toString(ha2,16).getBytes(StringUtil.__ISO_8859_1));
byte[] digest=md.digest();
// check digest
return encode(digest);
}
private static String encode(byte[] data)
{
StringBuffer buffer = new StringBuffer();
for (int i=0; i<data.length; i++)
{
buffer.append(Integer.toHexString((data[i] & 0xf0) >>> 4));
buffer.append(Integer.toHexString(data[i] & 0x0f));
}
return buffer.toString();
}
}