Desenvolvimento Web, Web development

Programming inception (or understanding Node.js event loop)

    One of the main problems today in web development is the cost of I/O. I mean, everybody is talking about it. Every web app nowadays, even the smallest ones like blogs or personal pages follow the internet trend of being interconnected. So they always present a link to connect to Facebook, Twitter, LinkedIn and whatnot. Also many of them connects to Databases and clouds. Either way, they use a lot of I/O. And that’s where lies the problem.

But I don’t want to play with my threads anymore ma!

    Imagine a web app that receives millions of requests per minute and communicates with databases (wait for queries and updates) and other services around the web. Now take a look at this table to see why I/O is such a big problem for current programming technologies:

io-cost

    Notice that most Databases nowadays writes on disk, which means it’s slow. When the application needs to communicate with another service through network the waste is even bigger.
How to deal with many requests at a time? Well, today there are some fundamental ways to deal with concurrency in web servers, namely thread-per-request and process-per-request (using Unix’s fork() ).

Thread-per-request: Each request makes the server start a new thread to deal with it. Threads may not consume memory like processes, of course. But, needless to say it eats a lot of memory from the machine anyway. Take a look at this graph to see the difference between a server that uses this model (Apache with mpm_event) against one that uses a single-thread approach (Nginx):

nginx-apache-memory

Ooh! And don’t forget that when working with threads, things can get “real messy real quickly”.

Process-per-request: Each request starts a new process in the server. A process is heavier on the machine than a thread: it consumes even more memory and the overhead to start a process is higher. But some servers found a workaround: Unix forking. With forking we can create a new copy of the main process and instead of allocating a whole new memory for the child process, we can “share” the same memory of the main parent process. Servers like Passenger and Unicorn use this feature. Still, with hundreds, even thousands of connections, fork will be called until we have thousands of processes, which is not the way to go.

Enter Node.js

    The powerful concept behind Node is its single-threaded event-loop that leverages asynchronous calls for doing many things, even (take a guess) I/O! For instance in Java, while executing some code, if you send a query to the database, the code will have to wait for the database to complete it. No, not Node.js. It will execute the lines of code after the database command and then go back to it when the result is ready. It’s really only one thread running, which means if you put a “sleep” in your code, it will block the server for the amount of time stipulated on the sleep function. The idea is pretty cool, but let’s see some example of it. Imagine you have an array of objects you need to save on database and afterwards calculate some stats about these saved records:

//P.S: Really tried to display this code as a snippet.
//It just was not WordPress' day =/

// Loop through some items
items.forEach(function(item){
//Now write each of this items to the disk (I/O here)
item.saveAsync();
});
// This function is meant to be called once all the async
// calls above are done, but we don't know if/when they are,
// and therein lies the problem with this approach
calculateStatsAfterSaving();

It’s not about Node.js, it’s about the Reactor Pattern

    To better understand how the Reactor pattern works, let me help you with an analogy. Imagine you have a coffee shop in one of the most crowded corners in NYC. People are ordering all the time and you serve the tables yourself. Sometimes the order is something previously prepared like a milk or a water. But once in a while some smarty pants will order a spaghetti. If you stop to make the spaghetti, people will be waiting a while since you are the only who serves the tables. But you’re awesome and decides to delegate the task of preparing meals to your butlers: Jeeves and Alfred. You tell them: “Jeeves, you make the pasta and Alfred makes the sauce. When you guys finish, put it on the counter and notify me.” This way it becomes pretty simple for you to handle more orders quickly. If more complicated orders like this shows up, well, just imagine Jeeves and Alfred are so competent that they can receive concurrent requests, no problem. That’s the idea behind the Reactor pattern.
     The single-thread keeps receiving requests all the time and responding, but it cannot block to do more “complex” ones, like blocking I/O (database queries, connect to Facebook, write to a file and so on) otherwise all the other requests will have to wait to be served. Internally, this single-thread delegates the work to fibers, which can run in parallel and avoid blocking the thread by doing I/O. Think of these fibers as threads. Except they’re light-weight and decide when they will pause or resume, not the kernel. See the picture to understand better.

