/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2017 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.pentaho.di.trans.steps.http;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.pentaho.di.core.KettleClientEnvironment;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettleStepException;
import org.pentaho.di.core.logging.LoggingObjectInterface;
import org.pentaho.di.core.row.RowMeta;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.value.ValueMetaInteger;
import org.pentaho.di.core.row.value.ValueMetaString;
import org.pentaho.di.core.util.Assert;
import org.pentaho.di.trans.Trans;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.step.StepDataInterface;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.trans.steps.mock.StepMockHelper;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
/**
* User: Dzmitry Stsiapanau Date: 12/2/13 Time: 1:24 PM
*/
public class HTTPIT {
private class HTTPHandler extends HTTP {
Object[] row;
Object[] outputRow;
boolean override = false;
public HTTPHandler( StepMeta stepMeta, StepDataInterface stepDataInterface, int copyNr, TransMeta transMeta,
Trans trans, boolean override ) {
super( stepMeta, stepDataInterface, copyNr, transMeta, trans );
this.row = new Object[] { "anyData" };
this.override = override;
}
/**
* In case of getRow, we receive data from previous steps through the input rowset. In case we split the stream, we
* have to copy the data to the alternate splits: rowsets 1 through n.
*/
@Override
public Object[] getRow() throws KettleException {
return row;
}
/**
* putRow is used to copy a row, to the alternate rowset(s) This should get priority over everything else!
* (synchronized) If distribute is true, a row is copied only once to the output rowsets, otherwise copies are sent
* to each rowset!
*
* @param row
* The row to put to the destination rowset(s).
* @throws org.pentaho.di.core.exception.KettleStepException
*
*/
@Override
public void putRow( RowMetaInterface rowMeta, Object[] row ) throws KettleStepException {
outputRow = row;
}
public Object[] getOutputRow() {
return outputRow;
}
@Override
protected int requestStatusCode( HttpMethod method, HostConfiguration hostConfiguration, HttpClient httpClient )
throws IOException {
if ( override ) {
return 402;
} else {
return super.requestStatusCode( method, hostConfiguration, httpClient );
}
}
@Override
protected InputStreamReader openStream( String encoding, HttpMethod method ) throws Exception {
if ( override ) {
InputStreamReader mockInputStreamReader = Mockito.mock( InputStreamReader.class );
when( mockInputStreamReader.read() ).thenReturn( -1 );
return mockInputStreamReader;
} else {
return super.openStream( encoding, method );
}
}
@Override
protected Header[] searchForHeaders( HttpMethod method ) {
Header[] headers = { new Header( "host", host ) };
if ( override ) {
return headers;
} else {
return super.searchForHeaders( method );
}
}
}
public static final String host = "localhost";
public static final int port = 9998;
public static final String HTTP_LOCALHOST_9998 = "http://localhost:9998/";
@InjectMocks
private StepMockHelper<HTTPMeta, HTTPData> stepMockHelper;
private HttpServer httpServer;
@BeforeClass
public static void setupBeforeClass() throws KettleException {
KettleClientEnvironment.init();
}
@Before
public void setUp() throws Exception {
stepMockHelper = new StepMockHelper<HTTPMeta, HTTPData>( "HTTP CLIENT TEST", HTTPMeta.class, HTTPData.class );
when( stepMockHelper.logChannelInterfaceFactory.create( any(), any( LoggingObjectInterface.class ) ) ).thenReturn(
stepMockHelper.logChannelInterface );
when( stepMockHelper.trans.isRunning() ).thenReturn( true );
verify( stepMockHelper.trans, never() ).stopAll();
}
@After
public void tearDown() throws Exception {
httpServer.stop( 5 );
}
@Test
public void test204Answer() throws Exception {
startHttpServer( get204AnswerHandler() );
HTTPData data = new HTTPData();
int[] index = { 0, 1 };
RowMeta meta = new RowMeta();
meta.addValueMeta( new ValueMetaString( "fieldName" ) );
meta.addValueMeta( new ValueMetaInteger( "codeFieldName" ) );
Object[] expectedRow = new Object[] { "", 204L };
HTTP http =
new HTTPHandler( stepMockHelper.stepMeta, data, 0, stepMockHelper.transMeta, stepMockHelper.trans, false );
RowMetaInterface inputRowMeta = mock( RowMetaInterface.class );
http.setInputRowMeta( inputRowMeta );
when( inputRowMeta.clone() ).thenReturn( inputRowMeta );
when( stepMockHelper.processRowsStepMetaInterface.getUrl() ).thenReturn( HTTP_LOCALHOST_9998 );
when( stepMockHelper.processRowsStepMetaInterface.getHeaderField() ).thenReturn( new String[] {} );
when( stepMockHelper.processRowsStepMetaInterface.getArgumentField() ).thenReturn( new String[] {} );
when( stepMockHelper.processRowsStepMetaInterface.getResultCodeFieldName() ).thenReturn( "ResultCodeFieldName" );
when( stepMockHelper.processRowsStepMetaInterface.getFieldName() ).thenReturn( "ResultFieldName" );
http.init( stepMockHelper.processRowsStepMetaInterface, data );
Assert.assertTrue( http.processRow( stepMockHelper.processRowsStepMetaInterface, data ) );
Object[] out = ( (HTTPHandler) http ).getOutputRow();
Assert.assertTrue( meta.equals( out, expectedRow, index ) );
}
@Test
public void testResponseHeader() throws Exception {
startHttpServer( get204AnswerHandler() );
HTTPData data = new HTTPData();
int[] index = { 0, 1, 3 };
RowMeta meta = new RowMeta();
meta.addValueMeta( new ValueMetaString( "fieldName" ) );
meta.addValueMeta( new ValueMetaInteger( "codeFieldName" ) );
meta.addValueMeta( new ValueMetaInteger( "responseTimeFieldName" ) );
meta.addValueMeta( new ValueMetaString( "headerFieldName" ) );
Object[] expectedRow =
new Object[] { "", 402L, 0L, "{\"host\":\"localhost\"}" };
HTTP http =
new HTTPHandler( stepMockHelper.stepMeta, data, 0, stepMockHelper.transMeta, stepMockHelper.trans, true );
RowMetaInterface inputRowMeta = mock( RowMetaInterface.class );
http.setInputRowMeta( inputRowMeta );
when( inputRowMeta.clone() ).thenReturn( inputRowMeta );
when( stepMockHelper.processRowsStepMetaInterface.getUrl() ).thenReturn( HTTP_LOCALHOST_9998 );
when( stepMockHelper.processRowsStepMetaInterface.getHeaderField() ).thenReturn( new String[] {} );
when( stepMockHelper.processRowsStepMetaInterface.getArgumentField() ).thenReturn( new String[] {} );
when( stepMockHelper.processRowsStepMetaInterface.getResultCodeFieldName() ).thenReturn( "ResultCodeFieldName" );
when( stepMockHelper.processRowsStepMetaInterface.getFieldName() ).thenReturn( "ResultFieldName" );
when( stepMockHelper.processRowsStepMetaInterface.getEncoding() ).thenReturn( "UTF8" );
when( stepMockHelper.processRowsStepMetaInterface.getResponseTimeFieldName() ).thenReturn(
"ResponseTimeFieldName" );
when( stepMockHelper.processRowsStepMetaInterface.getResponseHeaderFieldName() ).thenReturn(
"ResponseHeaderFieldName" );
http.init( stepMockHelper.processRowsStepMetaInterface, data );
Assert.assertTrue( http.processRow( stepMockHelper.processRowsStepMetaInterface, data ) );
Object[] out = ( (HTTPHandler) http ).getOutputRow();
Assert.assertTrue( meta.equals( out, expectedRow, index ) );
}
@Test
public void testDuplicateNamesInHeader() throws Exception {
startHttpServer( getDuplicateHeadersHandler() );
HTTPData data = new HTTPData();
RowMeta meta = new RowMeta();
meta.addValueMeta( new ValueMetaString( "headerFieldName" ) );
HTTP http =
new HTTPHandler( stepMockHelper.stepMeta, data, 0, stepMockHelper.transMeta, stepMockHelper.trans, false );
RowMetaInterface inputRowMeta = mock( RowMetaInterface.class );
http.setInputRowMeta( inputRowMeta );
when( inputRowMeta.clone() ).thenReturn( inputRowMeta );
when( stepMockHelper.processRowsStepMetaInterface.getUrl() ).thenReturn( HTTP_LOCALHOST_9998 );
when( stepMockHelper.processRowsStepMetaInterface.getHeaderField() ).thenReturn( new String[] {} );
when( stepMockHelper.processRowsStepMetaInterface.getArgumentField() ).thenReturn( new String[] {} );
when( stepMockHelper.processRowsStepMetaInterface.getEncoding() ).thenReturn( "UTF8" );
when( stepMockHelper.processRowsStepMetaInterface.getResponseHeaderFieldName() ).thenReturn(
"ResponseHeaderFieldName" );
http.init( stepMockHelper.processRowsStepMetaInterface, data );
Assert.assertTrue( http.processRow( stepMockHelper.processRowsStepMetaInterface, data ) );
Object[] out = ( (HTTPHandler) http ).getOutputRow();
Assert.assertTrue( out.length == 1 );
JSONParser parser = new JSONParser();
JSONObject json = (JSONObject) parser.parse( (String) out[0] );
Object userAgent = json.get( "User-agent" );
Assert.assertTrue( "HTTPTool/1.0".equals( userAgent ) );
Object cookies = json.get( "Set-cookie" );
Assert.assertTrue( cookies instanceof JSONArray );
for ( int i = 0; i < 3; i++ ) {
String cookie = ( (String) ( (JSONArray) cookies ).get( i ) );
Assert.assertTrue( cookie.startsWith( "cookie" + i ) );
}
}
private void startHttpServer( HttpHandler httpHandler ) throws IOException {
httpServer = HttpServer.create( new InetSocketAddress( HTTPIT.host, HTTPIT.port ), 10 );
httpServer.createContext( "/", httpHandler );
httpServer.start();
}
private HttpHandler get204AnswerHandler() {
return httpExchange -> {
httpExchange.sendResponseHeaders( 204, 0 );
httpExchange.close();
};
}
private HttpHandler getDuplicateHeadersHandler() {
return httpExchange -> {
Headers headers = httpExchange.getResponseHeaders();
headers.add( "User-agent", "HTTPTool/1.0" );
headers.add( "Set-cookie", "cookie0=value0; Max-Age=3600" );
headers.add( "Set-cookie", "cookie1=value1; HttpOnly" );
headers.add( "Set-cookie", "cookie2=value2; Secure" );
httpExchange.sendResponseHeaders( 200, 0 );
httpExchange.close();
};
}
// LoadSave Test is a unit test of the meta, not an integration test. Moved to new class.
// MB 5/2016
}