Skip to content

kajooly/xml_ex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

XmlEx

Codificador/decodificador SOAP/XML para Elixir, construido sobre OTP (xmerl para parsing). El transporte HTTP es opcional e intercambiable: trae tu propio cliente (HTTPoison, Tesla, Req, Finch...) o usa el :httpc integrado sin agregar ninguna dependencia.

Instalación

def deps do
  [
    {:xml_ex, "~> 0.2.0"},
    # opcionales — solo si quieres usarlos como transporte:
    {:httpoison, "~> 2.0"},
    {:tesla, "~> 1.7"},
    {:finch, "~> 0.16"}
  ]
end

Uso como codificador/decodificador (trae tu cliente HTTP)

Los elementos se representan como tuplas {nombre, atributos, hijos}. encode/2 genera el envelope y los headers HTTP listos para enviar; decode/1 parsea la respuesta y detecta SOAP Faults:

body = {"tns:ObtenerSaldo", %{}, [{"tns:cuenta", %{}, "12345"}]}

request =
  XmlEx.encode(body,
    namespaces: %{"tns" => "http://example.com/banco"},
    soap_action: "http://example.com/banco/ObtenerSaldo"
  )
#=> %{body: "<?xml ...>", headers: [{"Content-Type", ...}, {"SOAPAction", ...}]}

# Con HTTPoison
{:ok, resp} = HTTPoison.post(url, request.body, request.headers)
XmlEx.decode(resp.body)

# Con Tesla
{:ok, env} = Tesla.post(client, url, request.body, headers: request.headers)
XmlEx.decode(env.body)

decode/1 regresa {:ok, mapa}, {:error, %XmlEx.Fault{}} si la respuesta trae un SOAP Fault, o {:error, reason} si el XML es inválido.

Uso todo-en-uno: call/3

Codifica, envía y decodifica en un paso. El transporte es un módulo que implementa el behaviour XmlEx.Transport:

# default: :httpc (OTP), sin dependencias
XmlEx.call(url, body, soap_action: "...")

# por llamada...
XmlEx.call(url, body, transport: XmlEx.Transport.HTTPoison)
XmlEx.call(url, body, transport: XmlEx.Transport.Tesla, tesla_client: client)
XmlEx.call(url, body, transport: XmlEx.Transport.Finch, finch_name: MiApp.Finch)

# ...o global, en config/config.exs
config :xml_ex, transport: XmlEx.Transport.HTTPoison

Los adapters de HTTPoison, Tesla y Finch solo se compilan si el paquete correspondiente está entre tus dependencias. Para usar otro cliente, implementa el behaviour (un solo callback, post/4):

defmodule MiApp.ReqTransport do
  @behaviour XmlEx.Transport

  @impl true
  def post(url, body, headers, _opts) do
    case Req.post(url, body: body, headers: headers) do
      {:ok, resp} -> {:ok, %{status: resp.status, body: resp.body}}
      {:error, reason} -> {:error, reason}
    end
  end
end

TLS / verificación del certificado

Los transportes integrados heredan la configuración TLS del cliente HTTP subyacente, que por defecto no verifica el certificado del servidor: la conexión va cifrada pero el servidor no se autentica. Esto es cómodo para pruebas locales (servidores planos o con certificado self-signed).

En producción sí conviene activar la verificación con XmlEx.TLS.secure_options/1:

# transporte default (:httpc)
XmlEx.call(url, body, ssl: XmlEx.TLS.secure_options())

# HTTPoison
XmlEx.call(url, body,
  transport: XmlEx.Transport.HTTPoison,
  httpoison_options: [ssl: XmlEx.TLS.secure_options()]
)

# Tesla (adapter :httpc por defecto)
XmlEx.call(url, body, transport: XmlEx.Transport.Tesla, ssl: XmlEx.TLS.secure_options())

Con Tesla la opción :ssl apunta al adapter :httpc por defecto; si traes otro adapter (hackney, Mint, Finch) configura TLS en tu propio :tesla_client.