node_loop

    Another detail important to mention is that Node.js expects to return quickly to the client. So if a callback function works with a very long computation, the single-thread will be busy with it, which can stop the event-loop completely. The advantage comes when you have a lot of blocking I/O. Disaster comes when you have CPU-intensive work.

Is Node.js really “the king’s new clothes” ?

    Callbacks can be hard to understand. For all of us used to program in a synchronous way, async can get real messy. Starting with the code. Imagine that: for every function, pass another function with the commands to execute when the first function is finished. What if the callback has a callback itself ? See the point? Node.js is generally not easy to understand, like, say, Ruby or Java. The strategy changes. The way of thinking changes. It can be a lot of different things at once. But no worries: there are solutions. Just to mention some of them, node-async and node-Fibers try to solve the problem with many nested callbacks and exception handling. Going back to the problem some lines above, node-async has some functions that help us to write more readable code:

//P.S: Really tried to display this code as a snippet.
//It just was not WordPress' day =/
async = require("async");
// 1st is the array of items
async.each(items,
// 2nd the function that each item is passed into
function(item, callback){
// Call an asynchronous function
item.saveAsync (function (){
// When done, alert via callback
callback();
});
},
// 3rd parameter is the function call when everything is done
function(err){
// All done
calculateStatsAfterSaving();
}
);

    Yeah, yeah, I agree with you that either way synchronous code beats asynchronous code. So remember that before choosing Node.js for your next project. Although, threads are a pain to work with. You need to be aware of what classes/libraries are thread-safe, be careful to avoid deadlocks and memory leaks, not to mention the whole parafernalia of options to use: mutexes, threads, semaphores, locks, synchronized blocks etc. On the other hand, the Node.js concurrency model is very easy to learn! Although its code will not always present an elegant approach. So either way there will be trouble. The thing is: concurrency is not a simple problem. Therefore solving this complex issue can get messy, one way or another.

Anúncios
Padrão
Desenvolvimento Web, Design

Exceções: “checked ou unchecked – Eis a questão!”

Você é designado para procurar um bug num sistema super importante onde você trabalha. Ao ler o código, você percebe um trecho assim:


try {
    controladorUsuario.salvar(diretor);
 }
 catch(Exception e) {}

E agora? Largo tudo, ligo pra minha mãe dizendo que a amo e me jogo da varanda? Calma, ainda não. Você, como bom programador deve consertar o código primeiro. Pensando em como as exceções são tratadas de forma desordenada nos sistemas legados que eu já trabalhei/já fiz, resolvi pesquisar mais sobre a forma correta de se tratar erros/exceções nos nossos  sistema. Há tempos já vinha pensando sobre o assunto e resolvi escrever no blog agora, como uma forma de difundir essas técnicas. Neste post, eu dou algumas dicas e conceitos sobre exceções que pude coletar em alguns livros de peso na área de desenvolvimento de Software. Aí vão elas:

Checked ou unchecked?

Primeiramente, vamos aprender/rever o conceito de exceções checked (checadas) e unchecked (não checadas) que servirão de base para alguns argumentos mais a frente neste post.

Checked:  São exceções que obrigatoriamente devem ser tratadas de alguma forma. Ou seja, o programador é obrigado a fazer algo com ela – tratar com um bloco try-catch (e suas combinações) ou relançar com throws na assinatura do método. Note que esse tipo de exceção não existe na maioria das linguagens. Por exemplo, Ruby e C# não possuem o conceito de checked exceptions.

Unchecked: São exceções que o programador não é obrigado a tratar.  Elas podem ser tratadas, mas o ponto é que isto não é obrigatório! Caso não sejam tratadas,  são automaticamente relançadas para a camada acima até achar alguém em alguma camada que a trate – em última instância, o erro é lançado na tela ou pior, estoura silenciosamente e pára. Em Java, essas exceções são subclasses de RuntimeException (exceções de tempo de execução).

