Parrot.Sip.Message (Parrot Platform v0.0.1-alpha.2)

Represents a SIP message (request or response).

This module provides a struct and functions for working with SIP messages as defined in RFC 3261 and related specifications. It provides a pure functional implementation that models both SIP requests and responses, along with utility functions for manipulation, analysis, and conversion.

References:

  • RFC 3261 Section 7: SIP Messages
  • RFC 3261 Section 8.1: UAC Behavior
  • RFC 3261 Section 8.2: UAS Behavior
  • RFC 3261 Section 20: Header Fields

Summary

Functions

Gets all Via headers from a message.

Gets the branch parameter from the top Via header.

Gets the Call-ID header from a message.

Gets the Contact header from a message.

Gets the CSeq header from a message.

Returns the default reason phrase for a given SIP status code.

Gets a dialog ID from a message.

Gets the From header from a message.

Gets a header from the message by name.

Gets all headers of a given name. Returns a list, even if only one header is present.

Determines if a message is within a dialog.

Checks if a response message is a client error (4xx).

Checks if a response message is a failure (4xx, 5xx, or 6xx).

Checks if a response message is a global error (6xx).

Checks if a response message is provisional (1xx).

Checks if a response message is a redirection (3xx).

Check if a message is a request.

Check if a message is a response.

Checks if a response message is a server error (5xx).

Checks if a response message is successful (2xx).

Creates a new request message with the specified method, request URI, and headers.

Creates a new response message with standard reason phrase based on status code.

Creates a new response message with the specified status code, reason phrase, and headers.

Creates a response from a request with standard reason phrase based on status code.

Creates a response from a request, copying necessary headers and setting the status code and reason phrase.

Sets the body of the message and updates the Content-Length header.

Sets a header in the message.

Gets the status class of a response message (1xx, 2xx, etc.).

Gets the To header from a message.

Converts a message to binary format for transmission.

Convert the message to a string representation.

Gets the top Via header from a message.

Types

t()

@type t() :: %Parrot.Sip.Message{
  body: String.t(),
  dialog_id: String.t() | nil,
  direction: :incoming | :outgoing | nil,
  headers: map(),
  method: Parrot.Sip.Method.t() | nil,
  reason_phrase: String.t() | nil,
  request_uri: String.t() | nil,
  source: map() | nil,
  status_code: integer() | nil,
  transaction_id: String.t() | nil,
  type: :request | :response | nil,
  version: String.t()
}

Functions

all_vias(message)

@spec all_vias(t()) :: [Parrot.Sip.Headers.Via.t()]

Gets all Via headers from a message.

Examples

iex> message = Parrot.Sip.Message.new_request(:invite, "sip:bob@example.com")
iex> via = %Parrot.Sip.Headers.Via{host: "example.com", port: 5060}
iex> message = Parrot.Sip.Message.set_header(message, "Via", via)
iex> Parrot.Sip.Message.all_vias(message)
[%Parrot.Sip.Headers.Via{host: "example.com", port: 5060}]

branch(message)

@spec branch(t()) :: String.t() | nil

Gets the branch parameter from the top Via header.

Examples

iex> message = Parrot.Sip.Message.new_request(:invite, "sip:bob@example.com")
iex> via = %Parrot.Sip.Headers.Via{parameters: %{"branch" => "z9hG4bK123"}}
iex> message = Parrot.Sip.Message.set_header(message, "Via", via)
iex> Parrot.Sip.Message.branch(message)
"z9hG4bK123"

call_id(message)

@spec call_id(t()) :: String.t() | Parrot.Sip.Headers.CallId.t() | nil

Gets the Call-ID header from a message.

Examples

iex> message = Parrot.Sip.Message.new_request(:invite, "sip:bob@example.com")
iex> message = Parrot.Sip.Message.set_header(message, "Call-ID", "abc123@example.com")
iex> Parrot.Sip.Message.call_id(message)
"abc123@example.com"

contact(message)

