blob: ae5e75f512cb2c05765d084e8e08b2d2e5e93f3a [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 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.spdy.server.http;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.ReplyInfo;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Fields;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class HttpTransportOverSPDYTest
{
@Mock
Connector connector;
@Mock
HttpConfiguration httpConfiguration;
@Mock
EndPoint endPoint;
@Mock
PushStrategy pushStrategy;
@Mock
Stream stream;
@Mock
Callback callback;
@Mock
Session session;
@Mock
HttpGenerator.ResponseInfo responseInfo;
Random random = new Random();
short version = SPDY.V3;
HttpTransportOverSPDY httpTransportOverSPDY;
@Before
public void setUp() throws Exception
{
Fields requestHeaders = new Fields();
requestHeaders.add(HTTPSPDYHeader.METHOD.name(version), "GET");
when(responseInfo.getStatus()).thenReturn(HttpStatus.OK_200);
when(stream.getSession()).thenReturn(session);
when(session.getVersion()).thenReturn(SPDY.V3);
when(stream.isClosed()).thenReturn(false);
httpTransportOverSPDY = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint, pushStrategy,
stream, requestHeaders);
}
@Test
public void testSendWithResponseInfoNullAndContentNullAndLastContentTrue() throws Exception
{
ByteBuffer content = null;
boolean lastContent = true;
httpTransportOverSPDY.send(null, content, lastContent, callback);
ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
callbackCaptor.getValue().succeeded();
verify(callback, times(1)).succeeded();
assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
assertThat("ByteBuffer is empty", dataInfoCaptor.getValue().length(), is(0));
}
@Test
public void testSendWithResponseInfoNullAndContentAndLastContentTrue() throws Exception
{
ByteBuffer content = createRandomByteBuffer();
boolean lastContent = true;
httpTransportOverSPDY.send(null, content, lastContent, callback);
ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
callbackCaptor.getValue().succeeded();
verify(callback, times(1)).succeeded();
assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
assertThat("ByteBuffer length is 4096", dataInfoCaptor.getValue().length(), is(4096));
}
@Test
public void testSendWithResponseInfoNullAndEmptyContentAndLastContentTrue() throws Exception
{
ByteBuffer content = BufferUtil.EMPTY_BUFFER;
boolean lastContent = true;
httpTransportOverSPDY.send(null, content, lastContent, callback);
ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
callbackCaptor.getValue().succeeded();
verify(callback, times(1)).succeeded();
assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
assertThat("ByteBuffer is empty", dataInfoCaptor.getValue().length(), is(0));
}
@Test
public void testSendWithResponseInfoNullAndContentAndLastContentFalse() throws Exception
{
ByteBuffer content = createRandomByteBuffer();
boolean lastContent = false;
httpTransportOverSPDY.send(null, content, lastContent, callback);
ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
callbackCaptor.getValue().succeeded();
verify(callback, times(1)).succeeded();
assertThat("lastContent is false", dataInfoCaptor.getValue().isClose(), is(false));
assertThat("ByteBuffer is empty", dataInfoCaptor.getValue().length(), is(4096));
}
@Test(expected = IllegalStateException.class)
public void testSendWithResponseInfoNullAndContentNullAndLastContentFalse() throws Exception
{
ByteBuffer content = null;
boolean lastContent = false;
httpTransportOverSPDY.send(null, content, lastContent, callback);
}
@Test(expected = IllegalStateException.class)
public void testSendWithResponseInfoNullAndEmptyContentAndLastContentFalse() throws Exception
{
ByteBuffer content = BufferUtil.EMPTY_BUFFER;
boolean lastContent = false;
httpTransportOverSPDY.send(null, content, lastContent, callback);
}
@Test
public void testSendWithResponseInfoAndContentNullAndLastContentFalse() throws Exception
{
ByteBuffer content = null;
boolean lastContent = false;
httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
callbackCaptor.getValue().succeeded();
assertThat("ReplyInfo close is true", replyInfoCaptor.getValue().isClose(), is(false));
verify(stream, times(0)).data(any(ByteBufferDataInfo.class), any(Callback.class));
verify(callback, times(1)).succeeded();
}
@Test
public void testSendWithResponseInfoAndContentNullAndLastContentTrue() throws Exception
{
ByteBuffer content = null;
boolean lastContent = true;
// when stream.isClosed() is called a 2nd time, the reply has closed the stream already
when(stream.isClosed()).thenReturn(false).thenReturn(true);
httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
callbackCaptor.getValue().succeeded();
assertThat("ReplyInfo close is true", replyInfoCaptor.getValue().isClose(), is(true));
verify(callback, times(1)).succeeded();
}
@Test
public void testSendWithResponseInfoAndContentAndLastContentTrue() throws Exception
{
ByteBuffer content = createRandomByteBuffer();
boolean lastContent = true;
httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
callbackCaptor.getValue().succeeded();
assertThat("ReplyInfo close is false", replyInfoCaptor.getValue().isClose(), is(false));
ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
callbackCaptor.getValue().succeeded();
assertThat("lastContent is true", dataInfoCaptor.getValue().isClose(), is(true));
assertThat("ByteBuffer length is 4096", dataInfoCaptor.getValue().length(), is(4096));
verify(callback, times(1)).succeeded();
}
@Test
public void testSendWithResponseInfoAndContentAndLastContentFalse() throws Exception
{
ByteBuffer content = createRandomByteBuffer();
boolean lastContent = false;
httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
ArgumentCaptor<Callback> callbackCaptor = ArgumentCaptor.forClass(Callback.class);
verify(stream, times(1)).reply(replyInfoCaptor.capture(), callbackCaptor.capture());
callbackCaptor.getValue().succeeded();
assertThat("ReplyInfo close is false", replyInfoCaptor.getValue().isClose(), is(false));
ArgumentCaptor<ByteBufferDataInfo> dataInfoCaptor = ArgumentCaptor.forClass(ByteBufferDataInfo.class);
verify(stream, times(1)).data(dataInfoCaptor.capture(), callbackCaptor.capture());
callbackCaptor.getValue().succeeded();
assertThat("lastContent is false", dataInfoCaptor.getValue().isClose(), is(false));
assertThat("ByteBuffer length is 4096", dataInfoCaptor.getValue().length(), is(4096));
verify(callback, times(1)).succeeded();
}
@Test
public void testVerifyThatAStreamIsNotCommittedTwice() throws IOException, InterruptedException
{
final CountDownLatch failedCalledLatch = new CountDownLatch(1);
ByteBuffer content = createRandomByteBuffer();
boolean lastContent = false;
httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
verify(stream, times(1)).reply(replyInfoCaptor.capture(), any(Callback.class));
assertThat("ReplyInfo close is false", replyInfoCaptor.getValue().isClose(), is(false));
httpTransportOverSPDY.send(HttpGenerator.RESPONSE_500_INFO, null, true, new Callback.Adapter()
{
@Override
public void failed(Throwable x)
{
failedCalledLatch.countDown();
}
});
verify(stream, times(1)).data(any(DataInfo.class), any(Callback.class));
assertThat("callback.failed has been called", failedCalledLatch.await(5, TimeUnit.SECONDS), is(true));
}
private ByteBuffer createRandomByteBuffer()
{
ByteBuffer content = BufferUtil.allocate(8192);
BufferUtil.flipToFill(content);
byte[] randomBytes = new byte[4096];
random.nextBytes(randomBytes);
content.put(randomBytes);
return content;
}
}