Ok então. Mas, qual das duas usar? “Se a maioria das linguagens não tem exceções checadas, é porque não são realmente muito importantes” você talvez pense.  Esse é um assunto que dá muito pano pra manga e vários escritores técnicos bem reconhecidos já opinaram sobre isso. Alguns contra, outros a favor. Mas, gosto de ver pelo seguinte ponto de vista: Isso depende muito do que você quer. Pense por um momento no conceito de capturar exceções. É uma chance que o programador tem de se recuperar de uma falha na execução do programa. Quando você lança uma exceção checada, você está na verdade deixando bem explícito pra quem for cliente do seu código que ele tem sim uma chance de se recuperar da falha que ocorreu.  Por exemplo, ao tentar sacar R$50,00 numa conta com R$10,00 de saldo, você deve lançar algo do tipo SaldoInsuficienteException – checada. Dessa forma, não tem perigo de o programador deixar essa falha vazar para a camada acima e você o dá a chance de redirecionar o usuário para uma página de erro mais amigável. Agora, caso haja um incêndio no prédio onde o seu banco de dados está e nessa mesma hora o usuário tentou persistir um novo saldo, o que fazer? Bom, não tem como se recuperar, tem? Normalmente não.  Você não quer obrigar o programador a tratar um erro que não tem mais o que fazer, quer? Então lance uma exceção unchecked. São justamente essas 2 perguntas que você deve se fazer. “Existe uma chance de se recuperar da falha? ” e/ou “Quero dar uma chance de se recuperar dessa falha?”. É por isso que exceções como a famosa NullPointerException são não-checadas. Mesmo que você trate dentro do bloco catch para que o procedimento seja repetido ou algo assim, não vai mudar o fato que você está tentando acessar uma referência nula. Simplesmente ocorreu um erro de programação.

Cada um no seu quadrado

É importante respeitar o nível de abstração dos seus componentes. Tipo,  você já deve ter visto algo como (assuma que essa essa exceção é checked):


class TelaSalvarConta {

...

    try {

        conta.salvar();

   }

