anchor.diff:          606 -> <<"Not Acceptable">>
       end.
   
  +check(true, _Reason) -> ok;
  +check(false, Reason) -> error_m:fail(Reason).
  +
  +count(Name, Msg) ->
  +    length(header_values(Name, Msg)).
  +
  +is_single_contact(Msg) ->
  +    case header_values(contact, Msg) of
  +        [Contact] when is_record(Contact#sip_hdr_address.uri, sip_uri) -> true;
  +        _Other -> false % either no contact header or multiple headers
  +    end.
  +
  +is_contact_secure(Request) ->
  +    IsRequestSecure = sip_uri:is_sips(Request#sip_request.uri),
  +    IsRouteSecure =
  +        case has_header(route, Request) of
  +            true ->
  +                #sip_hdr_address{uri = Route} = header_top_value(route, Request),
  +                sip_uri:is_sips(Route);
  +            false -> false
  +        end,
  +    case has_header(contact, Request) of
  +        true ->
  +            Contact = header_top_value(contact, Request),
  +            IsContactSecure = sip_uri:is_sips(Contact#sip_hdr_address.uri),
  +
  +            % Contact can be non-secure only if both Request-URI and top Route are not secure, 12.1.2
  +            IsContactSecure orelse (not IsRequestSecure andalso not IsRouteSecure);
  +        false ->
  +            true % no Contact header, so no check
  +    end.
  +
   %% @doc Retrieve headers from SIP request/response
   %% @end
   -spec headers(sip_message()) -> sip_headers().
  
positive.code: -spec create_response(sip_message(), integer()) -> #sip_response{}.
  create_response(Request, Status) ->
      create_response(Request, Status, default_reason(Status)).
  
  check(true, _Reason) -> ok;
  check(false, Reason) -> error_m:fail(Reason).
  
  count(Name, Msg) ->
      length(header_values(Name, Msg)).
  
  is_contact_valid(Msg) ->
      case header_values(contact, Msg) of
          [Contact] when is_record(Contact#sip_hdr_address.uri, sip_uri) -> true;
          _Other -> false % either no contact header or multiple headers
      end.
  
  is_contact_secure(Request) ->
      IsRequestSecure = sip_uri:is_sips(Request#sip_request.uri),
      IsRouteSecure =
          case has_header(route, Request) of
              true ->
                  #sip_hdr_address{uri = Route} = header_top_value(route, Request),
                  sip_uri:is_sips(Route);
              false -> false
          end,
      Contact = header_top_value(contact, Request),
      IsContactSecure = sip_uri:is_sips(Contact#sip_hdr_address.uri),
  
      % Contact can be non-secure only if both Request-URI and top Route are not secure, 12.1.2
      IsContactSecure orelse (not IsRequestSecure andalso not IsRouteSecure).
  
  %% @doc Validate that request contains all required headers
  %% A valid SIP request formulated by a UAC MUST, at a minimum, contain
  %% the following header fields: To, From, CSeq, Call-ID, Max-Forwards,
  %% and Via; all of these header fields are mandatory in all SIP
  %% requests.
  %% @end
  -spec validate_request(#sip_request{}) -> ok | {error, Reason :: term()}.
  validate_request(#sip_request{method = Method} = Msg) ->
      IsNotDialogEstablishing = (Method =/= 'INVITE'),
      do([error_m ||
          check(count(to, Msg) =:= 1, {invalid, to}),
          check(count(from, Msg) =:= 1, {invalid, from}),
          check(count(cseq, Msg) =:= 1, {invalid, cseq}),
          check(count('call-id', Msg) =:= 1, {invalid, 'call-id'}),
          check(count('max-forwards', Msg) =:= 1, {invalid, 'max-forwards'}),
          % Must be at least one via
          check(count(via, Msg) >= 1, {invalid, via}),
          % The Contact header field MUST be present and contain exactly one SIP
          % or SIPS URI in any request that can result in the establishment of a
          % dialog.
          check(IsNotDialogEstablishing orelse (is_contact_valid(Msg) andalso is_contact_secure(Msg)), {invalid, contact})]).
  
  %% @doc Validate that response contains all required headers
  %% A valid SIP response formulated by a UAS MUST, at a minimum, contain
  %% the following header fields: To, From, CSeq, Call-ID and Via;
  %% all of these header fields are mandatory in all SIP responses.
  %% @end
  -spec validate_response(#sip_response{}) -> ok | {error, Reason :: term()}.
  validate_response(#sip_response{} = Msg) ->
      IsNotDialogEstablishing = not is_dialog_establishing(Msg),
      do([error_m ||
          check(count(to, Msg) =:= 1, {invalid, to}),
          check(count(from, Msg) =:= 1, {invalid, from}),
          check(count(cseq, Msg) =:= 1, {invalid, cseq}),
          check(count('call-id', Msg) =:= 1, {invalid, 'call-id'}),
          % Must be at least one via
          check(count(via, Msg) >= 1, {invalid, via}),
          % 12.1.1 The UAS MUST add a Contact header field to the response.
          check(IsNotDialogEstablishing orelse is_contact_valid(Msg), {invalid, contact})]).
  
  
  %% @doc
  %% Parses the datagram for SIP packet. The headers of the returned message are
  
negative.code:                                 {'max-forwards', 70},
                                  {via, sip_headers:via(udp, "localhost", [])},
                                  {require, <<"foo">>}]},
      ValidResponse = sip_message:create_response(ValidRequest, 200),
  
      % No contact in dialog-establishing request
      InvalidRequest = sip_message:replace_top_header(
                         cseq,
                         sip_headers:cseq(1, 'INVITE'),
                         ValidRequest#sip_request{method = 'INVITE'}),
  
      % with added contact header
      ValidRequest2 = sip_message:append_header(contact, sip_headers:address(<<>>, <<"sip:alice@localhost">>, []), InvalidRequest),
  
      % Contact is not sips URI, but top route is
      InvalidRequest2 = sip_message:append_header(route, sip_headers:address(<<>>, <<"sips:proxy@localhost">>, []), ValidRequest2),
      ValidRequest3 = sip_message:replace_top_header(contact, sip_headers:address(<<>>, <<"sips:alice@localhost">>, []), InvalidRequest2),
  
      % Valid response
      ValidResponse = sip_message:create_response(ValidRequest, 200),
  
      % Without a Via header
      InvalidResponse = ValidResponse#sip_response{headers = [{Name, Value} || {Name, Value} <- ValidResponse#sip_response.headers, Name =/= via]},
  
      [% Request validation
       ?_assertEqual(ok, validate_request(ValidRequest)),
       ?_assertEqual({error, {invalid, contact}}, validate_request(InvalidRequest)),
       ?_assertEqual(ok, validate_request(ValidRequest2)),
       ?_assertEqual({error, {invalid, contact_must_be_sips}}, validate_request(InvalidRequest2)),
       ?_assertEqual(ok, validate_request(ValidRequest3)),
  
  
       % Response validation
       ?_assertEqual(ok, validate_response(ValidResponse)),
       ?_assertEqual({error, {invalid, via}}, validate_response(InvalidResponse))
       ].
  
  -spec update_header_test() -> ok.
  