Extensiones de directivas: Casos de uso

Algunas aplicaciones de cliente tienen requisitos que no se pueden abordar con directivas y expresiones existentes. La función de extensión de directivas permite a los clientes agregar funciones personalizadas a sus aplicaciones para satisfacer sus requisitos.

Los siguientes casos de uso ilustran la adición de nuevas funciones mediante la función de extensión de directivas en el dispositivo Citrix ADC.

  • Caso 1: Hash personalizado
  • Caso 2: Contraer barras diagonales dobles en las URL
  • Caso 3: Combinar encabezados

Caso 1: Hash personalizado

La función CUSTOM_HASH proporciona un mecanismo para insertar cualquier tipo de valor hash en las respuestas enviadas al cliente. En este caso de uso, la función hash se utiliza para calcular el hash de la cadena de consulta para una solicitud HTTP de reescritura e insertar un encabezado HTTP llamado CUSTOM_HASH con el valor calculado. La función CUSTOM_HASH implementa el algoritmo hash DJB2.

Ejemplo de uso de CUSTOM_HASH:

> add rewrite action test_custom_hash insert_http_header "CUSTOM_HASH" "HTTP.REQ.URL.QUERY.CUSTOM_HASH"

Ejemplo de definición de CUSTOM_HASH ():

    -- Extension function to compute custom hash on the text

    -- Uses the djb2 string hash algorithm
    function NSTEXT:CUSTOM_HASH() : NSTEXT

        local hash = 5381

        local len = string.len(self)

        for i = 1, len do

             hash = bit32.bxor((hash * 33), string.byte(self, i))

        end

        return tostring(hash)

    end

Descripción línea por línea de la muestra anterior:

function NSTEXT:CUSTOM_HASH() : NSTEXT

Defines the CUSTOM_HASH() function, with text input and a text return value.

local hash = 5381
local len = string.len(self)

Declares two local variables:

 - hash. Accumulates the compute hash value and is seeded with the number 5381

 - len. Sets to the length of the self input text string, using the built-in string.len() function.

for i = 1, len do
      hash = bit32.bxor((hash * 33), string.byte(self, i))
end

Iterates through each byte of the input string and adds the byte to the hash. It uses the built-in string.byte() function to get the byte and the built-in bit32.bxor() function to compute the XOR of the existing hash value (multiplied by 33) and the byte.

return tostring(hash)

Calls the built-in tostring() function to convert the numeric hash value to a string and returns the string as the value of the function.

Caso 2: Contraer barras diagonales dobles en las URL

La contracción de barras dobles en las URL mejora el tiempo de representación del sitio web, ya que los exploradores analizan las URL de barra única de forma más eficiente. Las direcciones URL de barra única también para mantener la compatibilidad con aplicaciones que no aceptan barras dobles. La función de extensión de directivas permite a los clientes agregar una función que reemplaza las barras diagonales dobles por barras diagonales simples en las direcciones URL. El ejemplo siguiente ilustra la adición de una función de extensión de directiva que contrae barras dobles en las direcciones URL.

Ejemplo de definición de PULSE_DOUBLE_SLASES ():

    -- Collapse double slashes in URL to a single slash and return the result
    function NSTEXT:COLLAPSE_DOUBLE_SLASHES() : NSTEXT

        local result = string.gsub(self, "//", "/")

        return result

    end

Descripción línea por línea de la muestra anterior:

function NSTEXT:COLLAPSE_DOUBLE_SLASHES() : NSTEXT

Declares the COLLAPSE_DOUBLE_SLASHES() function with text input and return.

local result = string.gsub(self, "//", "/")

Declares a local variable named result and uses the built-in string.gsub() function to replace all double slashes with single slashes in the self input text.

The second parameter of string.gsub() is actually a regular expression pattern, although here a simple string is used for the pattern.

return result

Returns the resulting string.

Caso 3: Combinar encabezados