    catch(ConcurrentSerializationException exc) {...}

Aqui temos uma classe de alto nível manipulando um objeto de domínio e lançando uma exceção de baixíssimo nível. Isso é uma exposição indevida de seus objetos e significa que você quebrou o encapsulamento violentamente. Essa exceção deve ter vindo de há muito tempo sendo relançada pela própria API  até chegar a camada mais alta do sistema. Isso significa que a assinatura do método salvar() da classe Conta tem uma exceção (throws) com esse mesmo nome: ConcurrentSerializationException. Absurdo, não? Isso jamais devia ter acontecido. Em algum ponto, algum programador da API deveria ter feito:


catch(ConcurrentSerializationException exc) {

    throw new ObjectAlreadyInTransactionException(exc);

}

Sendo ObjectAlreadyInTransactionException uma exceção não-checada, não seria necessário o programador declarar uma exceção na assinatura do método, além disso, se ele desejar tratar essa exceção mesmo sem ser obrigado a isso, ele tem uma exceção mais próxima da camada de negócios.

Em resumo, trate cada exceção de acordo com a camada do sistema a qual ela pertence.

Evite sujeiras

E por sujeira eu quero dizer exceções checadas. Por exemplo, você gosta da forma como a API IO de Java trata as exceções? Todas elas são checadas! Muitas vezes eu quero fazer algum teste bem bobo no qual eu preciso ler um arquivo ou um properties e adivinha… sim, preciso de um try-catch pra um erro que não há recuperação.  Lembre-se que o tratamento de exceções por meio de try-catch impossibilita melhoria de performance no seu código pela JVM. Isso é muito importante. Sem contar que deixa seu código sujo e “fedido”. Ou então você precisa de mais uma exceção declarada na assinatura do método, o que quebra o encapsulamento (dando dicas pra o cliente do código o que aquele método faz) e dificulta a flexibilidade da API impondo regras a quem quiser herdar aquele método.  Faça o máximo para sempre lançar exceções não checadas, exceto (como já falado) se existir uma forma de se recuperar do erro e você quiser realmente obrigar o cliente a fazer isso. Um aspecto em que você sai ganhando é que em exceções não checadas você pode simplesmente deixar que elas cheguem a parte mais “alta” da aplicação e tratá-las de forma centralizada. Por exemplo, num ambiente web com filtros.  Senão utilizando algum framework de AOP.

Os erros são amigos

É de partir o coração ver algumas pessoas fazerem o seguinte ao tratar erros:


catch(Exception e) {}

ou


catch(Exception e) {

throw new Exception("Erro!");

}

Esses dois exemplos mostram código terrível e jamais devem ser usados. No primeiro o programador simplesmente ignora o erro e este se perde no limbo. Se você for forçado a tratar o erro no seu código, você deve tentar ao menos logar a exceção. Jamais ignore-a completamente como no primeiro caso.  Uma boa abordagem é tentar relançar a exceção em forma de RuntimeException, dando um throw new RuntimeException(e) – ou qualquer outra exceção do tipo RuntimeException que se adeque semanticamente ao erro. Dessa forma, você pára de impor que as camadas acima tenham de tratar ou declarar a exceção. Repito, jamais ignore exceções no catch.

O segundo exemplo é melhor que o primeiro mas ainda assim não é o ideal. Se um outro desenvolvedor está testando sua aplicação e se depara com uma mensagem de exceção dessa? Tipo… o que aconteceu que deu erro? Pois é, você pode ser mais específico e criar uma exceção mais específica para o erro, assim também como uma mensagem mais explicativa. Lembre sempre disso: “Programe sempre como se o cara que fosse  manter seu código fosse um psicopata violento que sabe onde você mora”.

Conclusão

Mostrei aqui algumas dicas de boas práticas sobre como lidar com exceções. Não é nada muito difícil, realmente. Apenas exige esforço constante do desenvolvedor para que o código seja facilmente compreendido. Pense sempre se deve realmente usar uma exceção checada. Na maioria dos casos uma exceção não checada dá conta do recado de forma mais sucinta. Não misture as abstrações das exceções. Exceções de baixo nível em baixo nível, as de alto nível em alto nível. E sempre que for tratar uma exceção, verifique se está tratando no nível correto de abstração. Se estiver, utilize mensagens explicativas sobre o erro ocorrido. E caso encontre aberrações (como a mencionada no começo do post), não se jogue da varanda. Respire fundo e comece a aplicar o que aprendeu.

Padrão
Desenvolvimento Web, Design, Design Patterns, Experiências

MVC não é sobre camadas!

– Como você separa as camadas do seu sistema?

– Ah… Sistema Web eu uso MVC mesmo.

Apesar de pouco tempo de carreira, já ouvi respostas como essas de tuia. Desenvolvedores graduados, mestrados, doutorados, verdadeiros alquimistas (note o tom irônico) argumentando que MVC é um padrão arquitetural onde você divide seu sistema em camadas, sendo elas: Model, View e Controller.  E, logo, se você utiliza MVC não há espaço para as camadas mais tradicionais: Apresentação, Negócios e Persistência. A verdade é que esse argumento é tão infundado quanto errôneo.  Apesar de achar essa confusão um tanto quanto natural (afinal eu já fiz essa confusão também), entendo porque ela existe. A sigla MVC tem sido “passada” para os aprendizes de desenvolvimento de software como… justamente, um sigla. Não é explicada a origem do padrão, nem a definição formal do que é, nem tampouco onde ela deve ser aplicada.  Enfim, não vou nem me delongar muito sobre o porquê da confusão, mas antes, pretendo esclarecê-la. Primeiro, vamos ver um pouco sobre a origem do MVC e sua definição.

O que danado é MVC ?


MVC é um padrão arquitetural originalmente criado em Smalltalk no fim da década de 70 que teve por objetivo inicial trazer simplicidade ao manipular interfaces  gráficas.  O que simbolizam essas letras?

M: Model. É o modelo de negócio da sua aplicação. Suas classes básicas, seus Services, o que quer que você utilize e seja particular do seu domínio. Seria o core da sua aplicação. Classes como Conta, Banco, classes de repositórios (não confundir com DAOs), classes de serviço, as exceções específicas do seu domínio e  classes utilitárias do domínio se encaixam no ‘M’ de Model, ou modelo.

V: View. É a parte responsável por mostrar visual para o usuário. O objetivo da View é mostrar o estado atual do seu modelo. É papel da View saber quando o modelo foi mudado (o modelo também pode notificar a view da mudança, apesar de eu não gostar desta versão), de forma que ela possa refletir isso de forma plena. Todos os componentes gráficos estão incluídos nessa categoria. E por componente eu não quero dizer classe, por exemplo, JSPs são views. Também arquivos html, javascript, swf, além de classes que tratam de interfaces gráficas – Actions da vida em ambiente web e TelaCadastro ou TelaLogin em ambientes Desktop etc.

C: Controller. É a parte mais inteligente desse padrão. É responsável por tomar a decisão “para quem devo direcionar, dado que o usuário propagou esse estímulo”. Por exemplo, uma classe ‘Controller’  recebe uma requisição do usuário e, baseada nessa requisição, decide qual view será mostrada para o usuário. Em ambientes Web essa classe faz o papel de “Front Controller”, que já é outro padrão arquitetural.

Já vi muito, mas muito mesmo, ocorrer a confusão entre Controllers e Controladores. Controller é completamente diferente de Controladores de objetos de domínio. Controladores são parte do domínio: realizam serviços utilizando classes básicas e suas dependências.  São Model. Controllers não devem fazer parte do seu domínio. Alguns colocam como View e em alguns casos utilizam como parte da camada de aplicação. Certa vez,  decidimos não utilizar esse nome confuso, mas o sufixo “Service” para nomear as classes que realizavam operações nas classes básicas. Mais tarde, um dos líderes técnicos disse que tinha uma observação a fazer. Ele incentivou a equipe a prezar por código de qualidade, porque no nosso sistema estávamos manipulando objetos de negócio diretamente na “Camada de View” sem passar pela “Camada do Controlador”. Um verdadeiro estupro. Claro que não o critiquei publicamente e não expus o comentário cheio de infelicidades, mas me limitei a esclarecer que os controladores estavam sim ali, apenas com sufixos diferentes. Também expliquei a diferença entre Controller e Controladores, e que aquilo estava causando confusão, de forma que todos ficaram satisfeitos.

MVC são apenas alguns patterns!

MVC na verdade tem por objetivo definir interações entre componentes do sistema e não agrupar componentes da aplicação, como muitos o entendem. Para entender isso, note que  MVC realmente é sobre design patterns (padrões de projeto). Mais especificamente – Strategy e Observer. Mais especificamente são 2 interações entre componentes:

1 – Quando o Model (Observable) muda seu estado, a View (um Observer) é notificada.

2 – Quando a View propaga alguma ação, como o Controller é o Strategy da View, a View delega ao Controller a decisão de “para que parte do Model notificar a mudança”. Esse Controller, por ser um Strategy, pode ser modificado em tempo de execução.

 

Interações são descritas por meio de Patterns.

Head First Design Patterns (Freeman & Freeman)

Estes são os 2 princípios fortes por trás do MVC. Alguns ainda dizem que existe um Composite limitado ao Model, mas isso é se prender demais a versão original do padrão feita em SmallTalk. Enfim, note que estas interações não dizem NADA a respeito de camadas. São interações ora essa! Ou seja, não é necessário você ter um componente View pra estar utilizando MVC, apesar de eu nunca ter visto isso acontecer na prática. Mas sim, você pode utilizar esse mesmo modelo de interações entre componentes de inúmeras formas diferentes, usando View ou não.  Também não é necessário estar usando Web pra utilizar MVC. De fato, originalmente MVC surgiu pra aplicações Desktop… Enfim, você pode sim utilizar o padrão MVC num sistema Desktop tipo um Swing do Java ou na sua aplicação Android ou Flash! Lembre-se, MVC é sobre interações entre componentes! Repita pra você mesmo: MVC não é sobre camadas! É sobre interações!


Tá, mas e a divisão por camadas que a minha avó me ensinou?


Camadas são utilizadas nas aplicações modernas para separar responsabilidades do sistema. Componentes com responsabilidades iguais ou similares ficam na mesma camada. Como já dito, numa aplicação Enterprise clássica por exemplo, temos Apresentação (componentes de interface gráfica), Negócios (classes básicas, services, helpers etc.) e Persistência. Imagine que essas camadas são empilhadas de forma que as que lidam com infra-estrutura estão na parte de baixo da pilha e as que lidam com interação com usuário estão mais acima. Nesse caso, teríamos de cima pra baixo: Apresentação -> Negócio -> Persistência. O ideal é que as camadas se comuniquem sempre com as abaixo e jamais com as acima delas:

Camadas separando responsabilidades

Camadas separando responsabilidades

Repare bem que agora estou falando de separar responsabilidades. MVC me provê 2 interações chave,  e eu as aplico onde quiser. Já separação por camadas me provê onde colocar os componentes, e eu faço interações como quiser. Inclusive posso utilizar… MVC! Não, esses 2 padrões não são auto-excludentes! Posso sim usar ambos na minha aplicação. E muitas fazem isso.

Uma vez definidas as camadas (sendo da forma tradicional ou não), eu posso agora olhar para as classes que interagem entre si, aplicar os patterns mencionados e estarei usando MVC. Utilizando os 2 padrões, normalmente a camada de apresentação fica com todos os elementos da View, e a camada de Negócios fica com o Model. O Controller não é tão simples porque é uma classe bem inteligente e exige bastante controle. Se sua aplicação for Web você “ganha ele de graça” com o framework que estiver usando ou com Servlets. Caso precise implementar, acho preferível colocar na camada de apresentação para não gerar dependências desnecessárias.

Conclusão

Jamais confunda esses dois padrões. MVC diz respeito a interações entre componentes, não quer dizer que a aplicação tem que ser web nem mesmo que precise ter uma View. Camadas separam componentes da aplicação de acordo com responsabilidades. Ambos os padrões são muito úteis como ferramenta para qualquer desenvolvedor, mas é necessário ter perspicácia para ver as coisas além do óbvio, além do que é passado por aí. Não force o uso destes padrões (nem de algum outro). Mas use-os com moderação, onde os benefícios deles fiquem bem claros para você e para o seu cliente. Eu gosto muito deste post do Phillip Calçado sobre MVC e da explicação deste livro.  Qualquer dúvida, postem comentários. Ah! E usem filtro solar! =]

