diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index 4c061c4..fdb9f5c 100644 --- a/README.md +++ b/README.md @@ -1,286 +1,142 @@ # RPC Server -Um servidor RPC STCP altamente concorrente implementado em Erlang/OTP. +Servidor RPC TCP altamente concorrente em Erlang/OTP, composto por dois apps: `rpc_server_http` (conexões TCP) e `rpc_server_shell` (execução de comandos e sessões persistentes). -## Arquitetura - -O servidor é construído usando uma arquitetura supervisionada em camadas, projetada para alta disponibilidade e escalabilidade. Cada conexão STCP é gerenciada por um processo dedicado, permitindo processamento não-bloqueante e isolamento de falhas. - -### Árvore de Supervisores +## Estrutura do projeto ``` -rpc_server_sup (Root) -├── rpc_server_shell_manager_sup -│ └── rpc_server_shell_instance (dinâmico, temporary) -│ └── [Gerencia instâncias do shell interativo] -│ -└── rpc_server_sctp_listen_sup - ├── rpc_server_sctp_acceptor_sup - │ └── rpc_server_sctp_connection (dinâmico, temporary) - │ └── [Gerencia conexões STCP individuais] - │ - └── rpc_server_sctp_listen - └── [Gerencia socket de escuta e async_accept] - -Legenda: -- (dinâmico) - Processos criados sob demanda -- (temporary) - Processos que não são reiniciados automaticamente +rpc_server/ +├── apps/ +│ ├── rpc_server_http/ +│ │ ├── src/ +│ │ │ ├── rpc_server_http_app.erl +│ │ │ ├── rpc_server_http_listen_sup.erl +│ │ │ ├── rpc_server_http_acceptor_sup.erl +│ │ │ ├── rpc_server_http_listen.erl +│ │ │ ├── rpc_server_http_connection.erl +│ │ │ └── rpc_server_http_parser_command.erl +│ │ └── include/rpc_server.hrl +│ └── rpc_server_shell/ +│ ├── src/ +│ │ ├── rpc_server_shell_app.erl +│ │ ├── rpc_server_shell_sup.erl +│ │ ├── rpc_server_shell_manager_sup.erl +│ │ └── rpc_server_shell_instance.erl +│ └── include/rpc_server.hrl +├── config/ +│ ├── dev_sys.config +│ ├── dev_vm.config +│ ├── dev_http.vm.args +│ └── dev_shell.vm.args +└── rebar.config ``` -### Estratégias de Supervisão - -- `rpc_server_sup`: one_for_one -- `rpc_server_sctp_listen_sup`: rest_for_one -- `rpc_server_sctp_acceptor_sup`: simple_one_for_one -- `rpc_server_shell_manager_sup`: simple_one_for_one - -## Módulos - -### Supervisores - -#### `rpc_server_sup.erl` -- Supervisor raiz da aplicação -- Responsável por iniciar e supervisionar todos os subsistemas -- Gerencia o ciclo de vida da aplicação -- Estratégia: one_for_one - -#### `rpc_server_sctp_listen_sup.erl` -- Supervisor do subsistema STCP -- Gerencia o listener e o supervisor de acceptors -- Garante que o servidor STCP esteja sempre disponível -- Estratégia: rest_for_one (garante que o acceptor_sup seja iniciado antes do listener) - -#### `rpc_server_sctp_acceptor_sup.erl` -- Supervisor dos processos de conexão -- Gerencia o pool de conexões usando simple_one_for_one -- Cria processos temporários para cada conexão -- Responsável por transferir o controle do socket para o processo de conexão - -#### `rpc_server_shell_manager_sup.erl` -- Supervisor das instâncias do shell interativo -- Gerencia o pool de shells usando simple_one_for_one -- Cria processos temporários para cada instância de shell -- Responsável por iniciar novas instâncias de shell conforme necessário - -### Workers - -#### `rpc_server_sctp_listen.erl` -- Implementa o servidor STCP principal -- Gerencia o socket de escuta -- Implementa async_accept para melhor escalabilidade -- Coordena a criação de novos processos de conexão -- Mantém o estado do servidor e referências de async_accept -- Aguarda a disponibilidade do supervisor de acceptors antes de iniciar +## Arquitetura (alto nível) -#### `rpc_server_sctp_connection.erl` -- Gerencia uma conexão STCP individual -- Processa mensagens recebidas -- Implementa o protocolo RPC -- Gerencia o ciclo de vida da conexão -- Configura o socket para modo ativo -- Trata eventos STCP (dados, fechamento, erros) +- `rpc_server_http`: + - `rpc_server_http_listen_sup` (rest_for_one): supervisiona `rpc_server_http_acceptor_sup` e `rpc_server_http_listen`. + - `rpc_server_http_acceptor_sup` (simple_one_for_one): cria `rpc_server_http_connection` (temporary) por conexão. + - `rpc_server_http_listen`: abre o socket TCP e faz `async_accept` (padrão porta 8080). + - `rpc_server_http_connection`: trata I/O, parse de comandos e orquestra o shell. -#### `rpc_server_shell_instance.erl` -- Gerencia uma instância do shell interativo -- Fornece interface de linha de comando para administração -- Permite monitoramento e controle do servidor em tempo real -- Implementa comandos administrativos via shell -- Gerencia o sistema de reconexão baseado em hash +- `rpc_server_shell`: + - `rpc_server_shell_sup` (one_for_all): supervisiona `rpc_server_shell_manager_sup`. + - `rpc_server_shell_manager_sup` (simple_one_for_one): cria `rpc_server_shell_instance` (temporary). + - `rpc_server_shell_instance`: executa comandos do sistema e gerencia reconexão da sessão. -#### `parser_command.erl` -- Responsável por fazer o parsing de comandos recebidos via rede -- Remove caracteres de controle como \r e \n -- Divide comandos com base no caractere ":" para identificar comandos com ou sem valor -- Suporta formatos: `<<"comando:valor">>` e `<<"comando">>` +## Requisitos -## Fluxo de Conexão +- Erlang/OTP 27+ +- `rebar3` -1. O listener (`rpc_server_sctp_listen`) inicia escutando na porta configurada -2. Quando uma nova conexão chega: - - O listener inicia um novo processo de conexão via acceptor_sup - - O controle do socket é transferido para o processo de conexão - - O processo de conexão configura o socket para modo ativo - - O processo de conexão processa as mensagens recebidas -3. O listener inicia um novo async_accept para a próxima conexão - -## Sistema de Reconexão - -O servidor implementa um sistema inteligente de reconexão baseado em hash que permite que clientes se reconectem e retomem suas sessões anteriores. - -### Como Funciona - -1. **Identificação Única**: Quando um cliente se conecta pela primeira vez, o servidor gera um hash único baseado no IP e porta do cliente usando o algoritmo DJB hash. - -2. **Armazenamento**: O hash e o PID da instância do shell são armazenados em uma tabela ETS (`connection_table`) para referência futura. - -3. **Estado de Desconexão**: Quando a conexão é perdida, a instância do shell permanece ativa mas é marcada como "desconectada". - -4. **Reconexão**: Quando o cliente se reconecta e envia `reconnect:hash` como primeira mensagem, o servidor: - - Busca o hash na tabela ETS - - Verifica se a instância do shell anterior ainda está ativa - - Reconecta o cliente à instância existente - - Restaura o estado da sessão - -### Exemplo de Uso +## Build ```bash -# Primeira conexão -$ telnet localhost 8080 -Trying 127.0.0.1... -Connected to localhost. -Escape character is '^]'. -Client identification 123456789 - -# Execute alguns comandos -ls -pwd - -# Desconecte (Ctrl+C ou feche a conexão) -^] -telnet> quit -Connection closed. - -# Reconecte usando o hash recebido -$ telnet localhost 8080 -Trying 127.0.0.1... -Connected to localhost. -Escape character is '^]'. - -# Envie o comando de reconexão como primeira mensagem -reconnect:123456789 -Conexao restabelecida - -# Agora você está reconectado à mesma sessão -``` - -### Formato do Comando de Reconexão - -``` -reconnect:HASH +rebar3 compile ``` -Onde `HASH` é o identificador numérico recebido na primeira conexão. +## Como rodar -### Benefícios +### Desenvolvimento (interativo) -- **Persistência de Sessão**: Mantém o estado e histórico de comandos -- **Recuperação Automática**: Reconecta automaticamente à sessão anterior -- **Eficiência**: Evita recriar instâncias desnecessariamente -- **Robustez**: Funciona mesmo com interrupções de rede temporárias +```bash +rebar3 shell --config config/dev_sys.config +``` -## Características +Os dois apps sobem no mesmo nó (nome e cookie definidos em `config/dev_vm.config`). -- **Alta Concorrência**: Cada conexão é gerenciada por um processo dedicado -- **Não-Bloqueante**: Uso de async_accept para melhor escalabilidade -- **Fault Tolerance**: Isolamento de falhas através da árvore de supervisores -- **Resource Management**: Processos são criados/destruídos conforme necessário -- **High Availability**: Supervisores reiniciam componentes críticos -- **Reconexão Inteligente**: Sistema de reconexão baseado em hash para persistência de sessão +### Release único (ambos os apps) -## Configuração +```bash +rebar3 release +./_build/default/rel/rpc_server/bin/rpc_server console +# para parar +./_build/default/rel/rpc_server/bin/rpc_server stop +``` -O servidor pode ser configurado através do arquivo `config/sys.config`: +### Releases separados (um nó por app) -```erlang -[ - {kernel, [ - {logger_level, info} - ]}, - {rpc_server, [ - {tcp_port, 8080}, - {num_acceptors, 100}, % Número de processos aceitando conexões - {max_connections, 10000}, % Limite máximo de conexões simultâneas - {tcp_listen_options, [ % Esse é utilizado no listener global - binary, - {packet, raw}, % 0 = raw - Sem cabeçalho, dados brutos - {reuseaddr, true}, % Permite reutilizar o endereço mesmo se estiver em estado TIME_WAIT - {active, true}, % Modo ativo total (passagem automática de mensagens) - {backlog, 128} % Número máximo de conexões pendentes na fila - ]}, - {tcp_connection_options, [ % Esse é utilizado para cada conexão individual - binary, - {packet, raw}, - {active, false}, - {keepalive, true}, % Mantém a conexão ativa usando STCP keepalive - {send_timeout, 5000}, % Define um timeout de 5 segundos para operações de envio - {send_timeout_close, true}, - {exit_on_close, true} % Encerra o processo quando o socket é fechado - ]} - ]} -]. +- HTTP somente: +```bash +rebar3 as http_node release -n rpc_server_http +./_build/http_node/rel/rpc_server_http/bin/rpc_server_http console ``` -## Uso - -1. Compile o projeto: +- SHELL somente: ```bash -rebar3 compile +rebar3 as shell_node release -n rpc_server_shell +./_build/shell_node/rel/rpc_server_shell/bin/rpc_server_shell console ``` -2. Inicie o servidor: +- Parar: ```bash -rebar3 shell +./_build/http_node/rel/rpc_server_http/bin/rpc_server_http stop +./_build/shell_node/rel/rpc_server_shell/bin/rpc_server_shell stop ``` -3. O servidor estará escutando na porta configurada (padrão: 8080) +## Configuração + +Arquivo principal: `config/dev_sys.config`. + +Parâmetros relevantes do `rpc_server_http` (padrões): -4. Use o shell interativo para administração: ```erlang -% No shell do Erlang -rpc_server_shell:start(). +{rpc_server_http, [ + {tcp_port, 8080}, + {num_acceptors, 100}, + {max_connections, 10000} +]} ``` -### Comandos do Shell +Nomes de nós e cookie (releases e dev): -O shell interativo fornece os seguintes comandos: -- `help` - Lista todos os comandos disponíveis -- `status` - Mostra o status atual do servidor -- `connections` - Lista todas as conexões ativas -- `stats` - Exibe estatísticas do servidor +- Nó único: `config/dev_vm.config` (ex.: `-name rpc_server@127.0.0.1`, cookie `service_discovery_cookie`). +- HTTP: `config/dev_http.vm.args` (ex.: `-name rpc_server_http@127.0.0.1`). +- SHELL: `config/dev_shell.vm.args` (ex.: `-name rpc_server_shell@127.0.0.1`). -## Logs +Logs são configurados via `lager` em `config/dev_sys.config` e gravados em `log/`. -O servidor utiliza o sistema de logging do Erlang/OTP. Logs importantes incluem: -- Inicialização do servidor -- Novas conexões -- Erros de conexão -- Processamento de mensagens -- Fechamento de conexões +## Uso rápido -## Desenvolvimento +1) Conecte via telnet/netcat na porta configurada (padrão 8080): -### Estrutura de Diretórios +```bash +telnet localhost 8080 +# ou +nc localhost 8080 +``` + +2) Na primeira conexão, o servidor envia uma identificação do cliente (hash baseado em IP:porta). + +3) Para reconectar à mesma sessão, envie como primeira mensagem: ``` -rpc_server/ -├── app/ # Módulos da aplicação principal -│ ├── rpc_server_app.erl # Callback do comportamento application -│ └── rpc_server_sup.erl # Supervisor raiz -│ -├── supervisors/ # Hierarquia de supervisores -│ ├── rpc_server_sctp_listen_sup.erl -│ ├── rpc_server_sctp_acceptor_sup.erl -│ └── rpc_server_shell_manager_sup.erl -│ -├── sctp/ # Implementação STCP (mantido nome por compatibilidade) -│ └── rpc_server_sctp_listen.erl -│ -├── shell/ # Interface de administração -│ └── rpc_server_shell_instance.erl -│ -├── connections/ # Gerenciamento de conexões -│ └── rpc_server_sctp_connection.erl -│ -└── commands/ # Parser de comandos - └── parser_command.erl +reconnect:HASH ``` -### Dependências - -- Erlang/OTP 27 ou superior -- rebar3 para build +Em seguida, use comandos de sistema simples (ex.: `ls`, `pwd`). A saída é devolvida na mesma conexão. -### Testes +## Testes ```bash rebar3 eunit @@ -289,12 +145,12 @@ rebar3 ct ## Contribuição -1. Fork o projeto -2. Crie uma branch para sua feature (`git checkout -b feature/xpto`) -3. Commit suas mudanças (`git commit -m 'Add some xpto'`) -4. Push para a branch (`git push origin feature/xpto`) -5. Abra um Pull Request +1. Faça fork +2. Crie uma branch (`git checkout -b feature/xpto`) +3. Commit (`git commit -m 'feat: xpto'`) +4. Push (`git push origin feature/xpto`) +5. Abra um PR ## Licença -Este projeto está licenciado sob a MIT License - veja o arquivo LICENSE para detalhes. +MIT. Veja `LICENSE.md`. \ No newline at end of file diff --git a/apps/rpc_server_http/.gitignore b/apps/rpc_server_http/.gitignore new file mode 100644 index 0000000..df53f7d --- /dev/null +++ b/apps/rpc_server_http/.gitignore @@ -0,0 +1,20 @@ +.rebar3 +_build +_checkouts +_vendor +.eunit +*.o +*.beam +*.plt +*.swp +*.swo +.erlang.cookie +ebin +log +erl_crash.dump +.rebar +logs +.idea +*.iml +rebar3.crashdump +*~ diff --git a/apps/rpc_server_http/LICENSE.md b/apps/rpc_server_http/LICENSE.md new file mode 100644 index 0000000..fadbfdc --- /dev/null +++ b/apps/rpc_server_http/LICENSE.md @@ -0,0 +1,186 @@ +# Apache License +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +## 1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +## 2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +## 3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +## 4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy of + this License; and + +2. You must cause any modified files to carry prominent notices stating that + You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You + distribute, all copyright, patent, trademark, and attribution notices from + the Source form of the Work, excluding those notices that do not pertain to + any part of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, then + any Derivative Works that You distribute must include a readable copy of the + attribution notices contained within such NOTICE file, excluding those + notices that do not pertain to any part of the Derivative Works, in at least + one of the following places: within a NOTICE text file distributed as part + of the Derivative Works; within the Source form or documentation, if + provided along with the Derivative Works; or, within a display generated by + the Derivative Works, if and wherever such third-party notices normally + appear. The contents of the NOTICE file are for informational purposes only + and do not modify the License. You may add Your own attribution notices + within Derivative Works that You distribute, alongside or as an addendum to + the NOTICE text from the Work, provided that such additional attribution + notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +## 5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +## 6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +## 7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, NON- +INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +## 8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +## 9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +Copyright 2025, Fernando Areias . + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/apps/rpc_server_http/README.md b/apps/rpc_server_http/README.md new file mode 100644 index 0000000..3cee36e --- /dev/null +++ b/apps/rpc_server_http/README.md @@ -0,0 +1,9 @@ +rpc_server_http +===== + +An OTP application + +Build +----- + + $ rebar3 compile diff --git a/include/rpc_server.hrl b/apps/rpc_server_http/include/rpc_server.hrl similarity index 99% rename from include/rpc_server.hrl rename to apps/rpc_server_http/include/rpc_server.hrl index 7518e9f..c671610 100644 --- a/include/rpc_server.hrl +++ b/apps/rpc_server_http/include/rpc_server.hrl @@ -9,6 +9,7 @@ -define(RPC_SERVER_HRL, true). -include_lib("kernel/include/logger.hrl"). + %% Types -type socket() :: port(). diff --git a/apps/rpc_server_http/rebar.config b/apps/rpc_server_http/rebar.config new file mode 100644 index 0000000..d689ef8 --- /dev/null +++ b/apps/rpc_server_http/rebar.config @@ -0,0 +1,9 @@ +{erl_opts, [debug_info]}. +{deps, [ + {lager, "3.9.2"} +]}. + +{shell, [ + %% {config, "config/sys.config"}, + {apps, [rpc_server_http]} +]}. diff --git a/src/rpc_server.app.src b/apps/rpc_server_http/src/rpc_server_http.app.src similarity index 61% rename from src/rpc_server.app.src rename to apps/rpc_server_http/src/rpc_server_http.app.src index 8ed1608..b6cd9af 100644 --- a/src/rpc_server.app.src +++ b/apps/rpc_server_http/src/rpc_server_http.app.src @@ -1,16 +1,17 @@ -{application, rpc_server, [ - {description, "RPC Server Application"}, +{application, rpc_server_http, [ + {description, "An OTP application"}, {vsn, "0.1.0"}, {registered, [ rpc_server_sup, - rpc_server_sctp_listen_sup, - rpc_server_sctp_acceptor_sup + rpc_server_http_listen_sup, + rpc_server_http_acceptor_sup ]}, - {mod, {rpc_server_app, []}}, + {mod, {rpc_server_http_app, []}}, {applications, [ kernel, stdlib, - sasl + sasl, + lager ]}, {env, [ {tcp_port, 8080}, @@ -29,13 +30,7 @@ {send_timeout_close, true} ]} ]}, - {modules, [ - rpc_server_app, - rpc_server_sup, - rpc_server_sctp_listen_sup, - rpc_server_sctp_acceptor_sup, - rpc_server_sctp_connection - ]}, + {modules, []}, {licenses, ["Apache-2.0"]}, {links, []} -]}. + ]}. diff --git a/src/supervisors/rpc_server_sctp_acceptor_sup.erl b/apps/rpc_server_http/src/rpc_server_http_acceptor_sup.erl similarity index 93% rename from src/supervisors/rpc_server_sctp_acceptor_sup.erl rename to apps/rpc_server_http/src/rpc_server_http_acceptor_sup.erl index e41c56b..d32977f 100644 --- a/src/supervisors/rpc_server_sctp_acceptor_sup.erl +++ b/apps/rpc_server_http/src/rpc_server_http_acceptor_sup.erl @@ -1,4 +1,4 @@ --module(rpc_server_sctp_acceptor_sup). +-module(rpc_server_http_acceptor_sup). -author('Fernando Areias '). -include("rpc_server.hrl"). @@ -37,12 +37,12 @@ init([]) -> ChildSpecs = [ #{ - id => rpc_server_sctp_connection, - start => {rpc_server_sctp_connection, start_link, []}, + id => rpc_server_http_connection, + start => {rpc_server_http_connection, start_link, []}, restart => temporary, % Não inicia automaticamente shutdown => 90, type => worker, - modules => [rpc_server_sctp_connection] + modules => [rpc_server_http_connection] } ], diff --git a/apps/rpc_server_http/src/rpc_server_http_app.erl b/apps/rpc_server_http/src/rpc_server_http_app.erl new file mode 100644 index 0000000..571994a --- /dev/null +++ b/apps/rpc_server_http/src/rpc_server_http_app.erl @@ -0,0 +1,18 @@ +%%%------------------------------------------------------------------- +%% @doc rpc_server_http public API +%% @end +%%%------------------------------------------------------------------- + +-module(rpc_server_http_app). + +-behaviour(application). + +-export([start/2, stop/1]). + +start(_StartType, _StartArgs) -> + rpc_server_http_listen_sup:start_link(). + +stop(_State) -> + ok. + +%% internal functions diff --git a/src/connections/rpc_server_sctp_connection.erl b/apps/rpc_server_http/src/rpc_server_http_connection.erl similarity index 79% rename from src/connections/rpc_server_sctp_connection.erl rename to apps/rpc_server_http/src/rpc_server_http_connection.erl index 490340a..e553864 100644 --- a/src/connections/rpc_server_sctp_connection.erl +++ b/apps/rpc_server_http/src/rpc_server_http_connection.erl @@ -1,4 +1,4 @@ --module(rpc_server_sctp_connection). +-module(rpc_server_http_connection). -author('Fernando Areias '). -include("rpc_server.hrl"). @@ -41,7 +41,7 @@ start_link([ClientSocket, ListenSocket]) -> gen_server:start_link(?MODULE, [ClientSocket, ListenSocket], []). -%%% @doc Inicializa o estado do servidor gen_server para gerenciar uma conexão SCTP. +%%% @doc Inicializa o estado do servidor gen_server para gerenciar uma conexão http. %%% %%% Esta função é chamada automaticamente quando o processo gen_server é iniciado. %%% Ela configura o socket do cliente, obtém informações do peer (cliente remoto), @@ -157,7 +157,7 @@ handle_cast(_Msg, State) -> -spec handle_info({tcp | tcp_closed | tcp_error, inet:socket(), term()}, #state{}) -> {noreply, #state{}}. handle_info({tcp, Socket, Data}, #state{clientSocket = Socket} = State) -> ?LOG_INFO("TCP handler chamado - Socket: ~p, Data: ~p", [Socket, Data]), - Command = parser_command:parse(Data), + Command = rpc_server_http_parser_command:parse(Data), NewState = handle_command(Command, State), {noreply, NewState}; @@ -258,33 +258,32 @@ code_change(_OldVsn, State, _Extra) -> % TODO: REFATORAR ISSO, SEM TEMPO AGORA -handle_command({<<"reconnect">>, HashBin}, State = #state{shellInstancePid = undefined}) when is_binary(HashBin) -> +handle_command({<<"reconnect">>, HashBin}, State = #state{shellInstancePid = undefined}) -> HashStr = binary_to_list(HashBin), - case catch list_to_integer(HashStr) of - HashInt when is_integer(HashInt) -> - case ets:lookup(connection_table, HashInt) of - [{HashInt, Pid}] when is_pid(Pid) -> - ?LOG_INFO("Encontrou a instância do shell anterior"), - ?LOG_INFO("Verificando se processo ~p está vivo: ~p", [Pid, is_process_alive(Pid)]), - ?LOG_INFO("Enviando mensagem de reconexão para ~p", [Pid]), - ?LOG_INFO("Mensagem sendo enviada: {reconnected, ~p, ~p}", [self(), State#state.clientSocket]), - gen_server:cast(Pid, {reconnected, self(), State#state.clientSocket}), - State#state{shellInstancePid = Pid}; - [] -> - ?LOG_INFO("Não havia instância do shell existente, criando uma nova instância do shell...~n"), - ShellPid = rpc_server_shell_manager_sup:start_shell(State#state.clientSocket, self()), - State#state{shellInstancePid = ShellPid}; - _ -> - ?LOG_INFO("Registro encontrado na ETS, mas PID inválido para hash ~p~n", [HashInt]), - State - end; + case ets:lookup(connection_table, HashStr) of + [{HashStr, Pid}] when is_pid(Pid) -> + ?LOG_INFO("Encontrou a instância do shell anterior"), + ?LOG_INFO("Verificando se processo ~p está vivo: ~p", [Pid, is_process_alive(Pid)]), + ?LOG_INFO("Enviando mensagem de reconexão para ~p", [Pid]), + ?LOG_INFO("Mensagem sendo enviada: {reconnected, ~p, ~p}", [self(), State#state.clientSocket]), + gen_server:cast(Pid, {reconnected, self(), State#state.clientSocket}), + State#state{shellInstancePid = Pid}; + [] -> + ?LOG_INFO("Não havia instância do shell existente, criando uma nova instância do shell (remota)...~n"), + {ok, {IP, Port}} = inet:peername(State#state.clientSocket), + HashId = gen_hash_identification({IP, Port}), + Meta = #{peer => {IP, Port}, hash_id => HashId}, + ShellPid = rpc:call(node(global:whereis_name(rpc_server_shell_manager_sup)), + rpc_server_shell_manager_sup, start_shell_remote, + [self(), Meta]), + State#state{shellInstancePid = ShellPid}; _ -> - ?LOG_ERROR("Hash inválido recebido para reconexão: ~p", [HashBin]), + ?LOG_INFO("Registro encontrado na ETS, mas PID inválido para hash ~p~n", [HashStr]), State end; -handle_command({<<"reconnect">>, HashBin}, State = #state{shellInstancePid = Pid}) - when is_binary(HashBin), is_pid(Pid) -> +handle_command({<<"reconnect">>, _HashBin}, State = #state{shellInstancePid = Pid}) + when is_pid(Pid) -> ?LOG_INFO("O cliente já possui uma conexão ativa. Ignorando tentativa de reconexão duplicada."), gen_server:cast(self(), {command_response, "Você ja esta conectado.\n"}), State; @@ -296,9 +295,45 @@ handle_command(Cmd, State) -> NewState. validar_conexao(State = #state{shellInstancePid = undefined}) -> - ?LOG_INFO("Não havia instância do shell existente, criando uma nova instância do shell...~n"), - ShellPid = rpc_server_shell_manager_sup:start_shell(State#state.clientSocket, self()), + ?LOG_INFO("Não havia instância do shell existente, criando uma nova instância do shell (remota)...~n"), + {ok, {IP, Port}} = inet:peername(State#state.clientSocket), + HashId = gen_hash_identification({IP, Port}), + Meta = #{peer => {IP, Port}, hash_id => HashId}, + ShellPid = rpc:call(node(global:whereis_name(rpc_server_shell_manager_sup)), + rpc_server_shell_manager_sup, start_shell_remote, + [self(), Meta]), State#state{shellInstancePid = ShellPid}; validar_conexao(State) -> - State. \ No newline at end of file + State. + + + +%% @doc +%% Gera um hash identificador único para uma conexão (baseado em djb_hash). +%% +%% Este hash pode ser usado como identificador persistente para reconexões, +%% baseado nas informações do socket do cliente (por exemplo, IP e porta). +%% +%% @param Socket :: pid() | {inet:ip_address(), inet:port_number()} +%% @return string() +-spec gen_hash_identification(gen_tcp:socket() | {inet:ip_address(), inet:port_number()}) -> string(). +gen_hash_identification(Socket) when is_port(Socket) -> + {ok, {IP, Port}} = inet:peername(Socket), + gen_hash_from_ip_port(IP, Port); + +gen_hash_identification({IP, Port}) when is_tuple(IP), (size(IP) == 4 orelse size(IP) == 8), is_integer(Port), Port > 0 -> + gen_hash_from_ip_port(IP, Port). + +%% @private +%% Converte o IP para string (ex: "192.168.0.1") e concatena com porta +%% Depois aplica o hash DJB +gen_hash_from_ip_port(IP, Port) -> + IPStr = inet:ntoa(IP), + Str = lists:flatten(io_lib:format("~s:~p", [IPStr, Port])), + Hash = crypto:hash(sha256, Str), + Base64 = base64:encode(Hash), + binary_to_list(Base64). + + + \ No newline at end of file diff --git a/src/sctp/rpc_server_sctp_listen.erl b/apps/rpc_server_http/src/rpc_server_http_listen.erl similarity index 97% rename from src/sctp/rpc_server_sctp_listen.erl rename to apps/rpc_server_http/src/rpc_server_http_listen.erl index 09da0ef..1c5707f 100644 --- a/src/sctp/rpc_server_sctp_listen.erl +++ b/apps/rpc_server_http/src/rpc_server_http_listen.erl @@ -1,4 +1,4 @@ --module(rpc_server_sctp_listen). +-module(rpc_server_http_listen). -author('Fernando Areias '). @@ -162,7 +162,7 @@ handle_info({inet_async, ListenSocket, Ref, {ok, ClientSocket}}, #state{socket = ?LOG_INFO("Nova conexão recebida de ~p", [inet:peername(ClientSocket)]), case set_sockopt(ListenSocket, ClientSocket) of ok -> - case rpc_server_sctp_acceptor_sup:start_acceptor(ClientSocket, ListenSocket) of + case rpc_server_http_acceptor_sup:start_acceptor(ClientSocket, ListenSocket) of {ok} -> {ok, NewRef} = prim_inet:async_accept(ListenSocket, -1), {noreply, State#state{ref = NewRef}}; @@ -210,7 +210,7 @@ handle_info(_Info, State) -> %%% @doc Aguarda até que o supervisor de acceptors esteja disponível. %%% %%% Esta função realiza tentativas periódicas para localizar o processo registrado como -%%% `rpc_server_sctp_acceptor_sup`. Se não encontrar após o número máximo de tentativas, +%%% `rpc_server_http_acceptor_sup`. Se não encontrar após o número máximo de tentativas, %%% retorna um erro. É útil durante a inicialização do servidor para garantir que os recursos %%% necessários estejam disponíveis antes de prosseguir. %%% @@ -226,7 +226,7 @@ handle_info(_Info, State) -> wait_for_acceptor_sup(0) -> {error, acceptor_supervisor_not_found}; wait_for_acceptor_sup(Attempts) -> - case whereis(rpc_server_sctp_acceptor_sup) of + case whereis(rpc_server_http_acceptor_sup) of undefined -> timer:sleep(200), wait_for_acceptor_sup(Attempts - 1); @@ -250,7 +250,7 @@ wait_for_acceptor_sup(Attempts) -> -spec start_listening(pid()) -> {ok, #state{}} | {stop, term()}. start_listening(AcceptorSup) -> - Port = application:get_env(rpc_server, tcp_port, ?DEFAULT_PORT), + Port = application:get_env(rpc_server_http, tcp_port, ?DEFAULT_PORT), Options = [ binary, {reuseaddr, true}, diff --git a/src/supervisors/rpc_server_sctp_listen_sup.erl b/apps/rpc_server_http/src/rpc_server_http_listen_sup.erl similarity index 76% rename from src/supervisors/rpc_server_sctp_listen_sup.erl rename to apps/rpc_server_http/src/rpc_server_http_listen_sup.erl index 07e281c..cc235b6 100644 --- a/src/supervisors/rpc_server_sctp_listen_sup.erl +++ b/apps/rpc_server_http/src/rpc_server_http_listen_sup.erl @@ -1,4 +1,4 @@ --module(rpc_server_sctp_listen_sup). +-module(rpc_server_http_listen_sup). -author('Fernando Areias '). -include("rpc_server.hrl"). @@ -34,20 +34,20 @@ init([]) -> ChildSpecs = [ #{ - id => rpc_server_sctp_acceptor_sup, - start => {rpc_server_sctp_acceptor_sup, start_link, []}, + id => rpc_server_http_acceptor_sup, + start => {rpc_server_http_acceptor_sup, start_link, []}, restart => permanent, shutdown => infinity, type => supervisor, - modules => [rpc_server_sctp_acceptor_sup] + modules => [rpc_server_http_acceptor_sup] }, #{ - id => rpc_server_sctp_listen, - start => {rpc_server_sctp_listen, start_link, []}, + id => rpc_server_http_listen, + start => {rpc_server_http_listen, start_link, []}, restart => permanent, shutdown => 2000, type => worker, - modules => [rpc_server_sctp_listen] + modules => [rpc_server_http_listen] } ], diff --git a/src/commands/parser_command.erl b/apps/rpc_server_http/src/rpc_server_http_parser_command.erl similarity index 97% rename from src/commands/parser_command.erl rename to apps/rpc_server_http/src/rpc_server_http_parser_command.erl index 0af388e..313eeee 100644 --- a/src/commands/parser_command.erl +++ b/apps/rpc_server_http/src/rpc_server_http_parser_command.erl @@ -1,4 +1,4 @@ --module(parser_command). +-module(rpc_server_http_parser_command). -author('Fernando Areias '). -export([parse/1]). diff --git a/apps/rpc_server_shell/.gitignore b/apps/rpc_server_shell/.gitignore new file mode 100644 index 0000000..df53f7d --- /dev/null +++ b/apps/rpc_server_shell/.gitignore @@ -0,0 +1,20 @@ +.rebar3 +_build +_checkouts +_vendor +.eunit +*.o +*.beam +*.plt +*.swp +*.swo +.erlang.cookie +ebin +log +erl_crash.dump +.rebar +logs +.idea +*.iml +rebar3.crashdump +*~ diff --git a/apps/rpc_server_shell/LICENSE.md b/apps/rpc_server_shell/LICENSE.md new file mode 100644 index 0000000..fadbfdc --- /dev/null +++ b/apps/rpc_server_shell/LICENSE.md @@ -0,0 +1,186 @@ +# Apache License +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +## 1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +## 2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +## 3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +## 4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy of + this License; and + +2. You must cause any modified files to carry prominent notices stating that + You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You + distribute, all copyright, patent, trademark, and attribution notices from + the Source form of the Work, excluding those notices that do not pertain to + any part of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, then + any Derivative Works that You distribute must include a readable copy of the + attribution notices contained within such NOTICE file, excluding those + notices that do not pertain to any part of the Derivative Works, in at least + one of the following places: within a NOTICE text file distributed as part + of the Derivative Works; within the Source form or documentation, if + provided along with the Derivative Works; or, within a display generated by + the Derivative Works, if and wherever such third-party notices normally + appear. The contents of the NOTICE file are for informational purposes only + and do not modify the License. You may add Your own attribution notices + within Derivative Works that You distribute, alongside or as an addendum to + the NOTICE text from the Work, provided that such additional attribution + notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +## 5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +## 6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +## 7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, NON- +INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +## 8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +## 9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +Copyright 2025, Fernando Areias . + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/apps/rpc_server_shell/README.md b/apps/rpc_server_shell/README.md new file mode 100644 index 0000000..cd16255 --- /dev/null +++ b/apps/rpc_server_shell/README.md @@ -0,0 +1,9 @@ +rpc_server_shell +===== + +An OTP application + +Build +----- + + $ rebar3 compile diff --git a/apps/rpc_server_shell/include/rpc_server.hrl b/apps/rpc_server_shell/include/rpc_server.hrl new file mode 100644 index 0000000..c671610 --- /dev/null +++ b/apps/rpc_server_shell/include/rpc_server.hrl @@ -0,0 +1,46 @@ +%%%------------------------------------------------------------------- +%%% @author Fernando Areias +%%% @copyright (C) 2024, Erlang Brasil +%%% @doc Common definitions for RPC Server +%%% @end +%%%------------------------------------------------------------------- + +-ifndef(RPC_SERVER_HRL). +-define(RPC_SERVER_HRL, true). + +-include_lib("kernel/include/logger.hrl"). + + +%% Types +-type socket() :: port(). + +%% Application environment keys +-define(TCP_PORT, tcp_port). +-define(TCP_LISTEN_OPTIONS, tcp_listen_options). +-define(TCP_CONNECTION_OPTIONS, tcp_connection_options). + +%% Default values +-define(DEFAULT_TCP_PORT, 8080). +-define(DEFAULT_TCP_LISTEN_OPTIONS, [ + binary, + {packet, raw}, % 0 = raw - Sem cabeçalho, dados brutos + {reuseaddr, true}, % Permite reutilizar o endereço mesmo se estiver em estado TIME_WAIT + {active, true}, % Modo ativo total (passagem automática de mensagens) + {backlog, 128} % Número máximo de conexões pendentes na fila +]). +-define(DEFAULT_TCP_CONNECTION_OPTIONS, [ + binary, + {packet, raw}, + {active, false}, + {keepalive, true}, % Mantém a conexão ativa usando TCP keepalive + {send_timeout, 5000}, % Define um timeout de 5 segundos para operações de envio + {send_timeout_close, true}, + {exit_on_close, true} % Encerra o processo quando o socket é fechado +]). + +%% Timeouts +-define(ACCEPT_TIMEOUT, 5000). +-define(CONNECTION_TIMEOUT, 5000). +-define(HANDLER_TIMEOUT, 5000). + +-endif. \ No newline at end of file diff --git a/apps/rpc_server_shell/rebar.config b/apps/rpc_server_shell/rebar.config new file mode 100644 index 0000000..fb14169 --- /dev/null +++ b/apps/rpc_server_shell/rebar.config @@ -0,0 +1,28 @@ +{erl_opts, [debug_info]}. +{deps, [ + {lager, "3.9.2"} +]}. + +{shell, [ + %% {config, "config/sys.config"}, + {apps, [rpc_server_shell]} +]}. + + + +{lager_extra_sinks, [ % Configuração de sinks adicionais do Lager + {rpc_server_lager_event, [ + {handlers, [ % Handlers de log configurados + {lager_console_backend, [ % Handler para console + {level, info} % Nível mínimo: info + ]}, + {lager_file_backend, [ % Handler para arquivo + {file, "log/rpc_server.log"}, % Arquivo de log + {level, info}, + {size, 10485760}, % Tamanho máximo: ~10MB + {date, "$D0"}, + {count, 5} % Mantém 5 arquivos de backup + ]} + ]} + ]} +]}. diff --git a/apps/rpc_server_shell/src/rpc_server_shell.app.src b/apps/rpc_server_shell/src/rpc_server_shell.app.src new file mode 100644 index 0000000..1e90067 --- /dev/null +++ b/apps/rpc_server_shell/src/rpc_server_shell.app.src @@ -0,0 +1,14 @@ +{application, rpc_server_shell, [ + {description, "An OTP application"}, + {vsn, "0.1.0"}, + {registered, []}, + {mod, {rpc_server_shell_app, []}}, + {applications, [ + kernel, + stdlib + ]}, + {env, []}, + {modules, []}, + {licenses, ["Apache-2.0"]}, + {links, []} + ]}. diff --git a/apps/rpc_server_shell/src/rpc_server_shell_app.erl b/apps/rpc_server_shell/src/rpc_server_shell_app.erl new file mode 100644 index 0000000..d296d4a --- /dev/null +++ b/apps/rpc_server_shell/src/rpc_server_shell_app.erl @@ -0,0 +1,18 @@ +%%%------------------------------------------------------------------- +%% @doc rpc_server_shell public API +%% @end +%%%------------------------------------------------------------------- + +-module(rpc_server_shell_app). + +-behaviour(application). + +-export([start/2, stop/1]). + +start(_StartType, _StartArgs) -> + rpc_server_shell_sup:start_link(). + +stop(_State) -> + ok. + +%% internal functions diff --git a/src/shell/rpc_server_shell_instance.erl b/apps/rpc_server_shell/src/rpc_server_shell_instance.erl similarity index 65% rename from src/shell/rpc_server_shell_instance.erl rename to apps/rpc_server_shell/src/rpc_server_shell_instance.erl index bcc813a..1ff69eb 100644 --- a/src/shell/rpc_server_shell_instance.erl +++ b/apps/rpc_server_shell/src/rpc_server_shell_instance.erl @@ -14,22 +14,22 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -record(state, { - socket :: socket() | undefined, connection :: pid() | undefined, hashId :: term() | undefined, status :: connected | disconnected }). -start_link([ClientSocket, ConnectionPid]) -> - gen_server:start_link(?MODULE, [ClientSocket, ConnectionPid], []). +start_link([ConnectionPid, Meta]) -> + gen_server:start_link(?MODULE, [ConnectionPid, Meta], []). -init([ClientSocket, ConnectionPid]) -> - HashId = gen_hash_identification(ClientSocket), - ets:insert(connection_table, {HashId, self()}), - ?LOG_INFO("Iniciando shell ClientSocket ~p | ConnectionPid ~p | Hash ~p | Versão ~p", [ClientSocket, ConnectionPid, HashId, ?MODULO_VERSAO]), - gen_server:cast(ConnectionPid, {command_response, io_lib:format("Client identification ~p~n", [HashId])}), - - {ok, #state{connection = ConnectionPid, socket = ClientSocket, hashId = HashId, status = connected}}. +init([ConnectionPid, Meta]) -> + HashId = maps:get(hash_id, Meta, undefined), + ?LOG_INFO("Iniciando shell | ConnectionPid ~p | Hash ~p | Versão ~p", [ConnectionPid, HashId, ?MODULO_VERSAO]), + case HashId of + undefined -> ok; + _ -> gen_server:cast(ConnectionPid, {command_response, io_lib:format("Client identification ~p~n", [HashId])}) + end, + {ok, #state{connection = ConnectionPid, hashId = HashId, status = connected}}. handle_call(stop, _From, State) -> {stop, normal, stopped, State}; @@ -37,24 +37,24 @@ handle_call(stop, _From, State) -> handle_call(_Request, _From, State) -> {reply, ok, State}. -handle_cast({hibernate}, State = #state{socket = _}) -> +handle_cast({hibernate}, State) -> ?LOG_INFO("[SHELL INSTANCE] - Conexão com o cliente interrompida, marcando como desconectado..."), ?LOG_INFO("[SHELL INSTANCE] - Estado antes da desconexão: ~p", [State]), NewState = State#state{status = disconnected}, ?LOG_INFO("[SHELL INSTANCE] - Estado após hibernação: ~p", [NewState]), {noreply, NewState}; -handle_cast({reconnected, ConnectionPid, ClientSocket}, State) -> - ?LOG_INFO("[SHELL INSTANCE] - Recebida mensagem de reconexão: ConnectionPid=~p, ClientSocket=~p", [ConnectionPid, ClientSocket]), +handle_cast({reconnected, ConnectionPid, _ClientSocket}, State) -> + ?LOG_INFO("[SHELL INSTANCE] - Recebida mensagem de reconexão: ConnectionPid=~p", [ConnectionPid]), ?LOG_INFO("[SHELL INSTANCE] - Conexão restabelecida com o cliente, shell pronto..."), - ?LOG_INFO("[SHELL INSTANCE] - Nova ConnectionPid: ~p, Novo ClientSocket: ~p", [ConnectionPid, ClientSocket]), + ?LOG_INFO("[SHELL INSTANCE] - Nova ConnectionPid: ~p", [ConnectionPid]), ?LOG_INFO("[SHELL INSTANCE] - Estado atual: ~p", [State]), gen_server:cast(ConnectionPid, {command_response, "Conexao restabelecida\n"}), - NewState = State#state{connection = ConnectionPid, socket = ClientSocket, hashId = State#state.hashId, status = connected}, + NewState = State#state{connection = ConnectionPid, hashId = State#state.hashId, status = connected}, ?LOG_INFO("[SHELL INSTANCE] - Estado após reconexão: ~p", [NewState]), {noreply, NewState}; -handle_cast({execute_command, Command}, State = #state{socket = _}) -> +handle_cast({execute_command, Command}, State) -> case State#state.status of disconnected -> ?LOG_INFO("[SHELL INSTANCE] - Tentativa de executar comando enquanto desconectado: ~p", [Command]), @@ -98,29 +98,7 @@ code_change(_OldVsn, State, _Extra) -> -%% @doc -%% Gera um hash identificador único para uma conexão (baseado em djb_hash). -%% -%% Este hash pode ser usado como identificador persistente para reconexões, -%% baseado nas informações do socket do cliente (por exemplo, IP e porta). -%% -%% @param Socket :: pid() | {inet:ip_address(), inet:port_number()} -%% @return non_neg_integer() --spec gen_hash_identification(gen_tcp:socket() | {inet:ip_address(), inet:port_number()}) -> non_neg_integer(). -gen_hash_identification(Socket) when is_port(Socket) -> - {ok, {IP, Port}} = inet:peername(Socket), - gen_hash_from_ip_port(IP, Port); - -gen_hash_identification({IP, Port}) when is_tuple(IP), (size(IP) == 4 orelse size(IP) == 8), is_integer(Port), Port > 0 -> - gen_hash_from_ip_port(IP, Port). - -%% @private -%% Converte o IP para string (ex: "192.168.0.1") e concatena com porta -%% Depois aplica o hash DJB -gen_hash_from_ip_port(IP, Port) -> - IPStr = inet:ntoa(IP), - Str = lists:flatten(io_lib:format("~s:~p", [IPStr, Port])), - crypto:hash(sha256, Str). +%% REMOVIDO: geração de hash agora ocorre no nó HTTP e é enviado via Meta diff --git a/src/supervisors/rpc_server_shell_manager_sup.erl b/apps/rpc_server_shell/src/rpc_server_shell_manager_sup.erl similarity index 66% rename from src/supervisors/rpc_server_shell_manager_sup.erl rename to apps/rpc_server_shell/src/rpc_server_shell_manager_sup.erl index 4e2a182..af0cba9 100644 --- a/src/supervisors/rpc_server_shell_manager_sup.erl +++ b/apps/rpc_server_shell/src/rpc_server_shell_manager_sup.erl @@ -11,7 +11,7 @@ -include("rpc_server.hrl"). -export([start_link/0]). --export([init/1, start_shell/2]). +-export([init/1, start_shell/2, start_shell_remote/2]). %%% @doc Inicia o supervisor do io manager. @@ -19,7 +19,7 @@ %%% @returns {ok, pid()} | {error, term()} -spec start_link() -> {ok, pid()} | {error, term()}. start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). + supervisor:start_link({global, ?MODULE}, ?MODULE, []). %%% @doc Callback de inicialização do supervisor. @@ -49,9 +49,9 @@ init([]) -> -spec start_shell(socket() | ssl:sslsocket(), pid()) -> pid() | {error, Reason :: term()}. -start_shell(ClientSocket, ConnectionPid) -> - Args = [ClientSocket, ConnectionPid], - case supervisor:start_child(?MODULE, [Args]) of +start_shell(_ClientSocket, ConnectionPid) -> + Args = [ConnectionPid, #{}], + case supervisor:start_child({global, ?MODULE}, [Args]) of {ok, ShellPid} -> ?LOG_INFO("Shell Iniciado PID ~p", [ShellPid]), ShellPid; @@ -60,4 +60,16 @@ start_shell(ClientSocket, ConnectionPid) -> {error, Reason} end. +-spec start_shell_remote(pid(), map()) -> pid() | {error, Reason :: term()}. +start_shell_remote(ConnectionPid, Meta) -> + Args = [ConnectionPid, Meta], + case supervisor:start_child({global, ?MODULE}, [Args]) of + {ok, ShellPid} -> + ?LOG_INFO("Shell Iniciado PID ~p (remoto)", [ShellPid]), + ShellPid; + {error, Reason} -> + ?LOG_ERROR("Falha ao iniciar o shell remoto ~p", [Reason]), + {error, Reason} + end. + \ No newline at end of file diff --git a/apps/rpc_server_shell/src/rpc_server_shell_sup.erl b/apps/rpc_server_shell/src/rpc_server_shell_sup.erl new file mode 100644 index 0000000..4a88dfa --- /dev/null +++ b/apps/rpc_server_shell/src/rpc_server_shell_sup.erl @@ -0,0 +1,46 @@ +%%%------------------------------------------------------------------- +%% @doc rpc_server_shell top level supervisor. +%% @end +%%%------------------------------------------------------------------- + +-module(rpc_server_shell_sup). + +-behaviour(supervisor). + +-export([start_link/0]). + +-export([init/1]). + +-define(SERVER, ?MODULE). + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%% sup_flags() = #{strategy => strategy(), % optional +%% intensity => non_neg_integer(), % optional +%% period => pos_integer()} % optional +%% child_spec() = #{id => child_id(), % mandatory +%% start => mfargs(), % mandatory +%% restart => restart(), % optional +%% shutdown => shutdown(), % optional +%% type => worker(), % optional +%% modules => modules()} % optional +init([]) -> + SupFlags = #{ + strategy => one_for_all, + intensity => 0, + period => 1 + }, + ChildSpecs = [ + #{ + id => rpc_server_shell_manager_sup, + start => {rpc_server_shell_manager_sup, start_link, []}, + restart => permanent, + shutdown => infinity, + type => supervisor, + modules => [rpc_server_shell_manager_sup] + } + ], + {ok, {SupFlags, ChildSpecs}}. + +%% internal functions diff --git a/config/dev_http.vm.args b/config/dev_http.vm.args new file mode 100644 index 0000000..46f5f09 --- /dev/null +++ b/config/dev_http.vm.args @@ -0,0 +1,7 @@ +-name rpc_server_http@127.0.0.1 +-setcookie service_discovery_cookie +-env ERL_MAX_PORTS 4096 +-env ERL_CRASH_DUMP ./log/erl_crash_http.dump + + +-kernel logger_level info \ No newline at end of file diff --git a/config/dev_shell.vm.args b/config/dev_shell.vm.args new file mode 100644 index 0000000..6a467f2 --- /dev/null +++ b/config/dev_shell.vm.args @@ -0,0 +1,7 @@ +-name rpc_server_shell@127.0.0.1 +-setcookie service_discovery_cookie +-env ERL_MAX_PORTS 4096 +-env ERL_CRASH_DUMP ./log/erl_crash_shell.dump + + +-kernel logger_level info \ No newline at end of file diff --git a/config/dev_sys.config b/config/dev_sys.config new file mode 100644 index 0000000..58aeddf --- /dev/null +++ b/config/dev_sys.config @@ -0,0 +1,52 @@ +[ + {kernel, [ + {sync_nodes_optional, ['rpc_server_http@127.0.0.1','rpc_server_shell@127.0.0.1']}, + {sync_nodes_timeout, 30000}, + {logger_level, info}, + {logger, [ + {handler, default, logger_std_h, + #{level => info, + formatter => {logger_formatter, #{single_line => true}}}}, + {handler, error_logger, logger_std_h, + #{level => info, + formatter => {logger_formatter, #{single_line => true}}}} + ]} + ]}, + + {lager, [ + {handlers, [ + {lager_console_backend, [{level, info}]}, + {lager_file_backend, [ + {file, "log/rpc_server.log"}, + {level, info}, + {size, 10485760}, + {date, "$D0"}, + {count, 5} + ]} + ]} + ]}, + + {rpc_server_http, [ + {tcp_port, 8080}, + {num_acceptors, 100}, + {max_connections, 10000}, + {tcp_listen_options, [ + binary, + {packet, raw}, + {reuseaddr, true}, + {active, true}, + {backlog, 128} + ]}, + {tcp_connection_options, [ + binary, + {packet, raw}, + {active, false}, + {keepalive, true}, + {send_timeout, 5000}, + {send_timeout_close, true}, + {exit_on_close, true} + ]} + ]}, + + {rpc_server_shell, []} +]. \ No newline at end of file diff --git a/config/dev_vm.config b/config/dev_vm.config new file mode 100644 index 0000000..f2a1f6a --- /dev/null +++ b/config/dev_vm.config @@ -0,0 +1,11 @@ +-name rpc_server@127.0.0.1 + +-setcookie service_discovery_cookie + ++S 1 + ++C multi_time_warp + ++sbwt none + ++A30 \ No newline at end of file diff --git a/config/sys.config b/config/sys.config deleted file mode 100644 index 0ca5870..0000000 --- a/config/sys.config +++ /dev/null @@ -1,26 +0,0 @@ -[ - {kernel, [ - {logger_level, info} - ]}, - {rpc_server, [ - {tcp_port, 8080}, - {num_acceptors, 100}, % Número de processos aceitando conexões - {max_connections, 10000}, % Limite máximo de conexões simultâneas - {tcp_listen_options, [ % Esse é utilizado no listener global - binary, - {packet, raw}, % 0 = raw - Sem cabeçalho, dados brutos - {reuseaddr, true}, % Permite reutilizar o endereço mesmo se estiver em estado TIME_WAIT - {active, true}, % Modo ativo total (passagem automática de mensagens) - {backlog, 128} % Número máximo de conexões pendentes na fila - ]}, - {tcp_connection_options, [ % Esse é utilizado para cada conexão individual - binary, - {packet, raw}, - {active, false}, - {keepalive, true}, % Mantém a conexão ativa usando TCP keepalive - {send_timeout, 5000}, % Define um timeout de 5 segundos para operações de envio - {send_timeout_close, true}, - {exit_on_close, true} % Encerra o processo quando o socket é fechado - ]} - ]} -]. \ No newline at end of file diff --git a/rebar.config b/rebar.config index a983772..d931497 100644 --- a/rebar.config +++ b/rebar.config @@ -1,47 +1,42 @@ -{erl_opts, [ - debug_info, - {i, "include"}, - {parse_transform, lager_transform} -]}. - +{erl_opts, [debug_info]}. {deps, [ {lager, "3.9.2"} % Dependência do Lager(https://github.com/erlang-lager/lager) para logging estruturado ]}. -{shell, [ - {config, "config/sys.config"}, - {apps, [rpc_server]} -]}. +{apps_dirs, ["apps"]}. {relx, [ - {release, {rpc_server, "0.1.0"}, [ - kernel, - stdlib, - sasl, - rpc_server - ]}, + {release, {rpc_server, "0.1.0"}, + [kernel, + stdlib, + sasl, + crypto, + lager, + runtime_tools, observer, wx, + rpc_server_http, + rpc_server_shell]}, - {mode, dev}, + {sys_config, "./config/dev_sys.config"}, + {vm_args, "./config/dev_vm.config"}, + + {dev_mode, true}, + {include_erts, false}, {extended_start_script, true}, - {config, "config/sys.config"}, {overlay, [ {mkdir, "log/"} ]} ]}. -{profiles, [ - {test, [ - {deps, [ - {proper, "1.4.0"}, - {eunit_formatters, "0.5.0"} - ]}, - {erl_opts, [nowarn_export_all]}, - {src_dirs, ["src"]}, - {test_dirs, ["tests"]} +{shell, [ + {config, "config/dev_sys.config"}, + {apps, [ + rpc_server_http, + rpc_server_shell ]} ]}. + {edoc_opts, [ {preprocess, true}, {macros, [{d, 'EDOC'}]}, @@ -49,27 +44,51 @@ {todo, true} ]}. + {cover_enabled, true}. {cover_opts, [verbose]}. {plugins, [ {rebar3_proper, "0.12.1"}, - {rebar3_lint, "4.0.0"} + {rebar3_lint, "4.0.0"} ]}. -{lager_extra_sinks, [ % Configuração de sinks adicionais do Lager - {rpc_server_lager_event, [ - {handlers, [ % Handlers de log configurados - {lager_console_backend, [ % Handler para console - {level, info} % Nível mínimo: info - ]}, - {lager_file_backend, [ % Handler para arquivo - {file, "log/rpc_server.log"}, % Arquivo de log - {level, info}, - {size, 10485760}, % Tamanho máximo: ~10MB - {date, "$D0"}, - {count, 5} % Mantém 5 arquivos de backup - ]} +{project_plugins, [rebar3_format]}. + + +{alias, [ + {test, [{format, "--verify"}, eunit, ct, cover]} +]}. + +{format, [{files, [ + "./src/rpc_server_http_acceptor_sup.erl" +]}]}. + +{cover_enabled, true}. +{cover_opts, [verbose]}. + +{profiles, [ + {http_node, [ + {relx, [ + {release, {rpc_server_http, "0.1.0"}, + [kernel, stdlib, sasl, crypto, lager, runtime_tools, observer, wx, rpc_server_http]}, + {sys_config, "./config/dev_sys.config"}, + {vm_args, "./config/dev_http.vm.args"}, + {dev_mode, true}, + {include_erts, false}, + {extended_start_script, true} + ]} + ]}, + {shell_node, [ + {relx, [ + {release, {rpc_server_shell, "0.1.0"}, + [kernel, stdlib, sasl, crypto, lager, runtime_tools, observer, wx, rpc_server_shell]}, + {sys_config, "./config/dev_sys.config"}, + {vm_args, "./config/dev_shell.vm.args"}, + {dev_mode, true}, + {include_erts, false}, + {extended_start_script, true} ]} ]} ]}. + diff --git a/src/app/rpc_server_app.erl b/src/app/rpc_server_app.erl deleted file mode 100644 index 4959a38..0000000 --- a/src/app/rpc_server_app.erl +++ /dev/null @@ -1,37 +0,0 @@ --module(rpc_server_app). - --define(MODULO_VERSAO, 1). --vsn(?MODULO_VERSAO). - --author('Fernando Areias '). --behaviour(application). - --include_lib("kernel/include/logger.hrl"). - --export([start/2, stop/1]). - -%%% @doc Inicia a aplicação. -%%% -%%% @param StartType application:start_type() - Tipo de início -%%% @param StartArgs term() - Argumentos de início -%%% @returns {ok, pid()} | {error, term()} --spec start(application:start_type(), term()) -> {ok, pid()} | {error, term()}. -start(_StartType, _StartArgs) -> - case rpc_server_sup:start_link() of - {ok, Pid} -> - ?LOG_INFO("RPC Server iniciado"), - {ok, Pid}; - Error -> - ?LOG_ERROR("Falha ao iniciar RPC Server: ~p", [Error]), - Error - end. - -%%% @doc Para a aplicação. -%%% -%%% @param State term() - Estado da aplicação -%%% @returns ok --spec stop(term()) -> ok. -stop(_State) -> - ?LOG_INFO("RPC Server parado"), - ok. - \ No newline at end of file diff --git a/src/app/rpc_server_sup.erl b/src/app/rpc_server_sup.erl deleted file mode 100644 index 8b4687c..0000000 --- a/src/app/rpc_server_sup.erl +++ /dev/null @@ -1,56 +0,0 @@ -%%% @doc Supervisor principal da aplicação (Raiz da árvore) -%%% -%%% Este supervisor gerencia todos os processos da aplicação, -%%% incluindo o listener TCP e seus componentes. -%%% -%%% @end --module(rpc_server_sup). --author('Fernando Areias '). - - --behaviour(supervisor). - --export([start_link/0]). - --export([init/1]). - -%%% @doc Inicia o supervisor principal. -%%% -%%% @returns {ok, pid()} | {error, term()} --spec start_link() -> {ok, pid()} | {error, term()}. -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - -%%% @doc Callback de inicialização do supervisor. -%%% -%%% @returns {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}} --spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}. -init([]) -> - process_flag(trap_exit, true), - - SupFlags = #{ - strategy => one_for_one, - intensity => 10, - period => 60 - }, - - ChildSpecs = [ - #{ - id => rpc_server_sctp_listen_sup, - start => {rpc_server_sctp_listen_sup, start_link, []}, - restart => permanent, - shutdown => infinity, - type => supervisor, - modules => [rpc_server_sctp_listen_sup] - }, - #{ - id => rpc_server_shell_manager_sup, - start => {rpc_server_shell_manager_sup, start_link, []}, - restart => permanent, - shutdown => infinity, - type => supervisor, - modules => [rpc_server_shell_manager_sup] - } - ], - - {ok, {SupFlags, ChildSpecs}}. diff --git a/tests/app/rpc_server_app_tests.erl b/tests/app/rpc_server_app_tests.erl deleted file mode 100644 index 10658ea..0000000 --- a/tests/app/rpc_server_app_tests.erl +++ /dev/null @@ -1,26 +0,0 @@ --module(rpc_server_app_tests). - --include_lib("common_test/include/ct.hrl"). - --compile(export_all). - -all() -> - [ - init_test - ]. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_test(_Config) -> - {ok, _} = application:ensure_all_started(rpc_server), - ct:pal("Application started"), - application:stop(rpc_server), - timer:sleep(100), - false = is_process_alive(whereis(rpc_server_sup)). - - - diff --git a/tests/sctp/rpc_server_sctp_listen_tests.erl b/tests/sctp/rpc_server_sctp_listen_tests.erl deleted file mode 100644 index f750401..0000000 --- a/tests/sctp/rpc_server_sctp_listen_tests.erl +++ /dev/null @@ -1,34 +0,0 @@ --module(rpc_server_sctp_listen_tests). - --include_lib("common_test/include/ct.hrl"). --include_lib("eunit/include/eunit.hrl"). - --compile(export_all). - -all() -> - [ - test_is_running, - test_stop_server - ]. - -init_per_suite(Config) -> - application:ensure_all_started(rpc_server), - Config. - -end_per_suite(_Config) -> - application:stop(rpc_server), - ok. - -test_is_running(_Config) -> - Pid = whereis(rpc_server_sctp_listen), - ?assert(is_pid(Pid)), - ?assert(process_info(Pid) =/= undefined). - -test_stop_server(_Config) -> - OldPid = whereis(rpc_server_sctp_listen), - ?assert(is_pid(OldPid)), - ?assertEqual(stopped, gen_server:call(OldPid, stop)), - timer:sleep(200), - NewPid = whereis(rpc_server_sctp_listen), - ?assert(is_pid(NewPid)), - ?assertNotEqual(OldPid, NewPid). \ No newline at end of file