QR Code em AdvPL – Parte 04

Introdução

No post anterior, vimos sem grandes detalhes as etapas de criar um QR Code “from scratch” (do zero), e as áreas reservadas da imagem. Agora vamos ver os passos necessários com mais detalhes.

Passo 01 – Escolha da correção de erro

Como vimos no post anterior, quanto maior o nível de correção de erro, mais módulos (quadradinhos) são necessários para representar a informação. Como cada versão de QR Code possui um tamanho fixo, a quantidade de informações que podemos colocar dentro de um QR Code varia de acordo com o tipo do alfabeto usado para representar os dados (numérico, alfanumérico, bytes). Por exemplo, a versão 1 com correção de erro “L” (baixa), cabem 25 símbolos alfanuméricos ou 17 bytes. Quando usada a mesma versão, mas o nível de correção de erro “H” (alto), cabem 10 símbolos alfanuméricos ou 7 bytes.

Desse modo, primeiro verificamos qual é o tipo de alfabeto que podemos utilizar, e escolhemos uma correção de erro que atenda a necessidade. Vários sites de informações e recursos de QR Code comentam que níveis mais altos são recomendáveis para utilização em ambientes onde a impressão está mais sujeita a danos — como etiquetas e manifestos colados em caixas, para um chão de fábrica. Tão importante quanto isso, é a escolha posterior de um tamanho para a imagem impressa, deve-se certificar que o equipamento usado para fazer a leitura (scanner) seja capaz de ler a informação.

Uma vez sabendo a quantidade de símbolos no alfabeto utilizado, e o nível de correção de erro desejado, podemos escolher uma versão de QR Code que as informações a serem representadas “caibam dentro”.

Dentro da classe ZQRCODE() existe uma função chamada GetQRLimits(), que justamente retorna um array pré-calculado com as versões, correção de erro, e capacidade de símbolos por modo de codificação (alfabeto) utilizado.

Passo 02 – Codificar os dados

Seguindo uma receita de bolo para cada codificação usada, os dados a serem representados no QR Code devem passar por uma primeira etapa de processamento, gerando um stream ou sequência de BITS (os zeros e uns) para então ser re-codificado em uma sequência de números inteiros de tamanho igual a capacidade da versão do QR Code em uso. Para isso são usadas conversões de numérico decimal para binário e vice-versa. No final desta etapa, teremos um array de números inteiros com valores entre 0 e 255, que vamos chamar de “Code Words de Dados”.

O método responsável pelas etapas de codificação de dados chama-se BuildData(). O dicionário alfanumérico “restrito” de 44 símbolos é armazenado em um array estático (_Alpha) e alimentado na carga do programa através da função GetAlpha(). Para chamá-lo é necessário no mínimo informar a correção de erro desejada e atribuir os dados que vão compor o QR Code. De posse dessa informação, caso você ainda não tenha escolhido uma versão para o QR Code, internamente será chamado o método BestVersion(), que vai identificar se os dados informados podem ser codificados com o alfabeto restrito ou codificados em bytes mesmo, buscando a menor versão necessária para caber as informações desejadas.

** Por hora este método assume que os dados serão codificados em bytes, e calcula a menor versão necessária para a codificação neste formato ***

Passo 03 – Codificar a recuperação de erros

Feita a codificação dos dados, consultamos uma outra lista pronta, através da função GetECCW(), que a partir da versão e da correção de erro utilizada, retorna a quantidade de CodeWords de correção de erro que devem ser geradas para fazer parte do QR Code.

O menor número de CodeWords de erro a ser gerado são 7, quando usamos a versão 1 com correção de erro “L”. Embora a quantidade de CodeWords aumente significativamente em versões mais altas, existe uma regra para divisão de blocos de dados em agrupamento de dois tipos, a partir da versão 3 com correção de erro “H”, onde a lista de CodeWords para correção de erro são geradas por grupo. Logo, mesmo que um QR Code de versão 40 com correção de erro “L” suporte 2956 CodeWords de dados, cada grupo terá no máximo 30 CodeWords para correção de erro, geradas para 19 blocos tipo 1 com 118 CodeWords de dados cada, e 6 blocos tipo 2 com 119 CodeWords de dados cada. Logo, serão 30 gerações de 30 CodeWords de correção de erro. Todos estes valores podem ser obtidos na lista retornada pela função GetECCW().

A geração destes códigos envolve algumas fórmulas matemáticas com polinômios (equações), porém como as fórmulas de base para o cálculo são montadas de acordo com a quantidade de CodeWords de correção de erro a serem montados, basta criar uma lista com as fórmulas prontas para cada número necessário para cada versão. As fórmulas estão representadas em arrays bidimensionais, retornados pela função GetPolyGen(), que recebe como parâmetro o número de bytes (ou CodeWords) de correção de erro necessários.

Cada cálculo de correção de erro parte de um grupo de dados — representado por um array de números (que convenientemente vamos chamar de “mensagem”) — e uma sequência de operações polinomiais feitas com a mensagem. O resultado final destas operações será justamente um array com a lista de bytes (CodeWords) de correção de erro gerados para aquela mensagem.

As etapas do cálculo são multiplicações e divisões polinomiais, algo relativamente complexo. Porém, as operações necessárias tornam-se possíveis de ser feitas com um programa simples, graças ao uso de uma representação aritmética de campos finitos (ou Galois Field). Na prática, e visto de forma superficial, a utilização de duas listas de conversão pré-calculadas , é possível transformar o polinômio com fatores gerados com os dados da mensagem para a aritmética GF(256). Com isso, ao invés de precisar criar ou usar um algoritmo complexo para fazer uma multiplicação ou divisão polinomial, a representação de fatores usando essa aritmética permite que as operações de multiplicação sejam feitas apenas somando expoentes na equação, e a divisão polinomial longa seja feita usando o operador binário XOR (“ou exclusivo”) diretamente com os fatores. As conversões entre os fatores decimais e os fatores em notação GF(256) são feitas com base nas funções GetGFA2Int() e GetGFInt2A(), usando listas pré-calculadas.

Passo 04 – Estruturação da mensagem final

Após calcular as duas listas (ou arrays) de CodeWords (dados e correção de erro), a estruturação da mensagem final terá mais etapas quanto maior for a versão do QR Code a ser emitida. As etapas consistem na criação de um ou mais grupos de dados, de tamanho limitado e pré-definido, com os dados e a correção de erro seqüenciados ou intercalados. A função GetECCW() também retorna a informação de quantos grupos de dados são necessários para cada versão, a intercalação de dados e códigos de erro somente é necessária quando temos mais de um grupo de dados, e a regra aplicada é a mesma, independente de quantos grupos de dados são necessários para estruturar a mensagem final.

O objetivo desta etapa é obter a sequência final de CodeWords ou bytes, contemplando o array de dados e o array de correção de erro, que será usado para gerar a sequência de módulos binária para compor a matriz final do QR Code.

Passo 05 – Disposição dos módulos da mensagem final

O método DeployBits() gera a seqüencia binária de dados para o QR Code, alimentando as áreas de dados do QRCode, ignorando as áreas reservadas. É um malabarismo de bits em zigue-zague, comecando de baixo para cima, da direita para a esquerda, preenchendo sempre 2 colunas de bits.

Passo 06 – Mascaramento de dados