Ciertas aplicaciones de cliente no pueden manejar múltiples encabezados en una solicitud. Además, el análisis de encabezados duplicados con los mismos valores de encabezado, o múltiples encabezados con el mismo nombre pero valores diferentes en una solicitud, consume tiempo y recursos de red. La función de extensión de directiva permite a los clientes agregar una función para combinar estos encabezados en encabezados individuales con un valor que combina los valores originales. Por ejemplo, combinando los valores de los encabezados H1 y H2.

Solicitud original:

GET /combine_headers HTTP/1.1
User-Agent: amigo unit test
Host: myhost
H2: h2val1
H1: abcd
Accept: \*/\*
H2: h2val2
Content-Length: 0
H2: h2val3
H1: 1234

Solicitud modificada:

GET /combine_headers HTTP/1.1
User-Agent: amigo unit test
Host: myhost
H2: h2val1, h2val2, h2val3
H1: abcd, 1234
Accept: \*/\*
Content-Length: 0

En general, este tipo de modificación de solicitud se realiza mediante la función Rewrite, mediante expresiones de directiva para delinear la parte de la solicitud que se va a modificar (el destino) y la modificación que se va a realizar (la expresión del generador de cadenas). Sin embargo, las expresiones de directiva no tienen la capacidad de iterar sobre un número arbitrario de encabezados.

La solución a este problema requiere una extensión al servicio de directivas. Para ello, vamos a definir una función de extensión, llamada COMBINE_HEADERS. Con esta función, podemos configurar la siguiente acción de reescritura:

> add rewrite action combine_headers_act replace 'HTTP.REQ.FULL_HEADER.AFTER_STR("HTTP/1.1rn")' 'HTTP.REQ.FULL_HEADER.AFTER_STR("HTTP/1.1rn").COMBINE_HEADERS'

Aquí, el destino de reescritura es HTTP.REQ.FULL_HEADER.AFTER_STR (“HTTP/1.1RN”). El AFTER_STR (“HTTP/1.1RN”) es necesario porque FULL_HEADER incluye la primera línea de la solicitud HTTP (por ejemplo, GET /combine_headers HTTP/1.1).

La expresión del generador de cadenas es HTTP.REQ.FULL_HEADER.AFTER_STR (“HTTP/1.1RN”) .COMBINE_HEADERS, donde los encabezados (menos la primera línea) se introducen en la función de extensión COMBINE_HEADERS, que combina y devuelve los valores de los encabezados.

Ejemplo de definición de COMBINE_HEADERS ():

    -- Extension function to combine multiple headers of the same name into one header.



    function NSTEXT:COMBINE_HEADERS(): NSTEXT

        local headers = {} -- headers

        local combined_headers = {} -- headers with final combined values
        -- Iterate over each header (format "name:valuer\r\n")

        -- and build a list of values for each unique header name.

        for name, value in string.gmatch(self, "([^:]+):([^\r\n]*)\r\n") do

            if headers[name] then

                local next_value_index = #(headers[name]) + 1

                headers[name][next_value_index] = value

            else

                headers[name] = {name .. ":" .. value}

            end

        end



        -- iterate over the headers and concat the values with separator ","

        for name, values in pairs(headers) do

            local next_header_index = #combined_headers + 1

            combined_headers[next_header_index] = table.concat(values, ",")

        end



        -- Construct the result headers using table.concat()

        local result_str = table.concat(combined_headers, "\r\n") .. "\r\n\r\n"

        return result_str

    end

Descripción línea por línea de la muestra anterior:

function NSTEXT:COMBINE_HEADERS(): NSTEXT

Defines the COMBINE_HEADERS extension function, with the text input into the function from the policy expression and a text return type to the policy expression.

local headers = {} -- headers
local combined_headers = {} -- headers with final combined values

Declares local variables headers and combined_headers and initialize these variables to empty tables. headers will be a table of arrays of strings, where each array holds one or more values for a header. combined_headers will be an array of strings, where each array element is a header with its combined values.

for name, value in string.gmatch(self, "([^:]+):([^\r\n]*)\r\n") do
. . .
end