@spec contact(t()) :: Parrot.Sip.Headers.Contact.t() | nil

Gets the Contact header from a message.

Examples

iex> message = Parrot.Sip.Message.new_request(:invite, "sip:bob@example.com")
iex> contact = %Parrot.Sip.Headers.Contact{uri: "sip:alice@example.com"}
iex> message = Parrot.Sip.Message.set_header(message, "Contact", contact)
iex> Parrot.Sip.Message.contact(message)
%Parrot.Sip.Headers.Contact{uri: "sip:alice@example.com"}

cseq(message)

@spec cseq(t()) :: Parrot.Sip.Headers.CSeq.t() | nil

Gets the CSeq header from a message.

Examples

iex> message = Parrot.Sip.Message.new_request(:invite, "sip:bob@example.com")
iex> cseq = %Parrot.Sip.Headers.CSeq{sequence: 1, method: :invite}
iex> message = Parrot.Sip.Message.set_header(message, "CSeq", cseq)
iex> Parrot.Sip.Message.cseq(message)
%Parrot.Sip.Headers.CSeq{sequence: 1, method: :invite}

default_reason_phrase(status_code)

@spec default_reason_phrase(integer()) :: String.t()

Returns the default reason phrase for a given SIP status code.

dialog_id(message)

@spec dialog_id(t()) :: Parrot.Sip.DialogId.t()

Gets a dialog ID from a message.

Examples

iex> message = Parrot.Sip.Message.new_request(:invite, "sip:bob@example.com")
iex> from = %Parrot.Sip.Headers.From{parameters: %{"tag" => "123"}}
iex> to = %Parrot.Sip.Headers.To{parameters: %{"tag" => "456"}}
iex> message = message |> Parrot.Sip.Message.set_header("From", from)
iex> message = message |> Parrot.Sip.Message.set_header("To", to)
iex> message = message |> Parrot.Sip.Message.set_header("Call-ID", "abc@example.com")
iex> dialog_id = Parrot.Sip.Message.dialog_id(message)
iex> dialog_id.call_id
"abc@example.com"

from(message)

@spec from(t()) :: Parrot.Sip.Headers.From.t() | nil

Gets the From header from a message.

Examples

iex> message = Parrot.Sip.Message.new_request(:invite, "sip:bob@example.com")
iex> from = %Parrot.Sip.Headers.From{uri: "sip:alice@example.com"}
iex> message = Parrot.Sip.Message.set_header(message, "From", from)
iex> Parrot.Sip.Message.from(message)
%Parrot.Sip.Headers.From{uri: "sip:alice@example.com"}

get_header(message, name)

@spec get_header(t(), String.t()) :: any()

Gets a header from the message by name.

Header names are case-insensitive as per RFC 3261. If the header is a map, it will be converted to the appropriate struct.

Examples

iex> message = Parrot.Sip.Message.new_request(:invite, "sip:bob@example.com")
iex> message = Parrot.Sip.Message.set_header(message, "Via", %{protocol: "SIP", version: "2.0", transport: "udp"})
iex> Parrot.Sip.Message.get_header(message, "via")
%Parrot.Sip.Headers.Via{protocol: "SIP", version: "2.0", transport: :udp}

get_headers(message, name)

@spec get_headers(t(), String.t()) :: [any()]

Gets all headers of a given name. Returns a list, even if only one header is present.

Useful for headers that can appear multiple times like Record-Route or Via.

in_dialog?(message)

@spec in_dialog?(t()) :: boolean()

Determines if a message is within a dialog.

A message is within a dialog if it has a To tag and a From tag.

Examples

iex> message = Parrot.Sip.Message.new_request(:invite, "sip:bob@example.com")
iex> from = %Parrot.Sip.Headers.From{parameters: %{"tag" => "123"}}
iex> to = %Parrot.Sip.Headers.To{parameters: %{"tag" => "456"}}
iex> message = message |> Parrot.Sip.Message.set_header("From", from)
iex> message = message |> Parrot.Sip.Message.set_header("To", to)
iex> Parrot.Sip.Message.in_dialog?(message)
true

