Search in sources :

Example 1 with HttpContextMap

use of org.apache.nifi.http.HttpContextMap in project nifi by apache.

the class HandleHttpResponse method onTrigger.

@Override
public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
    FlowFile flowFile = session.get();
    if (flowFile == null) {
        return;
    }
    final StopWatch stopWatch = new StopWatch(true);
    final String contextIdentifier = flowFile.getAttribute(HTTPUtils.HTTP_CONTEXT_ID);
    if (contextIdentifier == null) {
        session.transfer(flowFile, REL_FAILURE);
        getLogger().warn("Failed to respond to HTTP request for {} because FlowFile did not have an '" + HTTPUtils.HTTP_CONTEXT_ID + "' attribute", new Object[] { flowFile });
        return;
    }
    final String statusCodeValue = context.getProperty(STATUS_CODE).evaluateAttributeExpressions(flowFile).getValue();
    if (!isNumber(statusCodeValue)) {
        session.transfer(flowFile, REL_FAILURE);
        getLogger().error("Failed to respond to HTTP request for {} because status code was '{}', which is not a valid number", new Object[] { flowFile, statusCodeValue });
        return;
    }
    final HttpContextMap contextMap = context.getProperty(HTTP_CONTEXT_MAP).asControllerService(HttpContextMap.class);
    final HttpServletResponse response = contextMap.getResponse(contextIdentifier);
    if (response == null) {
        session.transfer(flowFile, REL_FAILURE);
        getLogger().error("Failed to respond to HTTP request for {} because FlowFile had an '{}' attribute of {} but could not find an HTTP Response Object for this identifier", new Object[] { flowFile, HTTPUtils.HTTP_CONTEXT_ID, contextIdentifier });
        return;
    }
    final int statusCode = Integer.parseInt(statusCodeValue);
    response.setStatus(statusCode);
    for (final Map.Entry<PropertyDescriptor, String> entry : context.getProperties().entrySet()) {
        final PropertyDescriptor descriptor = entry.getKey();
        if (descriptor.isDynamic()) {
            final String headerName = descriptor.getName();
            final String headerValue = context.getProperty(descriptor).evaluateAttributeExpressions(flowFile).getValue();
            if (!headerValue.trim().isEmpty()) {
                response.setHeader(headerName, headerValue);
            }
        }
    }
    try {
        session.exportTo(flowFile, response.getOutputStream());
        response.flushBuffer();
    } catch (final ProcessException e) {
        session.transfer(flowFile, REL_FAILURE);
        getLogger().error("Failed to respond to HTTP request for {} due to {}", new Object[] { flowFile, e });
        contextMap.complete(contextIdentifier);
        return;
    } catch (final Exception e) {
        session.transfer(flowFile, REL_FAILURE);
        getLogger().error("Failed to respond to HTTP request for {} due to {}", new Object[] { flowFile, e });
        return;
    }
    try {
        contextMap.complete(contextIdentifier);
    } catch (final IllegalStateException ise) {
        getLogger().error("Failed to complete HTTP Transaction for {} due to {}", new Object[] { flowFile, ise });
        session.transfer(flowFile, REL_FAILURE);
        return;
    }
    session.getProvenanceReporter().send(flowFile, HTTPUtils.getURI(flowFile.getAttributes()), stopWatch.getElapsed(TimeUnit.MILLISECONDS));
    session.transfer(flowFile, REL_SUCCESS);
    getLogger().info("Successfully responded to HTTP Request for {} with status code {}", new Object[] { flowFile, statusCode });
}
Also used : FlowFile(org.apache.nifi.flowfile.FlowFile) PropertyDescriptor(org.apache.nifi.components.PropertyDescriptor) HttpContextMap(org.apache.nifi.http.HttpContextMap) HttpServletResponse(javax.servlet.http.HttpServletResponse) ProcessException(org.apache.nifi.processor.exception.ProcessException) StopWatch(org.apache.nifi.util.StopWatch) ProcessException(org.apache.nifi.processor.exception.ProcessException) Map(java.util.Map) HttpContextMap(org.apache.nifi.http.HttpContextMap)

Example 2 with HttpContextMap

use of org.apache.nifi.http.HttpContextMap in project nifi by apache.

the class HandleHttpRequest method onTrigger.

