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://ouhttp://.- A include já fornece o
publicque processa a fila e o callback interno; você só precisa configurar umSetTimer.- O nome do seu callback (passado em
https(...)) é livre, mas não pode ser o mesmo do callback interno da fila.HEADnão segue redirects; retorna apenas o status com corpo vazio.- Este documento descreve a API Pawn e comportamentos do plugin.
-
Coloque o binário do plugin em
plugins/. -
Adicione o nome do plugin em
server.cfg(linhaplugins). -
Inclua no seu script:
#include <a_https>
-
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.
// 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;
}// 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");// 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 edataestiver 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.
Dispara uma requisição.
index— identificador livre (ex.: 0 para servidor, playerid em comandos, ou outro inteiro).request_type—HTTPS_GET(1),HTTPS_POST(2) ouHTTPS_HEAD(3).url[]— precisa começar comhttp://ouhttps://.data[]— corpo para POST; use""para GET/HEAD. Em POST, sedatafor"", o payload preparado (one‑shot) será usado.callback[]— nome dopublicque 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.
Pensados para facilitar POST sem precisar formatar tudo na mesma linha.
Define corpo RAW diretamente para o próximo POST (one‑shot). Não altera Content-Type automaticamente.
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.
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.
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.
Global. Aplicado em todas as requisições futuras até limpar.
Remove todos os cabeçalhos globais.
Sobreposição: cabeçalhos temporários sobrescrevem os globais em caso de chave repetida.
Processa respostas pendentes e dispara seus callbacks.
Já é chamado pelo
public ProcessHTTPSQueuefornecido (veja abaixo). Cada chamada processa até 64 respostas.
Retorna quantas respostas estão pendentes na fila.
Tamanho da fila de respostas: 1024 itens.
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.
Retorna o limite atual (bytes).
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/302 → POST 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).
HEADnão segue redirect (retorna o 30x como chegou, com corpo vazio).
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.
https_mtls_set_pem(pem[]) -> int— Configura a identidade a partir de uma string PEM contendo cert + key. Retorna1se OK,0se falhar.https_mtls_set_pem_file(path[]) -> int— Lê um arquivo PEM (cert+key) e configura. Retorna1se OK,0se 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-----
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");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 duashttps(...)entreseteclear, ambas usarão mTLS. Evite intercalar outras chamadas.
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();- 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_timeout≈ 7s etimeouttotal ≈ 12s.
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.
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).
#define HTTPS_GET 1
#define HTTPS_POST 2
#define HTTPS_HEAD 3#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 categorizadoDica: sempre verifique
errorantes de usarresponse. Em erro de timeout ou policy,statuspode estar 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;
}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;
}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;
}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;
}// Permita conscientemente e dispare em seguida
https_allow_cross_host_once(true);
https(0, HTTPS_GET, "https://a.exemplo.com/redirect", "", "OnHttpDone");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;
}- Cliente HTTP: baseado em
reqwest::blockingcom Rustls (sem OpenSSL), HTTP/2 habilitado e descompressão automática (gzip,brotli,deflate,zstd). - Timeouts:
connect_timeout≈ 7s;timeouttotal ≈ 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).
- 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. EviteAuthorizationglobal 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_bytesconforme seu caso. - Sempre verifique
error. ParaHTTPS_ERROR_POLICY_BLOCKED (8), revise a URL/redirects esperados ou habilite cross‑host conscientemente.
- Autor: Craft Games
- Versão do plugin: 1.3.3
- Repositório:
https://github.com/craftgamesof/https-samp
Abra um issue para dúvidas ou sugestões: https://github.com/craftgamesof/https-samp/issues
Distribuição binária. Uso livre, mas o código‑fonte não está incluído.