Padrão
Desenvolvimento Web

Filtro Anti-XSS em Java

Há um bom tempo, na empresa onde trabalho, enfrentei uma situação curiosa. Eu ainda estagiava e um colega (hoje na globo.com) me perguntou: “quão segura é a aplicação que você está desenvolvendo?”. Eu, muito ingênuo (bem, muito mais do que hoje), respondi que era possível encontrar bugs, mas nada catastrófico a ponto de pôr tudo a perder. Outro colega (talvez mais ingênuo) teve a audácia de dizer que o sistema sob sua responsabilidade era livre de erros de segurança de qualquer tipo. A questão voltou-se completamente contra o programador super-confiante. Todos começaram a pensar em formas de encontrar bugs catastróficos e logo, o primeiro colega, autor da questão inicial, encontrou a brecha. Em poucos minutos, ele mostrou como cadastrar um item num dos cadastros do sistema para que, quando alguém clicasse nesse item, o sistema direcionaria o usuário para o site do google (ele foi bonzinho, poderia direcionar para algo bem pior…). Não preciso mencionar que o responsável pelo sistema ficou sem fala… Logo após, ele mencionou como era fácil encontrar sites expostos a este tipo de ataque e mostrou uma meia dúzia deles. Nos próximos dias comecei a trocar umas idéias com alguns desenvolvedores sobre como defender-se de ataques XSS. Esse post mostra basicamente uma forma de defesa “server-side” contra esse tipo de ataque (mais especificamente em sistemas Java, mas obviamente, a idéia se aplica a muitas outras linguagens).

