/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.processors.standard;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.nifi.controller.AbstractControllerService;
import org.apache.nifi.http.HttpContextMap;
import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.stream.io.NullOutputStream;
import org.apache.nifi.stream.io.StreamUtils;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.junit.Assert;
import org.junit.Test;
public class TestHandleHttpRequest {
@Test(timeout=10000)
public void testRequestAddedToService() throws InitializationException, MalformedURLException, IOException, InterruptedException {
final TestRunner runner = TestRunners.newTestRunner(HandleHttpRequest.class);
runner.setProperty(HandleHttpRequest.PORT, "0");
final MockHttpContextMap contextMap = new MockHttpContextMap();
runner.addControllerService("http-context-map", contextMap);
runner.enableControllerService(contextMap);
runner.setProperty(HandleHttpRequest.HTTP_CONTEXT_MAP, "http-context-map");
// trigger processor to stop but not shutdown.
runner.run(1, false);
try {
final Thread httpThread = new Thread(new Runnable() {
@Override
public void run() {
try {
final int port = ((HandleHttpRequest) runner.getProcessor()).getPort();
final HttpURLConnection connection = (HttpURLConnection) new URL("http://localhost:"
+ port + "/my/path?query=true&value1=value1&value2=&value3&value4=apple=orange").openConnection();
connection.setDoOutput(false);
connection.setRequestMethod("GET");
connection.setRequestProperty("header1", "value1");
connection.setRequestProperty("header2", "");
connection.setRequestProperty("header3", "apple=orange");
connection.setConnectTimeout(3000);
connection.setReadTimeout(3000);
StreamUtils.copy(connection.getInputStream(), new NullOutputStream());
} catch (final Throwable t) {
t.printStackTrace();
Assert.fail(t.toString());
}
}
});
httpThread.start();
while ( runner.getFlowFilesForRelationship(HandleHttpRequest.REL_SUCCESS).isEmpty() ) {
// process the request.
runner.run(1, false, false);
}
runner.assertAllFlowFilesTransferred(HandleHttpRequest.REL_SUCCESS, 1);
assertEquals(1, contextMap.size());
final MockFlowFile mff = runner.getFlowFilesForRelationship(HandleHttpRequest.REL_SUCCESS).get(0);
mff.assertAttributeEquals("http.query.param.query", "true");
mff.assertAttributeEquals("http.query.param.value1", "value1");
mff.assertAttributeEquals("http.query.param.value2", "");
mff.assertAttributeEquals("http.query.param.value3", "");
mff.assertAttributeEquals("http.query.param.value4", "apple=orange");
mff.assertAttributeEquals("http.headers.header1", "value1");
mff.assertAttributeEquals("http.headers.header3", "apple=orange");
} finally {
// shut down the server
runner.run(1, true);
}
}
@Test(timeout=10000)
public void testFailToRegister() throws InitializationException, MalformedURLException, IOException, InterruptedException {
final TestRunner runner = TestRunners.newTestRunner(HandleHttpRequest.class);
runner.setProperty(HandleHttpRequest.PORT, "0");
final MockHttpContextMap contextMap = new MockHttpContextMap();
runner.addControllerService("http-context-map", contextMap);
runner.enableControllerService(contextMap);
runner.setProperty(HandleHttpRequest.HTTP_CONTEXT_MAP, "http-context-map");
contextMap.setRegisterSuccessfully(false);
// trigger processor to stop but not shutdown.
runner.run(1, false);
try {
final int[] responseCode = new int[1];
responseCode[0] = 0;
final Thread httpThread = new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
try {
final int port = ((HandleHttpRequest) runner.getProcessor()).getPort();
connection = (HttpURLConnection) new URL("http://localhost:"
+ port + "/my/path?query=true&value1=value1&value2=&value3&value4=apple=orange").openConnection();
connection.setDoOutput(false);
connection.setRequestMethod("GET");
connection.setRequestProperty("header1", "value1");
connection.setRequestProperty("header2", "");
connection.setRequestProperty("header3", "apple=orange");
connection.setConnectTimeout(3000);
connection.setReadTimeout(3000);
StreamUtils.copy(connection.getInputStream(), new NullOutputStream());
} catch (final Throwable t) {
t.printStackTrace();
if(connection != null ) {
try {
responseCode[0] = connection.getResponseCode();
} catch (IOException e) {
responseCode[0] = -1;
}
} else {
responseCode[0] = -2;
}
}
}
});
httpThread.start();
while (responseCode[0] == 0) {
// process the request.
runner.run(1, false, false);
}
runner.assertTransferCount(HandleHttpRequest.REL_SUCCESS, 0);
assertEquals(503, responseCode[0]);
} finally {
// shut down the server
runner.run(1, true);
}
}
private static class MockHttpContextMap extends AbstractControllerService implements HttpContextMap {
private boolean registerSuccessfully = true;
private final ConcurrentMap<String, HttpServletResponse> responseMap = new ConcurrentHashMap<>();
@Override
public boolean register(final String identifier, final HttpServletRequest request, final HttpServletResponse response, final AsyncContext context) {
if(registerSuccessfully) {
responseMap.put(identifier, response);
}
return registerSuccessfully;
}
@Override
public HttpServletResponse getResponse(final String identifier) {
return responseMap.get(identifier);
}
@Override
public void complete(final String identifier) {
responseMap.remove(identifier);
}
public int size() {
return responseMap.size();
}
public boolean isRegisterSuccessfully() {
return registerSuccessfully;
}
public void setRegisterSuccessfully(boolean registerSuccessfully) {
this.registerSuccessfully = registerSuccessfully;
}
}
}