Extensions de stratégie - cas d’utilisation

Certaines applications client ont des exigences qui ne peuvent pas être prises en compte avec les stratégies et expressions existantes. La fonction d’extension de stratégie permet aux clients d’ajouter des fonctions personnalisées à leurs applications afin de répondre à leurs besoins.

Les cas d’utilisation suivants illustrent l’ajout de nouvelles fonctions à l’aide de la fonction d’extension de stratégie de l’appliance NetScaler.

  • Cas 1 : hachage personnalisé
  • Cas 2 : réduire les barres obliques doubles dans les URL
  • Cas 3 : Combiner les en-têtes

Cas 1 : hachage personnalisé

La fonction CUSTOM_HASH fournit un mécanisme permettant d’insérer n’importe quel type de valeur de hachage dans les réponses envoyées au client. Dans ce cas d’utilisation, la fonction de hachage est utilisée pour calculer le hachage de la chaîne de requête pour une demande HTTP de réécriture et pour insérer un en-tête HTTP nommé CUSTOM_HASH avec la valeur calculée. La fonction CUSTOM_HASH implémente l’algorithme de hachage DJB2.

Exemple d’utilisation de CUSTOM_HASH :

> add rewrite action test_custom_hash insert_http_header "CUSTOM_HASH" "HTTP.REQ.URL.QUERY.CUSTOM_HASH"
<!--NeedCopy-->

Exemple de définition 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-->

Description ligne par ligne de l’exemple ci-dessus :

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-->

Cas 2 : réduire les barres obliques doubles dans les URL

La réduction des barres obliques doubles dans les URL améliore le temps d’affichage du site Web, car les navigateurs analysent les URL à barre oblique unique de manière plus efficace. Les URL à barre oblique unique permettent également de maintenir la compatibilité avec les applications qui n’acceptent pas les barres obliques doubles. La fonction d’extension de stratégie permet aux clients d’ajouter une fonction qui remplace les barres obliques doubles par des barres obliques simples dans les URL. L’exemple suivant illustre l’ajout d’une fonction d’extension de stratégie qui réduit les doubles barres obliques dans les URL.

Exemple de définition 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-->

Description ligne par ligne de l’exemple ci-dessus :

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-->

Cas 3 : Combiner les en-têtes

Certaines applications client ne peuvent pas gérer plusieurs en-têtes dans une demande. De plus, l’analyse d’en-têtes dupliqués avec les mêmes valeurs d’en-tête, ou de plusieurs en-têtes portant le même nom mais des valeurs différentes dans une requête, consomme du temps et des ressources réseau. La fonction d’extension de stratégie permet aux clients d’ajouter une fonction pour combiner ces en-têtes en en-têtes simples avec une valeur combinant les valeurs d’origine. Par exemple, en combinant les valeurs des en-têtes H1 et H2.

Demande originale :

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-->

Demande modifiée :

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 général, ce type de modification de demande est effectué à l’aide de la fonction Réécriture, à l’aide d’expressions de stratégie pour délimiter la partie de la demande à modifier (la cible) et la modification à effectuer (l’expression du générateur de chaîne). Cependant, les expressions de stratégie n’ont pas la possibilité d’itérer sur un nombre arbitraire d’en-têtes.

La solution à ce problème nécessite une extension de la fonction de stratégie. Pour ce faire, nous allons définir une fonction d’extension, appelée COMBINE_HEADERS. Avec cette fonction, nous pouvons configurer l’action de réécriture suivante :

> 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'

Ici, la cible de réécriture est HTTP.REQ.FULL_HEADER.AFTER_STR(“HTTP/1.1rn”). Le AFTER_STR (« HTTP/1.1rn ») est obligatoire car FULL_HEADER inclut la première ligne de la requête HTTP (par exemple GET /combine_headers HTTP/1.1).

L’expression du générateur de chaînes est HTTP.REQ.FULL_HEADER.AFTER_STR (« http/1.1rn ») .COMBINE_HEADERS, où les en-têtes (moins la première ligne) sont introduits dans la fonction d’extension COMBINE_HEADERS, qui combine et renvoie les valeurs des en-têtes.

