# HTTP headers & cookies

Nosso servidor HTTP, até o momento, responde apenas um texto simples "Hello".

```ruby
require 'socket'

socket = TCPServer.new(3000)
puts 'Listening to the port 3000...'

loop do
  client = socket.accept

  response =
"""
HTTP/1.1 200\r\n
\r\n
\r\n
Hello
"""

  client.puts(response.strip.gsub(/\n+/, "\n"))
  client.close
end
```

Ao acessarmos `http://localhost:3000` no navegador, o resultado é este:

![Screenshot 2022-01-17 at 21 34 02](https://user-images.githubusercontent.com/385640/149839261-220df512-85cd-4ec8-81b7-3a1fb446273f.png)

Entretanto o conteúdo apresentado é um texto simples. Se decidirmos escrever uma página inteira apenas com texto simples, seria irritante para o usuário. Por isto, precisamos **formatar** o conteúdo, *marcar* algumas partes com destaque e prover uma experiência melhor ao usuário do site.

## Um pouco de HTML

[HTML](https://en.wikipedia.org/wiki/HTML) é uma linguagem de marcação para conteúdo hypertexto, que pode ter diferentes características de acordo com a marcação desejada.

Vamos supor que queremos responder com conteúdo HTML um **contador** de quantas vezes uma *mesma* pessoa visita a página. Vamos alterar a resposta HTTP para conter o *corpo* da mensagem em formato HTML:

```ruby
# ...
  response =
"""
HTTP/1.1 200\r\n
\r\n
\r\n
<h1>Counter: 1</h1> 
"""
# ...
```

O texto com o counter (`<h1>Counter: 1</h1>`) deve ser destacado em forma de título na página. Ao entrarmos no site:

![Screenshot 2022-01-17 at 22 29 51](https://user-images.githubusercontent.com/385640/149843992-7114a4c7-ecef-4cad-b607-189d88086042.png)

O conteúdo foi mostrado exatamente da forma como enviamos no socket, pois tudo é `string` de dados sendo transportada via socket TCP.

## HTTP headers

Mas como fazer o navegador "interpretar" aquele conteúdo HTTP como sendo HTML? Devemos enviar este "metadado" em uma parte especial da mensagem HTTP, que se chama **HTTP header**.

```ruby
# ...
  response =
"""
HTTP/1.1 200\r\n
Content-Type: text/html\r\n
\r\n
<h1>Counter: 1</h1> 
"""
# ...
```

Desta forma, estamos instruindo o HTTP client, no caso o web browser, que o conteúdo é do tipo HTML, assim o browser consegue renderizar o HTML corretamente:

![Screenshot 2022-01-17 at 22 39 14](https://user-images.githubusercontent.com/385640/149844540-c7da0590-f7bb-4818-b177-5816543e0576.png)

Ok, mas o counter está sempre fixo no valor "1". Como podemos deixar isto dinâmico?

```ruby
# ...
counter = 1       # <-- como fazer o counter ser dinâmico a ponto de ser enviado entre browser e server diversas vezes? 

  response =
"""
HTTP/1.1 200\r\n
Content-Type: text/html\r\n
\r\n
<h1>Counter: #{counter}</h1> 
"""
# ...
```

### HTTP é stateless por definição

Vamos lembrar que, por estar condicionado a uma conexão TCP, o HTTP não guarda estado, isto é, a cada vez que um user fizer refresh à página, o server não consegue saber, a princípio, quem é o cliente, sempre tratando o pedido *como se fosse um novo cliente*.

```ruby
# (...)
loop do
  client = socket.accept                        # <-- aguarda até chegar novo cliente

  client.puts("HTTP/1.1  (etc ....)")       # <-- envia resposta ao cliente
  client.close                                          # <-- fecha conexão com cliente e o ciclo de aguardo inicia novamente
end
# (...)
```

Para que o cliente seja "lembrado", é preciso que o server envie algum *metadado* ao cliente, para que este reencaminhe o metadado *de volta ao server* nos pedidos subsequentes.

A especificação HTTP contempla este cenário onde podemos ter algum tipo de "estado" entre conexões HTTP distintas de um mesmo cliente, sendo que web browsers e servidores web já implementam este *envio mútuo de metadado* de forma automática.

Já sabemos que para enviar um metadado, é através de *HTTP headers*, como vimos no exemplo do `Content-Type`.

Para o caso do "counter" ser enviado entre vários pedidos, o HTTP especifica o envio deste metadado através de headers que são **HTTP Cookies**.

## HTTP Cookies

1. o server envia um metadado qualquer através do header `Set-Cookie`
2. o browser recebe a mensagem e verifica que há um header `Set-Cookie`, então pega o valor do cookie e coloca numa área específica da **memória do navegador** chamada *cookie storage*.
3. para pedidos futuros neste mesmo site, o browser já sabe que tem que enviar o cookie **de volta** ao servidor, portanto, no request HTTP, inclui um header chamado `Cookie` com o valor armazenado.
4. o server verifica se o browser enviou algum header `Cookie`, e caso tenha sido enviado, lê o valor do cookie para manipular/atualizar a informação de alguma forma e **o ciclo se repete**

#### Como funciona um típico sistema de Login na Web

Convém lembrar que um sistema de login web funciona exatamente desta forma, onde a primeira resposta do servidor é através de `Set-Cookie` que contém a identificação do user ou algo do gênero. Desta forma, o browser envia o cookie nos pedidos subsequentes e com isto temos um sistema de login onde temos a impressão que estamos "autenticados".

"Autenticados" com muitas aspas. É apenas uma sensação, pois a informação é sempre trocada através de headers em cima de um protocolo que NÃO guarda estado por *definicão*.

Para enviar um metadado ao cliente, já vimos que é preciso utilizar HTTP headers. Neste caso não será diferente. E a especificação HTTP contempla um header chamado `Set-Cookie` que permite "persis

#### Voltando ao nosso exemplo do counter

Vamos então ver como fica o response HTTP com o header `Set-Cookie` enviando o valor do counter:

```ruby
# ...
counter = 1 

  response =
"""
HTTP/1.1 200\r\n
Content-Type: text/html\r\n
Set-Cookie: counter=#{counter}; path=/; HttpOnly\r\n
\r\n
<h1>Counter: #{counter}</h1> 
"""
# ...
```

A resposta HTTP que chega ao browser é esta:

![Screenshot 2022-01-17 at 23 04 35](https://user-images.githubusercontent.com/385640/149846000-2c89213d-ab21-4231-8662-47957184989b.png)

Pelo que o browser guarda o cookie no cookie storage do próprio browser, ou seja, *fica na memória do browser*:

![Screenshot 2022-01-17 at 23 04 51](https://user-images.githubusercontent.com/385640/149846086-31f26fad-34f8-4c8b-a719-851267dfc150.png)

Por conta disto, se o user apagar as cookies, o próximo pedido ao server não vai ser o valor no header portanto para o server será como se fosse a "primeira vez" daquele cliente.

Vamos ver como fica o request HTTP enviado do browser *ao* servidor:

![Screenshot 2022-01-17 at 23 08 45](https://user-images.githubusercontent.com/385640/149846213-3b624470-dfd9-427a-81c6-ed1ae7807c7e.png)

Nesta imagem, podemos ver que há diversos HTTP headers que o browser envia ao server, dentre eles o nosso cookie:

```
Cookie: counter=1
```

Yay!

## Lendo o valor do request HTTP Cookie

Apesar de que conseguimos enviar do server ao browser, nosso server ainda não é capaz de ler os headers da mensagem, pois ainda não escrevemos este código.

Para isto, precisamos ler a mensagem através do socket TCP, *uma linha de cada vez no socket*:

```ruby
client = socket.accept

first_line = client.gets  
second_line = client.gets

# and so on...
```

Já deu pra entender quantas linhas teríamos que escrever para ler a mensagem toda, pois não? Vamos então fazer um *loop* e ler todas as linhas enquanto houver mensagem no socket:

```ruby
request = ''

while line = client.gets
  break if line == "\r\n"

  request +=  line
end
```

Só isto não basta, temos que agora conseguir encontrar um padrão `Cookie: <qualquerChave>:<qualquerValor>` no meio da mensagem. Para encontrar padrões, vamos utilizar **expressões regulares**:

```ruby
cookie = {}

if cookie_match = request.match(/Cookie:\s(.*)=(.*)\r$/)
  cookie[cookie_match[1]] = cookie_match[2]
end
```

Vamos pular explicação de expressões regulares por agora, pode ser tema para outra sessão. Mas com este código conseguimos guardar numa hash todos os cookies vindo do request HTTP.

Próxima linha é buscar o valor do counter na hash `cookie`, caso esteja ausente (primeiro request de um cliente, por exemplo), o valor é `0`. Caso contrário, é o valor encontrado no cookie.

A este valor, incrementamos o valor `1`, dando assim a característica de um counter:

```ruby
counter = cookie.fetch('counter', 0).to_i + 1
```

E pra finalizar, nosso response com os devidos headers:

```ruby
  response =
"""
HTTP/1.1 200\r\n
Content-Type: text/html\r\n
Set-Cookie: counter=#{counter}; path=/; HttpOnly\r\n
\r\n
<h1>Counter: #{counter}</h1>
"""
```

Ao rodarmos o server e entrarmos 2 vezes na página:

![Screenshot 2022-01-17 at 23 17 01](https://user-images.githubusercontent.com/385640/149846684-742c8d4b-49bd-47bc-9884-493e6a0a81b4.png)

*Yay!* Já temos nosso counter funcionando com HTTP cookies!

## Conclusão

Esta sessão foi uma explicação de como funcionam HTTP headers, HTTP cookies e como podemos tirar proveito disto para envio mútuo de metadados para que sempre consigamos "lembrar" do cliente, mesmo utilizando um protocolo que não guarda estado.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yatax.leandronsp.com/http-headers-and-cookies.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