Aqui a coisa enrosca … após distribuir os dados, você já tem um QRCode … MAS, como os dados usados para compor o QRCode podem gerar seqüencias longas ou contínuas de blocos preenchidos ou blocos vazios, e até mesmo gerar algo parecido com uma marca de controle, o IDEAL [e que a aplicação que emita o QRCode seja capaz de aplicar os 8 (oito) fórmulas de mascaramento de dados sobre os dados originais do QRCode, e escolha qual a melhor alternativa de apresentação. Essa etapa ainda está em desenvolvimento, por hora a classe permite eu definir ou escolher um método, ela ainda não escolhe sozinha … Essa tarefa, quando completa, será feita pelo método SelectMask().

Passo 07 – Informações de versão e formato

As informações de versão e formato do QR Code são colocadas em áreas pré-determinadas, de acordo com a versão e formato escolhidos. Existem máscaras de dados prontas já armazenadas no fonte para informar esses dados nos lugares corretos.

Após aplicar a máscara com o SelectMask(), o array aGrid da classe vai conter as informações necessárias para a plotagem do QRCode. Basta varrer todas as linhas e colunas do array, e onde o elemento oObj:aGrid[nRow][nCol] for igual a 1 ou 3 ( dado preenchido ou dado de controle ), deve ser plotado um módulo preenchido, senão o módulo deve ser vazio. No fonte de exemplo do post anterior (QR Code em AdvPL – Parte 03), a imagem do QRCode é gerada com um bitmap em memória, onde cada módulo ocupa um pixel, e depois de gerada, é usado o método Stretch() da classe de Bitmap, para aumentar a imagem 12x — assim , cada módulo tera 12×12 pixels — definido em

MODULE_PIXEL_SIZE 

Conclusão

Todo esse esforço para gerar um QRCode tem apenas um objetivo: Mostrar que é possível de ser feito … e mostrar quais as dificuldades em lidar com blocos de informações em baixo nível — de pixels a bits — e que dá para fazer em AdvPL 😀

Espero que todas essas informações sejam de utilidade e apreciação de todos aqueles que buscam saber e fazer sempre mais !!! E conhecer cada vez mais !!!

Desejo a todos, como sempre, TERABYTES DE SUCESSO !!!!

Referências

O Protocolo HTTP – Parte 06

Introdução

No post anterior (O Protocolo HTTP – Parte 05), o Web Site local de testes com o TOTVS Application Server ganhou mais uma funcionalidade, vimos como o Browse montou uma requisição de POST de um formulário HTML com campos de preenchimento do usuário, e mais detalhes sobre GET e POST. Agora, vamos fazer alguns ajustes nas páginas HTML do site de testes, e ver se ou quais impactos terão estes ajustes, e acrescentar mais algumas coisas …

Utilizando HTML5

HTML versão 5 traz novos identificadores, recursos, e já está bem suportado pelos Web Browses de mercado. Embora marcadores foram declarados “obsoletos”, já que é para colocar a mão na massa, é elegante declarar na primeira linha de cada página HTML, que a versão usada é a HTML5. Para isso, basta inserir a linha abaixo como primeira linha do arquivo:

<!DOCTYPE html>

HTML Head e HTML Body

De forma similar ao protocolo HTTP, uma página HTML possui identificadores para declaração de elementos de cabeçalho de página (Header), e para delimitar o corpo do HTML (Body), inclusive delimitar o inicio e fim do HTML. Se nada disso for colocado, e voce simplesmente criar um arquivo HTML sem estes identificadores, normalmente o Web Browse vai interpretar tudo como HTML Body… Mas é deveras recomendável que você monte as suas páginas HTML declarando onde fica o que, vide exemplo abaixo:

<!DOCTYPE html>
<html>
<head>
<title>Título do Documento</title>
</head>
<body>
Conteúdo do Documento...
</body>
</html>

Entre o inicio e final do cabeçalho HTML, delimitado pelos marcadores <head> e </head>, usamos outros marcadores HTML para identificar características (ou meta-dados) de um documento e/ou página HTML, como um título — usado na barra de navegação e por mecanismos de busca –, scripts — executados pelo web browser na estação onde a página e/ou documento HTML está sendo mostrado, entre outras coisas interessantes.

Salve esse arquivo com o nome “html5.htm” na pasta htto-root, e com o Application Server em execução, abra a URL http://locahost/html5.htm, e vejamos como a tela apareceu:

Se o arquivo foi salvo usando CodePage 1252, os caracteres acentuados serão mostrados corretamente. Se ele foi salvo pelo NOTEPAD usando UTF-8, serão mostrados caracteres “estranhos” nos acentos … Isso é UTF-8 interpretado como CP1252 …

Agora, vamos pegar esse “padrão”, e refazer os arquivos do nosso site de testes. Eles devem ficar assim:

  • Arquivo default.htm
<!DOCTYPE html>
<html>
<head>
<title>Meu primeiro Web Site - Página Inicial</title>
</head>
<body>
<h3>Meu primeiro Web Site</h3>
<a href="testepost.htm">Teste de Post</a>
</body>
</html>
  • Arquivo testepost.htm
<!DOCTYPE html>
<html>
<head>
<title>Meu primeiro Web Site - Teste de POST</title>
</head>
<body>
<h3>Teste de Post</h3>
<hr>
<form name="mydataform" method="post" action="testepost.htm">
<input type="text" name="CAMPO1"></input>
<input type="text" name="CAMPO2"></input>
input type="submit"/>
</form>
<hr>
<a href="/">Voltar ao Início</a>
</body>
</html>

Agora, com tudo funcionando, vamos passar a usar a codificação UTF-8 nos nossos arquivos HTML, bastando para isso suas coisas :

  • Usando por exemplo o editor NOTEPAD++, insira logo após a linha de abertura do header <head>, antes do <title>, a linha abaixo:
<meta charset="UTF-8">
  • Agora, use a opção Encoding -> Convert to UTF-8, e salve o arquivo. ( Verifique se o arquivo já não está com esta codificação … os editores de textos simples mais recentes, mesmo o NOTEPAD do Windows, já codifica o arquivo como UTF-8 se você cria o arquivo e preenche com algum caractere acentuado.)

E, finalmente, vamos abrir novamente os arquivos no Web Browser. Com o conteúdo codificado em UTF-8, e a informação meta charset no HTML head, tudo deve ser mostrado corretamente pelo Web Browser.

Executando o POST novamente, agora informando caracteres acentuados

No teste de post anterior, sem utilizar o charset UTF-8 nos nossos HTML(s), a letra “a” minúsculo acentuado foi enviada pelo Web Browser ao realizar o POST usando o (Codepage) CP-1252, e enviada pelo post codificada como “%E1″. Agora que estamos usando UTF-8, se acessarmos a tela de teste de post, digitarmos no primeiro campo “Olá” e no segundo “Mundo”, e verificarmos usando o painel do desenvolvedor como o Google Chrome codificou a letra “á”, veremos novidades:

Reparem que agora a letra “á” foi codificada como “%C3%A1” .. Essa é a seqüência de bytes no padrão UTF-8 que representa a letra a minúscula com acento agudo no alfabeto latino. Deste ponto em diante, todos os arquivos HTML dos nossos testes passam a declarar HTML5 e codificação em UTF-8. 😀 E, conseqüentemente, o Web Browser vai entender que, se o conteúdo do arquivo é UTF-8, e existe um formulário de entrada de dados, esses dados serão codificados como UTF-8.

Mas, o que eu ganho com isso ?

Utilizando UTF-8, é possível codificar e representar múltiplos alfabetos e caracteres de outros idiomas — mais de 1 milhão — do formato internacional “Unicode”, até “emoticons”. UTF-8 signfica “Unicode Transformation Format – 8 bits”. Eu posso por exemplo, usar uma ferramenta como o Google Translator, e traduzir “olá” de português para Russo e Chinês, e colocar dois parágrafos no arquivo “default.htm”, copiando e colando os caracteres da tradução, usando o Notepad++ ou o NOTEPAD do Windows … veja como o ficou o arquivo:

E, ao ser mostrado no Web Browser, ficou assim:

Vamos colocar uma imagem no HTML ?

Eu peguei uma foto do meu perfil do Facebook, salvei como uma uma imagem JPG, usei o PaintBrush do Windows para fazer um “resize” na imagem .. e ela ficou um “quadrado” de 192×192 pixels…. Salvei essa imagem no disco, com o nome “mypicture.jpg”, dentro de uma nova pasta chamada “pictures”, criada dentro da pasta http-root.

Agora, vamos editar a nossa pagina “default.htm”, e colocar essa imagem para ser mostrada no HTML, acrescentando as duas linha em negrito abaixo, antes do </body>:

<hr>
<img src="./pictures/mypicture.jpg">

A linha com <hr> insere uma barra horizontal no documento, e o marcador <img permite a inserção de uma imagem dentro do HTML. Reparem que, para a imagem ser localizada, eu informo na propriedade “src=” a informação “./pictures/mypicture.jpg”. Vamos ver como ficou ?

Agora, ao usar as ferramentas do desenvolvedor do Google Chrome, e pedir para recarregar essa página, vamos ver uma informação nova:

Quando o Web Browser pediu a página “default.htm” ao HTTP Server, ele recebeu apenas a página HTML… Após receber a página e começar a “desenhar” o HTML na tela, o Web Browse encontrou o marcador de imagem, para mostrar a imagem “./pictures/mypicture.jpg” … Então, o Web Browse abre internamente uma nova solicitação de GET, pedindo esse arquivo para o HTTP Server. O HTTP Server, ao receber o pedido, verifica que o arquivo existe em disco, na pasta identificada, e retorna o arquivo ao Browser, que mostra a imagem na tela.

Como o HTTP server sabia que a imagem era do tipo JPEG ?

Então, existem algumas extensões de arquivo conhecidas e tratadas pelo Totvs Application Server como servidor de HTTP. Ao receber uma instrução GET, foi informado no protocolo que o recurso “mypicture.jpg” foi solicitado .. Ao receber uma solicitação, o servidor verifica qual o path, nome e extensão do recurso solicitado, e baseado na extensão informada (.jpg ou .jpeg), ele já sabe que é um arquivo em disco, e que o formato do arquivo é “image/jpeg”. Veja abaixo a lista de arquivos tratadas pelo HTTP Server — Totvs Application Server, e o content-type retornado para cada uma:

HTM ou HTML : text/html
XML         : text/xml
TXT         : text/plain
GIF         : image/gif
JPG ou JPEG : image/jpeg
BMP         : image/bmp
SVG         : image/svg+xml
ZIP         : application/zip
DOC         : application/msword
AU          : audio/au
WAV         : audio/wav
MPEG ou MPG : video/mpeg
AVI         : video/avi
CSS         : text/css
JS          : application/x-javascript
TODAS AS DEMAIS : application/octet-stream";

Dessa forma, baseado na extensão utilizada no nome do recurso solicitado pelo Browser, verificando a extensão do recurso, ele já sabe qual Content-type de retorno deve ser utilizado para o Web Browser interpretar o recurso corretamente. Caso seja um arquivo de extensão desconhecida pelo Application Server, ele retorna ao Browser que o conteúdo do arquivo é uma seqüencia de bytes (
octet stream“, ou “seqüencia de octetos” — afinal 1 byte corresponde a uma seqüência de 8 bits) 😛

Por que foi criada a pasta “pictures” ? A imagem não poderia estar na pasta “http-root”?

Sim, claro que poderia. Criar pastas ou subdiretórios a partir da pasta raiz de publicação (no nosso caso, http-root) permite ORGANIZAR melhor os arquivos dentro do site. Podemos criar uma pasta para centralizar ou agrupar as folhas de estilo utilizadas (CSS), imagens, scripts (JavaScript), etc …. Calma que a gente chega lá ..rs..

Referências

O Protocolo HTTP – Parte 05

Introdução

No post anterior (O Protocolo HTTP – Parte 04), vimos como funciona por dentro uma requisição GET de HTTP, como passar parâmetros pela URL, e como fazer um fonte em AdvPL com os tratamentos de códigos de retorno necessários. Agora, vamos ver mais alguns detalhes do GET , e ver por dentro o método POST do HTTP, primeiro pelo visão do protocolo e pelo Web Browse.

HTTP GET – Mais informações

Como já vimos anteriormente, uma requisição do tipo GET do HTTP possui restrições de tamanho e não deve ser usada para trafegar dados sensíveis. Um outro ponto muito importante é : Uma requisição do tipo GET somente é usada para CONSULTAR uma informação, e não atualizar ou mudar o estado de uma informação. Como o “dite de uma página” que começou a ser criado no post anterior ainda está muito simples, ainda tem muita coisa a ser vista sobre o HTML e como as páginas são processadas pelo Web Browser.

Quando precisamos mudar o estado de uma informação em um Web Site, usamos uma requisição POST, não apenas por ser mais flexível quando ao tamanho dos dados, mas também pela forma de passagem dos dados — dentro do “Corpo do HTTP” — não visível na URL — mas também pelos comportamentos das aplicações intermediárias envolvidas, a começar pelo Web Browser.

Fazendo um POST pelo Web Browser

Lembrando que o Web Browser carrega páginas HTML e arquivos do Web Server, para fazer uma requisição POST, precisamos fazer no mínimo uma página HTML com um formulário (form), indicando para onde (qual URL) os dados desse formulário será enviado, e como será enviado — GET ou POST. E, antes de mais nada, para conhecimento: Ao abrir o seu Web Browser e digitar uma URL qualquer, o Web Browse vai sempre fazer um GET daquela URL.

Agora, vamos incrementar a página de entrada do Web Site de testes, editando o arquivo “default.htm” da pasta “http-root” com o NOTEPAD ou outro editor de textos ASCII simples, e acrescentar a linha em negrito no arquivo … a nova página deve ficar assim:

<html>
<body">
<h3>Meu primeiro Web Site</h3>
<a href="testepost.htm">Teste de Post</a>
</body>
</html>

Ao executar o Totvs Application Server configurado para servidor de HTTP, e acessar o endereço http://localhost/, deve ser mostrado o seguinte:

O elemento <a > no HTML é uma “âncora”, onde podemos referenciar um conteúdo qualquer, e o Web Browser vai criar um LINK na tela para este conteúdo. No caso, criamos um link para uma nova página no Web Site, chamada “testepost.htm” (que ainda não existe no servidor). Se clicarmos no link “Teste de Post”, deve ser apresentada a tela de erro 404 – Not Found pelo Web Browser.

Agora, vamos criar — usando o editor de textos simples — o arquivo “testepost.htm”, dentro da pasta http-root, com o seguinte conteúdo:

<html>
<body>
<h3>Teste de Post</h3>
<hr>
<form name="mydataform" method="post" action="testepost.htm">
<input type="text" name="CAMPO1"></input>
<input type="text" name="CAMPO2"></input>
<input type="submit"/>
</form>
<hr>
<a href="/">Voltar ao Início</a>
</body>
</html>

Neste exemplo simples, criamos um formulário HTML, nomeado “mydataform”, definimos que o método de submeter esse formulário ao Web Server será “POST”, e na propriedade “action” definimos que ele deve abrir novamente o arquivo “testepost.htm”. Agora sim, ao clicar no link “Teste de Post” da página inicial do nosso site de testes, devemos ver o seguinte no Web Browser:

Testando o formulário

Preencha o primeiro campo na tela com “Ola”, o segundo com “Mundo”, e clique no botão “Enviar”. E, para nossa surpresa …

O que aconteceu ?

Bem, primeiramente … o Web Browse está “reclamando” do retorno que ele recebeu do servidor … ele fez a requisição POST do HTTP, para o arquivo “/testepost.htm”, os dados foram enviados — já vamos ver como confirmar isso –, mas o HTTP Server não retornou “nada”… sim, “nada”. Agora, vamos ver POR QUE.

  • Uma requisição de POST recebida pelo HTTP Server — no nosso caso estamos usando o Totvs Application Server — somente será “atendida” corretamente pelo Totvs Application Server, se ele estiver configurado para execução de páginas DINÂMICAS — conhecidas por “AdvPL ASP”. Calma, vamos ver isso em muitos detalhes mais para a frente. Lembre-se dos posts anteriores, que por hora o Totvs Application Server está configurado como servidor HTTP de páginas e arquivos ESTÁTICOS. Isto é, você pode montar um site “fixo”, com páginas e fotos … mas ele vai aceitar apenas GET, para mostrar as páginas e fazer download de arquivos. — Também vou montar exemplo disso …

De qualquer forma, o Web Browse não sabe disso… e ele vai fazer o que o formulário mandou .. POST para “testepost.htm”. Agora, vamos entrar nos detalhes de como o Web Browse montou o POST, usando neste caso o F12 do Google Chrome — que abre as “ferramentas do desenvolvedor” na página que você está acessando.

Refazendo o teste no Chrome

Estamos com o Google Chrome aberto em “http://localhost/testepost.htm&#8221;, vendo aquela tela de erro … clique no botão “Voltar” na parte superior esquerda da janela do Web Browser, e você deve ver novamente a tela “testepost.htm”, inclusive com os campos preenchidos.

Agora, pressione a tecla F12 do teclado, OU, pressione a combinação de teclas CONTROL + SHIFT + I — Isso abre a janela de ferramentas do desenvolvedor. Se a janela de desenvolvedor aparecer “dentro” da janela do Browse, aumente o tamanho da tela ( maximize a janela ). E você deve ver algo assim:

Agora,

Na janela do desenvolvedor, existem varias “abas”. Clique na aba “Network”, ela é que nos interessa nesse momento. Agora, vamos fazer a mágica acontecer … clique novamente no botão “Enviar”… e vamos ver o que aparece na janela do desenvolvedor, do lado direito:

Repare que a primeira linha das atividades de rede (Network) do Web Browser aparece em vermelho, indicando uma falha. O retorno recebido do HTTP Server foi um pacote de 0 bytes — ele não retornou “nada”… Agora, vamos ver o que foi enviado, clicando com o mouse em cima no nome “testepost.htm” que aparece em vermelho … Vou redimensionar a tela para a janela do desenvolvedor ficar maior ..

Ao clicar sobre uma requisição na lista de requisições feitas pelo Web Browser, ele mostra os detalhes dessa requisição, mostrando primeiro definições gerais, e logo depois em “Request Headers” tudo o que ele enviou no HTTP Header da requisição realizada — são muitas informações. As duas mais importantes agora são “Content-Type: application/x-www-form-urlencoded” , e “Content-Length: 23“.

Elas indicam que essa requisição de POST possui um Http Body, onde o conteúdo possui 23 bytes, e está CODIFICADO no formato “x-www-form-urlencoded“. Agora, se “rolarmos” essa tela para baixo, veremos algo interessante no final da página:

O Web Browse mostra que os valores preenchidos nos campos de input dentro do formulário foram enviados … Se clicarmos em “view source”, veremos COMO ele realmente enviou o corpo (Http Body) dessa requisição:

CAMPO1=Ola&CAMPO2=Mundo

Agora, conte quantas letras e símbolos aparecem nessa linha … Exatamente 23, que é o Content-length indicado no HTTP Header 😀 Após o HTTP Header, foi enviado um HTTP Body.

E, você também reparou ? Os dados estão “codificados” como se fossem os dados de uma requisição do tipo GET — URL Encoded … por isso o Web Browser informou que o Content-Type é “x-www-form-urlencoded” ( dados codificados igual a uma URL).

E AGORA ?

Faça um teste… volte para a página anterior, e no lugar da palavra “Mundo”, digite “Você S&A”.. e veja novamente nos detalhes da requisição, como os dados foram enviados pelo Web Browser:

CAMPO1=Ola&CAMPO2=Voc%EA+S%26A

Repare QUATRO coisas importantes:

  • Ao usar um caractere acentuado (ê – letra e minúscula com acento circunflexo), ela foi enviada utilizando o PERCENT ENCODING contendo o valor hexadecimal “EA” (recomendo fortemente a leitura de O que é CODEPAGE e ENCODING – Parte 01 e demais posts). Se consultarmos a tabela de caracteres do Windows (em português ou inglês, CP1252 ou CodePage 1252), veremos que o código Hexadecimal EA representa a letra “ê”
  • O espaço em branco foi codificado com o sinal de “+” (mais)
  • Como usamos no conteúdo do texto um caractere separador de parâmetros do protocolo “&”, ele foi codificado como “%26” — que na prática é o código hexadecimal do caractere “&”
  • E , por fim, o Content-length aumentou para 33 … veja, o tamanho do HTTP Body é o numero de bytes usados para representar os dados CODIFICADOS.

Mais informações, mais …

Se você ligar o log de requisições HTTP do Totvs Application Server, você vai reparar que o conteúdo do POST não e mostrado no log … ele não é mostrado pois pode ser muito grande, e codificado de formas que ele não pode ser visualmente visto no log, por isso o LOG das requisições recebidas mostra apenas o HTTP Header no Application Server.

Já existem outros posts sobre AdvPL ASP, mas que não entram “tão fundo” no protocolo … se voce quiser complementar seus conhecimentos, recomendo a leitura a partir do Protheus e AdvPL ASP – Parte 01 e demais posts da seqüência. Mais para frente vamos abordar boa parte do que foi mostrado sobre AdvPL ASP aqui no protocolo HTTP, porém com uma visão mais “interna” de como as coisas funcionam 😀

F.A.Q.

E, como cada post pode levantar muitas dúvidas, vamos ver algumas… respondidas de forma simples.

  • Esses exemplos de HTML, estão “corretos” ? Que versão de HTML esses arquivos estão usando ?

Então, os exemplos funcionam, o Web Browse “engole”, mas eles não estão usando as melhores práticas de codificação. Por convenção, a primeira linha de um arquivo HTML “elegante” informa a versão do HTML utilizada. Para indicar o uso de HTML5, a primeira linha do arquivo HTML deve ser

<!DOCTYPE html>

A partir de agora, nossos exemplos vão ser “elegantes”, e declarar o uso de HTML5.

  • Por quê foi usado o CodePage CP-1252 ? Em nenhum lugar isso aparece ?

Essa é a pergunta de 1 milhão ! No meio do HTTP Header da requisição feita pelo Web Browser, ele informa em alguns pontos o que ele “aceita” de retorno …

Accept-Encoding: gzip, deflate, br
Accept-Language: pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7,es;q=0.6,sv;q=0.5

Como o Browser está configurado para Português do Brasil, rodando em uma máquina Windows, e em nenhum momento o HTTP Server informa um detalhamento sobre quais caracteres estão sendo utilizados — ao pedir o arquivo “default.htm” ou “testepost.htm”, é informado no HTTP Header apenas que o content-type é “text/html”, sem mais detalhes inclusive no corpo do HTML — o Web Browser assume que ele deve tratar o retorno baseado no que ele aceita .. e trata os caracteres acentuados enviados na página como “CP1252”.

  • CP-1252, WINDOWS-1252 e ANSI são a mesma coisa ?

R: Na prática, SIM. Em versões anteriores do Windows, os CodePages utilizados pelo DOS ( 437 e 850, para inglês e português, respectivamente) foram nomeados como code-pages OEM, e a nova tabela ASCII do Windows como ANSI, e posteriormente WINDOWS-1252 e CP-1252. Existe um codepage muito parecido com o CP1252, usado ainda em algumas aplicações, chamado IS8-8859-15 — mas que possui diferenças na interpretação gráficas dos caracteres em faixas específicas. ** Curiosidade: Desde a primvera versão do Totvs Application Server, em meados de 1999, para recompilar e executar os códigos do ERP da Microsiga – ainda escritos para MS-DOS — havia a necessidade de conversões de OEM para ANSI e vice-versa, desde arquivos de texto e dados. Para isso, foram disponibilizadas as funções OemToAnsi() e AnsiToOem() na linguagem AdvPL, que estão presentes desde então.

  • Posso usar UTF8 dentro do meu arquivo HTML ?

SIM, mas isso exige que você informe dentro do HTML que os caracteres do corpo do HTML ( HTML Body – não confunda com HTTP Body, o corpo do HTML começa após a declaração “<body>” e termina na declaração “</body>” ). Vamos entrar em mais detalhes disso mais para a frente, mas a título de curiosidade, uma das formas de você especificar que o texto dentro do seu HTML está codificado como UTF-8, é acrescentar antes do <body> a sequencia de linhas abaixo:

 <head>
 <meta charset="UTF-8">
 </head>

Quer fazer um teste ? Abra o arquivo “testepost.htm” com um editor um pouco mais avançado ( recomendo o NOTEPAD++), troque a codificação do arquivo para UTF-8, e coloque acento na palavra início … faça isso, salve e reabra a página no Web Browse — sem colocar o meta-charset .. Você vai ver que no lugar do “í” vão aparecer duas letras esquisitas … ( UTF8 interpretado erroneamente como CP1252), e depois de acrescentar as linhas head e meta-charset, salvar e recarregar o arquivo pelo Web Browser … o acento aparece certinho ! Depois vamos ver como fazer isso usando o HTTP Header 😀

*** Usar UTF-8 traz outras implicações, que veremos mais adiante. A mais imediata é que, ao abrir um campo de digitação de informação para o usuário preencher, os dados enviados para o servidor HTTP estarão codificados em UTF-8. E isso exige que você trate isso adequadamente ao receber os dados para a execução de páginas dinâmicas. Não se preocupe com isso agora, veremos esses detalhes mais para a frente.

  • Existem outros Content-Type usados para POST ?

R: SIM, para muitas outras finalidades. Normalmente o Web Browser usa o Content-Type “multipart/form-data” para submeter arquivos (POST de formulários HTML com upload de arquivos para o HTTP Server), e diversos outros Content-Types são usados na comunicação entre aplicações por serviços SOAP ou REST sobre HTTP, como text/xml, text/json … calma que veremos isso em detalhes mais pra frente.

Conclusão

Quanto mais informações são apresentadas, mais informações são necessárias. Aos poucos vamos abrindo o que tem por baixo do capô do Web Browse e dos protocolos, e vamos ver várias formas de se fazer muita coisa ! Espero que este conteúdo sempre lhe seja útil, e novamente desejo a todos TERABYTES DE SUCESSO ❤

Referências

O Protocolo HTTP – Parte 04

Introdução

No post anterior, vimos as requisições GET que um Web Browse fez a um Totvs Application Server configurado como um servidor de páginas estáticas, e mais detalhes do protocolo, criando uma página html bem simples, e colocando ela como “tela de entrada” do site. Agora, vamos ver como fazer um programa AdvPL recuperar essa página do Web Site.

Função HTTPGET()

De uma forma simples, a função HttpGet() do AdvPL permite ao servidor de aplicação conectar em um Web Site, e recuperar um conteúdo usando a instrução GET do protocolo HTTP. Vamos ver isso um pouco mais a fundo, usando o Protheus Server como servidor de HTTP, e também dando atenção a funções auxiliares para os tratamentos dos possíveis retornos, entre outros truques interessantes. Para isso, vamos começar com um programinha bem simples:

#include "protheus.ch"

USER Function Myget1()
cHtml := HttpGet("http://localhost/")
conout("HTTP Body retornado:")
conout(cHtml)
return

Antes de mais nada, remova ou comente as configurações de log de requisições da seção http, senão o log vai ficar muito cheio … Ao compilar e executar o programa acima — pelo SmartClient , e verificar o log de console do Application Server, no nosso ambiente HTTP montado no post anterior, devemos obter o seguinte resultado:

HTTP Body retornado:
<html>
<body>
Meu primeiro Web Site 
</body>
</html>

Então, onde estão os Headers de retorno ?

A função HttpGet() retorna o “Html Body” retornado pelo Servidor Http acessado, mas também é possível retornar o Header da requisição usando o 5o parâmetro por referência.. vamos melhorar o programa, criando agora a função MyGet2():

USER Function Myget2()
cHeadRet := ''
cHtml := HttpGet("http://localhost/",,,,@cHEadRet)
conout("HTTP Header retornado:")
conout(cHEadRet)
conout("HTTP Body retornado:")
conout(cHtml)
return

Agora, ao executar o programa, vamos conferir o retorno:

HTTP Header retornado:
HTTP/1.1 200 OK
Date: Sat, 23 Jan 2021 02:33:11 GMT
Server: Application Web Server
MIME-version: 1.0
Content-type: text/html
Last-modified: Fri, 22 Jan 2021 23:33:29 GMT
Content-length: 55
X-Frame-Options: SAMEORIGIN

HTTP Body retornado:
<html>
<body>
Meu primeiro Web Site 
</body>
</html>

E se houver um erro ? O que pode dar errado ?

Então, muita coisa pode dar errado, desde não ser possível estabelecer a conexão HTTP com o host/servidor informado na URL, até ser possível estabelecer a conexão, mas o servidor não entender o pedido, ou não conseguir processar a requisição … Vamos começar com uma requisição de algo que não existe, alterando a linha 2 desse fonte, para pedir o arquivo “naoexiste.htm”:

cHtml := HttpGet("http://localhost/naoexiste.htm",,,,@cHEadRet)

Agora, vamos ver o que é retornado ao executar esse código, no log de console:

HTTP Header retornado:

HTTP Body retornado:
NIL

Então, com esse retorno a gente fica simplesmente “a pé”, não temos idéia do que aconteceu … Vimos apenas que o Header retornado por referencia está vazio, e o corpo HTTP retornado para a variável cHTML não contém uma string, mas o valor “NIL” (nulo) AdvPL.

Agora, vamos usar algumas funções auxiliares de tratamento de retorno de requisição.. e criar a função MyGet3():

USER Function Myget3()
  cHeadRet := ''
  cHtml := HttpGet("http://localhost/naoexiste.htm",,,,@cHEadRet)

  // Apos chamar a função HttpGet(), podemos obter alguns resultados \
  // usando algumas funções auxiliares 
  cHttpStatus := ''
  nHttpCode := HTTPGETSTATUS(@cHttpStatus)

  conout("HTTP Return Code ... "+cValToChar(nHttpCode))
  conout("HTTP Return Status ... "+cValToChar(cHttpStatus))

  If empty(cHEadRet)
    conout("HTTP Header nao retornado...")
  Else
    conout("HTTP Header retornado:")
    conout(cHEadRet)
  Endif

  if empty(cHtml)
    conout("HTTP Body nao retornado...")
  Else
    conout("HTTP Body retornado:")
    conout(cHtml)
  Endif

return

E, agora, ao executar esse código, temos algumas novas informações:

HTTP Return Code … 404
HTTP Return Status … Requested file not found
HTTP Header nao retornado:
HTTP Body nao retornado…

Repare que agora a gente sabe o que aconteceu … o status 404 retornado indica que a página solicitada (naoexiste.htm) realmente não existe no servidor … risos … Agora, vamos mexer no fonte novamente, para buscar explicitamente a página de entrada do site, chamada “default.htm”.

cHtml := HttpGet("http://localhost/default.htm",,,,@cHEadRet)

Agora, ao rodar o U_MYGET3 com esta alteração, obtemos o seguinte resultado:

HTTP Return Code … 200
HTTP Return Status … OK

( o resto está igual ) 

E se der erro de conexão ?

Vamos ver … alteramos o programa para buscar a página “default.htm”, mas agora vamos colocar uma porta DIFERENTE da 80 … Simples assim:

cHtml := HttpGet("http://localhost:8080/default.htm",,,,@cHEadRet)

Se não tiver nenhum serviço HTTP na porta 8080 do seu computador, essa requisição deve falhar .. então, vamos ver o resultado da execução do programa com esta alteração no log de console:

HTTP Return Code … 10061
HTTP Return Status … Connection refused.
HTTP Header nao retornado…
HTTP Body nao retornado…

O erro retornado não é do HTTP, mas da interface de SOCKETS do sistema operacional … A conexão foi “recusada” pois não tem nenhum serviço “ouvindo” na porta 8080. Agora, vamos colocar um nome de host ou de um site que não existe …

cHtml := HttpGet("http://naoexiste/default.htm",,,,@cHEadRet)

E, temos então o seguinte retorno:

HTTP Return Code … 11001
HTTP Return Status … Host not found.
HTTP Header nao retornado…
HTTP Body nao retornado… ... 

E os outros parâmetros ?

Sim, tem vários …. Vamos ver primeiro o segundo parâmetro da função. Ele chama-se “cGetParms” — vide documentação completa na TDN no link https://tdn.totvs.com/display/tec/HTTPGet — e serve para a passagem de parâmetros pela URL.

A operação de GET do HTTP permite eu solicitar um arquivo ou página ao servidor HTTP, e na própria URL podemos colocar um ou mais parâmetros, na forma “?chave=valor[,chave2=valor2…]” . Estes parametros são conhecidos como parâmetros de busca ou “search parameters”. E existem duas formas de passar estes parâmetros no AdvPL : Eles podem estar diretamente na URL — informada no primeiro parâmetro — ou separados da URL, no segundo parâmetro da chamada da função HttpGet() — mas não podem estar em ambas ao mesmo tempo.

A passagem de parâmetros via URL deve ser feita após a página solicitada, usando o separador “?”(interrogação), seguido de um identificador ou chave, o sinal de igual “=” e um determinado valor. Para especificar mais combinações de chave e valor, use o separador “&” (e comercial). Por exemplo:

http://localhost/default.htm?id=no&page=1

Dessa forma, estou solicitando uma requisição para o protocolo HTTP, para o host “localhost”, solicitando a página “default.htm”, e informando pela URL os parâmetros “id” com o valor “no”, e “page” com o valor “1”. A nossa página default não tem avaliação de parâmetros, então qualquer coisa informada será “ignorada”, mas será transmitida ao HTTP Server.

Para passar a URL acima para a função HTTPGET, podemos passar os parâmetros juntos da URL ou separados. Vale lembrar que, não é necessário colocar a “?” interrogação para passar os parâmetros separados, veja abaixo os exemplos, o resultado final será o mesmo :

cReturn := HttpGet("http://localhost/default.htm?id=no&page=1")
cReturn := HttpGet("http://localhost/default.htm","id=no&page=1")

Observações muito importantes

Além dos separadores a serem utilizados para indicar o inicio dos parâmetros de busca (“?”), o separador entre o identificador (ou chave) “=” e o separador para informar mais parâmetros “&”, existem alguns pontos muito importantes:

  • Nos parâmetros de busca, em uma chave deve-se evitar usar caracteres especiais, símbolos e acentuação.
  • Caso seja necessário o uso de caracteres deferentes do alfabeto alfanumérico (letras maiúsculas e/ou minúsculas não acentuadas e números), quaisquer caracteres diferentes disso devem ser escritos em um formato chamado “URL Encoding” ou “Percent Encoding”

PERCENT QUEM ?

Cada caractere especial ou diferenciado pode ser convertido no formato “%HH”, onde HH é a representação HEXADECIMAL — com letras maiúsculas — do código ASCII do caractere a ser enviado. Por exemplo, se eu quero passar a chave “nome” com o valor “C&A” — lembrando que “&” é um caractere separador da lista de parâmetros, eu devo obter o código ASCII da letra “&” — que é 38 — e converter para hexadecimal ( 26 ), então informar na URL http://localhost/default.htm?nome=C%26A

Outro detalhe importante, é que o caractere “espaço em branco”, pode ser passado de duas formas como parâmetro da URL: Pode ser %20, ou o sinal de soma “+”.

E como isso entra no HTTP Header ?

Simples assim, todos os parâmetros passados por GET são colocados na primeira linha do HTTP Header:

GET /default.htm?id=no&page=1 HTTP/1.1
Host: localhost
(... other fields ...)

Isso tem limite ?

Essa é uma pergunta importante… sim, têm limite sim. Mas o limite é arbitrário, depende da capacidade do servidor HTTP e do Web Browser utilizado. Em linhas gerais, a recomendação é que não se use mais do que 2000 caracteres em uma URL. A maioria dos Web Browses e HTTP Servers lida tranqüilamente dentro desse limite. Veja mais informações das referências.

Posso passar dados sensíveis pela URL ?

Então, poder você pode, mas não deve … afinal os dados ficam expostos na URL … para passar dados sensíveis, e maiores que 2000 bytes, veremos mais pra frente o comando POST do protocolo HTTP, que permite receber parâmetros pela URL (igual ao GET), mas também permite receber parâmetros pelo HTTP Body 😀

Conclusão

Aos poucos vamos mergulhando no HTTP, e ao mesmo tempo vamos ver o que o AdvPL nos oferece para lidar com essa camada. Espero que as informações lhe sejam muito úteis, e como de costume lhes desejo TERABYTES DE SUCESSSO !!!

Referências

O Protocolo HTTP – Parte 03

Introdução

Nos posts anteriores, vimos uma introdução ao protocolo, TCP, redes, e um glossário de muitos termos ligados a Internet e Redes de computadores. Agora, vamos voltar pro HTTP, explicando um pouco mais por dentro do que acontece, e usando o Totvs Application Server como um servidor de HTTP de arquivos e páginas estáticas.

Mas, já vimos isso no blog …

Sim, viram mesmo, em posts anteriores, e eu recomendo que você leia esses dois post, pois todas as informações sobre o protocolo HTTP preenchem algumas “lacunas” de informação que estes dois posts não contemplavam.

E agora ?

Se você não quiser, não precisa… mas eu recomendo ler os posts novamente, e configurar um Application Server como servidor de HTTP, porta 80. Basta criar uma pasta/diretório dentro do rootpath do seu ambiente — por exemplo “http-root”, e acrescentar no arquivo de configuração appserver.ini as seguintes chaves:

[HTTP]
ENABLE=1
PORT=80
PATH=C:\Protheus12\envp12127\http-root

No exemplo acima, basta trocar o PATH informado para a pasta que você criou. Agora, vamos fazer algumas brincadeiras e ver como o lance funciona por dentro. Esta é a configuração mínima para subir o Totvs Application Server como um servidor de páginas estáticas e arquivos.

Detalhes para novas builds de Application Server

O Totvs Application Server LG (Lobo Guará) possui implementações de porta muilti-protocolo — a mesma porta do Application Server que atende a conexões do SmartClient pode ser usada. No exemplo acima, vamos usar a configuração “tradicional”, sem a porta multiprotocolo. Para isso, basta acrescentar as chaves abaixo (destacadas em negrito) na seção [drivers] do arquivo de configuração (appserver.ini):

[drivers]
active=tcp
MultiProtocolPortSecure=0
MultiProtocolPort=0

Subindo o serviço

Ao iniciar o Totvs Application Server em modo console, nas mensagens de inicialização — em caso de sucesso — devemos ver entre as mensagens o seguinte:

Http server is ready.
Root path is c:\protheus12\envp12127\http-root\
Listening port 80 (default)

Então, a nossa pasta de publicações “raiz” está vazia, não tem nenhum arquivo. Agora, vamos ver o que acontece quando abrimos um Web Browse nessa máquina, e abrimos a URL http://localhost/

Agora, vamos ver o que o servidor HTTP recebeu e retornou para o Web Browse. Nas builds mais atuais do Totvs Application Server, existem duas novas configurações para a seção HTTP — usadas para teste e depuração, pois elas colocam no log de console cada requisição recebida pela porta HTTP, e cada resposta retornada para uma requisição. Vejamos como ficará a seção http com as novas configurações :

[HTTP]
ENABLE=1
PORT=80
PATH=C:\Protheus12\envp12127\http-root
LogRequest=1
LogResponse=1

Okay, agora paramos e iniciamos o Application Server, e pressionamos F5 no Web Browser — para tentar buscar a página raiz de publicação novamente, e vemos no log de console a mensagem recebida pelo Application Server, e o que ele retornou ao Web Browser.

[INFO ][SERVER] [22/01/2021 20:15:30]-[THREAD 11228] Received Http Connection!!! Starting Reading Data…
 Connection: keep-alive
 Cache-Control: max-age=0
 sec-ch-ua: "Google Chrome";v="87", " Not;A Brand";v="99", "Chromium";v="87"
 sec-ch-ua-mobile: ?0
 Upgrade-Insecure-Requests: 1
 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
 Sec-Fetch-Site: cross-site
 Sec-Fetch-Mode: navigate
 Sec-Fetch-User: ?1
 Sec-Fetch-Dest: document
 Accept-Encoding: gzip, deflate, br
 Accept-Language: pt,en-US;q=0.9,en;q=0.8
 [THREAD 5348] End Message
 
[22/01/2021 20:15:30]-[THREAD 5348] Starting Parsing
 [22/01/2021 20:15:30]-[THREAD 5348] End Parsing. Result 1
 [22/01/2021 20:15:30]-[THREAD 5348] Processing GET | CUT_HEAD

 [INFO ][SERVER] [22/01/2021 20:15:30]-[THREAD 5348] Sending Message
 HTTP/1.1 404 Requested file not found

O servidor HTTP recebeu um HTTP Header cheio de detalhes, solicitando a página raiz do Web Site. Como essa página ainda não existe — na verdade não tem nenhuma página (arquivo HTML) dentro da pasta de publicação http-root, foi retornado apenas o HTTP Header em negrito, informando o erro 404 — página não encontrada. A tela “bonitinha” que o Google Chrome desenhou é a forma dele mostrar ao usuário o que significa esse retorno do servidor.

Criando a página de entrada do site

De forma simples, apenas para ver o servidor funcionando, use um editor de texto simples — como o Notepad (Bloco de notas), crie um arquivo com o conteúdo abaixo, e salve ele dentro da pasta “http-root” com o nome “default.htm” :

<html>
<body>
Meu primeiro Web Site
</body>
</html>

E, agora, sem parar ou reiniciar o Application Server, volte no Web Browse, que aponta para http://localhost/, e pressione F5 novamente para tentar carregar a página, e veja o que aparece na tela:

E, ao verificar o log de console do Application Server, vamos ver o que foi enviado pelo browser, e o que o Application Server respondeu:

[22/01/2021 21:12:31]-[THREAD 23252] Received Message
GET / HTTP/1.1
Host: localhost
Connection: keep-alive
sec-ch-ua: "Google Chrome";v="87", " Not;A Brand";v="99", "Chromium";v="87"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7,es;q=0.6,sv;q=0.5
[THREAD 23252] End Message

[22/01/2021 21:12:31]-[THREAD 23252] Starting Parsing
[22/01/2021 21:12:31]-[THREAD 23252] End Parsing. Result 1
[22/01/2021 21:12:31]-[THREAD 23252] Processing GET | CUT_HEAD

[INFO ][SERVER] [22/01/2021 21:12:31]-[THREAD 23252] Sending Message
HTTP/1.1 200 OK
Date: Sat, 23 Jan 2021 00:12:31 GMT
Server: Application Web Server
MIME-version: 1.0
Content-type: text/html
Last-modified: Fri, 22 Jan 2021 23:33:29 GMT
Content-length: 55
X-Frame-Options: SAMEORIGIN

[INFO ][SERVER] [22/01/2021 21:12:31]-[THREAD 23252] Sending Message
<html>
<body>
Meu primeiro Web Site 
</body>
</html>

Entendendo o HTTP Header retornado

  • Primeira linha do Header de retorno, informa o protocolo utilizado ( Http versão 1.1 ), o código de retorno ( 200 significa “sucesso” ), e um texto complementar (opcional) relacionado ao código de retorno (OK).
HTTP/1.1 200 OK 
  • Segunda linha do Header de retorno, informa ao cliente (Web Browser) através da propriedade “Date” a data e hora atuais no servidor — no formato GMT (Greenwitch Mean Time), hoje chamado de UTC (Universal Time Coordinated).
Date: Sat, 23 Jan 2021 00:12:31 GMT
  • Terceira linha do header, informa através da propriedade “Server” uma informação sobre quem é a aplicação que respondeu a requisição. Meramente informativo para o Web Browse.
Server: Application Web Server
  • Quarta linha do header — mantida por compatibilidade no servidor Protheus, não é importante neste contexto.
MIME-version: 1.0
  • Quinta linha do header, importante, informa ao cliente que o conteúdo do HTTP Body (Corpo de retorno HTTP) deve ser interpretado como texto HTML.
Content-type: text/html
  • Sexta linha do header, informa ao cliente a data e horário no formato GMT da última modificação desse arquivo html no servidor — vamos ver por que isso é útil daqui a pouco.
Last-modified: Fri, 22 Jan 2021 23:33:29 GMT
  • Sétima linha do header, importante, informa ao cliente o tamanho do texto html que será retornado no corpo HTTP.
Content-length: 55
  • Oitava linha do header, informada por questões de segurança, instrui ao Client que ele não deve permitir uma requisição para este conteúdo quando ele estiver processando uma página de um outro website. Vamos ver isso mais pra frente.
X-Frame-Options: SAMEORIGIN

Até aqui tudo bem ? Então, tudo funcionando bem … agora, sem fechar o navegador — estou usando o Google Chrome — vamos pressionar F5 novamente, para “recarregar essa página”. Tudo deve ser igual, certo ? — Não necessariamente ….. a página mostrada no Browse foi a mesma … mas alguma coisa mudou na requisição feita pelo Browser, e no retorno do Application Server… Veja abaixo o que mudou na solicitação e no retorno, destacado em negrito:

[22/01/2021 21:25:18]-[THREAD 23252] Received Message
GET / HTTP/1.1
Host: localhost
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Google Chrome";v="87", " Not;A Brand";v="99", "Chromium";v="87"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7,es;q=0.6,sv;q=0.5
If-Modified-Since: Fri, 22 Jan 2021 23:33:29 GMT
[THREAD 23252] End Message

[22/01/2021 21:25:18]-[THREAD 23252] Starting Parsing
[22/01/2021 21:25:18]-[THREAD 23252] End Parsing. Result 1
[22/01/2021 21:25:18]-[THREAD 23252] Processing GET | CUT_HEAD

[INFO ][SERVER] [22/01/2021 21:25:18]-[THREAD 23252] Sending Message
HTTP/1.1 304 Not Modified
Date: Sat, 23 Jan 2021 00:25:18 GMT
Server: Application Web Server
MIME-version: 1.0
X-Frame-Options: SAMEORIGIN

Na última linha do header http da requisição feita pelo Web Browser, apareceu uma nova informação :

If-Modified-Since: Fri, 22 Jan 2021 23:33:29 GMT

Isso indica que, essa página html já está na memória do Web Browse ( conhecido por CACHE de navegação do Browser). Neste caso, ele informa que o servidor não precisa enviar de novo o arquivo , DESDE QUE ele não tenha sido modificado desde a ultima vez que ele pediu o arquivo. Ele recebeu essa informação na primeira requisição que ele fez, quando o Application Server informou a propriedade Modified-since ao retornar o HTML.

Quando o Application Server recebe essa informação, ele verifica se o arquivo no disco realmente ainda está com a última data de modificação igual ao que o Browser conhece … e como não houve alteração nesse arquivo desde então, o servidor não manda mais para ele o arquivo, e nem retorna status “200 OK”, mas sim um Header HTTP de retorno com o status 304 (Not Modified). Desse modo, ele não precisou trafegar pela rede o conteúdo da página default.htm … e o browse pode usar a informação que ele tem no cache dele. E ele mostra a página 😀

Reparem que nesse header de retorno, não entrou nenhuma informação de content-type, content-length ou modified-since … afinal, o Web Browse já tem tudo isso … isso economiza banda de rede na navegação de páginas.

Acessando a mesma página, mas usando o “nome” da máquina

Agora, vamos no prompt de comando do sistema operacional, e usamos o comando “tracert localhost”. Esse comando serve para coisas mais avançadas, mas atende a nossa necessidade agora, para obter de uma forma simples o HOSTNAME, ou o “nome” da máquina para efeitos de conexão de rede.

C:\Users\juliow>tracert localhost
 Tracing route to NOTE-I7-JULIOW [::1]
 over a maximum of 30 hops:
 1    <1 ms    <1 ms    <1 ms  NOTE-I7-JULIOW [::1]
 Trace complete.

Com isso, descobrimos que o nome da minha máquina é “note-i7-juliow”. Agora, vamos no Chrome novamente, e entramos com a URL http://note-i7-juliow/, e vejamos o que acontece com os logs do Totvs Application Server:

[INFO ][SERVER] [22/01/2021 21:38:27]-[THREAD 23252] Received Http Connection!!! Starting Reading Data…
[22/01/2021 21:38:27]-[THREAD 23076] Received Message
GET / HTTP/1.1
Host: note-i7-juliow
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7,es;q=0.6,sv;q=0.5
[THREAD 23076] End Message

[22/01/2021 21:38:27]-[THREAD 23076] Starting Parsing
[22/01/2021 21:38:27]-[THREAD 23076] End Parsing. Result 1
[22/01/2021 21:38:27]-[THREAD 23076] Processing GET | CUT_HEAD

[INFO ][SERVER] [22/01/2021 21:38:27]-[THREAD 23076] Sending Message
HTTP/1.1 200 OK
Date: Sat, 23 Jan 2021 00:38:27 GMT
Server: Application Web Server
MIME-version: 1.0
Content-type: text/html
Last-modified: Fri, 22 Jan 2021 23:33:29 GMT
Content-length: 55
X-Frame-Options: SAMEORIGIN
[INFO ][SERVER] [22/01/2021 21:38:27]-[THREAD 23076] Sending Message
<html>
<body>
Meu primeiro Web Site 
</body>
</html>

[22/01/2021 21:38:28]-[THREAD 23252] Received Message
GET /favicon.ico HTTP/1.1
Host: note-i7-juliow
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
Accept: image/avif,image/webp,image/apng,image/,/*;q=0.8
Referer: http://note-i7-juliow/
Accept-Encoding: gzip, deflate
Accept-Language: pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7,es;q=0.6,sv;q=0.5
[THREAD 23252] End Message

[22/01/2021 21:38:28]-[THREAD 23252] Starting Parsing
[22/01/2021 21:38:28]-[THREAD 23252] End Parsing. Result 1
[22/01/2021 21:38:28]-[THREAD 23252] Processing GET | CUT_HEAD

[INFO ][SERVER] [22/01/2021 21:38:28]-[THREAD 23252] Sending Message
HTTP/1.1 404 Requested file not found

Nossa, algo mudou bem aqui … vamos por partes:

  • O browse não foi fechado, eu pedi a página de entrada do site, que não mudou … por que o Browse não mandou um “if-modified-since” ?
    R: O browse já tem em cache a página de entrada do site “localhost”, agora voce pediu a página de entrada do site “note-i7-juliow” … Para o Browse, isso é um OUTRO Web Site. Ele não pode misturar os caches ..
  • Por que apareceu mais uma requisição “GET favicon.ico” feita pelo Browse ?
    R: Então, isso é um comportamento do Browse, e é bem provável que ele tenha feito isso também no primeiro teste — http://localhost/ — e eu não tenha visto … O Browse pede ao servidor http um arquivo chamado “favicon.ico” – trata-se de um arquivo de imagem contendo um “ícone”, que quando existe na pasta de publicações raiz ( no nosso caso, http-root), o Browse recebe esse arquivo e mostra a imagem do ícone contida no arquivo na barra de navegação do site, e caso você adicione esse site aos seus “Sites Favoritos” no seu navegador, ele também mostra o ícone do site junto do nome do site. Repare quando você abre o site do blog, aparece o ícone do “WordPress” na aba do Browser…

Vamos criar um ícone ?

Embora ele seja uma imagem, a extensão “.ico” é um formato diferente, não é igual a um BMP ou JPEG … e não é qualquer programa gráfico que permite você criar um ícone .. Mas, graças a internet, eu fiz uma “carinha” com o PaintBrush, salvei em disco, usei um site de conversão de imagens para ícone gratuito — https://icoconvert.com/ — , criei um ícone, fiz download para o meu computador, e agora eu vou salvar ele no disco, dentro da pasta “http-root”, com o nome de “favicon.ico” .. e vamos ver o que acontece ?

  • Observação : Como o navegador já tinha aberto o site, e viu que não tinha o ícone, ele não vai mais perguntar isso ao servidor, a menos que você feche o Browse e abra novamente.

Agora, vejam na aba superior do browse… do lado esquerdo ao nome do host, “apareceu” o ícone que eu criei 😀

Tem mais diferenças ?

Sim… reparem que, ao trocar “locahost” para o nome da máquina, o Web Browse mostra uma informação adicional na barra de endereços … um ícone em forma de triângulo com uma exclamação dentro, e do lado a mensagem “Não seguro” — isso serve para informar ao usuário que está navegando no Web Site, que a conexão “não é segura”.

Calma, não criemos pânico … o termo “seguro” na prática significa “não criptografado”… aí já entramos em conexão segura ( Protocolo HTTPS ..), calma que vamos ver isso mais pra frente, e já tem posos no blog que falam sobre criptografia e segurança — que eu fortemente recomendo a leitura …

Conclusão

Por hora eu acho que aqui já tem bastante o que mastigar … muita informação ao mesmo tempo dá “tilt” 😛 Então, por hoje é só pessoal, aguardem mais novidades e detalhes no próximo post. E desejo como sempre a todos TERABYTES DE SUCESSO!!!

Referências

O Protocolo HTTP – Parte 02 – Glossário

Introdução

No post anterior, vimos com alguns detalhes “internos” uma requisição HTTP, e várias terminologias — como HTTP, Web Browser, Web Server — e alguma coisas “por dentro” do protocolo. Agora, para continuar sem ficar “voando” muito, vamos ver o que significam algumas terminologias usadas para definir componentes do universo de redes, conectividade e internet, de uma forma “conectada”.

Glossário

Rede de computadores — Dois ou mais computadores, ligados entre si — conectados — por um meio físico (cabeamento) ou sem fio (wireless), onde os equipamentos conectados trocam informações entre si usando a rede.

Wireless (Rede Sem Fio)— Termo utilizado para indicar que a conexão entre os equipamentos é “sem fio” (Wi-fi, Bluetooth e 3G são exemplos de conexões Wireless).

Rede cabeada — (ou wired — com fio) — Termo utilizado para indicar que a conexão entre os equipamentos possui um ou mais “fios” e/ou “cabos”. Os tipos dos fios e cabos podem variar muito dependendo do tipo da rede. Pode ser desde cabo coaxial (NET/Claro, TVs a Cabo), cabo de par trançado (redes cabeadas domésticas e industriais), Fibra Óptica (redes de alta velocidade ou “Banda Larga”).

Roteador — (Router) — Equipamento responsável por direcionamento e encaminhamento de pacotes de rede entre redes diferentes. É uma das peças chave responsáveis pelo funcionamento da Internet.

Switch (ou Comutador) e Hub — Outros dois tipos de equipamento para conectar computadores em rede. São mais simples que um roteador, vamos ver depois onde e por quê.

MODEM — Nome dado a um equipamento de telecomunicações, capaz de converter e desconverter (ou modular e demodular — MO-DEM) uma informação de um determinado tipo de sinal ou informação para ser trafegado por um meio físico. Por exemplo, as conexões primeiras conexões com a Internet para uso doméstico no Brasil foram feitas através de linhas telefônicas convencionais, que suportavam apenas modulação de áudio (linhas analógicas). Usando um MODEM no seu computador, você estabelecia uma conexão com outro MODEM, no servidor ou provedor de internet, e o MODEM transformava os pacotes TCP em áudio analógico para serem enviados pela linha telefônica, e o MODEM no servidor recebia esse áudio, e recodificava o pacote original que o seu computador enviou, e vice-versa.

Protocolo de comunicação — Conjunto de regras e definições de como os dados são transmitidos (enviados e recebidos) em telecomunicações e redes de computadores. Por exemplo TCP/IP, HTTP, FTP, SMTP, IMAP, POP, SSH…

Cliente (Client) — Aplicação que inicia uma conexão para uma aplicação Servidora (Server).

Servidor (Server) — Aplicação que espera e atende a requisições de conexões feitas por uma aplicação Cliente.

IP – Numero de identificação único de uma interface de rede atribuído manualmente ou automaticamente, que permite endereçar (acessar) portas de comunicação da interface através da rede. Ele “identifica” o computador na rede.

Porta (TCP) – Número atribuído a um canal de conexão, associado ao IP. Uma conexão de rede TCP envolve o par de informações IP + Porta da interface de rede de origem e de destino. No protocolo TCP/IP v4, uma interface de rede suporta 65536 portas diferentes por interface — numeradas de 0 a 65535. Vale lembrar que a faixa de portas de 0 a 1024 possuem uso reservado para determinados protocolos, e as as demais portas são utilizadas a critério do sistema operacional do equipamento — incluindo uma faixa de portas reservada para aceite de conexões — conhecidas por “faixa de portas efêmeras”.

Porta efêmera (TCP – Ephemeral port) — Número de porta atribuído automaticamente para um IP em uma conexão.

Porta ouvinte (TCP – Listening Port) — Número de porta fixo, atribuído a uma interface de rede, para ficar esperando (listening) por uma conexão de rede. Por exemplo, um Web Server HTTP aloca a porta 80 (que faz parte de uma faixa de numeração pré-definida) em uma ou mais interfaces de rede de um equipamento para aceitar as conexões HTTP vindas de um ou mais navegadores Web ou aplicativos.

DDOS (Distributed Denial of Service) — Um tipo de ataque coordenado utilizando vários computadores a um determinado Web Site/ Web Server, tendo como objetivo “metralhar” o Web Server com requisições, até que haja o esgotamento de recursos computacionais e ele não consiga responder as requisições — causando a “negação de serviço” (Denial of Service).

DHCP (Dynamic Host Configuration Protocol) — Protocolo utilizado entre os equipamentos de uma rede e um equipamento ou serviço dedicado (DHCP Server) para atribuir dinamicamente um IP a um equipamento que quer ingressar em uma rede.

Pacote de Rede TCP (TCP Packet) — Nome dado a uma sequência de bytes que são transferidos entre interfaces de rede através de uma linha de comunicação (ou conexão). Um Cliente envia um pacote contendo uma requisição HTTP GET para um servidor HTTP, o servidor responde essa requisição para este cliente, enviando um ou mais pacotes em sequência com a resposta.

Socket — Como uma conexão TCP é identificada pelo par de informações IP+Porta das duas interfaces envolvidas, cada uma dessas “pontas” (end points) é chamada de Socket. Ao criar uma conexão, usamos um conjunto de funções (API) do sistema operacional para criar e manipular conexões. Um programa Cliente, ao criar uma conexão, recebe um número identificador dessa conexão, chamado de manipulador de socket (Socket Handle), e através dele usamos as demais funções da API para usar este Socket para enviar e receber pacotes de dados.

Firewall – Nome dado a uma barreira de proteção entre redes, ou entre um determinado equipamento e a rede. Permite criar regras de bloqueio para aceite e respostas de conexões, entre outros recursos. Firewall em si é uma funcionalidade, que pode ser exercida por um software em um equipamento, ou um equipamento inteiro dedicado para esta finalidade.

F.A.Q.

A estas alturas do campeonato muitas informações de uma vez dão um nó na cabeça da gente, e geram mais dúvidas… vamos tentar tirar algumas dessas dúvidas com alguns questionamentos devidamente respondidos.

  • Eu tenho NET com Internet em casa. Aquele MODEM da Net, o que ele faz ?
    R:
    Ele não é “apenas” um modem, ele acumula as funções de um roteador, decodificador, um DHCP local, e um modulador Wi-fi (rede sem fio). Ao aceitar a conexão de um dispositivo qualquer (Computador, telefone celular. SmartTV), ele atribui um endereço IP para cada equipamento — através do DHCP — criando uma rede local dentro da sua casa, fazendo a “ponte” de comunicação entre os seus dispositivos, e entre cada dispositivo e a Internet.
  • Tem umas entradas para cabos de rede atrás do modem da NET … pra que serve isso?
    R:
    Cada entrada daquela permite uma conexão cabeada entre os dispositivos e o modem. A conexão cabeada é mais rápida que a conexão wireless (Wi-fi). Se você tem uma SmartTv com NetFlix, por exemplo, embora a SmartTv tenha configuração de conexão por Wi-fi, procure instalar o Modem da Net perto da TV, e prefira uma conexão cabeada para ligar a SmartTv ao modem da NET.
  • Dá pra conectar dois computadores sem roteador ou modem ?
    R:
    Sim, a mais simples possível seria uma rede cabeada com um HUB ou SWITCH — dê preferência ao switch — Basta ter dois computadores com conexões para o cabo de rede RJ45 (aquele cabo com fiozinhos coloridos nas pontas)… mas sem um roteador e um DHCP, você tem que atribuir “manualmente” um endereço de IP para os 2, e ligar os dois em um Hub ou Switch. Você não vai ter uma “rede” efetivamente, mas vai conseguir abrir conexões via aplicativos entre uma máquina e outra. Como você não tem um DNS, ao colocar um servidor HTTP em uma das máquinas, você deve usar o IP da máquina que você definiu para abrir o site através da outra máquina.
  • Quais linguagens tem suporte a sockets e conexão TCP/IP ?
    R:
    Praticamente todas as linguagens de mercado possuem este recurso. Algumas como um recurso nativo da linguagem, outras mediante a utilização de Libs (bibliotecas de funções) ou Plugins. A maioria delas já possui classes ou componentes para lidar diretamente com os protocolos HTTP, FTP, IMAP, SMTP, etc..
  • O quanto a Internet é “segura” ?
    R:
    Depende do que você usa e de como você usa. Existem várias formas de colocar conteúdo na Internet, e algumas pessoas ou grupos de pessoas mal intencionadas, que buscam formas de usar as suas informações em benefício próprio. Phishing, Spam, Virus, Trojans, Ransomare, tem de tudo .. é prudente e importante estar atento a isso, uma vez que você vai usar esses recursos no seu dia-a-dia. Mas não importa o quanto se invista em segurança nos protocolos e equipamentos, os procedimentos de segurança começam no usuário.
  • Têm tudo isso no AdvPL ?
    R: SIM,
    com recursos nativos do servidor de aplicação — TOTVS Application Server — e da linguagem AdvPL. Vamos entrar nos detalhes de alguns posts anteriores, só que agora explicando a coisa “por dentro”, e usando AdvPL na brincadeira.

Conclusão

Eu sei, o tamanho disso assusta, mas é um conhecimento muito legal, e as tecnologias vindas dele são muito utilizadas — em larga escala e para implementar soluções integradas. Envolvem conhecimentos em diversas áreas — eletrônica, telecomunicações, hardware, software — mas abrem um universo de possibilidades. Espero que tenham gostado do post, já já sai mais !!!! E desejo a todos TERABYTES DE SUCESSO !!!

Referências

O Protocolo HTTP – Parte 01

Introdução

Para quebrar o gelo após um turbulento — eu diria, pandêmico — período de inatividade, vamos ver uma introdução ao protocolo HTTP — usado entre os navegadores de internet e os sites que acessamos, e entre serviços cliente e servidor de WebServices, entre outros. Vamos ver como funciona por dentro o que você vê aparecendo na tela do seu Web Browser.

Visão básica do Protocolo HTTP

O protocolo HTTP pode ser usado para transferir textos e demais dados de diversos formatos através de uma conexão de rede entre computadores. Para isso ser possível, uma requisição e uma resposta de requisição possuem sempre um cabeçalho — conhecido como “HTTP Header“, composto por uma ou mais linhas de texto em um formato e/ou disposição pré-determinados, e podem (ou não) conter dados adicionais após o cabeçalho, que chamamos de “corpo” (HTTP Body). No cabeçalho HTTP, cada linha deve ser finalizada com a seqüencia de caracteres ASCII 13 + ASCII 10 ( ou CRLF ), e a última linha do cabeçalho deve ser uma linha em branco, apenas com o “fim de linha” (CRLF). O Formato do corpo HTTP depende das definições e parâmetros informados no Header HTTP.

Conceito Cliente-Servidor

Um servidor HTTP é um programa que captura para uso exclusivo uma porta TCP ( por default, para o protocolo HTTP, porta 80, para HTTPS, porta 443), e atende a requisições feitas por um Programa cliente — normalmente um navegador de internet (Web Browse), realizadas através de uma conexão TCP-IP, iniciada pelo programa Cliente e atendida pelo Servidor HTTP.

Requisição mínima para HTTP

Uma requisição mínima HTTP precisa ter pelo menos a primeira linha de Header. Essa primeira linha deve conter uma operação ( GET por exemplo ), no seguinte formato:

GET / HTTP/1.0\r\n\r\n

Na descrição acima, “\r” significa o caractere ASCII 13 (ou CR), e “\n” significa o caractere ASCII 10 (ou LF). E, vale mencionar também, que essa requisição “mínima” pode não ser aceita por todos os serviços de HTTP, pois a maioria exige pelo menos uma linha a mais de cabeçalho, informando o nome do host (IP ou HostName) do website que você deseja acessar. E, essa requisição mínima indica ao servidor HTTP que você deseja recuperar a página inicial (ou “raiz”) do website hospedado no endereço solicitado. Na mensagem também vai a versão do protocolo usada, pode ser HTTP/1.0 ou HTTP/1.1 — veremos depois as diferenças entre eles.

Requisição mínima usando AdvPL

Quando utilizamos a função HttpGet() do AdvPL, da forma exemplificada abaixo, passamos para a função apenas o primeiro parâmetro, na forma de URL — protocolo : host [ : porta ] [ /pagina/ … ]

cRet := HttpGet("http://www.google.com.br/")

Internamente, a função HttpGet() vai “desmontar” a URL informada, para realizar a conexão com o host informado — na porta 80 (Http) — e montar um header de requisição http. Porém, o header http montado pela função vai ter mais conteúdo:

GET / HTTP/1.1
Host: localhost
Connection: Keep-Alive

Uma requisição de GET montada pela função HttpGet() do AdvPL após desmembrar a URL, contém apenas um header http contendo o Host, e um parâmetro de conexão ( Keep-Alive ), e por default informa o uso do protocolo HTTP 1.1

Retornos de requisição HTTP

Quanto ao retorno da requisição, feito pelo servidor que a atendeu, a estrutura dos dados retornados é similar a uma requisição http: O retorno do protocolo http contém um header http, onde a primeira linha possui o protocolo de resposta e código de retorno http, e normalmente contém mais linhas de header com outras informações — como o tipo e tamanho do retorno — e também pode (ou não) conter um corpo de retorno (http body), vamos ver abaixo um exemplo de retorno http:

HTTP/1.1 200 OK
Date: Tue, 19 Jan 2021 13:31:50 GMT
Server: Application Web Server
MIME-version: 1.0
Content-type: text/html
Last-modified: Tue, 19 Jan 2021 13:31:50 GMT
Pragma: no-cache
Set-cookie: SESSIONID=fcd8b19e003a5f8596abd71af7715bad
X-Frame-Options: SAMEORIGIN
Content-Length: 110

Essa resposta de requisição contém várias informações no http header. Na primeira linha, temos o protocolo de retorno ( HTTP 1.1 ), o código de retorno (200), e uma string opcional, descrevendo o código (OK), e as informações mais relevantes para este momento são o tipo de conteúdo retornado (Content-Type) e o tamanho do conteúdo (Content-Length). Vamos ver elas e as demais em detalhes mais pra frente.

Mais um retorno

Se fizermos uma requisição para a página inicial do Google em português — http://www.google.com.br — devemos obter um header http de retorno com outras informações, veja abaixo:

HTTP/1.1 200 OK
Date: Tue, 19 Jan 2021 14:53:36 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
Server: gws
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Set-Cookie: 1P_JAR=2021-01-19-14; expires=Thu, 18-Feb-2021 14:53:36 GMT; path=/; domain=.google.com.br; Secure
Set-Cookie: NID=207=g6_nLFdLmIpY9MoV1ueTqoa_pCkzwnnNf3WCIacz5e2t3vPKE2FrniMSldXPAENrRninRNCr0NjXVr2_tOHqkwX9O2CXNr5IMQs1YFK1Gam1-SGlCM_5WOpQeLAFw9NksyR6zVAsWinZms7zdL0tsI7NBM5z38rS1Gpvb1iqp_Y; expires=Wed, 21-Jul-2021 14:53:36 GMT; path=/; domain=.google.com.br; HttpOnly
Accept-Ranges: none
Vary: Accept-Encoding
Transfer-Encoding: chunked

Temos bem mais informações… desde instruções para caches/proxy, tipo detalhado do retorno, e instruções de como um navegador de internet (Web Browser) deve tratar esse conteúdo.

Pontos de atenção

Devemos lembrar que o protocolo HTTP serve para transporte de texto, arquivos de diversos formatos, imagens, scripts, XML, JSON, etc… tudo depende de o quê está sendo solicitado, e quem está atendendo ao pedido. Uma requisição GET para uma URL onde o servidor Http está atendendo a um Web Service do tipo REST deve retornar um text/json, um GET de uma URL onde está uma imagem deve retornar no corpo do http (Http body) o conteúdo (bytes) da imagem, e assim por diante.

Outro ponto importante, é que o conteúdo http retornado será tratado e avaliado pelo cliente que fez a requisição. Ao retornar um determinado tipo de conteúdo, como um arquivo PDF por exemplo, o navegador de internet utilizado pode optar por mostrar o conteúdo do arquivo na tela do usuário, ou oferecer a opção de “download” desse arquivo. Vamos começar com a abordagem mais simples, para depois acrescentar os comportamentos e os “por quês” das coisas.

HTML, o que é afinal ?

HTML (Hyper Text Markup Language) é uma forma de representar um conteúdo de uma página em um Web Site. Visto de uma forma simples, é um arquivo de TEXTO, onde podemos colocar caracteres especiais — chamados de “marcadores” — que indicam ao programa Cliente usado para a navegação no site (Web Browser) como ele deve “mostrar” esse conteúdo na tela. HTML não é um protocolo ou uma linguagem de programação, mas apenas uma forma de representar uma informação, criado para disponibilizar conteúdo de forma padronizada através da Internet. Para permitir a evolução do HTML, ele também é “versionado” — versões mais novas do protocolo possuem novos marcadores para mostrar conteúdos de novas formas, e novos tipos de conteúdo. Por outro lado, esse conteúdo somente será mostrado corretamente se o Web Browse utilizado suportar (entender) aquela versão do HTML. Atualmente o HTML está na versão 5, a versão anterior é a 4.01. As especificações do HTML e de outros formatos e protocolos para Internet são criados por uma comunidade internacional de colaboradores, chamada W3C (World Wide Web Consortium).

F.A.Q.

  • Um servidor HTTP funciona como se fosse um servidor de “arquivos” ?
    R: SIM
    — Na prática, ao configurar um servidor HTTP, na máquina onde está este servidor, você tem uma pasta no disco — chamada “pasta raiz de publicação”, onde você coloca pelo menos uma página (arquivo HTML) de “entrada” do seu Web Site, e pastas contendo outras páginas, e imagens e scripts e folhas de estilo (CSS) usadas pelas páginas.
  • Como o servidor HTTP “sabe” qual é a página inicial do meu site ?
    R:
    Isto normalmente é uma configuração do servidor, que já possui um nome de arquivo inicial da pasta raiz pré-definido (default), como por exemplo “index.html”.
  • Dá pra fazer um programa em HTML ?
    R: Não
    … MAS, dentro do HTML você pode usar JavaScript, que é capaz de acessar e alterar os componentes HTML da página durante a execução dos scripts e exibição da página. Os códigos JavaScript colocados dentro do HTML são executados pelo Web Browser, na estação do usuário que abriu a página.
  • Dá para fazer um site inteiro com apenas uma página?
    R:
    Sim, dá sim. Essa técnica se chama Single Page Application, onde você atualiza dinamicamente o conteúdo da página do site usando scripts, que buscam informações no Web Server sem a necessidade de alterar a URL da página em exibição, redesenhando a página no Web Browse.
  • Posso criar e publicar um Web Site no meu computador ?
    R: SIM, pode. Criar, com certeza .. MAS … mesmo que você instale um Web Server no seu equipamento, tornar o seu site “publicado” na internet, para que ele possa ser acessado através de uma URL (www.seusite.com.br) exige várias outras etapas e recursos. Vamos ver isso mais pra frente.
  • Posso criar e disponibilizar um site em uma rede “local” ? |
    R: Sim
    . Nestes casos, uma rede menor (Intranet) pode hospedar por exemplo um site que não estará publicado na Internet, mas será acessado por um ou mais Web Browser’s apenas dentro da Intranet.
  • Posso publicar um site dentro da rede da minha casa ?
    R: Sim
    , cada computador acessando a rede da sua casa tem um “nome” — conhecido por “hostname”. Ao publicar o servidor HTTP na sua máquina, qualquer máquina de dentro da sua rede pode acessar o seu site usando http://nomedamaquina/
  • Eu realmente preciso saber de tudo isso?
    R:
    Não necessariamente … mas saber como a BASE daquilo que você usa funciona, abre as portas para você entender tudo o que foi criado SOBRE esta base, e escolher a melhor forma de usá-la. E, isso é só o começo ….

Glossário

  • Web Browser – Programas que abrem sites e páginas, feitos para navegar na Internet — Como por exemplo o Mozzila Firefox, Microsoft Internet Explorer, Microsoft Edge, Google Chrome, Opera, Safari, entre outros.
  • Web Server – Programas utilizados para serem servidores de Web Sites para Internet. Os mais conhecidos e utilizados são o Apache, e o Microsoft IIS.
  • TCP/IP – Nome do conjunto (ou pilha) de protocolos projetado para comunicação entre computadores conectados em uma rede. Atualmente são utilizadas as versões TCP/IP v4 e TCP/IP v6.

Conclusão

O universo de Internet e Web Sites é imenso, mas quando dividido em camadas e componentes, fica mais fácil entender como ele funciona. Ao longo dos próximos posts, vamos entrar mais no que a linguagem AdvPL oferece para realizar integrações com Web Sites e Serviços que utilizam o protocolo HTTP. Espero que as informações desse post lhe sejam úteis, e lhes desejo como sempre TERABYTES DE SUCESSO 😀

Referências adicionais e leitura complementar

RUST – Primeiras Impressões

Introdução

Cada linguagem de programação tem o seu diferencial. Rust definitivamente coloca o termo “diferencial” em outro patamar.

“Rust é uma linguagem de programação multi-paradigma compilada, desenvolvida pela Mozilla Research. É projetada para ser “segura, concorrente e prática”, suportando os estilos puramente funcional, procedural, e orientado a objetos.” — Fonte: Wikipedia

Diferenciais

Rust e C++ estão no mesmo “patamar” de linguagens de desenvolvimento em nível de sistema (system-level development). Embora a sintaxe seja similar — escopo entre chaves, ponto-e-vírgula como final de instrução, etc — elas possuem diferenças significativas em muitos aspectos. Ambas possuem uma curva de aprendizagem “acentuada”, requerem um esforço maior para iniciantes.

Rust foi construído com foco em segurança e desempenho, ele consegue te dar a velocidade de um C++ (ou mais) com um mecanismo de gerenciamento de memória segura e eficiente. A compilação mais restritiva e assertiva consegue proteger melhor a execução, identificando situações que fatalmente resultariam em comportamentos “inesperados”, desde leaks de memória a race-conditions e acessos indevidos de memória. A compilação valida ao máximo o código antes de rodar a aplicação, gerando o executável final.

Em poucas linhas, a implementação da orientação a objetos gira em torno da implementação de métodos em uma estrutura ( similar ao Struct do c++ ), uma variável que é alterada deve ser declarada como mutável dentro de um escopo, mas o que realmente exige um mind-set diferenciado são os comportamentos de escopo e passagem de parâmetros — por default um objeto passado como parâmetro sem ser uma referência  “move” o escopo do objeto para a função. Graças a este comportamento e as declarações envolvidas nas funções e métodos, o compilador consegue “pegar” as situações de uso adversas que causariam problemas em um fonte C++ em tempo de compilação, sem a necessidade de um “garbage”.

Ping-Pong em Rust

Baseado nos exemplos de Ping-Pong em terminal texto usando Java (veja os posts em Java from Scratch), eu montei o mesmo aplicativo em Rust, usando uma implementação básica muti-plataforma de terminal, identificando o numero de linhas e colunas disponível — ao invés de “chumbar” 80×25 ! O fonte “main.rs” ficou assim:

extern crate ctrlc;

use console::Term;
use rand::Rng;
use std::env;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;

fn main() {
    // Pega os argumentos da linha de comando
    // espera apenas um argumento, numerico, indicando quantas
    // bolinhas o ping-pong deve mostrar
    let args: Vec<String> = env::args().collect();
    let mut maxballs = 1;
    if args.len() > 1 {
        maxballs = args[1]
        .trim()
        .parse()
        .expect("Please type a number for pingpong balls!");
    }

    // Tempo de cada iteração ( 1/4 de segundo )
    let mswait = std::time::Duration::from_millis(250);

    // Cria um terminal nao-bufferizado usando stdout
    let term = Term::stdout();
    let term_features = term.features();

    // Se o console nao tem o comportamento de um "terminal"
    // mostra o tipo do console em uso e finaliza a aplicação
    if !term_features.is_attended() {
        term.write_line(&format!("Terminal family = {:?}", term_features.family()))
        .unwrap();
        panic!("Terminal Features Unattended");
    }

    // Cria uma tupla para armazenar o limite de tela
    let pscreen = (term.size().0 as i32 + 1, term.size().1 as i32 + 1);

    // Cria o vetor com as bolinhas
    let mut balls: Vec<PingPongBall> = Vec::new();
    for _x in 0..maxballs {
        let mut myball = PingPongBall::new();
        // Sorteia a coordenada de tela inicial de cada bolinha
        myball.row = rand::thread_rng().gen_range(0, pscreen.0) + 1;
        myball.col = rand::thread_rng().gen_range(0, pscreen.1) + 1;
        // Acrescenta a bolinha no vetor
        balls.push(myball);
    }

    // Cria uma thread separada usando a implementacao
    // de tratamento de Control+C multi-plataforma
    let running = Arc::new(AtomicBool::new(true));
    let r = running.clone();

    ctrlc::set_handler(move || {
        r.store(false, Ordering::SeqCst);
    })
    .expect("Error setting Ctrl-C handler");

    // Esconde o cursor
    term.hide_cursor().unwrap();

    // Loop de processamento com
    // Tratamento elegante para control+c
    while running.load(Ordering::SeqCst) {
        // itera todas as bolinhas
        for myball in balls.iter_mut() {
            // Apaga a bolinha
            term.move_cursor_to(myball.col as usize, myball.row as usize)
                .unwrap();
            term.write_line(" ").unwrap();
 
            // Move a bolinha
            myball.pingstep(pscreen);
  
            // Desenha a bolinha
            term.move_cursor_to(myball.col as usize, myball.row as usize)
                .unwrap();
            term.write_line("O").unwrap();
        }
        // Espera um pouquinho ...
        thread::sleep(mswait);
    }

    // Ao sair, Mostra o cursor novamente
    term.show_cursor().unwrap();
}

struct PingPongBall {
    row: i32,
    col: i32,
    rowinc: i32,
    colinc: i32,
    step: i32,
    ballsize: i32,
}

impl PingPongBall {
    // Construtor do objeto
    // nomedafuncao(parametros) -> Retorno
    fn new() -> PingPongBall {
        PingPongBall {
            row: 1,
            col: 1,
            rowinc: 1,
            colinc: 1,
            step: 1,
            ballsize: 1,
        }
    }

    // Método para movimentação da bolinha
    // Recebe as coordenadas de tela como uma tupla
    fn pingstep(&mut self, pscreen: (i32, i32)) {
        if self.row + (self.step * self.rowinc) < 1
            || self.row + self.ballsize + (self.step * self.rowinc) >= pscreen.0
        {
            self.rowinc *= -1;
        }
        self.row += self.step * self.rowinc;

        if self.col + (self.step * self.colinc) < 1
            || self.col + self.ballsize + (self.step * self.colinc) >= pscreen.1
        {
            self.colinc *= -1;
        }
        self.col += self.step * self.colinc;
    }
}

Conclusão

Rust é uma linguagem relativamente “nova”, mas definitivamente seus diferenciais são bem atrativos !!! Veja nas referências do post mais informações a respeito !!! Se você gostou e quer saber mais sobre isso, comente no post 😀 E, claro, dê uma pesquisada, já tem bastante informação e exemplos na Internet !!

E, como de costume, desejo a vocês TERABYTES DE SUCESSO !!! 

Referências

 

Java from Scratch – Parte 06

Introdução

No post anterior (Java from Scratch – Parte 05), separamos a aplicação da classe que desenha e anima as bolinhas, vimos o que é um package, e uma forma simples de gerar um arquivo único para a aplicação (JAR). Agora, vamos aproveitar esse código, e criar um Ping-Pong com interface gráfica 😀

Interface gráfica com Java

O Java tem suporte nativo para aplicações com interface gráfica (Janelas/GUI) através de duas bibliotecas: AWT e SWING. A AWT usa e depende de recursos do sistema operacional em uso no equipamento para desenhar as telas, enquanto a SWING é independente de plataforma — ela é a responsável pela renderização — entre outras diferenças. Embora existam diferenças de “peso” de execução entre os componentes de ambas, Swing possui mais funcionalidades, suporte a MVC, features avançadas, etc.

Como desenhar gráficos na interface ?

Bem, essa pergunta consumiu algumas horas de pesquisa… Os componentes padrão quando se cria uma aplicação com interface gráfica (GUI), normalmente usamos componentes que desenham-se na interface, e podem usar painéis e mecanismos de alinhamento… como eu quero desenhar uma ou mais “bolinhas”, existem duas alternativas: Usar uma imagem de uma bolinha, ou o modo “roots” : Fazer o desenho de uma bolinha 😀

Vamos no modo “roots” 

A pesquisa e o modo encontrado foi — segundo as fontes pesquisadas — a forma mais  “elegante” de se implementar o mecanismo de pintura da tela. Eu consigo desenhar um círculo usando o componente “java/awt/geom/Ellipse2D”, então eu crio uma classe especialista para o PingPong — chamada PingPongPanel, herdando um JPanel — acrescento o painel como o conteúdo do meu JFrame, e sobrescrevo o método responsável pela pintura dos componentes do painel, para também pintar os círculos que eu quero, na posição da tela desejadas para cada círculo. Ao contrário da implementação original em modo texto, eu não “apago” cada bolinha, eu simplesmente redesenho a tela com as bolinhas nas posições novas, no loop de execução do programa principal.

A classe PingPong2, que no post anterior virou um componente do pacote siga0984, também precisa de alterações. Primeiro, o método PingStep() inicialmente implementado também apagava e desenhava as bolinhas, e ele não previa um passo de movimento maior que um , e nem uma “margem” para verificar se uma bolinha vai bater no limite da tela … Então, vamos começar pela “nova classe” PingPong2:

*** arquivo siga0984/PingPong2.java ***

package siga0984;

import java.util.Random;

public class PingPong2
{
  private static Random rand = new Random();

  private int row,col;
  private int ballsize = 1, step = 1;
  private int rowinc = 1 ,colinc = 1; 
  private int maxrow = 25;
  private int maxcol = 80;

  public int getRow() { return row; };
  public int getCol() { return col; };
  public void setSize( int r ) { ballsize = r; }
  public void setStep( int s ) { step = s; }

  public void setBounds( int mrow, int mcol )
  {
    maxrow = mrow;
    maxcol = mcol;
  }

  public void InitBall()
  {
    row = rand.nextInt(maxrow-ballsize)+1;
    col = rand.nextInt(maxcol-ballsize)+1;
    if ( rand.nextInt(2) > 0 )
      rowinc = -1 ;
    if ( rand.nextInt(2) > 0 )
      colinc = -1 ;
  }

  public void PingStep()
  {
    if ( row + ( step * rowinc ) < 1 || row + ballsize + ( step * rowinc ) >= maxrow )
      rowinc = -rowinc;
    row += ( step * rowinc );
    if ( col + ( step * colinc ) < 1 || col + ballsize + ( step * colinc ) >= maxcol )
      colinc = -colinc;
    col += ( step * colinc );
  }

}

Agora a nossa classe de PingPong possui mais propriedades:

row e col — Já existentes, correspondem a coordenada de tela (linha e coluna respectivamente, em modo texto ou gráfico) onde a bolinha será desenhada.

ballsize  — Corresponde ao tamanho da bolinha, valor dafault = 1.

step — Corresponde ao “passo”, isto é, quantas unidades serão acrescentadas nas coordenadas ao mover a bolinha em uma determinada direção — default = 1.

rowinc e colinc  permanecem iguais, eles apenas tem o valor indicador de direção ( 1 ou -1 )

maxrow — Numero de linhas disponíveis na tela, seja modo texto ou gráfico, default = 25 para modo texto.

maxcol — Numero de colunas disponíveis na tela, seja modo texto ou gráfico, default = 80 para modo texto.

Como agora temos mais parâmetros para considerar, e a classe não será mais responsável por desenhar as bolinhas, apenas fazer a “movimentação” delas, eu preciso disponibilizar mais alguns métodos, para consultar as coordenadas de uma bolinha, e para informar os valores a serem usados nas propriedades adicionais, necessárias para o modo gráfico.

Através dos métodos getRow() e getCol(), eu consigo de “fora” da classe — do programa que usa / consome a classe — pegar o número da linha e coluna atuais da bolinha, e através dos métodos setSize() e setStep(), definir o tamanho (diâmetro) da bolinha e o “passo” (incremento de movimento) da bolinha.

Usando o método SetBounds(), eu informo os limites de tamanho da minha interface, seja texto ou gr;afico, e ao invés de sobrescrever o construtor, eu criei um método de inicialização, que sorteia a posição inicial da bolinha levando em conta o tamanho da bolinha e da tela onde ela será pintada.

E, por fim, o método PingStep() agora não pinta mais nada, e faz as verificações de atingimento do limite de tela considerando também o tamanho flexível da interface, e o tamanho da bolinha.

Fonte final da aplicação

Vamos pro “finalmente”, e depois as explicações. Assim ficou o fonte PingPongGUI.java, feito para executar o Ping-Pong em interface gráfica:

// Importa tudo o que é necessário para essa aplicação 

import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.geom.Ellipse2D;

// inclusive a classe PingPong2 que está no Package 
import siga0984.PingPong2;

public class PingPongGUI 
{
  public static void main(String args[]) throws InterruptedException
  { 
    int balls = 1;
    // Recebe o numero de bolinhas pela linha de comando 
    if ( args.length > 0 )
      balls = Integer.parseInt(args[0]);
    if ( balls < 1 )
      balls = 1;

    // Cria a janela principal
    JFrame frame = new JFrame("Ping-Ping GUI in Java");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(800, 600);
    frame.setLocation(100,100);

    // Cria o array para guardar as bolinhas
    PingPong2[] aBalls = new PingPong2[balls];

    // Cria o painel do PingPong para colocar como conteudo do JFrame
    PingPongPanel panel = new PingPongPanel();
    frame.getContentPane().add(panel);

    // Passa a referencia do array de bolinhas para o painel
    panel.aBalls = aBalls;

    // Ativa a janela 
    frame.setVisible(true);

    // Pega as dimensões internas do painel 
    Rectangle r = panel.getBounds();

    // Cria e Inicializa as bolinhas 
    for ( int i = 0; i < balls; i++)
    { 
      PingPong2 oBall = new PingPong2();
      oBall.setBounds(r.width,r.height);
      oBall.setSize(10);
      oBall.setStep(i+1);
      oBall.InitBall();
      aBalls[i] = oBall;
    }

    // Looping que invalida a tela 
    // e repinta todas as bolinhas 
    while ( true )
    {
      panel.validate();
      panel.repaint();
      Thread.sleep(50);
    }

  }

};

// Cria uma classe de painel para o Ping-Pong, herdando o JPanel

class PingPongPanel extends JPanel
{

  // Cria uma bolinha padrao , diametro de 10 pixels
  static Ellipse2D oCircle = new Ellipse2D.Double(0, 0, 10, 10 );

  // Propriedade para guardar o array de bolinhas
  PingPong2[] aBalls;

  // Sobrescreve o evento de pintura de componentes, 
  // Para que a pintura da janela pinte as bolinhas 

  public void paintComponent (Graphics g)
  {
 
    // Seta que a cor de fundo será preta 
    setBackground(Color.BLACK);

    // Propaga a pintura dos demais componentes para a classe superior
    super.paintComponent(g);

    // Faz um cast da interface gráfica 
    // para ser possivel desenhar os círculos 
    Graphics2D g2 = (Graphics2D) g;

    // Seta a cor do desenho 
    g2.setColor(Color.WHITE);
  
    // E Para cada bolinha do array 
    for ( PingPong2 oBall : aBalls )
    {
      // Faz a movimentação
      oBall.PingStep();

      // usa o objeto do círculo já montado 
      // apenas setando as coordenadas atuais, e desenha o círculo na tela 
      oCircle.setFrame(oBall.getRow(),oBall.getCol(),10,10);
      g2.draw(oCircle);
    }
  }
};

Por hora, em ambiente gráfico, o diâmetro da bolinha está fixo em 10 pontos, a janela desenhada na tela possui 800 x 600 pontos gráficos (colunas x linhas), e uma coisa interessante: As bolinhas possuem velocidade diferente. Ao criar cada bolinha, eu defino que o “stepping” ( incremento de coordenadas de movimento ) é 1 para a primeira bolinha, 2 para a segunda, e assim por diante. Então, com 10 bolinhas , a décima bolinha “pula” de 10 em 10 pontos cada vez que ela é desenhada, fazendo ela parecer mais  “rápida” que as outras … quando na verdade ela “pulou” um intervalo maior !

Conclusão

Pode parecer um pouco “complicado” no início … depois piora, e depois melhora ! São muitos eventos e componentes envolvidos em uma sequência hierárquica para criar e interagir com componentes de interface, tudo ao seu tempo, em passos 😀

Espero que vocês estejam aproveitando este conteúdo, e lhes desejo TERABYTES DE SUCESSO !!! 

Referências

 

Java from Scratch – Parte 05

Introdução

No post anterior (Java from Scratch – Parte 04), aproveitamos todo o código original do primeiro PingPong e criamos o PingPong2 MULTIBALL, usando a orientação a objetos do Java. Agora, vamos completar alguns conceitos que ainda não foram abordados, e ver como gerar e disponibilizar uma aplicação em um arquivo único – o “JAR” 😀

Classes, objetos, instâncias, propriedades e métodos

No post anterior, a definição de “Classe” foi descrita como “… uma forma de agrupar um conjunto de atributos nomeados e qualificados, que representam uma unidade de informação complexa, para a qual podemos criar funções associadas a este conjunto, que realizem ações baseadas nas propriedades e mudanças de estado dessas propriedades.

No exemplo do PingPong, as propriedades da classe são os valores que registram o estado do objeto — as coordenadas atuais da bolinha e a direção que ela está se movendo — e o método de movimentação da bolinha — PingStep() — muda o estado do objeto, atualizando as coordenadas e desenhando novamente a bolinha na tela.

Usando um Array de objetos, eu posso criar várias instâncias da classe PingPong2, cada uma delas com o “seu” controle de movimento. Eu consigo facilmente acrescentar uma cor para as bolinhas, criando uma nova propriedade na classe, e sorteando uma cor no construtor, ou criando um método pata atribuir a cor, e consumindo essa propriedade no momento de fazer a pintura.

Modificadores de acesso

Existem atributos especiais que eu posso usar da declaração de classes, propriedades e métodos. Eles permitem modificar a visibilidade da classe, propriedade ou método, quando utilizamos herança de classes. Nem todos os modificadores podem ser aplicados a estes três componentes ( classe, propriedade e método), vamos a eles:

private

Este modificador pode ser aplicado apenas para propriedades e métodos, e indica que o elemento em questão somente será visível para os métodos da própria classe.

protected

Este modificador permite que o elemento em questão somente seja visível por uma classe que use esta como herança.

<default>

Quando não utilizamos nenhum modificador ao declarar a classe/propriedade/método, o Java assume o comportamento default, que garante a visibilidade da classe, propriedade ou método apenas para outras classes do mesmo pacote.

public

Este modificador pode ser aplicado para a classe, propriedades e métodos. Ele indica explicitamente que o elemento para o qual ele foi aplicado será visível ( e pode ser utilizado ) por qualquer outro código que consuma a classe.

final

Indica que o elemento para o qual ele foi aplicado não pode ser sobrescrito por herança. Isto é, você não pode herdar a classe, ou sobrescrever um método. Vamos ver isso com mais clareza na herança de classes.

abstract

Modificador aplicado apenas para a classe, impede a classe de ser instanciada. Uma classe abstrata é apenas uma definição, sem a implementação. Parece não fazer sentido no momento, mas isso é um recurso bem interessante.

static

Esse modificador especial, quando aplicado a uma propriedade da classe, faz com que a propriedade faça parte da classe em si, e não da sua instância. Ela passa a fazer parte da classe em si, e se comporta como se fosse “compartilhada” entre as instâncias. Inclusive, uma propriedade estática pode ser consultada sem eu precisar criar um novo objeto ou instância da classe, bem como um método static pode ser executado sem a criação de uma instância — desde que este método não acesse nenhuma propriedade, ou acesse apenas uma propriedade também static.

A criação de métodos static permite emular um comportamento de uma “função”. Um método public static pode ser chamado para uma determinada finalidade, sem a necessidade de se criar uma instância da classe em si, da mesma forma que uma função de escopo global de aplicação escrita em outras linguagens.

Antes do pacote e da herança …

Bem, para chegar nesses conceitos, antes de mais nada precisamos entender como podemos criar e usar mais de uma classe. A grosso modo, devemos prezar por usar uma classe por arquivo fonte. É possível criar mais de uma classe em um fonte .java, porém apenas uma pode ser declarada como pública, e isso tem seus “impactos”.  Vamos começar quebrando nosso PingPong2 em dois arquivos java!

O método “main” da classe PingPong2 ficou muito “prático”, mas ele é a aplicação em si, que consome instâncias da sua própria classe … Isso ficaria bem mais elegante se eu criasse uma classe de aplicação em outro arquivo — PingPongApp.java por exemplo — e esse fonte consumisse a classe PingPong2  — que não precisa mais ter o “main”. Vamos ver como fica ?

(arquivo PingPongApp.java)

public class PingPongApp
{
  public static void main(String args[]) throws InterruptedException
  {
    int balls = 1;
    if ( args.length > 0 )
      balls = Integer.parseInt(args[0]);
    if ( balls < 1 )
      balls = 1;
    PingPong2[] aBalls = new PingPong2[balls];
    for ( int i = 0; i < balls; i++)
      aBalls[i] = new PingPong2();
    while (true)
    {
      for ( PingPong2 oBall : aBalls )
        oBall.PingStep();
      Thread.sleep(250);
    }
  } 
}

Agora, removemos o “main” do arquivo PingPong2.java, que fica apenas com a classe e a sua implementação:

(arquivo PingPong2.java)

import java.util.Random;

public class PingPong2
{

  private static char escCode = 27;
  private static Random rand = new Random();

  private int row,col;
  private int rowinc = 1 ,colinc = 1;

  public PingPong2()
  {
    row = rand.nextInt(25)+1;
    col = rand.nextInt(80)+1;
    if ( rand.nextInt(2) > 0 )
      rowinc = -1 ;
    if ( rand.nextInt(2) > 0 )
      colinc = -1 ;
  }

  public void PingStep()
  {
    System.out.printf("%c[%d;%df ",escCode,row,col);
    if ( row + rowinc < 1 || row + rowinc > 25 )
      rowinc = -rowinc;
    row += rowinc;
    if ( col + colinc < 1 || col + colinc > 80 )
      colinc = -colinc;
    col += colinc;
    System.out.printf("%c[%d;%dfO%c[1D",escCode,row,col,escCode);
  }

}

Agora, com estes dois arquivos na nossa pasta de testes “\Projetos\Java”, vamos apagar todos os arquivos “.class” ( byecodes compilados ) , compilar a nossa aplicação “PingPongApp.java”, e executá-la, usando os comandos :

del *.class
javac PingPongApp.java
java PingPongApp

Por que funcionou ? 

Hummm… e não era pra funcionar ? Bem, eu tenho dois fontes java … o PingPongApp.java e o PingPong2.java. Eu mandei compilar apenas o PingPongApp.java, que gerou o bytecode PingPongApp.class … mas eu não menciono em nenhum lugar do meu PingPongApp que eu preciso do fonte PingPong2.java … e se eu olhar na minha pasta, o bytecode  “PingPong2.class” foi gerado também ?! Bruxaria !

R: O compilador do Java identifica que o fonte PingPongApp.java cria uma uma instância de uma classe chamada “PingPong2”. Para ser possível consumir essa classe, o compilador verifica se existe um “PingPong2.class” (bytecode) na pasta atual, e depois nas pastas configuradas na variável de ambiente CLASSPATH. Se ele encontrar o bytecode dessa classe, ele consegue compilar o PingPongApp. Mas como o bytecode PingPong2.class ainda não existe, o compilador verifica se existe um fonte java com o mesmo nome no diretório atual … e se existir ele compila 😀 Afinal, como o nome do fonte é o nome da classe, fica fácil para ele resolver !

E, o que pode dar errado ? 

Da forma que este pequeno aplicativo foi feito, eu tenho dois arquivos “.class”, onde o PingPongApp.class depende do PingPong2.class. Sem esta dependência compilada, se eu executar o PingPongApp, ocorre o erro :

Exception in thread "main" java.lang.NoClassDefFoundError: PingPong2
at PingPongApp.main(PingPongApp.java:16)
Caused by: java.lang.ClassNotFoundException: PingPong2
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)

JAR  – Java ARquive

Uma das formas de você disponibilizar uma aplicação ou um conjunto de classes é o arquivo JAR. Ele é um arquivo compactado, que contém as classes (bytecodes) e um arquivo de “manifesto”, entre outros recursos que a aplicação ou biblioteca utilize. Por exemplo, o nosso programa de PingPong poderia ser disponibilizado em um arquivo JAR.

Primeiro, vamos criar um arquivo texto para ser o “manifesto”. O nome do arquivo deve ser  “manifest.mf”, e deve conter apenas a linha:

Main-class: PingPongApp  

Agora, criamos o JAR do Ping Pong Multiball usando o comando abaixo:

jar -cmf manifest.mf ThePingPong.jar PingPong2.class PingPongApp.class

Com isso, eu crio um arquivo chamado “ThePingPong.jar”, e ele contém o aplicativo PingPongApp.class e a classe que ele depende PingPong2.class, e o manifesto. Agora, para executar o aplicativo no formato JAR, eu chamo o java com o parâmetro  “-jar” seguido do arquivo em questão:

java -jar ThePingPong.jar

Com isso, eu disponibilizo esta aplicação em um arquivo único, que leva consigo as dependências internas do código. O manifesto possui muitas funcionalidades, por hora a única que nós usamos foi informar qual é a classe que contém o método “main” que deve ser executado quando o JAR for executado.

Packages

“Package” é uma forma do Java usada para aglutinar e organizar grupos de classes, sub-pacotes e interfaces. É um mecanismo de organização diretamente ligado a estrutura de diretórios da aplicação. Em poucas palavras, você cria um subdiretório para conter as suas classes, e o nome desse subdiretório é o nome do pacote. E dentro dos fontes das classes que você coloca nessa pasta, você identifica o pacote ao qual ele pertence.

Por exemplo, eu quero disponibilizar o “core” do Ping-Pong como um pacote. Meu pacote vai se chamar “siga0984”. Então, dentro da minha pasta /Projetos/Java, eu crio a pasta “siga0984”, e movo o arquivo “PingPong2.java” para dentro dessa pasta. E, no início do arquivo PingPong2.java, eu acrescento a linha abaixo:

package siga0984;

Então, eu compilo o fonte “PingPong2.java” usando o comando

javac PingPong2.java

Agora, na pasta anterior, onde está o aplicativo “PingPongApp.java”, vamos editá-lo para ele importar esse pacote, acrescentando no inicio desse fonte a linha abaixo:

import siga0984.PingPong2;

Apos apagar os arquivos *.class da pasta, eu compilo novamente o arquivo PingPongApp.java:

javac PingPongApp.java

Agora, para gerar o “ThePingPong.jar”, podemos colocar esse pacote dentro do JAR, junto do manifesto, usando o comando :

jar -cmf manifest.mf ThePingPong.jar PingPongApp.class siga0984\PingPong2.class

Pronto, agora temos um JAR com o aplicativo PingPong e o package siga0984, com a classe PingPong2 ! E esse JAR pode ser distribuído 😀

Conclusão

O mecanismo de pacotes permite criar ainda sub-pastas e sub-pacotes, veremos isso um pouco mais para frente. Mas de modo geral, já dá para se ter uma ideia da flexibilidade desse mecanismo !

Agora que já temos uma “base” da orientação a objetos, pacotes e afins, no próximo post vamos aproveitar os códigos escritos até agora, e criar um Ping-Pong em modo gráfico, dentro de uma janela — apenas criando uma nova classe e implementando uma herança !!!

Espero que estas informações lhes sejam úteis e interessantes, e desejo a todos TERABYTES DE SUCESSO !! 

Referências