Primeiro, uma tentativa de definição do que é Cross-Site Scripting:

Cross-Site Scripting (XSS) é um tipo de vulnerabilidade que pode existir em aplicações Web, na qual o ataque começa no lado do cliente, “atravessa” o sistema e permite ao atacante obter informações não permitidas de outros usuários que acessam ou acessaram a aplicação. Isso é feito por meio de injeção de código malicioso dentro das páginas web acessadas pelos usuários.

Ataque XSS

Ataque XSS

Esses ataques normalmente são códigos javascript inseridos por meio de campos de texto da aplicação e é bem fácil de simular. Aqui você encontra como simular um ataque XSS. É bem simples mesmo. Da mesma forma, a solução que eu utilizei é bem simples, é server-side e aqui vai um passo-a-passo de como implementar isso:

1 – A idéia é construir uma classe para tratar a requisição (request) da forma como você deseja. No meu caso, precisávamos tratar requests com tags html, como por exemplo “<script>” e “<a href>”. Um método estático que recebe a String original e retorna uma String modificada (do jeito que você precisa fazer) deve dar conta do serviço. Para casos mais gerais – e acredito que tratar TODAS as tags do tipo “<*>” – aconselho usar expressões regulares.

– Exemplo de classe TratarTags:


public class TratarTags {