@Override
public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
    try {
        if (!initialized.get()) {
            initializeServer(context);
        }
    } catch (Exception e) {
        context.yield();
        try {
            // shutdown to release any resources allocated during the failed initialization
            shutdown();
        } catch (final Exception shutdownException) {
            getLogger().debug("Failed to shutdown following a failed initialization: " + shutdownException);
        }
        throw new ProcessException("Failed to initialize the server", e);
    }
    HttpRequestContainer container;
    try {
        container = containerQueue.poll(2, TimeUnit.MILLISECONDS);
    } catch (final InterruptedException e1) {
        Thread.currentThread().interrupt();
        return;
    }
    if (container == null) {
        return;
    }
    final long start = System.nanoTime();
    final HttpServletRequest request = container.getRequest();
    FlowFile flowFile = session.create();
    try (OutputStream flowFileOut = session.write(flowFile)) {
        StreamUtils.copy(request.getInputStream(), flowFileOut);
    } catch (final IOException e) {
        // There may be many reasons which can produce an IOException on the HTTP stream and in some of them, eg.
        // bad requests, the connection to the client is not closed. In order to address also these cases, we try
        // and answer with a BAD_REQUEST, which lets the client know that the request has not been correctly
        // processed and makes it aware that the connection can be closed.
        getLogger().error("Failed to receive content from HTTP Request from {} due to {}", new Object[] { request.getRemoteAddr(), e });
        session.remove(flowFile);
        try {
            HttpServletResponse response = container.getResponse();
            response.sendError(Status.BAD_REQUEST.getStatusCode());
            response.flushBuffer();
            container.getContext().complete();
        } catch (final IOException ioe) {
            getLogger().warn("Failed to send HTTP response to {} due to {}", new Object[] { request.getRemoteAddr(), ioe });
        }
        return;
    }
    final String charset = request.getCharacterEncoding() == null ? context.getProperty(URL_CHARACTER_SET).getValue() : request.getCharacterEncoding();
    final String contextIdentifier = UUID.randomUUID().toString();
    final Map<String, String> attributes = new HashMap<>();
    try {
        putAttribute(attributes, HTTPUtils.HTTP_CONTEXT_ID, contextIdentifier);
        putAttribute(attributes, "mime.type", request.getContentType());
        putAttribute(attributes, "http.servlet.path", request.getServletPath());
        putAttribute(attributes, "http.context.path", request.getContextPath());
        putAttribute(attributes, "http.method", request.getMethod());
        putAttribute(attributes, "http.local.addr", request.getLocalAddr());
        putAttribute(attributes, HTTPUtils.HTTP_LOCAL_NAME, request.getLocalName());
        final String queryString = request.getQueryString();
        if (queryString != null) {
            putAttribute(attributes, "http.query.string", URLDecoder.decode(queryString, charset));
        }
        putAttribute(attributes, HTTPUtils.HTTP_REMOTE_HOST, request.getRemoteHost());
        putAttribute(attributes, "http.remote.addr", request.getRemoteAddr());
        putAttribute(attributes, "http.remote.user", request.getRemoteUser());
        putAttribute(attributes, "http.protocol", request.getProtocol());
        putAttribute(attributes, HTTPUtils.HTTP_REQUEST_URI, request.getRequestURI());
        putAttribute(attributes, "http.request.url", request.getRequestURL().toString());
        putAttribute(attributes, "http.auth.type", request.getAuthType());
        putAttribute(attributes, "http.requested.session.id", request.getRequestedSessionId());
        final DispatcherType dispatcherType = request.getDispatcherType();
        if (dispatcherType != null) {
            putAttribute(attributes, "http.dispatcher.type", dispatcherType.name());
        }
        putAttribute(attributes, "http.character.encoding", request.getCharacterEncoding());
        putAttribute(attributes, "http.locale", request.getLocale());
        putAttribute(attributes, "http.server.name", request.getServerName());
        putAttribute(attributes, HTTPUtils.HTTP_PORT, request.getServerPort());
        final Enumeration<String> paramEnumeration = request.getParameterNames();
        while (paramEnumeration.hasMoreElements()) {
            final String paramName = paramEnumeration.nextElement();
            final String value = request.getParameter(paramName);
            attributes.put("http.param." + paramName, value);
        }
        final Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (final Cookie cookie : cookies) {
                final String name = cookie.getName();
                final String cookiePrefix = "http.cookie." + name + ".";
                attributes.put(cookiePrefix + "value", cookie.getValue());
                attributes.put(cookiePrefix + "domain", cookie.getDomain());
                attributes.put(cookiePrefix + "path", cookie.getPath());
                attributes.put(cookiePrefix + "max.age", String.valueOf(cookie.getMaxAge()));
                attributes.put(cookiePrefix + "version", String.valueOf(cookie.getVersion()));
                attributes.put(cookiePrefix + "secure", String.valueOf(cookie.getSecure()));
            }
        }
        if (queryString != null) {
            final String[] params = URL_QUERY_PARAM_DELIMITER.split(queryString);
            for (final String keyValueString : params) {
                final int indexOf = keyValueString.indexOf("=");
                if (indexOf < 0) {
                    // no =, then it's just a key with no value
                    attributes.put("http.query.param." + URLDecoder.decode(keyValueString, charset), "");
                } else {
                    final String key = keyValueString.substring(0, indexOf);
                    final String value;
                    if (indexOf == keyValueString.length() - 1) {
                        value = "";
                    } else {
                        value = keyValueString.substring(indexOf + 1);
                    }
                    attributes.put("http.query.param." + URLDecoder.decode(key, charset), URLDecoder.decode(value, charset));
                }
            }
        }
    } catch (final UnsupportedEncodingException uee) {
        // won't happen because charset has been validated
        throw new ProcessException("Invalid character encoding", uee);
    }
    final Enumeration<String> headerNames = request.getHeaderNames();
    while (headerNames.hasMoreElements()) {
        final String headerName = headerNames.nextElement();
        final String headerValue = request.getHeader(headerName);
        putAttribute(attributes, "http.headers." + headerName, headerValue);
    }
    final Principal principal = request.getUserPrincipal();
    if (principal != null) {
        putAttribute(attributes, "http.principal.name", principal.getName());
    }
    final X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
    final String subjectDn;
    if (certs != null && certs.length > 0) {
        final X509Certificate cert = certs[0];
        subjectDn = cert.getSubjectDN().getName();
        final String issuerDn = cert.getIssuerDN().getName();
        putAttribute(attributes, HTTPUtils.HTTP_SSL_CERT, subjectDn);
        putAttribute(attributes, "http.issuer.dn", issuerDn);
    } else {
        subjectDn = null;
    }
    flowFile = session.putAllAttributes(flowFile, attributes);
    final HttpContextMap contextMap = context.getProperty(HTTP_CONTEXT_MAP).asControllerService(HttpContextMap.class);
    final boolean registered = contextMap.register(contextIdentifier, request, container.getResponse(), container.getContext());
    if (!registered) {
        getLogger().warn("Received request from {} but could not process it because too many requests are already outstanding; responding with SERVICE_UNAVAILABLE", new Object[] { request.getRemoteAddr() });
        try {
            container.getResponse().setStatus(Status.SERVICE_UNAVAILABLE.getStatusCode());
            container.getResponse().flushBuffer();
            container.getContext().complete();
        } catch (final Exception e) {
            getLogger().warn("Failed to respond with SERVICE_UNAVAILABLE message to {} due to {}", new Object[] { request.getRemoteAddr(), e });
        }
        session.remove(flowFile);
        return;
    }
    final long receiveMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
    session.getProvenanceReporter().receive(flowFile, HTTPUtils.getURI(attributes), "Received from " + request.getRemoteAddr() + (subjectDn == null ? "" : " with DN=" + subjectDn), receiveMillis);
    session.transfer(flowFile, REL_SUCCESS);
    getLogger().info("Transferring {} to 'success'; received from {}", new Object[] { flowFile, request.getRemoteAddr() });
}
Also used : HashMap(java.util.HashMap) HttpContextMap(org.apache.nifi.http.HttpContextMap) OutputStream(java.io.OutputStream) HttpServletRequest(javax.servlet.http.HttpServletRequest) DispatcherType(javax.servlet.DispatcherType) Cookie(javax.servlet.http.Cookie) FlowFile(org.apache.nifi.flowfile.FlowFile) HttpServletResponse(javax.servlet.http.HttpServletResponse) UnsupportedEncodingException(java.io.UnsupportedEncodingException) IOException(java.io.IOException) ServletException(javax.servlet.ServletException) URISyntaxException(java.net.URISyntaxException) UnsupportedEncodingException(java.io.UnsupportedEncodingException) ProcessException(org.apache.nifi.processor.exception.ProcessException) IOException(java.io.IOException) X509Certificate(java.security.cert.X509Certificate) ProcessException(org.apache.nifi.processor.exception.ProcessException) Principal(java.security.Principal)

