A requisição condicional, ou conditional request em inglês, é um importante recurso do protocolo HTTP, pois permite que o cliente manipule a resposta que o servidor irá enviar. Isso significa economia de tempo, praticidade e agilidade nas comunicações HTTP. E quando esse processo é feito no edge, há uma melhoria significativa do ponto de vista da arquitetura e, consequentemente, um aumento na escalabilidade dos serviços e na receita das empresas.
Requisições condicionais HTTP
Fazer os processos ficarem mais práticos e melhorar a performance dos produtos são duas das exigências para quem trabalha com tecnologia, tanto para quem é desenvolvedor quanto para quem é proprietário. E isso não seria diferente quando se trata de tentar otimizar a base da comunicação entre redes, o protocolo HTTP. E uma das formas de otimizar o processo de comunicação é utilizar requisições condicionais.
Com as requisições condicionais é possível dizer ao servidor o que se deseja obter na resposta de uma comunicação HTTP – o que significa economia de tempo e de banda, praticidade e agilidade, tanto do lado do cliente quanto do lado do servidor.
As requisições condicionais são, portanto, um pouco diferentes das requisições comuns, porque elas operam de acordo com o valor que estiver especificado em determinados headers, ou seja, esse valor define a ação que será feita na requisição.
Por que fazer uma requisição condicional?
Há vários motivos, mas alguns deles são: validar o conteúdo de um cache, verificar a integridade de um documento, retomar um download ou evitar a perda de atualizações ao enviar ou modificar um documento no servidor.
O que acontece em uma requisição condicional?
O que é acontece é que o resultado de uma conexão feita com esse tipo de requisição pode ser alterado ao se comparar os recursos que estão sendo verificados com o valor de um validador.
Como o comportamento desejado pelo cliente é atendido?
O comportamento que o cliente espera do servidor é obtido de acordo com o método e o header (cabeçalho) utilizado na requisição.
Antes de entendermos como funciona uma requisição condicional, vamos ver do que se tratam alguns dos elementos que fazem parte desse processo.
Headers
É nos headers que se define a pré-condição de um requisição e o resultado dela pode ser diferente, o que vai depender da validação ou não dessa pré-condição. Os headers dão, então, diretivas para esse processo da comunicação, havendo aqueles que são usados para verificar o estado de atualização do cache, como o Cache-Control
e o Expires
, e aqueles que especificam condições prévias para o uso de cache ou para a revalidação do armazenamento, como é o caso dos headers Last-Modified
e ETag
.
Pré-condições
Sobre as diretivas pré-condicionais, elas podem atribuir condições somente para o servidor, para o estado geral do recurso no destino ou para um grupo de recursos. Essas condições prévias são dadas pelas requisições condicionais, que têm como base os validadores, metadados utilizados pelo cliente e pelo servidor para fazer a verificação do que está sendo pedido.
O processo de validação acontece da seguinte forma: o cliente envia os metadados para o servidor e o servidor verifica se o que foi requisitado pelo cliente é válido ou não. Se a validação for inconsistente e o servidor não for capaz de selecionar a representação apropriada, a pré-condição é avaliada como falsa, sem que isso, porém, acarrete em nenhum dano para a conexão.
As condições prévias tomam como base o estado geral em que se encontra o recurso de destino, isto é, o seu valor atual, ou no estado da representação obtida anteriormente, no valor prévio. Além disso, um recurso pode ter não somente uma, mas várias representações atuais, cada uma possuidora de um estado próprio.
Recursos
Nós vimos que o termo recurso é recorrente e é um elemento importante no processo das requisições condicionais, mas o que ele é exatamente? Primeiramente, é importante saber que o recurso é o alvo de requisição HTTP condicional – ou seja, é o recurso que é o objeto a ser buscado e que vai dar as informações que esse tipo de requisição quer.
Além disso, um recurso não tem uma natureza específica – pode ser uma imagem, um documento eletrônico, gráficos, uma fonte de informação ou qualquer coisa que você queira buscar na internet – e o seu principal propósito é ser utilizado pela requisição como uma forma de interface, um elemento para permitir a interação com representações do recurso através de uma rede. Assim, o termo recurso é usado em um sentido geral para tudo o que pode ser identificado por um URI.
Para identificar um recurso, a requisição HTTP utiliza um URI (Uniform Resource Identifier, ou Identificador Uniforme de Recursos em português), uma sequência de caracteres – uma string – que nomeia ou identifica um recurso. E há dois tipos de URI: um deles pode ser classificado como um localizador (URL, sigla para Uniform Resource Locator) e o outro como um nome (URN, Uniform Resource Name), ou ainda como ambos. Simplificando, o URL é o endereço de um recurso, enquanto o URN define a identidade dele, é um nome único e que nunca muda.
Exemplos de URIs:
Quando o cliente faz uma requisição, ele envia o URI de destino, e quando o servidor recebe essa requisição, ele refaz uma requisição URI que atenda efetivamente o recurso de destino.
Representações
Como mencionamos anteriormente, uma uma requisição condicional baseia-se no estado da representação de um recurso. Mas o que é uma representação então? Uma representação é a informação utilizada para se verificar o estado de um determinado recurso, podendo ser o atual ou o passado, em um formato que pode ser prontamente comunicado por meio do protocolo, e que consiste em um conjunto de metadados de representação e um fluxo potencialmente ilimitado de dados de representação.
Nesse processo, um servidor de origem pode receber ou ser capaz de gerar múltiplas representações, que refletem o estado atual de um recurso. Nesses casos, o servidor de origem utiliza um algoritmo para selecionar uma dessas representações como mais aplicável a uma determinada requisição, geralmente com base no conteúdo da negociação da requisição condicional. A representação selecionada é utilizada para fornecer dados e metadados para avaliar requisições condicionais.
Validadores: verificando o recurso
Para verificar se o recurso que está armazenado em um servidor está de acordo com a versão específica que o cliente quer, os headers de requisições condicionais precisam fazer essa verificação. Para que isso aconteça, a requisição condicional indica a versão do recurso por meio de um valor – também conhecido como validador.
Existem dois tipos de validadores (ou metadados) para verificar o estado do recurso e testar as pré-condições: datas de modificação e entity tag opaca. Mais especificamente, é o header Last Modified que indica a data e a hora da modificação do recurso, e é o header ETag, também chamada de entity tag opaca, que representa a versão do recurso.
Header Last-Modified
O header Last-Modified é enviado pelo servidor de origem e indica, por meio de um timestamp (ou carimbo de tempo em português) a data e a hora em que identificou que o recurso selecionado foi modificado pela última vez. Isso permite que um destinatário faça uma avaliação precisa do tempo de modificação da representação, especialmente se a representação mudar próximo ao momento em que a resposta é gerada. Se não houver nenhuma modificação, o servidor envia uma resposta 304 Not Modified
ao navegador e é utilizada a cópia contida no cache.
Veja um exemplo de header Last-Modified
:
Header ETag
O header ETag (abreviação de entity tag, ou tag de entidade em português) em uma resposta apresenta a ETag atual da representação selecionada na requisição. Uma ETag é um validador opaco – isto é, validadores em um formato confidencial que normalmente contém algum identificador para informações no armazenamento permanente de um servidor – para diferenciar as diversas representações de um mesmo recurso.
Uma ETag representa um recurso solicitado e é uma sequência de caracteres ASCII dentro de aspas, como em "095hq78954bc-la63"
. Caso se queira indicar que é uma validação fraca (que não são uma representação byte-to-byte do recurso, mas semântica), deve-se inserir o prefixo W/ antes dos caracteres, como em W/"08bn21"
.
Tipos de validação
A validação é o retorno de uma requisição pré-condicional, ou seja, é a forma de saber se, para o servidor, o que foi requisitado pelo cliente é válido ou não. O processo de verificação das versões de um mesmo recurso pode ocorrer de duas formas:
Validação forte
A validação forte verifica se a identidade dos recursos comparados é igual. Ocorre quando o cliente precisa da identidade do recurso (byte-to-byte identity).
Validação fraca
A validação fraca verifica se o conteúdo dos recursos comparados é igual. Ocorre quando o cliente só precisa determinar se dois recursos possuem o mesmo conteúdo.
Como padrão, o protocolo HTTP usa validação forte e especifica quando é necessário utilizar a validação fraca.
Headers de requisições condicionais
Os headers utilizados para definir as condições em uma requisição são:
If-Match
É utilizado para verificar se a ETag dos recursos, do cliente e do servidor de origem, são iguais. É normalmente utilizado com o método GET. O cliente que usa essa pré-condição quer evitar que o método seja aplicado se houver qualquer alteração nos dados de representação.
Tipos de headers If-Match
:
Observação: Quando se quer representar qualquer recurso, utiliza-se um asterisco como um valor especial.
Exemplo de um requisição condicional com header If-Match
:
If-None-Match
Ao contrário do If-Match, o header If-None-Match é utilizado pelo cliente quando ele quer verificar se houve alterações no recurso. É utilizado normalmente com método GET quando o objetivo é habilitar atualizações de informações que estão em cache de forma eficiente, em que a transmissão é minimamente sobrecarregada.
Por exemplo, quando um cliente deseja atualizar um ou mais recursos armazenados que possuem ETags, ele deve fazer uma requisição com método GET e utilizar um header If-None-Match que contém uma lista dessas ETags. Isso permite que os servidores destinatários enviem um resposta 304 (que significa Não Modificado) para indicar quando um daqueles recursos armazenados correspondem à representação selecionada.
Exemplo de um requisição condicional com header If-None-Match
:
If-Modified-Since
É utilizado com os métodos GET ou HEAD para permitir atualizações de uma representação que está no cache, mas que não tem uma ETag. Outra função desse header é para limitar o escopo de uma web traversal para recursos que foram recentemente alterados. Assim, o header If-Modified-Since orienta o servidor a enviar de volta o recurso solicitado apenas se ele foi modificado pela última vez após a data fornecida. Se o recurso não foi modificado desde então, a resposta será 304 sem corpo. Se o recurso foi modificado, a resposta será 200 OK com a nova versão do recurso.
Exemplo de um requisição condicional com header If-Modified-Since
:
If-Unmodified-Since
É normalmente utilizado quando se quer que o servidor envie de volta o recurso solicitado apenas se ele tiver sido modificado pela última vez antes ou na mesma data fornecida. O header If-Unmodified-Since é normalmente com métodos de mudança de estado (como POST, PUT e DELETE) para evitar atualizações perdidas, isto é, substituições acidentais quando vários clientes atuam ao mesmo tempo em um recurso que não fornece ETags com suas representações. Também pode ser usado para cancelar um pedido se a representação selecionada não corresponder a um já armazenado (ou parcialmente armazenado) de uma requisição anterior.
Exemplo de um requisição condicional com header If-Unmodified-Since
:
If-Range
É utilizado quando um cliente tem uma cópia parcial de uma representação e quer ter uma cópia atualizada de toda a representação. É normalmente utilizado com o método GET e pode ser usado com os headers If-Unmodified-Since e If-Match, somente com um deles ou ambos. Por exemplo, em condições normais, se a representação foi modificada, a pré-condição falha e o cliente teria que fazer outra requisição pedindo toda a representação e não somente o trecho solicitado. Com o If-Range, se a representação não foi alterada, o servidor envia os trechos solicitados no range (no intervalo) desejado, mas se a representação foi alterada, o servidor envia a representação completa. Assim, o header If-Range permite que um cliente pule para a segunda requisição sem ter que fazer uma nova requisição.
Exemplo de um requisição condicional com header If-Range
:
Qual é a melhor forma de garantir controle e rapidez nas requisições HTTP?
Aqui na Azion, é possível que o cliente determine a resposta que ele quer receber do servidor de maneira rápida, prática e ágil: com o nosso Edge Functions.
Usando o nosso Edge Functions, você pode criar regras personalizadas de requisições e respostas ou escolher entre funções pré-construídas, como testes A/B, tokens de segurança ou massive redirect. Quando acionada, a função é executada em milissegundos no edge node mais próximo do usuário final.
Você pode usar funções para manipular o HTTP nas seguintes fases da requisição e da resposta:
- assim que a requisição de um usuário for recebida no edge node;
- antes que o edge node encaminhe a requisição à origem;
- assim que o edge node receber a resposta da origem;
- antes que o edge node encaminhe a resposta para o usuário.
Além disso, ao usar Edge Functions escritas em Lua e JavaScript na Plataforma de Edge da Azion, você poderá criar uma variedade de soluções, como:
- inspecionar cookies para reescrever URLs para diferentes versões de seu site em experimentos A/B Test;
- enviar recursos diferentes para seus usuários baseado em user-agent header, que contém informação sobre o dispositivo que enviou a requisição. Por exemplo, você pode enviar imagens em diferentes resoluções baseadas no dispositivo dos usuários;
- inspecionar headers e authorized tokens, inserindo um header e permitindo access control antes de encaminhar a requisição à origem;
- adicionar, remover ou modificar cabeçalhos e reescrita de path, direcionando usuários para diferentes recursos em cache;
- gerar respostas para tarefas como redirecionar usuários não autenticados para páginas de login, ou criar e entregar webpages estáticas geradas diretamente no edge.
Como o Edge Functions roda na nossa rede de edge global, ele escala automaticamente sem necessidade de gestão ou recursos de provisão. Em vez disso, você paga apenas quando o código é executado, eliminando custos antecipados e evitando recursos desperdiçados de servidores superprovisionados.
Ou seja, com Edge Functions você garante rapidez, praticidade e agilidade, tanto para o seu desenvolvedor quanto para o seu cliente. E o resultado de todos esses benefícios é a maximização da sua receita.
Para saber mais sobre esse e outros produtos, fale aqui com o nosso time de vendas.