Um iRule passa boa parte do tempo montando valores: um endereço de redirecionamento a partir de um host e um caminho, uma linha de log com vários campos, um nome de pool a partir de um cabeçalho. O (Tool Command Language) não tem operadores de string para isso. Ele monta os valores enquanto divide um comando em palavras, e as regras para isso são curtas. Os iRules rodam o Tcl 8.4.6 (F5 K6091), cuja página de manual Tcl as define.

Três jeitos de escrever uma palavra

  • Aspas duplas substituem: $name vira o valor da variável, [comando] vira o resultado do comando, e as sequências com barra invertida são trocadas. Depois as aspas somem, e o resultado é uma palavra só, com espaços e tudo.
  • Chaves não substituem nada. {$a $b} são exatamente os seis caracteres $a $b.
  • Colchetes rodam um comando e põem o resultado no lugar: [string tolower [HTTP::host]].

O manual acrescenta a regra que resolve a maioria das discussões: cada caractere é processado exatamente uma vez enquanto as palavras são montadas. Um valor que por acaso contém $ ou [ não é substituído de novo pelas regras das palavras. Nesta sessão do tclsh, cada comando vem depois do prompt % e o resultado fica na linha seguinte:

% set a "foo"
foo
% set b "bar"
bar
% set c "$a$b"
foobar
% set d "$a $b"
foo bar
% set e {$a $b}
$a $b

Onde termina o nome de uma variável

$name pega a sequência mais longa de letras, dígitos e sublinhados, então "$host_pool" procura uma variável chamada host_pool, e falha com "can't read host_pool: no such variable" se ela não existir. As chaves marcam o fim do nome: "${host}_pool" é o valor de host seguido de _pool. O iRules Style Guide do DevCentral pede a forma com chaves dentro das expressões pelo mesmo motivo.

append, concat e lappend

Três comandos montam valores mais longos, e cada um trata os espaços de um jeito:

set x " a "
# O texto é acrescentado exatamente como veio: x passa a valer " a  b"
append x " b"
# Cada argumento é aparado e depois unido com um espaço: y vale "a b"
set y [concat " a " " b"]
set l {}
# Cada argumento vira um elemento da lista: l vale {{one two} three}
lappend l "one two" three

append é para texto, e o manual observa que é o jeito eficiente de fazer um valor longo crescer. concat tira o espaço em branco das duas pontas de cada argumento, descarta os vazios e junta o que sobra com espaços simples. lappend acrescenta cada valor como um elemento de lista, pondo-o entre chaves se ele contiver espaços.

Um valor com espaços é uma lista

As listas do Tcl são strings divididas nos espaços em branco (com chaves e aspas para agrupar). Então foreach item "red green blue" roda três vezes, uma por palavra. Isso é prático para valores que você montou como listas e surpreendente para valores que só contêm espaços, como um cabeçalho. Monte listas com list e lappend, mantenha texto livre fora do foreach, e use split com um separador explícito quando um valor for delimitado por outra coisa:

foreach part [split [HTTP::path] "/"] {
    log local0. "segment: $part"
}

incr também lê octal

incr exige um inteiro e o lê do mesmo jeito que o expr, então uma variável com 010 é oito em octal e o incr a transforma em 9. Contadores e códigos com zero à esquerda merecem ser convertidos com scan $v %d v antes de serem usados como números.

Dupla substituição

Alguns comandos avaliam os argumentos de novo: o expr, e as condições de if e while, interpretam o argumento como expressão e fazem uma segunda substituição dentro dele. Se esse argumento não estiver entre chaves, um valor vindo da requisição participa da segunda rodada:

set count [HTTP::header value X-Count]
# Nunca faça isto: sem chaves, o valor do cabeçalho passa por uma segunda rodada de substituição
set next [expr $count + 1]
# Com chaves: ${count} é sempre só um valor
set next [expr { ${count} + 1 }]

Se um cliente mandar um cabeçalho X-Count contendo [...], a forma sem chaves roda o texto entre colchetes como comando durante a segunda substituição. O K15650046 da F5 descreve isso como uma exposição causada por prática de código, não como uma vulnerabilidade do produto, e o K57410758 documenta o alerta do validador "use curly braces to avoid double substitution". A correção é a mesma nos dois: coloque as expressões entre chaves e ponha -- antes de um valor passado para switch, para que um valor começando com hífen não seja lido como opção.

Experimente

O executor passo a passo roda um trecho um comando por vez e mostra as palavras que cada comando de fato recebeu, então as aspas, append contra concat e a dupla substituição ficam visíveis. O verificador de estilo aponta expressões sem chaves e a falta de -- nos seus próprios iRules.

Fontes

As regras das palavras e os três comandos de montagem estão documentados nas páginas do manual do Tcl 8.4 abaixo; o enquadramento de segurança e o alerta do validador são da F5.