Example 3 with HttpContextMap

use of org.apache.nifi.http.HttpContextMap in project nifi by apache.

the class HandleHttpRequest method initializeServer.

private synchronized void initializeServer(final ProcessContext context) throws Exception {
    if (initialized.get()) {
        return;
    }
    this.containerQueue = new LinkedBlockingQueue<>(context.getProperty(CONTAINER_QUEUE_SIZE).asInteger());
    final String host = context.getProperty(HOSTNAME).getValue();
    final int port = context.getProperty(PORT).asInteger();
    final SSLContextService sslService = context.getProperty(SSL_CONTEXT).asControllerService(SSLContextService.class);
    final HttpContextMap httpContextMap = context.getProperty(HTTP_CONTEXT_MAP).asControllerService(HttpContextMap.class);
    final long requestTimeout = httpContextMap.getRequestTimeout(TimeUnit.MILLISECONDS);
    final String clientAuthValue = context.getProperty(CLIENT_AUTH).getValue();
    final boolean need;
    final boolean want;
    if (CLIENT_NEED.equals(clientAuthValue)) {
        need = true;
        want = false;
    } else if (CLIENT_WANT.equals(clientAuthValue)) {
        need = false;
        want = true;
    } else {
        need = false;
        want = false;
    }
    final SslContextFactory sslFactory = (sslService == null) ? null : createSslFactory(sslService, need, want);
    final Server server = new Server(port);
    // create the http configuration
    final HttpConfiguration httpConfiguration = new HttpConfiguration();
    if (sslFactory == null) {
        // create the connector
        final ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration));
        // set host and port
        if (StringUtils.isNotBlank(host)) {
            http.setHost(host);
        }
        http.setPort(port);
        // add this connector
        server.setConnectors(new Connector[] { http });
    } else {
        // add some secure config
        final HttpConfiguration httpsConfiguration = new HttpConfiguration(httpConfiguration);
        httpsConfiguration.setSecureScheme("https");
        httpsConfiguration.setSecurePort(port);
        httpsConfiguration.addCustomizer(new SecureRequestCustomizer());
        // build the connector
        final ServerConnector https = new ServerConnector(server, new SslConnectionFactory(sslFactory, "http/1.1"), new HttpConnectionFactory(httpsConfiguration));
        // set host and port
        if (StringUtils.isNotBlank(host)) {
            https.setHost(host);
        }
        https.setPort(port);
        // add this connector
        server.setConnectors(new Connector[] { https });
    }
    final Set<String> allowedMethods = new HashSet<>();
    if (context.getProperty(ALLOW_GET).asBoolean()) {
        allowedMethods.add("GET");
    }
    if (context.getProperty(ALLOW_POST).asBoolean()) {
        allowedMethods.add("POST");
    }
    if (context.getProperty(ALLOW_PUT).asBoolean()) {
        allowedMethods.add("PUT");
    }
    if (context.getProperty(ALLOW_DELETE).asBoolean()) {
        allowedMethods.add("DELETE");
    }
    if (context.getProperty(ALLOW_HEAD).asBoolean()) {
        allowedMethods.add("HEAD");
    }
    if (context.getProperty(ALLOW_OPTIONS).asBoolean()) {
        allowedMethods.add("OPTIONS");
    }
    final String additionalMethods = context.getProperty(ADDITIONAL_METHODS).getValue();
    if (additionalMethods != null) {
        for (final String additionalMethod : additionalMethods.split(",")) {
            final String trimmed = additionalMethod.trim();
            if (!trimmed.isEmpty()) {
                allowedMethods.add(trimmed.toUpperCase());
            }
        }
    }
    final String pathRegex = context.getProperty(PATH_REGEX).getValue();
    final Pattern pathPattern = (pathRegex == null) ? null : Pattern.compile(pathRegex);
    server.setHandler(new AbstractHandler() {

        @Override
        public void handle(final String target, final Request baseRequest, final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException {
            final String requestUri = request.getRequestURI();
            if (!allowedMethods.contains(request.getMethod().toUpperCase())) {
                getLogger().info("Sending back METHOD_NOT_ALLOWED response to {}; method was {}; request URI was {}", new Object[] { request.getRemoteAddr(), request.getMethod(), requestUri });
                response.sendError(Status.METHOD_NOT_ALLOWED.getStatusCode());
                return;
            }
            if (pathPattern != null) {
                final URI uri;
                try {
                    uri = new URI(requestUri);
                } catch (final URISyntaxException e) {
                    throw new ServletException(e);
                }
                if (!pathPattern.matcher(uri.getPath()).matches()) {
                    response.sendError(Status.NOT_FOUND.getStatusCode());
                    getLogger().info("Sending back NOT_FOUND response to {}; request was {} {}", new Object[] { request.getRemoteAddr(), request.getMethod(), requestUri });
                    return;
                }
            }
            // If destination queues full, send back a 503: Service Unavailable.
            if (context.getAvailableRelationships().isEmpty()) {
                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
                return;
            }
            // Right now, that information, though, is only in the ProcessSession, not the ProcessContext,
            // so it is not known to us. Should see if it can be added to the ProcessContext.
            final AsyncContext async = baseRequest.startAsync();
            async.setTimeout(requestTimeout);
            final boolean added = containerQueue.offer(new HttpRequestContainer(request, response, async));
            if (added) {
                getLogger().debug("Added Http Request to queue for {} {} from {}", new Object[] { request.getMethod(), requestUri, request.getRemoteAddr() });
            } else {
                getLogger().info("Sending back a SERVICE_UNAVAILABLE response to {}; request was {} {}", new Object[] { request.getRemoteAddr(), request.getMethod(), request.getRemoteAddr() });
                response.sendError(Status.SERVICE_UNAVAILABLE.getStatusCode());
                response.flushBuffer();
                async.complete();
            }
        }
    });
    this.server = server;
    server.start();
    getLogger().info("Server started and listening on port " + getPort());
    initialized.set(true);
}
Also used : Server(org.eclipse.jetty.server.Server) HttpContextMap(org.apache.nifi.http.HttpContextMap) AsyncContext(javax.servlet.AsyncContext) HttpConfiguration(org.eclipse.jetty.server.HttpConfiguration) URISyntaxException(java.net.URISyntaxException) SslConnectionFactory(org.eclipse.jetty.server.SslConnectionFactory) URI(java.net.URI) AbstractHandler(org.eclipse.jetty.server.handler.AbstractHandler) ServerConnector(org.eclipse.jetty.server.ServerConnector) HttpServletRequest(javax.servlet.http.HttpServletRequest) ServletException(javax.servlet.ServletException) SslContextFactory(org.eclipse.jetty.util.ssl.SslContextFactory) HashSet(java.util.HashSet) Pattern(java.util.regex.Pattern) SecureRequestCustomizer(org.eclipse.jetty.server.SecureRequestCustomizer) HttpConnectionFactory(org.eclipse.jetty.server.HttpConnectionFactory) Request(org.eclipse.jetty.server.Request) HttpServletRequest(javax.servlet.http.HttpServletRequest) HttpServletResponse(javax.servlet.http.HttpServletResponse) IOException(java.io.IOException) SSLContextService(org.apache.nifi.ssl.SSLContextService) RestrictedSSLContextService(org.apache.nifi.ssl.RestrictedSSLContextService)

Aggregations

HttpServletResponse (javax.servlet.http.HttpServletResponse)3 HttpContextMap (org.apache.nifi.http.HttpContextMap)3 IOException (java.io.IOException)2 URISyntaxException (java.net.URISyntaxException)2 ServletException (javax.servlet.ServletException)2 HttpServletRequest (javax.servlet.http.HttpServletRequest)2 FlowFile (org.apache.nifi.flowfile.FlowFile)2 ProcessException (org.apache.nifi.processor.exception.ProcessException)2 OutputStream (java.io.OutputStream)1 UnsupportedEncodingException (java.io.UnsupportedEncodingException)1 URI (java.net.URI)1 Principal (java.security.Principal)1 X509Certificate (java.security.cert.X509Certificate)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 Map (java.util.Map)1 Pattern (java.util.regex.Pattern)1 AsyncContext (javax.servlet.AsyncContext)1 DispatcherType (javax.servlet.DispatcherType)1 Cookie (javax.servlet.http.Cookie)1