is_client_error?(message)

@spec is_client_error?(t()) :: boolean()

Checks if a response message is a client error (4xx).

Examples

iex> message = Parrot.Sip.Message.new_response(404, "Not Found")
iex> Parrot.Sip.Message.is_client_error?(message)
true

is_failure?(message)

@spec is_failure?(t()) :: boolean()

Checks if a response message is a failure (4xx, 5xx, or 6xx).

Examples

iex> message = Parrot.Sip.Message.new_response(404, "Not Found")
iex> Parrot.Sip.Message.is_failure?(message)
true

is_global_error?(message)

@spec is_global_error?(t()) :: boolean()

Checks if a response message is a global error (6xx).

Examples

iex> message = Parrot.Sip.Message.new_response(603, "Decline")
iex> Parrot.Sip.Message.is_global_error?(message)
true

is_provisional?(message)

@spec is_provisional?(t()) :: boolean()

Checks if a response message is provisional (1xx).

Examples

iex> message = Parrot.Sip.Message.new_response(180, "Ringing")
iex> Parrot.Sip.Message.is_provisional?(message)
true

is_redirect?(message)

@spec is_redirect?(t()) :: boolean()

Checks if a response message is a redirection (3xx).

Examples

iex> message = Parrot.Sip.Message.new_response(302, "Moved Temporarily")
iex> Parrot.Sip.Message.is_redirect?(message)
true

is_request?(message)

@spec is_request?(t()) :: boolean()

Check if a message is a request.

is_response?(message)

@spec is_response?(t()) :: boolean()

Check if a message is a response.

is_server_error?(message)

@spec is_server_error?(t()) :: boolean()

Checks if a response message is a server error (5xx).

Examples

iex> message = Parrot.Sip.Message.new_response(500, "Server Internal Error")
iex> Parrot.Sip.Message.is_server_error?(message)
true

is_success?(message)

@spec is_success?(t()) :: boolean()

Checks if a response message is successful (2xx).

Examples

iex> message = Parrot.Sip.Message.new_response(200, "OK")
iex> Parrot.Sip.Message.is_success?(message)
true

new_request(method, request_uri, headers \\ %{}, opts \\ [])

@spec new_request(Parrot.Sip.Method.t(), String.t(), map(), keyword()) :: t()

Creates a new request message with the specified method, request URI, and headers.

This function is the main entry point for creating SIP request messages.

Parameters

  • method: The SIP method (atom) for the request
  • request_uri: The request target URI
  • headers: Optional map of initial headers

Examples

iex> Parrot.Sip.Message.new_request(:invite, "sip:alice@example.com")
%Parrot.Sip.Message{
  method: :invite,
  request_uri: "sip:alice@example.com",
  version: "SIP/2.0",
  headers: %{},
  body: "",
  type: :request,
  direction: :outgoing
}

new_response(status_code)

@spec new_response(integer()) :: t()

Creates a new response message with standard reason phrase based on status code.

If no reason phrase is provided, a standard one will be used based on the status code.

Examples

iex> Parrot.Sip.Message.new_response(200)
%Parrot.Sip.Message{
  status_code: 200,
  reason_phrase: "OK",
  version: "SIP/2.0",
  headers: %{},
  body: "",
  type: :response,
  direction: :outgoing
}

new_response(status_code, reason_phrase)

@spec new_response(integer(), String.t()) :: t()
@spec new_response(
  integer(),
  keyword()
) :: t()

new_response(status_code, reason_phrase, headers)

@spec new_response(integer(), String.t(), map()) :: t()

new_response(status_code, reason_phrase, headers, opts)

@spec new_response(integer(), String.t(), map(), keyword()) :: t()

Creates a new response message with the specified status code, reason phrase, and headers.

Parameters

  • status_code: The SIP response status code (100-699)
  • reason_phrase: The reason phrase for the response
  • headers: Optional map of initial headers

