Introdução à Sintaxe e Semântica HTTP

Dalton Serey

Mensagens HTTP são escritas em texto simpes e seguem um formato geral, válido tanto para requisições, quanto para respostas. Antes de entrarmos em maiores detalhes, vejamos dois exemplos simples de mensagens HTTP. Primeiro, um exemplo de requisição. O request abaixo pode ser usado para solicitar ao servidor www.dsc.ufcg.edu.br o recurso /~dalton/exemplo.txt, usando o protocolo HTTP/1.1.

GET /~dalton/exemplo.txt HTTP/1.1
Host: www.dsc.ufcg.edu.br

Abaixo um exemplo de uma resposta ou response HTTP. De fato, este é o response que recebi do servidor mencionado acima, como resposta ao request acima.

HTTP/1.1 200 OK
Date: Sat, 31 Aug 2019 21:41:02 GMT
Server: Apache/2.4.37 (Unix) PHP/5.6.39
Last-Modified: Sat, 31 Aug 2019 21:37:42 GMT
ETag: "8f-591708c7df108"
Accept-Ranges: bytes
Content-Length: 143
Content-Type: text/plain

Exemplo de arquivo de texto disponível como recurso
na Web. Para acessá-lo, use o endereço:

http://www.dsc.ufcg.edu.br/~dalton/exemplo.txt

Sintaxe de Mensagens HTTP

Sintaxe geral

Mensagens HTTP seguem um formato geral único que consiste em 4 partes:

  1. uma linha inicial que descreve a requisição ou a resposta;
  2. uma sequência de cabeçalhos, contendo dados complementares para a comunicação entre cliente e servidor; um cabeçalho por linha;
  3. uma linha em branco que demarca o fim dos cabeçalhos; e
  4. um corpo, opcional, que é o conteúdo transportado pela mensagem.

Observação. Todas as linhas da mensagem devem ser terminadas por uma sequência conhecida como CRLF que consite em um caractere de carriage return (\r) e um caractere de line feed (\n). Isso inclui a linha em branco depois dos cabeçalhos.

Cabeçalhos Os cabeçalhos são usados para que cliente e servidor possam trocar dados complementares, necessários para o protocolo. Cada cabeçalho é um par nome, valor. O formato de cada linha de cabeçalho consiste no nome do cabeçalho (case-insensitive), seguido do caractere :, espaços em branco opcionais, o valor do cabeçalho e espaços em branco opcionais.

Em uma requisição HTTP/1.1, há um único cabeçalho obrigatório: Host. Ele deve ser usado para indicar o domínio a que se refere a requisição. É necessário porque uma mesmo servidor pode hospedar múltiplos domínios (os chamados domínios virtuais).

O corpo Tipicamente, o corpo é usado por responses para transmitir os dados solicitados pelo usuário. Assim, tipicamente, requests têm, um corpo vazio. Veremos mais adiante, contudo, que há requests que precisam incluir dados no corpo da mensagem. Isso ocorre quando o cliente precisa enviar dados do usuário para o servidor.

Observe as mensagens exemplos acima. Identifique nelas as várias partes das mensagens: a linha inicial, os cabeçalhos, a linha em branco e o corpo. Identifique os cabeçalhos usados e seus valores. Também observe a linha em branco colocada logo após os cabeçalhos. No caso do response, a linha em branco fica bastante evidente. No request a linha em branco também está lá, embora não seja tão fácil percebê-la, porque não há nada depois dela, na mensagem.

Request Line e Status Line

O único elemento sintático que diferencia uma requisição (request) de uma resposta (response) HTTP é a linha inicial. Requisições usam um formato de linha inicial chamado request line e respostas usam o formato status line. Vejamos cada um dos formatos.

O request line A linha inicial de uma requisição sempre consiste em três elementos separados por um espaço, denominados: método, alvo da requisição e versão do HTTP. O request line de nosso exemplo acima é destacado abaixo. Identifique nele cada uma das partes mencionadas.

GET /~dalton/exemplo.txt HTTP/1.1

O status line A linha inicial de uma resposta consiste em três elementos, separados por um espaço, denominados: versão do HTTP, código de status (ou status code) e a frase de motivo (ou _reason-phrase), sendo esta última opcional. O status code é um código de três digitos que resume o resultado do processamento da requisição. Todos os dados contidos no response, incluindo os cabeçalhos e o corpo, são interpretados a partir da semântica associada ao status code. A frase de motivo, que é opcional, é uma mera descrição textual que descreve o status code, incluída exclusivamente para tornar o response mais legível. O status line de nosso exemplo acima é destacado abaixo. Identifique nele cada uma das partes mencionadas.

HTTP/1.1 200 OK

Fazendo requisições com netcat

É importante desenvolver um entendimento detalhado do que ocorre quando fazemos requisições através de um browser ou outros aplicativos que operem como clientes web. Para isso, contudo, o melhor é fazermos algumas poucas requisições, diretamente através de um socket e uma conexão TCP.

Para isso, usaremos o aplicativo netcat que já usamos na lição sobre sockets e conexões TCP. Relembre que netcat nos permite criar uma conexão TCP para um servidor e que, uma vez estabelecida a conexão, nos permitirá enviar e receber bytes através da conexão. Se executarmos o netcat como indicado no comando abaixo, poderemos escrever o request manualmente.

nc -c www.dsc.ufcg.edu.br 80

O comando acima faz netcat criar uma conexão ao servidor de nome www.dsc.ufcg.edu.br na porta 80 que é a porta padrão de servidores HTTP. Observe que usei o parâmetro -c que, no netcat do MacOs, pede que todos os \n sejam devidamente substituídos por \r\n, garantindo que o protocolo HTTP seja atendido.

O vídeo abaixo mostra como o comando pode ser usado para fazer requisições semelhantes às do exemplo acima. No mesmo vídeo, quatro requisições são feitas, sendo que apenas a primeira com sucesso. Relembre que o cabeçalho Host é o único obrigatório em HTTP/1.1. Observe como o servidor responde cada requisição.

Requests via netcat

Faça você mesmo requisições, usando netcat, via linha de comando. Observe que o comando pode ser nc ou netcat, a depender do sistema que você está usando. No MacOS o argumento para fazer o netcat usar CRLF ao final de cada linha é -c. No Linux, contudo, é -C (ou --crlf). Se não funcionar, veja o manual do netcat, com o comando man nc ou man netcat.

Semântica de HTTP

A semântica de HTTP (1.1) é definida pelo RFC 7231. Por semântica entende-se o significado das mensagens. O documento define, portanto, como o servidor e o cliente devem interpretar requests e responses, respectivamente. Claramente, são informações essenciais para o desenvolvimento de user-agents e de origin-servers. Como este curso é focado no desenvolvimento de aplicações web, contudo, precisamos apenas de um entendimento dos conceitos básicos e não de detalhes. Além disso, como, ao vermos a sintaxe de HTTP já tratamos o essencial da semântica de requisições, nesta seção focaremos na semântica de responses.

Classes de status codes

Relembre que a sintaxe de responses consiste numa primeira linha denominada status line. Nessa linha, o item de maior importância é o status code (segundo elemento da linha). Os status codes pertencem a uma de cinco classes. O primeiro digito do status code indica a classe a que o status code pertence e determina a semântica geral do response. Há cinco classes de status codes, indicadas pelos digitos de 1 a 5. Cada classe agrupa vários status codes que compartilham o mesmo significado abstrato da classe. O protocolo não exige que os clientes saibam como interpretar cada um dos inúmeros status codes existentes, mas exige que saibam interpretar a classe a que pertencem. Por regra, se um cliente recebe um status code que desconhece, digamos 468, então ele deve reagir apenas baseado na classe. Neste caso, deve tratar o código como se fosse 400. Em todas as classes, o código x00 é reservado para expressar o significado mais geral da classe. Da forma análoga, se o desenvolvedor do servidor não sabe o status code exato a usar para determinada situação, é necessário que saiba ao menos escolher a classe correta de status code e, nesse caso, que use o código geral x00. As cinco classes e seus significados são descritas a seguir.

Um excelente site que uso sempre que preciso escolher o status code apropriado para uma dada situação é o do REST API Tutorial. O mesmo site contém bastante material sobre REST (o que não deixa de ser sobre conceitos HTTP).

1xx (Informational)

Respostas desta classe são respostas provisórias e indicam o andamento do processamento da requisição. São usadas pelo servidor para indicar que a requisição foi recebida e que está sendo processada. Tipicamente, outra resposta pode ser esperada pelo cliente. Em geral, o desenvolvedor de aplicações web não irá lidar com responses com este tipo de mensagem, porque são tratadas silenciosamente pelo browser.

O código 100, cuja reason-phrase é Continue, indica apenas que a parte inicial do request foi recebida e que, até o momento do response, não havia sido rejeitada e está sendo processada.

2xx (Success)

Respostas desta classe indicam que a requisição requisição foi recebida, compreendida e atendida com sucesso. A classe contém vários códigos para diferentes tipos de ações como o 201 (Created) e 202 (Accepted).

O status-code 200, cujo reason-phrase é Ok, indica que a requisição foi recebida, processada e atendida com sucesso.

3xx (Redirection)

Respostas desta classe indicam que o cliente precisa fazer uma nova ação para obter o recursos desejado. Tipicamente, isso pode implicar em: fazer uma nova requisição, usando um endereço diferente; escolher entre múltiplas opções de endereços; reusar uma versão do recurso baixada anteriormente (útil para as chamadas requisições condicionais).

O status-code 300, cujo reason-phrase é Multiple Choices, indica que o recurso indicado na requisição corresponde a múltiplas representações e que o cliente deve optar por uma. Em vários casos, a nova requisição é feita automaticamente pelo browser.

4xx (Client error)

Respostas desta classe indicam que o servidor não atendeu à requisição e que entende que isso ocorre devido a um erro na requisição, por parte do cliente. Dentre os códigos mais conhecidos e usados estão os famosos 401 (Unauthorized) e 404 (Not found), além de 403 (Forbidden) e 409 (Conflict).

O status-code 400, cujo reason-phrase é Bad Request, indica que o servidor não pode ou não vai processar a requisição devido a algum erro atribuído ao servidor (isso inclui desde erros de sintaxe na mensagem a problemas com autenticação e erros lógicos relativos a restrições em nível de aplicação para o recurso).

5xx (Internal Server Error)

Respostas desta classe indicam que o servidor encontrou alguma situação inesperada que o impediu de atender à requisição. Ao contrário da classe anterior, neste caso, a resposta indica que a requisição foi devidamente compreendida, mas que o servidor não pode processar e/ou atender à requisição. Pode indicar erros de lógica, falhas de hardware, indisponibilidade de certos recursos, etc. Dentre os códigos mais conhecidos e usados, estão o 501 (Not Implemented) e o 503 (Service Unavailable).

O status-code 500, cuja reason-phrase é Internal Server Error, indica apenas que o servidor se deparou com uma condição interna inesperada que o impediu de atender à requisição.