Skip to content

Include/Plugin HTTPS para SA:MP com GET/POST/HEAD, HTTP/2, compressão automática, fila com pool de workers e política de segurança (anti-downgrade, cross-host opt-in). API Pawn simples, callback pronto na include — basta configurar um SetTimer.

Notifications You must be signed in to change notification settings

NullSablex/https-samp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HTTPS-SAMP

Include e plugin para realizar GET/POST/HEAD com suporte a HTTPS, cabeçalhos, fila, callback e agora helpers de corpo (JSON/Form/RAW) e mTLS.

Notas importantes

  • As URLs devem incluir o esquema explicitamente: https:// ou http://.
  • A include já fornece o public que processa a fila e o callback interno; você só precisa configurar um SetTimer.
  • O nome do seu callback (passado em https(...)) é livre, mas não pode ser o mesmo do callback interno da fila.
  • HEAD não segue redirects; retorna apenas o status com corpo vazio.
  • Este documento descreve a API Pawn e comportamentos do plugin.

Instalação

  1. Coloque o binário do plugin em plugins/.

  2. Adicione o nome do plugin em server.cfg (linha plugins).

  3. Inclua no seu script:

    #include <a_https>
  4. Configure o timer para o processamento da fila:

    public OnGameModeInit()
    {
        SetTimer("ProcessHTTPSQueue", 100, true); // o public já vem na include
        return 1;
    }

    Em filterscript, use OnFilterScriptInit.


Uso rápido

GET

// index pode ser 0 (servidor), playerid, ou qualquer inteiro de correlação
https(0, HTTPS_GET, "https://api.exemplo.com/ping", "", "OnHttpDone");

public OnHttpDone(index, response[], status, error)
{
    if (error) {
        printf("[HTTPS] erro=%d status=%d", error, status);
        return 1;
    }
    printf("[HTTPS] status=%d body=%s", status, response);
    return 1;
}

POST (JSON) com helper de corpo (one‑shot)

// Prepare o corpo para o PRÓXIMO POST sem precisar lidar com escape de string
https_jsonf("{\"hello\":\"world\"}"); // também define Content-Type automaticamente
https(0, HTTPS_POST, "https://api.exemplo.com/echo", "", "OnHttpDone");

POST (Form URL-Encoded) com builder

// Você pode compor um form aos poucos; no POST seguinte (data="") o corpo é gerado
https_form_add("user", "erick");
https_form_add("scope", "basic");
https(0, HTTPS_POST, "https://api.exemplo.com/login", "", "OnLogin");

Regra de consumo (one‑shot): se https(...) for POST e data estiver vazio, o plugin usa, nessa ordem, o corpo RAW (https_bodyf) > JSON (https_jsonf) > FORM (https_form_add). Após enviar, o payload preparado é consumido.


Funções

https(index, request_type, url[], data[], callback[])

Dispara uma requisição.

  • index — identificador livre (ex.: 0 para servidor, playerid em comandos, ou outro inteiro).
  • request_typeHTTPS_GET (1), HTTPS_POST (2) ou HTTPS_HEAD (3).
  • url[]precisa começar com http:// ou https://.
  • data[] — corpo para POST; use "" para GET/HEAD. Em POST, se data for "", o payload preparado (one‑shot) será usado.
  • callback[] — nome do public que receberá a resposta (não use o mesmo nome do callback interno da fila).
  • Retorno: 1 (aceito para execução).

A resposta chegará via callback.


Helpers de corpo (one‑shot)

Pensados para facilitar POST sem precisar formatar tudo na mesma linha.

https_bodyf(data[])

Define corpo RAW diretamente para o próximo POST (one‑shot). Não altera Content-Type automaticamente.

https_jsonf(json[])

Valida a string como JSON e define Content-Type: application/json; charset=utf-8 para o próximo POST (one‑shot). Retorna 1 se válido, 0 se inválido.

https_form_add(key[], value[])

Acumula pares key=value para montar um corpo application/x-www-form-urlencoded no próximo POST (one‑shot). Define Content-Type automaticamente.

Prioridade quando há múltiplos preparados: RAW > JSON > FORM.


Cabeçalhos

https_set_header(key[], value[])

Temporário. Aplicado à próxima requisição. É limpo automaticamente ao fim da execução (sucesso ou erro). Útil para Authorization, Content-Type específico, etc.

https_set_global_header(key[], value[])

Global. Aplicado em todas as requisições futuras até limpar.

https_clear_global_headers()

Remove todos os cabeçalhos globais.

Sobreposição: cabeçalhos temporários sobrescrevem os globais em caso de chave repetida.


Fila (Queue)

https_process_queue()

Processa respostas pendentes e dispara seus callbacks.

Já é chamado pelo public ProcessHTTPSQueue fornecido (veja abaixo). Cada chamada processa até 64 respostas.

https_queue_len() -> int

Retorna quantas respostas estão pendentes na fila.

Tamanho da fila de respostas: 1024 itens.


Limite de corpo

https_set_max_body_bytes(bytes) -> int

Define o limite máximo, em bytes, para o corpo da resposta. Retorna o valor efetivo aplicado.

Faixa permitida: 4 KiB a 1 MiB (valores fora dessa faixa são ajustados). Padrão: 64 KiB.

https_get_max_body_bytes() -> int

Retorna o limite atual (bytes).


Redirect entre domínios (opt‑in por requisição)

https_allow_cross_host_once(bool: enable)

Permite seguir redirect para outro host na requisição que você vai disparar em seguida (one‑shot). Válido para GET e POST; a semântica HTTP é respeitada:

  • 303 → vira GET (sem corpo).
  • 301/302POST pode virar GET; GET permanece GET.
  • 307/308 → mantém método e corpo.

Ao trocar de host com essa permissão, o cabeçalho Authorization é removido automaticamente por segurança.

Políticas de redirect adicionais:

  • Máximo de 5 redirects; acima disso → HTTPS_ERROR_POLICY_BLOCKED.
  • Se durante a cadeia já passamos por HTTPS, não é permitido downgrade para HTTP (bloqueado por política).
  • HEAD não segue redirect (retorna o 30x como chegou, com corpo vazio).

mTLS (mutual TLS) — opcional

Use mTLS quando seu servidor exigir certificado de cliente. O plugin mantém uma identidade mTLS global (cert+key em um mesmo PEM). No momento do submit da requisição (https(...)), essa identidade é congelada junto com os cabeçalhos — então limpar a identidade depois do https(...) não afeta a requisição já enviada para a fila.

Nativas

  • https_mtls_set_pem(pem[]) -> int — Configura a identidade a partir de uma string PEM contendo cert + key. Retorna 1 se OK, 0 se falhar.
  • https_mtls_set_pem_file(path[]) -> int — Lê um arquivo PEM (cert+key) e configura. Retorna 1 se OK, 0 se falhar. Há um limite defensivo de 256 KiB.
  • https_mtls_clear() -> int — Remove a identidade atual.

Formato do PEM: o arquivo/string deve conter o certificado do cliente e a chave privada (em PEM) — por exemplo:

-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----

Modelo A — Global (todas as próximas requisições)

Configure uma vez no OnGameModeInit (ou OnFilterScriptInit):

public OnGameModeInit()
{
    if (!https_mtls_set_pem_file("scriptfiles/client_identity.pem"))
        print("[HTTPS] mTLS: falha ao carregar PEM");

    SetTimer("ProcessHTTPSQueue", 100, true);
    return 1;
}

Também é possível com uma string PEM (por exemplo, lida de DB):

new pem[] = "-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
"
           "-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
";
if (!https_mtls_set_pem(pem)) print("[HTTPS] mTLS: PEM inválido");

Modelo B — "Per‑requisição" (one‑shot por padrão de uso)

Não existe native específica "one‑shot" de mTLS. Para aplicar mTLS somente à próxima chamada, use o padrão arma → dispara → limpa. Como a identidade é congelada no submit, é seguro limpar logo depois:

// 1) Carrega a identidade
https_mtls_set_pem_file("scriptfiles/client_identity.pem");

// 2) Dispara a requisição que precisa de mTLS
https(0, HTTPS_POST, "https://api.exemplo.com/secure", "{\"ping\":1}", "OnSecure");