Este genérico para bucle analiza cada encabezado en la entrada. El iterador es la función incorporada string.gmatch (). Esta función toma dos parámetros: Una cadena para buscar, y un patrón para usar para hacer coincidir las piezas de la cadena. La cadena a buscar es suministrada por el parámetro self implícito, que es el texto de los encabezados introducidos en la función.

El patrón se expresa mediante una expresión regular (expresión regular para abreviar). Esta expresión regular coincide con el nombre de encabezado y el valor de cada encabezado, que el estándar HTTP define como name:valuern. Los paréntesis de la expresión regular especifican las partes coincidentes que se van a extraer, por lo que el esquema de expresiones regulares es (nombre de coincidencia): (valor de coincidencia) rn. El patrón de nombre de coincidencia debe coincidir con todos los caracteres excepto los dos puntos. Esto se escribe [^:]+([^:] es cualquier carácter excepto: Y + es una o más repeticiones). Del mismo modo, el patrón de valor de coincidencia tiene que coincidir con cualquier carácter excepto el \r\n, por lo que se escribe [^rn]*([^rn] coincide con cualquier carácter excepto \r y \n y * es cero o más repeticiones). Esto hace que la expresión regular completa ([^:]+): ([^rn]*) \r\n.

La sentencia for utiliza una asignación múltiple para establecer el nombre y el valor de las dos coincidencias devueltas por el iterador string.gmatch (). Estas se declaran implícitamente como variables locales dentro del cuerpo del bucle for.

if headers[name] then
     local next_value_index = #(headers[name]) + 1
     headers[name][next_value_index] = value
else
     headers[name] = {name .. ":" .. value}
end

Estas instrucciones dentro del bucle for ponen los nombres y valores de encabezado en la tabla de encabezados. La primera vez que se analiza un nombre de encabezado (digamos H2: H2val1 en la entrada de ejemplo), no hay entrada de encabezados para el nombre y encabezados [nombre] es nulo.

Dado que nil se trata como falso, se ejecuta la cláusula else. Esto establece la entrada de encabezados para el nombre en una matriz con un valor de cadena name:value.

Nota: El constructor de matriz en el bucle else es equivalente a {[1] = name.. “:”.. value}, que establece el primer elemento de la matriz.) Para el primer encabezado H2, establece encabezados[“H2”]= {“h2:h2val1”}.

En instancias posteriores de un encabezado, (por ejemplo, H2: H2val2 en la entrada de ejemplo). headers[nombre] no es nulo, por lo que se ejecuta la cláusula then. Esto determina el siguiente índice disponible en el valor de matriz para headers[nombre] y coloca el valor de encabezado en ese índice. Para el segundo encabezado H2, establece headers[“H2”] = {“h2:h2val1”, “h2val2”}.

for name, values in pairs(headers) do
    local next_header_index = #combined_headers + 1
    combined_headers[next_header_index] = table.concat(values, ",")
end

Después de analizar los encabezados originales y rellenar la tabla de encabezados, este bucle crea la matriz combined_headers. Utiliza la función pares () como iterador de bucle for.

Cada llamada a pares () devuelve el nombre y el valor de la siguiente entrada en la tabla de encabezados.

La siguiente línea determina el siguiente índice disponible en la matriz combined_headers, y la siguiente línea establece ese elemento de matriz en el encabezado combinado. Utiliza la función table.concat () incorporada, que toma como argumentos una matriz de cadenas y una cadena para usar como separador, y devuelve una cadena que es la concatenación de las cadenas de matriz, separadas por el separador.

Por ejemplo, para los valores = {“h2:h2val1”, “h2val2”}, esto produce “h2:h2val1, h2val2”

local result_str = table.concat(combined_headers, "\r\n") .. "\r\n\r\n"

Después de construir la matriz combined_headers, concatena los elementos en una cadena, y agrega un rn doble que termina los encabezados HTTP.

return result_str

Devuelve una cadena como resultado de la función de extensión COMBINE_HEADERS.

Extensiones de directivas: Casos de uso