Exemple de définition 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-->

Description ligne par ligne de l’exemple ci-dessus :

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-->

Ce générique for loop analyse chaque en-tête de l’entrée. L’itérateur est la fonction string.gmatch() intégrée. Cette fonction prend deux paramètres : une chaîne à rechercher et un modèle à utiliser pour faire correspondre des parties de la chaîne. La chaîne à rechercher est fournie par le paramètre implicite self, qui est le texte des en-têtes saisis dans la fonction.

Le modèle est exprimé à l’aide d’une expression régulière (regex en abrégé). Cette expression régulière correspond au nom et à la valeur de chaque en-tête, que la norme HTTP définit comme *name*:value\r\n. Les parenthèses dans la regex spécifient les parties correspondantes à extraire, de sorte que le schéma regex est (match-name):(match-value)\r\n. Le modèle de nom de correspondance doit correspondre à tous les caractères sauf les deux-points. C’est écrit [^:]+. [^:] est n’importe quel caractère à l’exception de : et + représente une ou plusieurs répétitions. De même, le modèle de valeur de correspondance doit correspondre à tous les caractères sauf le \r\n, il est donc écrit. [^\r\n]*[^\r\n] correspond à n’importe quel caractère sauf \r\n et * est égal à zéro ou plusieurs répétitions. Cela rend la regex ([^:]+):([^\r\n]*)\r\ncomplète.

L’instruction for utilise une attribution multiple pour définir le nom et la valeur des deux correspondances renvoyées par l’itérateur string.gmatch (). Elles sont implicitement déclarées en tant que variables locales dans le corps de la boucle 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-->

Ces instructions dans la boucle for placent les noms et les valeurs d’en-tête dans la table des en-têtes. La première fois qu’un nom d’en-tête est analysé (disons H2 : h2val1 dans l’exemple d’entrée), il n’y a aucune entrée d’en-tête pour le nom et les en-têtes [nom] sont nuls.

Puisque nil est traité comme false, la clause else est exécutée. Cela définit l’entrée des en-têtes pour name à un tableau avec une valeur de chaîne name :value.

Remarque : Le constructeur de tableau de la boucle else est équivalent à {[1] = name. « : ».. value}, qui définit le premier élément du tableau.) Pour le premier en-tête H2, il définit les en-têtes [“H2”] = {“H2:h2val1”}.

Sur les instances suivantes d’un en-tête (disons, H2 : h2val2 dans l’exemple d’entrée). headers [name] n’est pas nul, donc la clause then est exécutée. Cela détermine le prochain index disponible dans la valeur du tableau pour les en-têtes [nom] et place la valeur de l’en-tête dans cet index. Pour le deuxième en-tête H2, il définit les en-têtes [“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-->

Une fois que les en-têtes d’origine ont été analysés et que la table des en-têtes a été remplie, cette boucle crée le tableau combined_headers. Il utilise la fonction pairs() comme itérateur de boucle.

Chaque appel à pairs() renvoie le nom et la valeur de l’entrée suivante dans la table des en-têtes.

La ligne suivante détermine le prochain index disponible dans le tableau combined_headers, et la ligne suivante définit cet élément du tableau comme l’en-tête combiné. Il utilise la fonction table.concat() intégrée, qui prend comme arguments un tableau de chaînes et une chaîne à utiliser comme séparateur, et renvoie une chaîne qui est la concaténation des chaînes de tableau, séparées par le séparateur.

Par exemple, pour les valeurs = {“H2:h2val1”, “h2val2”}, cela produit “H2:h2val1, h2val2”

local result_str = table.concat(combined_headers, "\r\n") .. "\r\n\r\n"
<!--NeedCopy-->

Une fois le tableau combined_headers créé, il concatène les éléments en une seule chaîne et ajoute un double \ r \ n qui met fin aux en-têtes HTTP.

return result_str
<!--NeedCopy-->

Renvoie une chaîne comme résultat de la fonction d’extension COMBINE_HEADERS.

Extensions de stratégie - cas d’utilisation