null

Apache NiFi: работа с HTTPS внутри Groovy скрипта

Допустим, что мы используем Apache NiFi и в нашем потоке передачи данных мы встали перед необходимостью реализации сложной логики, требующей вызова дополнительных подзапросов к сторонним API внутри Groovy-скрипта. В силу некоторых причин мы не хотим (или не можем) разделять логику на несколько скриптов, связанных между собой встроенными NiFi-процессорами InvokeHttp для получения/отправки данных. В подобном случае при дополнительном условии, что взаимодействие со сторонней системой происходит по безопасному протоколу https, мы неизбежно сталкиваемся со специфичными для NiFi тонкостями отправки запросов в Groovy.  Если мы говорим об отправке запросов в языке Groovy безотносительно NiFi, то источников информации на эту тему довольно много. Если говорить об особенностях NiFi, то информации оказывается чуть меньше и порой она несколько противоречива. В связи с этим приведем упрощенный фрагмент кода, который помог нам отправить https запрос прямо в коде Groovy-скрипта:

import org.apache.commons.io.IOUtils
import java.nio.charset.StandardCharsets
import org.apache.nifi.ssl.SSLContextService
import javax.net.ssl.HttpsURLConnection
import org.apache.nifi.ssl.SSLContextService.ClientAuth
import org.apache.nifi.controller.ControllerService

def flowFile = session.get()
if (!flowFile) return

def requestHttps(sslContext) {
	def url ="https://#{external_url}:#{external_port}/something"
	def get = new URL(url).openConnection() as HttpsURLConnection
	get.setSSLSocketFactory(sslContext.getSocketFactory())
	get.setDoOutput(true)
	get.setRequestProperty("Accept", "application/json")
	get.setRequestProperty("Content-Type", "application/json")
	get.setRequestProperty("Host", "#{external_url}:#{external_port}")
	def w_getResponseCode = get.getResponseCode();
    if (w_getResponseCode.equals(200)) {
        return get.getInputStream().getText()
    } else {
        return get.getErrorStream().getText()
    }
}

try {
    flowFile = session.write(flowFile,
            { inputStream, outputStream ->

		def lookup = context.controllerServiceLookup 
    		def serviceId = lookup.getControllerServiceIdentifiers(ControllerService).find {
      			cs -> lookup.getControllerServiceName(cs) == 'StandardRestrictedSSLContextService'
    		}
    		def sslContextService = lookup.getControllerService(serviceId)
    		def sslContext = sslContextService.createContext()
                def result = requestHttps(sslContext)

        outputStream.write(result.getBytes(StandardCharsets.UTF_8))
    } as StreamCallback)
    session.transfer(flowFile, REL_SUCCESS)
} catch (Exception e) {
    session.transfer(flowFile, REL_FAILURE)
}

Самое важное место здесь -- это получение SSL контекста. В NiFi мы используем controllerServiceLookup чтобы "подглядеть" идентификатор Controller service'а, ответственного за SSL контекст. Ищем этот сервис мы по его имени "StandardRestrictedSSLContextService". Напомним, что создать и настроить данный сервис необходимо самостоятельно. Далее по id мы можем получить данный сервис и проинициализировать его в коде, получая тем самым возможность слать запросы, даже если сервер поддерживает взаимодействие только по https.

Вперед

Коротко о себе:

 

Работаю Java\Kotlin Backend Developer в компании Tune-it. На работе занимаюсь проектами, связанными с Liferay, NiFi, Spring Framework, а вне работы - философской антропологией