Examples

iex> Parrot.Sip.Message.new_response(200, "OK")
%Parrot.Sip.Message{
  status_code: 200,
  reason_phrase: "OK",
  version: "SIP/2.0",
  headers: %{},
  body: "",
  type: :response,
  direction: :outgoing
}

reply(request, status_code)

@spec reply(t(), integer()) :: t()

Creates a response from a request with standard reason phrase based on status code.

Examples

iex> request = Parrot.Sip.Message.new_request(:invite, "sip:alice@example.com")
iex> response = Parrot.Sip.Message.reply(request, 200)
iex> response.reason_phrase
"OK"

reply(request, status_code, reason_phrase)

@spec reply(t(), integer(), String.t()) :: t()

Creates a response from a request, copying necessary headers and setting the status code and reason phrase.

This function follows the requirements in RFC 3261 Section 8.2.6 for copying headers from requests to responses.

Parameters

  • request: The SIP request message
  • status_code: The response status code
  • reason_phrase: The reason phrase for the response

Examples

iex> request = Parrot.Sip.Message.new_request(:invite, "sip:alice@example.com")
iex> response = Parrot.Sip.Message.reply(request, 200, "OK")
iex> response.status_code
200

set_body(message, body)

@spec set_body(t(), String.t()) :: t()

Sets the body of the message and updates the Content-Length header.

This function automatically calculates and sets the Content-Length header based on the body's length.

Examples

iex> message = Parrot.Sip.Message.new_request(:invite, "sip:bob@example.com")
iex> message = Parrot.Sip.Message.set_body(message, "Hello, world!")
iex> message.body
"Hello, world!"
iex> message.headers["content-length"]
13

set_header(message, name, value)

@spec set_header(t(), String.t(), any()) :: t()

Sets a header in the message.

Header names are converted to lowercase for consistent storage and retrieval.

Examples

iex> message = Parrot.Sip.Message.new_request(:invite, "sip:bob@example.com")
iex> from_header = %Parrot.Sip.Headers.From{}
iex> message = Parrot.Sip.Message.set_header(message, "From", from_header)
iex> message.headers["from"] == from_header
true

status_class(arg1)

@spec status_class(t()) :: integer() | nil

Gets the status class of a response message (1xx, 2xx, etc.).

Examples

iex> message = Parrot.Sip.Message.new_response(200, "OK")
iex> Parrot.Sip.Message.status_class(message)
2

to(message)

@spec to(t()) :: Parrot.Sip.Headers.To.t() | nil

Gets the To header from a message.

Examples

iex> message = Parrot.Sip.Message.new_request(:invite, "sip:bob@example.com")
iex> to = %Parrot.Sip.Headers.To{uri: "sip:bob@example.com"}
iex> message = Parrot.Sip.Message.set_header(message, "To", to)
iex> Parrot.Sip.Message.to(message)
%Parrot.Sip.Headers.To{uri: "sip:bob@example.com"}

to_binary(message)

@spec to_binary(t()) :: binary()

Converts a message to binary format for transmission.

Examples

iex> message = Parrot.Sip.Message.new_request(:invite, "sip:bob@example.com")
iex> binary = Parrot.Sip.Message.to_binary(message)
iex> String.starts_with?(binary, "INVITE sip:bob@example.com SIP/2.0")
true

to_s(message)

@spec to_s(t()) :: String.t()

Convert the message to a string representation.

top_via(message)

@spec top_via(t()) :: Parrot.Sip.Headers.Via.t() | nil

Gets the top Via header from a message.

Examples

iex> message = Parrot.Sip.Message.new_request(:invite, "sip:bob@example.com")
iex> via = %Parrot.Sip.Headers.Via{host: "example.com", port: 5060}
iex> message = Parrot.Sip.Message.set_header(message, "Via", via)
iex> Parrot.Sip.Message.top_via(message)
%Parrot.Sip.Headers.Via{host: "example.com", port: 5060}