    /*Expressão representando todas as expressões com tags html. "?"
    significa não-gulosa(non-greedy).*/
    private static final String expressaoComTags = "<.*?>";

    public static String retirarTags(String html) {

        String retorno = html.replaceAll(expressaoComTags, "");
        return retorno;
    }
}

2 – Construir o filtro propriamente dito. É necessário que você implemente a interface javax.servlet.Filter que consiste dos métodos init, destroy e – chegaremos lá – doFilter. Dentro do init e do destroy não tem muito o que fazer. O Container Web (uso o Tomcat 6.0.14 no momento) é esperto o suficiente pra saber o que fazer com esses caras. Particularmente falando, coloquei um log pra sinalizar inicialização e destruição do filtro.

Dentro do doFilter() – e é agora que começa a ação – você deve pegar o objeto do tipo HttpServletRequest, decorá-lo dentro de um tipo que você criará (ver passo 3) e passá-lo adiante para o container. Daí em diante, o container decidirá se enviará o request embrulhado, ou seja, seu objeto “request” criado no passo 3, para o próximo filtro (se existir um) ou para o servlet em questão. Para passá-lo adiante é bem simples. Pegue o objeto FilterChain que foi passado por parâmetro e chame o método doFilter do mesmo passando o request embrulhado e o response (você também pode alterá-lo se quiser! =D).

3 – Para entender esse passo você precisa ter ao menos uma noção do que é um Decorator. Tá. É um padrão de projeto, eu sei. Quase todo mundo sabe. O que eu quero dizer é que você precisa ENTENDÊ-LO.

A má notícia é que você não pode alterar o request em si e passá-lo adiante. Por outro lado, ninguém nunca falou nada sobre passar um request “de mentirinha” para o container. É isso que você faz basicamente. Você cria um objeto wrapper, no caso um RequestWrapper, que faça tudo que um HTTPServletRequest normal faz. “Tenho que implementar a interface de HttpServletRequest então…” você pensa. Você até pode. Mas essa interface possui uma monstruosidade de métodos que, acredite em mim, você não vai querer implementar um a um. Por isso já existe na API Java Servlet uma classe chamada HttpServletRequestWrapper, a qual você só modifica os métodos que você quiser! É só estendê-la! Agora ficou fácil. Bom, pra tratar tags html eu sobrecarreguei todos os métodos getParameter* para pegar o valor do getParameter* original, aplicar a regra da minha classe TratarTags (passo 1) e retornar o valor modificado. Sempre que uma requisição chegar ao servlet e você quiser extrair um valor do request(um parâmetro por exemplo), esse valor já chegará SEM as tags. O que significa que o filtro deu conta do trabalho.