Para pruebas locales puedes omitir la opción :ssl o pasar XmlEx.TLS.secure_options(verify: :verify_none).

Pool de conexiones (carga sostenida)

Sin pooling, cada request abre una conexión nueva (y un handshake TLS nuevo). El pooling es opcional: para pruebas locales no hace falta, pero en producción bajo carga conviene reutilizar conexiones.

  • Finch (recomendado para pool nativo): el pool vive en la instancia que arrancas en tu árbol de supervisión. Una instancia simple sirve para pruebas; configura pools: para producción.

    # application.ex — instancia con pool y TLS verificado
    children = [
      {Finch,
       name: MiApp.Finch,
       pools: %{
         :default => [size: 25, conn_opts: [transport_opts: XmlEx.TLS.secure_options()]]
       }}
    ]
    
    XmlEx.call(url, body, transport: XmlEx.Transport.Finch, finch_name: MiApp.Finch)
  • HTTPoison/hackney: usa un pool con nombre vía :httpoison_options.

    # arranca el pool una vez (p. ej. en application.ex)
    :hackney_pool.start_pool(:xml_ex, timeout: 15_000, max_connections: 25)
    
    XmlEx.call(url, body,
      transport: XmlEx.Transport.HTTPoison,
      httpoison_options: [hackney: [pool: :xml_ex]]
    )
  • :httpc (default): reutiliza conexiones por profile cuando el servidor permite keep-alive; ajustable con las opciones max_sessions / max_keep_alive_length del perfil de :httpc.

Servidores legacy Java / JAX-WS

Algunos Listeners SOAP en Java (JAX-WS) se cuelgan ante el ClientHello de TLS 1.3 que OTP negocia por defecto. El síntoma es un connect colgado (no un recv lento). Solución: forzar TLS 1.2.

# combina con la verificación del certificado si la usas
ssl = XmlEx.TLS.secure_options(versions: [:"tlsv1.2"])

XmlEx.call(url, body, ssl: ssl)                                  # :httpc
XmlEx.call(url, body, transport: XmlEx.Transport.HTTPoison,
  httpoison_options: [ssl: ssl])                                 # HTTPoison

Como el cuelgue es en la conexión, conviene un timeout de conexión separado del de recepción. En XmlEx.Client (:httpc) están separados:

XmlEx.call(url, body, connect_timeout: 5_000, timeout: 30_000)

En HTTPoison, su opción :timeout es el timeout de conexión y :recv_timeout el de recepción:

XmlEx.call(url, body,
  transport: XmlEx.Transport.HTTPoison,
  httpoison_options: [timeout: 5_000, recv_timeout: 30_000]
)

Parseo de XML genérico

{:ok, parsed} = XmlEx.parse("<a><b>1</b><b>2</b><c>x</c></a>")
#=> {:ok, %{"a" => %{"b" => ["1", "2"], "c" => "x"}}}

XmlEx.Parser.dig(parsed, ["a", "c"])
#=> "x"

Módulos

Módulo Responsabilidad
XmlEx Fachada: encode/2, decode/1, call/3, build/2, parse/1
XmlEx.Builder Genera XML a partir de tuplas Elixir
XmlEx.Envelope Envelopes SOAP 1.1 / 1.2
XmlEx.Parser XML → mapas (vía xmerl)
XmlEx.Fault Extracción de SOAP Faults (1.1 y 1.2)
XmlEx.Transport Behaviour del transporte HTTP
XmlEx.TLS Opciones TLS seguras (verificación del certificado)
XmlEx.Client Transporte default vía :httpc (OTP)
XmlEx.Transport.HTTPoison Adapter para HTTPoison (opcional)
XmlEx.Transport.Tesla Adapter para Tesla (opcional)
XmlEx.Transport.Finch Adapter para Finch con pool nativo (opcional)

Tests

mix test

Incluye tests de integración que levantan un servidor HTTP local y ejercitan los tres transportes de verdad.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages