Extensiones de directivas: casos de uso
Algunas aplicaciones de clientes tienen requisitos que no se pueden abordar con las directivas y expresiones existentes. La función de extensión de directivas permite a los clientes añadir funciones personalizadas a sus aplicaciones para cumplir con sus requisitos.
Los siguientes casos de uso ilustran la adición de nuevas funciones mediante la función de extensión de directivas del dispositivo NetScaler.
- Caso 1: Hash personalizado
- Caso 2: Contraer barras 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 de hash en las respuestas enviadas al cliente. En este caso de uso, la función de hash se utiliza para calcular el hash de la cadena de consulta para una solicitud HTTP de reescritura e insertar un encabezado HTTP denominado CUSTOM_HASH con el valor calculado. La función CUSTOM_HASH implementa el algoritmo de 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"
<!--NeedCopy-->
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
<!--NeedCopy-->
Descripción línea por línea del ejemplo 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.
<!--NeedCopy-->
Caso 2: Contraer barras dobles en las URL
Al contraer las barras dobles en las URL, se mejora el tiempo de representación del sitio web, ya que los navegadores analizan las URL de barra única de manera más eficiente. Las URL de barra única también sirven para mantener la compatibilidad con las aplicaciones que no aceptan barras dobles. La función de extensión de directivas permite a los clientes añadir una función que sustituya las barras dobles por barras simples en las URL. El siguiente ejemplo ilustra la adición de una función de extensión de directivas que contrae las barras dobles en las URL.
Ejemplo de definición de COLLAPSE_DOUBLE_SLASHES ():
-- 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
<!--NeedCopy-->
Descripción línea por línea del ejemplo 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.
<!--NeedCopy-->
Caso 3: Combinar encabezados
Algunas aplicaciones de clientes no pueden gestionar varios encabezados en una solicitud. Además, el análisis de los encabezados duplicados con los mismos valores de encabezado, o de varios encabezados con el mismo nombre pero con valores diferentes en una solicitud, consume tiempo y recursos de red. La función de extensión de directivas permite a los clientes añadir una función para combinar estos encabezados en encabezados únicos con un valor que combine los valores originales. Por ejemplo, combinar 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
<!--NeedCopy-->
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
<!--NeedCopy-->
En general, este tipo de modificación de la solicitud se realiza mediante la función de reescritura, que utiliza expresiones de directiva para delimitar la parte de la solicitud que se va a modificar (el objetivo) y la modificación que se va a realizar (la expresión del generador de cadenas). Sin embargo, las expresiones de directiva no pueden repetirse en un número arbitrario de encabezados.
La solución a este problema requiere una ampliación del mecanismo de directivas. Para ello, definiremos 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 objetivo de reescritura es HTTP.REQ.FULL_HEADER.AFTER_STR (“HTTP/1.1rn”). El AFTER_STR (“HTTP/1.1rn”) es obligatorio 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
<!--NeedCopy-->
Descripción línea por línea del ejemplo 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
<!--NeedCopy-->
Este bucle genérico para analiza cada encabezado de la entrada. El iterador es la función string.gmatch () incorporada. Esta función utiliza dos parámetros: una cadena para buscar y un patrón para hacer coincidir partes de la cadena. La cadena que se va a buscar la proporciona 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 (regex para abreviar). Esta expresión regular coincide con el nombre del encabezado y el valor de cada encabezado, que el estándar HTTP define como *nombre*:value\ r\ n. 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)\ r\ n.* El patrón de nombre de coincidencia debe coincidir con todos los caracteres excepto los dos puntos. Esto está escrito [^:]+
. [^:]
es cualquier carácter excepto :
y +
tiene una o más repeticiones. Del mismo modo, el patrón de valores de coincidencia debe coincidir con todos los caracteres excepto con \r\n
, por lo que se escribe. [^\r\n]*
[^\r\n]
coincide con cualquier carácter excepto \r
\n
y *
tiene cero o más repeticiones. Esto crea la expresión regular completa. ([^:]+):([^\r\n]*)\r\n
La sentencia for usa una asignación múltiple para establecer el nombre y el valor de las dos coincidencias devueltas por el iterador string.gmatch (). 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
<!--NeedCopy-->
Estas instrucciones dentro del bucle for ponen los nombres y valores de encabezado en la tabla de encabezados. La primera vez que se analiza el nombre de un encabezado (por ejemplo, H2: h2val1 en la entrada del ejemplo), no hay ninguna entrada de encabezados para el nombre y los encabezados [nombre] son nulos.
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 matrices del bucle else equivale a {[1] = name.. “:”.. value}, que establece el primer elemento de la matriz.) Para el primer encabezado H2, establece los encabezados [“H2”] = {“ H2:H2val1”}.
En las instancias posteriores de un encabezado (por ejemplo, H2: h2val2 en la entrada del ejemplo). headers [name] no es nulo, por lo que se ejecuta la cláusula then. Esto determina el siguiente índice disponible en el valor de la matriz para los encabezados [nombre] y coloca el valor del encabezado en ese índice. Para el segundo encabezado H2, establece los encabezados [“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
<!--NeedCopy-->
Una vez analizados los encabezados originales y rellenada la tabla de encabezados, este bucle crea la matriz combined_headers. Utiliza la función pairs () como iterador del bucle for.
Cada llamada a pairs () devuelve el nombre y el valor de la siguiente entrada de la tabla de encabezados.
La siguiente línea determina el siguiente índice disponible en la matriz combined_headers y la siguiente establece ese elemento de la matriz en el encabezado combinado. Utiliza la función table.concat () integrada, que toma como argumentos una matriz de cadenas y una cadena para usarlas como separador, y devuelve una cadena que es la concatenación de las cadenas de la matriz, separadas por el separador.
Por ejemplo, para values = {“h2:h2val1”, “h2val2”}, esto produce “H2:h2val1, h2val2”
local result_str = table.concat(combined_headers, "\r\n") .. "\r\n\r\n"
<!--NeedCopy-->
Una vez creada la matriz combined_headers, concatena los elementos en una cadena y añade un\ r\ n doble que termina los encabezados HTTP.
return result_str
<!--NeedCopy-->
Devuelve una cadena como resultado de la función de extensión COMBINE_HEADERS.