Exemplo: TratarTagsFilter:

public class TratarTagsFilter implements Filter {
    private MapeamentoUrlCampo mapeamento = null;

    public TratarTagsFilter() {
        this.mapeamento = MapeamentoUrlCampo.getMapeamento();
        this.mapeamento.addMapeamento("campanhaAction.do", "script");
     }

     private static final Log logging =
     LogFactory.getLog(TratarTagsFilter.class);

     public void destroy() {
         logging.info("Terminando de executar o filtro. Classe= " + this);
     }

     public void doFilter(ServletRequest request,
     ServletResponse response, FilterChain chain) throws IOException,
     ServletException {
         logging.info("Executando o filtro neste momento. Classe= " + this);
         RequestWrapper requestWrapper = new RequestWrapper(request);
         chain.doFilter(requestWrapper, response);
     }

         public void init(FilterConfig arg0) throws ServletException {
         logging.info("Iniciando o filtro neste momento. Classe= " + this);
     }
     /*** Classe Interna estática que adiciona uma nova
     funcionalidade aos métodos da Classe HttpServletRequest* */
     private static class RequestWrapper extends HttpServletRequestWrapper {

         public RequestWrapper(ServletRequest request) {
             super((HttpServletRequest)request);
         }

         @Override
         public String getParameter(String name) {
             String valor = super.getParameter(name);
             String urlAtual = this.getRequestURL().toString();
             MapeamentoUrlCampo mapeamento = MapeamentoUrlCampo.getMapeamento();
             if(valor != null && !mapeamento.permiteCampoHtml(urlAtual, name)){
                 String novoNome = TratarTags.retirarTags(valor);
                 return novoNome;
             }
             else {
                 return valor;
             }
         }
         @Override
         public Map getParameterMap() {
             Map<String, String> map = super.getParameterMap();
             Set<String> chaves = map.keySet();
             List<String> listaChaves = new ArrayList<String>(chaves);
             Map<String, String> mapRetorno = new TreeMap<String, String>();
             for(String chave : listaChaves) {
                 mapRetorno.put(chave, this.getParameter(chave));
             }
             return mapRetorno;
         }
         @Override
         public String[] getParameterValues(String name) {
             String[] valores = super.getParameterValues(name);
             String[] novosValores;
             String urlAtual = this.getRequestURL().toString();
             MapeamentoUrlCampo mapeamento = MapeamentoUrlCampo.getMapeamento();
             if(valores != null && !mapeamento.permiteCampoHtml(urlAtual, name)) {
                 novosValores = new String[valores.length];
                 for(int i = 0; i < valores.length; i++) {
                     novosValores[i] = TratarTags.retirarTags(valores[i]);
                 }
                 return novosValores;
             }
             else {
                 return valores;
             }

         }
    }

}

4 – Agora basta configurar o filtro construído no seu web.xml. Dentro do elemento web-app você cria
duas tags:

A primeira é:


<filter>
    <filter-name>TratarTagsFilter</filter-name>
    <filter-class>com.projeto.filtro.TratarTagsFilter</filter-class>
</filter>

Que você direciona para interceptar apenas um padrão específico de url dessa forma:


<filter-mapping>
    <filter-name>TratarTagsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Pronto, o filtro está pronto pra funcionar na sua aplicação. Não sei se existe outra estratégia de evitar ataques via XSS fora essa de retirar tags html arriscadas. Sei que essa funciona eficientemente, por isso decidi utilizá-la. No entanto, se algum leitor souber de outra abordagem interessante, ficaria muito curioso em ouvi-la.

Padrão