// 3) Limpa imediatamente para não afetar outras chamadas futuras
https_mtls_clear();

Com PEM em string:

https_mtls_set_pem(pem_str);
https(0, HTTPS_GET, "https://secure.example.com/profile", "", "OnProfile");
https_mtls_clear();

Importante sobre concorrência: em SA‑MP o Pawn executa sequencialmente dentro do mesmo callback, então, se você mantiver o padrão acima sem intercalar outras https(...), apenas essa requisição capturará a identidade. Se você disparar duas https(...) entre set e clear, ambas usarão mTLS. Evite intercalar outras chamadas.

Modelo C — Sequência com identidades distintas

Você pode alternar identidades entre chamadas:

// Requisição A com identidade 1
https_mtls_set_pem_file("scriptfiles/id_cliente_A.pem");
https(0, HTTPS_GET, "https://a.secure.example/api", "", "OnA");
https_mtls_clear();

// Requisição B com identidade 2
https_mtls_set_pem_file("scriptfiles/id_cliente_B.pem");
https(0, HTTPS_GET, "https://b.secure.example/api", "", "OnB");
https_mtls_clear();

Observações técnicas

  • A identidade mTLS é capturada no momento do https(...) e carregada no Job de execução; limpar depois não a remove daquela requisição.
  • Se nenhuma identidade estiver configurada no momento do submit, a requisição usa o cliente HTTP padrão (sem mTLS).
  • O cliente usa Rustls, com connect_timeout7s e timeout total ≈ 12s.

Callback (modelo)

public MeuCallback(index, response[], status, error)
{
    // index: o mesmo informado na chamada (ex.: playerid ou 0)
    // response: corpo da resposta (string; vazio em HEAD)
    // status: código HTTP (ex.: 200, 404, 500)
    // error: código de erro (0 = sem erro)
    return 1;
}

Use nomes distintos para múltiplos callbacks, se desejar, mas não use o mesmo nome do callback interno da fila.


public interno da include

A include (a_https.inc) já fornece um public para você ligar no timer e processar a fila:

forward ProcessHTTPSQueue();
public ProcessHTTPSQueue()
{
    https_process_queue();
    return 1;
}

Chame-o periodicamente (ex.: a cada 100 ms com SetTimer).


Constantes

Métodos

#define HTTPS_GET   1
#define HTTPS_POST  2
#define HTTPS_HEAD  3

Códigos de erro

#define HTTPS_ERROR_NONE              0   // Sem erro
#define HTTPS_ERROR_BAD_URL           1   // URL inválida ou sem esquema http(s)
#define HTTPS_ERROR_TLS_HANDSHAKE     2   // Falha na conexão segura (aproximação)
#define HTTPS_ERROR_NO_SOCKET         3   // Rede/soquete indisponível (host/network unreachable, etc.)
#define HTTPS_ERROR_CANT_CONNECT      4   // Não foi possível conectar (conn refused, etc.)
#define HTTPS_ERROR_SEND_FAIL         5   // Falha ao enviar/receber
#define HTTPS_ERROR_CONTENT_TOO_BIG   6   // Corpo excedeu o limite configurado
#define HTTPS_ERROR_TIMEOUT           7   // Tempo esgotado (connect/read)
#define HTTPS_ERROR_POLICY_BLOCKED    8   // Bloqueado por política (ex.: downgrade https→http, cross‑host não permitido, redirects excessivos)
#define HTTPS_ERROR_UNKNOWN           10  // Erro não categorizado

Dica: sempre verifique error antes de usar response. Em erro de timeout ou policy, status pode estar 0.


Exemplos de uso

1) Requisição do servidor (index = 0)

https(0, HTTPS_GET, "https://status.exemplo.com/health", "", "OnHttpHealth");

public OnHttpHealth(i, body[], status, err)
{
    if (err) return printf("[HEALTH] err=%d status=%d", err, status);
    printf("[HEALTH] ok status=%d", status);
    return 1;
}

2) Em um comando por jogador (index = playerid)

CMD:perfil(playerid, params[])
{
    new url[128];
    format(url, sizeof(url), "https://api.exemplo.com/users/%d", playerid);

    https(playerid, HTTPS_GET, url, "", "OnPerfil");
    return 1;
}

public OnPerfil(playerid, body[], status, err)
{
    if (err) return SendClientMessage(playerid, -1, "Falha ao obter perfil.");
    SendClientMessage(playerid, -1, "Perfil recebido!");
    return 1;
}

3) POST autenticado (temporário no mesmo escopo)

CMD:login(playerid, params[])
{
    new token[128];
    // ... obter token do jogador ...

    new auth[160];
    format(auth, sizeof(auth), "Bearer %s", token);

    https_set_header("Authorization", auth); // temporário
    https_jsonf("{\"action\":\"login\"}");       // define corpo + Content-Type
    https(playerid, HTTPS_POST, "https://api.exemplo.com/session", "", "OnLogin");
    return 1;
}

4) Definir cabeçalhos globais

public OnGameModeInit()
{
    SetTimer("ProcessHTTPSQueue", 100, true);

    https_set_global_header("Accept", "application/json");
    https_set_global_header("User-Agent", "HTTPS-SAMP/1.0");
    return 1;
}

5) Cross‑host consciente (GET ou POST)

// Permita conscientemente e dispare em seguida
https_allow_cross_host_once(true);
https(0, HTTPS_GET, "https://a.exemplo.com/redirect", "", "OnHttpDone");

6) mTLS com PEM externo

public OnGameModeInit()
{
    // Carrega cert + key (PEM único) para mutual TLS
    if (!https_mtls_set_pem_file("scriptfiles/client_identity.pem")) {
        print("[HTTPS] Falha ao configurar mTLS");
    }

    SetTimer("ProcessHTTPSQueue", 100, true);
    return 1;
}

Comportamento interno (técnico)

  • Cliente HTTP: baseado em reqwest::blocking com Rustls (sem OpenSSL), HTTP/2 habilitado e descompressão automática (gzip, brotli, deflate, zstd).
  • Timeouts: connect_timeout7s; timeout total ≈ 12s.
  • Redirects: não automáticos; tratados manualmente (máx. 5), com regras de cross‑host e anti‑downgrade (HTTPS → HTTP bloqueado).
  • Cabeçalhos: globais + temporários são mesclados; temporários têm precedência e são limpos ao final de cada requisição.
  • Fila de execução: pool de 2 a 8 workers (≈ num_cpus()), fila de jobs 256. Se a fila encher, o job é executado em uma thread destacada. As respostas são empilhadas em uma fila separada (1024).
  • Processamento da fila: cada chamada ao https_process_queue() descarrega até 64 respostas (executa seus callbacks Pawn).
  • Leitura do corpo: feita com limite configurável (padrão 64 KiB). Se o corpo ultrapassar o limite, error = HTTPS_ERROR_CONTENT_TOO_BIG.
  • Proxy: não usa proxy do ambiente (no_proxy).

Recomendações de uso e segurança

  • Prefira sempre https://. http:// é aceito somente quando você passa explicitamente.
  • Use o padrão defina → chame para cabeçalhos temporários e helpers de corpo (one‑shot).
  • Cabeçalhos globais: bons para Accept, User-Agent. Evite Authorization global se houver possibilidade de redirect para outro domínio.
  • Habilite cross‑host apenas quando necessário (https_allow_cross_host_once(true) antes da requisição que precisa seguir).
  • Ajuste o limite de corpo com https_set_max_body_bytes conforme seu caso.
  • Sempre verifique error. Para HTTPS_ERROR_POLICY_BLOCKED (8), revise a URL/redirects esperados ou habilite cross‑host conscientemente.

Sobre o Projeto

  • Autor: Craft Games
  • Versão do plugin: 1.3.3
  • Repositório: https://github.com/craftgamesof/https-samp

Suporte

Abra um issue para dúvidas ou sugestões: https://github.com/craftgamesof/https-samp/issues


Licença

Distribuição binária. Uso livre, mas o código‑fonte não está incluído.

About

Include/Plugin HTTPS para SA:MP com GET/POST/HEAD, HTTP/2, compressão automática, fila com pool de workers e política de segurança (anti-downgrade, cross-host opt-in). API Pawn simples, callback pronto na include — basta configurar um SetTimer.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages