class SippyCup::Scenario
A representation of a SippyCup
scenario from a manifest or created in code. Allows building a scenario from a set of basic primitives, and then exporting to SIPp scenario files, including the XML scenario and PCAP audio.
Constants
- DEFAULT_RETRANS
- MSEC
- USER_AGENT
- VALID_DTMF
Attributes
@return [Array<Hash>] a collection of errors encountered while building the scenario.
@return [Hash] The options the scenario was created with, either from a manifest or passed as overrides
Public Class Methods
Build a scenario based on either a manifest string or a file handle. Manifests are supplied in YAML format. All manifest keys can be overridden by passing in a Hash of corresponding values.
@param [String, File] manifest The YAML manifest @param [Hash] options Options to override (see initialize) @option options [String] :input_filename The name of the input file if there is one. Used as a preferable fallback if no name is included in the manifest.
@return [SippyCup::Scenario]
@example Parse a manifest string
manifest = <<-MANIFEST source: 192.168.1.1 destination: 192.168.1.2 steps: - invite - wait_for_answer - ack_answer - sleep 3 - wait_for_hangup MANIFEST Scenario.from_manifest(manifest)
@example Parse a manifest file by path
File.open("/my/manifest.yml") { |f| Scenario.from_manifest(f) } # or Scenario.from_manifest(File.read("/my/manifest.yml"))
@example Override keys from the manifest
Scenario.from_manifest(manifest, source: '192.168.12.1')
# File lib/sippy_cup/scenario.rb, line 49 def self.from_manifest(manifest, options = {}) args = ActiveSupport::HashWithIndifferentAccess.new(Psych.safe_load(manifest)).merge options input_name = options.has_key?(:input_filename) ? File.basename(options[:input_filename]).gsub(/\.ya?ml/, '') : nil name = args.delete(:name) || input_name || 'My Scenario' scenario = if args[:scenario] media = args.has_key?(:media) ? File.read(args[:media], mode: 'rb') : nil SippyCup::XMLScenario.new name, File.read(args[:scenario]), media, args else steps = args.delete :steps scenario = Scenario.new name, args scenario.build steps scenario end scenario end
Create a scenario instance
@param [String] name The scenario's name @param [Hash] args options to customise the scenario @option options [String] :name The name of the scenario, used for the XML scenario and for determining the compiled filenames. Defaults to 'My Scenario'. @option options [String] :filename The name of the files to be saved to disk. @option options [String] :source The source IP/hostname with which to invoke SIPp. @option options [String, Numeric] :source_port The source port to bind SIPp to (defaults to 8836). @option options [String] :destination The target system at which to direct traffic. @option options [String] :advertise_address The IP address to advertise in SIP and SDP if different from the bind IP (defaults to the bind IP). @option options [String] :from_user The SIP user from which traffic should appear. @option options [String] :to_user The SIP user to send requests to. Alias for `:to` and deprecated in favour of the same. @option options [String] :to The SIP user / address to send requests to. @option options [Integer] :media_port The RTCP (media) port to bind to locally. @option options [String, Numeric] :max_concurrent The maximum number of concurrent calls to execute. @option options [String, Numeric] :number_of_calls The maximum number of calls to execute in the test run. @option options [String, Numeric] :calls_per_second The rate at which to initiate calls. @option options [String] :stats_file The path at which to dump statistics. @option options [String, Numeric] :stats_interval The interval (in seconds) at which to dump statistics (defaults to 1s). @option options [String] :transport_mode The transport mode over which to direct SIP traffic. @option options [String] :dtmf_mode The output DTMF mode, either rfc2833 (default) or info. @option options [String] :scenario_variables A path to a CSV file of variables to be interpolated with the scenario at runtime. @option options [Hash] :options A collection of options to pass through to SIPp, as key-value pairs. In cases of value-less options (eg -trace_err), specify a nil value. @option options [Array<String>] :steps A collection of steps
@yield [scenario] Builder block to construct scenario @yieldparam [Scenario] scenario the initialized scenario instance
# File lib/sippy_cup/scenario.rb, line 103 def initialize(name, args = {}, &block) parse_args args @scenario_options = args.merge name: name @filename = args[:filename] || name.downcase.gsub(/\W+/, '_') @filename = File.expand_path @filename, Dir.pwd @media = nil @message_variables = 0 # Reference variables don't generate warnings/errors if unused in the scenario @reference_variables = Set.new @media_nodes = [] @errors = [] @adv_ip = args[:advertise_address] || "[local_ip]" instance_eval &block if block_given? end
Public Instance Methods
Acknowledge a received answer message and start media playback
@param [Hash] opts A set of options to modify the message parameters
# File lib/sippy_cup/scenario.rb, line 445 def ack_answer(opts = {}) msg = <<-BODY ACK [next_url] SIP/2.0 Via: SIP/2.0/[transport] #{@adv_ip}:[local_port];branch=[branch] From: "#{@from_user}" <sip:#{@from_user}@#{@adv_ip}:[local_port]>;tag=[call_number] To: <sip:#{to_addr}>[peer_tag_param] Call-ID: [call_id] CSeq: [cseq] ACK Contact: <sip:[$local_addr];transport=[transport]> Max-Forwards: 100 User-Agent: #{USER_AGENT} Content-Length: 0 [routes] BODY send msg, opts start_media end
Helper method to answer an INVITE and expect the ACK
@param [Hash] opts A set of options containing SIPp element attributes - will be passed to both the <send> and <recv> elements
# File lib/sippy_cup/scenario.rb, line 344 def answer(opts = {}) send_answer opts receive_ack opts end
Build the scenario steps provided
@param [Array<String>] steps A collection of steps to build the scenario
# File lib/sippy_cup/scenario.rb, line 130 def build(steps) raise ArgumentError, "Must provide scenario steps" unless steps steps.each_with_index do |step, index| begin instruction, args = step.split ' ', 2 args = split_quoted_string args if args && !args.empty? self.__send__ instruction, *args else self.__send__ instruction end rescue => e @errors << {step: index + 1, message: "#{step}: #{e.message}"} end end end
Create partition table for Call Length
@param [Integer] min An value specifying the minimum time in milliseconds for the table @param [Integer] max An value specifying the maximum time in milliseconds for the table @param [Integer] interval An value specifying the interval in milliseconds for the table
# File lib/sippy_cup/scenario.rb, line 636 def call_length_repartition(min, max, interval) partition_table 'CallLengthRepartition', min.to_i, max.to_i, interval.to_i end
Compile the scenario and its media to disk
Writes the SIPp scenario file to disk at {filename}.xml, and the PCAP media to {filename}.pcap if applicable. {filename} is taken from the :filename option when creating the scenario, or falls back to a down-snake-cased version of the scenario name.
@return [String] the path to the resulting scenario file
@example Export a scenario to a specified filename
scenario = Scenario.new 'Test Scenario', filename: 'my_scenario' scenario.compile! # Leaves files at my_scenario.xml and my_scenario.pcap
@example Export a scenario to a calculated filename
scenario = Scenario.new 'Test Scenario' scenario.compile! # Leaves files at test_scenario.xml and test_scenario.pcap
# File lib/sippy_cup/scenario.rb, line 696 def compile! unless @media.nil? print "Compiling media to #{@filename}.pcap..." compile_media.to_file filename: "#{@filename}.pcap" puts "done." end scenario_filename = "#{@filename}.xml" print "Compiling scenario to #{scenario_filename}..." File.open scenario_filename, 'w' do |file| file.write to_xml(:pcap_path => "#{@filename}.pcap") end puts "done." scenario_filename end
Shortcut to send a BYE and wait for the acknowledgement
@param [Hash] opts A set of options containing SIPp <recv> element attributes - will be passed to both the <send> and <recv> elements
# File lib/sippy_cup/scenario.rb, line 626 def hangup(opts = {}) send_bye opts receive_ok opts end
Send an invite message
@param [Hash] opts A set of options to modify the message @option opts [Integer] :retrans @option opts [String] :headers Extra headers to place into the INVITE
# File lib/sippy_cup/scenario.rb, line 154 def invite(opts = {}) opts[:retrans] ||= 500 # FIXME: The DTMF mapping (101) is hard-coded. It would be better if we could # get this from the DTMF payload generator from_addr = "#{@from_user}@#{@adv_ip}:[local_port]" msg = <<-MSG INVITE sip:#{to_addr} SIP/2.0 Via: SIP/2.0/[transport] #{@adv_ip}:[local_port];branch=[branch] From: "#{@from_user}" <sip:#{from_addr}>;tag=[call_number] To: <sip:#{to_addr}> Call-ID: [call_id] CSeq: [cseq] INVITE Contact: <sip:#{from_addr};transport=[transport]> Max-Forwards: 100 User-Agent: #{USER_AGENT} Content-Type: application/sdp Content-Length: [len] #{opts.has_key?(:headers) ? opts.delete(:headers).sub(/\n*\Z/, "\n") : ''} v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] #{@adv_ip} s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 101 a=rtpmap:0 PCMU/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-15 MSG send msg, opts do |send| send << doc.create_element('action') do |action| action << doc.create_element('assignstr') do |assignstr| assignstr['assign_to'] = "remote_addr" assignstr['value'] = to_addr end action << doc.create_element('assignstr') do |assignstr| assignstr['assign_to'] = "local_addr" assignstr['value'] = from_addr end action << doc.create_element('assignstr') do |assignstr| assignstr['assign_to'] = "call_addr" assignstr['value'] = to_addr end end end # These variables will only be used if we initiate a hangup @reference_variables += %w(remote_addr local_addr call_addr) end
Acknowledge the last request
@param [Hash] opts A set of options to modify the message parameters
# File lib/sippy_cup/scenario.rb, line 592 def okay(opts = {}) msg = <<-ACK SIP/2.0 200 OK [last_Via:] [last_From:] [last_To:] [last_Call-ID:] [last_CSeq:] Contact: <sip:[$local_addr];transport=[transport]> Max-Forwards: 100 User-Agent: #{USER_AGENT} Content-Length: 0 [routes] ACK send msg, opts end
# File lib/sippy_cup/scenario.rb, line 349 def receive_ack(opts = {}) recv opts.merge request: 'ACK' end
Sets an expectation for a SIP 200 message from the remote party as well as storing the record set and the response time duration
@param [Hash] opts A set of options to modify the expectation @option opts [true, false] :optional Whether or not receipt of the message is optional. Defaults to false.
# File lib/sippy_cup/scenario.rb, line 393 def receive_answer(opts = {}) options = { rrs: true, # Record Record Set: Make the Route headers available via [routes] later rtd: true # Response Time Duration: Record the response time } receive_200(options.merge(opts)) do |recv| recv << doc.create_element('action') do |action| action << doc.create_element('ereg') do |ereg| ereg['regexp'] = '<sip:(.*)>.*;tag=([^;]*)' ereg['search_in'] = 'hdr' ereg['header'] = 'To:' ereg['assign_to'] = 'dummy,remote_addr,remote_tag' end end end # These variables will only be used if we initiate a hangup @reference_variables += %w(dummy remote_addr remote_tag) end
Expect to receive a BYE message
@param [Hash] opts A set of options to modify the expectation
# File lib/sippy_cup/scenario.rb, line 583 def receive_bye(opts = {}) recv opts.merge request: 'BYE' end
Expect to receive a SIP INVITE
@param [Hash] opts A set of options containing SIPp <recv> element attributes
# File lib/sippy_cup/scenario.rb, line 235 def receive_invite(opts = {}) recv(opts.merge(request: 'INVITE', rrs: true)) do |recv| action = doc.create_element('action') do |action| action << doc.create_element('ereg') do |ereg| ereg['regexp'] = '<sip:(.*)>.*;tag=([^;]*)' ereg['search_in'] = 'hdr' ereg['header'] = 'From:' ereg['assign_to'] = 'dummy,remote_addr,remote_tag' end action << doc.create_element('ereg') do |ereg| ereg['regexp'] = '<sip:(.*)>' ereg['search_in'] = 'hdr' ereg['header'] = 'To:' ereg['assign_to'] = 'dummy,local_addr' end action << doc.create_element('assignstr') do |assignstr| assignstr['assign_to'] = "call_addr" assignstr['value'] = "[$local_addr]" end end recv << action end # These variables (except dummy) will only be used if we initiate a hangup @reference_variables += %w(dummy remote_addr remote_tag local_addr call_addr) end
Expect to receive a MESSAGE message
@param [String] regexp A regular expression (as a String) to match the message body against
# File lib/sippy_cup/scenario.rb, line 531 def receive_message(regexp = nil) recv = Nokogiri::XML::Node.new 'recv', doc recv['request'] = 'MESSAGE' scenario_node << recv if regexp action = Nokogiri::XML::Node.new 'action', doc ereg = Nokogiri::XML::Node.new 'ereg', doc ereg['regexp'] = regexp ereg['search_in'] = 'body' ereg['check_it'] = true var = "message_#{@message_variables += 1}" ereg['assign_to'] = var @reference_variables << var action << ereg recv << action end okay end
Sets an expectation for a SIP 200 message from the remote party
@param [Hash] opts A set of options to modify the expectation @option opts [true, false] :optional Whether or not receipt of the message is optional. Defaults to false.
# File lib/sippy_cup/scenario.rb, line 419 def receive_ok(opts = {}, &block) recv({ response: 200 }.merge(opts), &block) end
Sets an expectation for a SIP 183 message from the remote party
@param [Hash] opts A set of options to modify the expectation @option opts [true, false] :optional Whether or not receipt of the message is optional. Defaults to true.
# File lib/sippy_cup/scenario.rb, line 381 def receive_progress(opts = {}) handle_response 183, opts end
Sets an expectation for a SIP 180 message from the remote party
@param [Hash] opts A set of options to modify the expectation @option opts [true, false] :optional Whether or not receipt of the message is optional. Defaults to true.
# File lib/sippy_cup/scenario.rb, line 370 def receive_ringing(opts = {}) handle_response 180, opts end
Sets an expectation for a SIP 100 message from the remote party
@param [Hash] opts A set of options to modify the expectation @option opts [true, false] :optional Whether or not receipt of the message is optional. Defaults to true.
# File lib/sippy_cup/scenario.rb, line 359 def receive_trying(opts = {}) handle_response 100, opts end
Send a REGISTER message with the specified credentials
@param [String] user the user to register as. May be given as a full SIP URI (sip:user@domain.com), in email-address format (user@domain.com) or as a simple username ('user'). If no domain is supplied, the source IP from SIPp will be used. @param [optional, String, nil] password the password to authenticate with. @param [Hash] opts A set of options to modify the message
@example Register with authentication
s.register 'frank@there.com', 'abc123'
@example Register without authentication or a domain
s.register 'frank'
# File lib/sippy_cup/scenario.rb, line 216 def register(user, password = nil, opts = {}) send_opts = opts.dup send_opts[:retrans] ||= DEFAULT_RETRANS user, domain = parse_user user if password send register_message(domain, user), send_opts recv opts.merge(response: 401, auth: true, optional: false) send register_auth(domain, user, password), send_opts receive_ok opts.merge(optional: false) else send register_message(domain, user), send_opts end end
Create partition table for Response Time
@param [Integer] min An value specifying the minimum time in milliseconds for the table @param [Integer] max An value specifying the maximum time in milliseconds for the table @param [Integer] interval An value specifying the interval in milliseconds for the table
# File lib/sippy_cup/scenario.rb, line 645 def response_time_repartition(min, max, interval) partition_table 'ResponseTimeRepartition', min.to_i, max.to_i, interval.to_i end
Answer an incoming call
@param [Hash] opts A set of options containing SIPp <send> element attributes
# File lib/sippy_cup/scenario.rb, line 311 def send_answer(opts = {}) opts[:retrans] ||= DEFAULT_RETRANS msg = <<-MSG SIP/2.0 200 Ok [last_Via:] From: <sip:[$remote_addr]>;tag=[$remote_tag] To: <sip:[$local_addr]>;tag=[call_number] [last_Call-ID:] [last_CSeq:] Server: #{USER_AGENT} Contact: <sip:[$local_addr];transport=[transport]> Content-Type: application/sdp [routes] Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] #{@adv_ip} s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 MSG start_media send msg, opts end
Send a BYE message
@param [Hash] opts A set of options to modify the message parameters
# File lib/sippy_cup/scenario.rb, line 560 def send_bye(opts = {}) msg = <<-MSG BYE sip:[$call_addr] SIP/2.0 Via: SIP/2.0/[transport] #{@adv_ip}:[local_port];branch=[branch] From: <sip:[$local_addr]>;tag=[call_number] To: <sip:[$remote_addr]>;tag=[$remote_tag] Contact: <sip:[$local_addr];transport=[transport]> Call-ID: [call_id] CSeq: [cseq] BYE Max-Forwards: 100 User-Agent: #{USER_AGENT} Content-Length: 0 [routes] MSG send msg, opts end
Send DTMF digits
@param [String] DTMF digits to send. Must be 0-9, *, # or A-D
@example Send a single DTMF digit
send_digits '1'
@example Enter a pin number
send_digits '1234'
# File lib/sippy_cup/scenario.rb, line 486 def send_digits(digits) raise "Media not started" unless @media delay = (0.250 * MSEC).to_i # FIXME: Need to pass this down to the media layer digits.split('').each do |digit| raise ArgumentError, "Invalid DTMF digit requested: #{digit}" unless VALID_DTMF.include? digit case @dtmf_mode when :rfc2833 @media << "dtmf:#{digit}" @media << "silence:#{delay}" when :info info = <<-INFO INFO [next_url] SIP/2.0 Via: SIP/2.0/[transport] #{@adv_ip}:[local_port];branch=[branch] From: "#{@from_user}" <sip:#{@from_user}@#{@adv_ip}:[local_port]>;tag=[call_number] To: <sip:#{to_addr}>[peer_tag_param] Call-ID: [call_id] CSeq: [cseq] INFO Contact: <sip:[$local_addr];transport=[transport]> Max-Forwards: 100 User-Agent: #{USER_AGENT} [routes] Content-Length: [len] Content-Type: application/dtmf-relay Signal=#{digit} Duration=#{delay} INFO send info recv response: 200 pause delay end end if @dtmf_mode == :rfc2833 pause delay * 2 * digits.size end end
Send a “180 Ringing” response
@param [Hash] opts A set of options containing SIPp <recv> element attributes
# File lib/sippy_cup/scenario.rb, line 289 def send_ringing(opts = {}) msg = <<-MSG SIP/2.0 180 Ringing [last_Via:] From: <sip:[$remote_addr]>;tag=[$remote_tag] To: <sip:[$local_addr]>;tag=[call_number] [last_Call-ID:] [last_CSeq:] Server: #{USER_AGENT} Contact: <sip:[$local_addr];transport=[transport]> Content-Length: 0 MSG send msg, opts end
Send a “100 Trying” response
@param [Hash] opts A set of options containing SIPp <recv> element attributes
# File lib/sippy_cup/scenario.rb, line 267 def send_trying(opts = {}) msg = <<-MSG SIP/2.0 100 Trying [last_Via:] From: <sip:[$remote_addr]>;tag=[$remote_tag] To: <sip:[$local_addr]>;tag=[call_number] [last_Call-ID:] [last_CSeq:] Server: #{USER_AGENT} Contact: <sip:[$local_addr];transport=[transport]> Content-Length: 0 MSG send msg, opts end
Insert a pause into the scenario and its media of the specified duration
@param [Numeric] seconds The duration of the pause in seconds
# File lib/sippy_cup/scenario.rb, line 469 def sleep(seconds) milliseconds = (seconds.to_f * MSEC).to_i pause milliseconds @media << "silence:#{milliseconds}" if @media end
Write compiled Scenario
XML and PCAP media (if applicable) to tempfiles.
These will automatically be closed and deleted once they have gone out of scope, and can be used to execute the scenario without leaving stuff behind.
@return [Hash<Symbol => Tempfile>] handles to created Tempfiles at :scenario and :media
@see www.ruby-doc.org/stdlib-1.9.3/libdoc/tempfile/rdoc/Tempfile.html
# File lib/sippy_cup/scenario.rb, line 722 def to_tmpfiles unless @media.nil? || @media.empty? media_file = Tempfile.new 'media' media_file.binmode media_file.write compile_media.to_s media_file.rewind end scenario_file = Tempfile.new 'scenario' scenario_file.write to_xml(:pcap_path => media_file.try(:path)) scenario_file.rewind {scenario: scenario_file, media: media_file} end
Dump the scenario to a SIPp XML string
@return [String] the SIPp XML scenario
# File lib/sippy_cup/scenario.rb, line 653 def to_xml(options = {}) pcap_path = options[:pcap_path] docdup = doc.dup # Not removing in reverse would most likely remove the wrong # nodes because of changing indices. @media_nodes.reverse.each do |nop| nopdup = docdup.xpath(nop.path) if pcap_path.nil? or @media.empty? nopdup.remove else exec = nopdup.xpath("./action/exec").first exec['play_pcap_audio'] = pcap_path end end unless @reference_variables.empty? scenario_node = docdup.xpath('scenario').first scenario_node << docdup.create_element('Reference') do |ref| ref[:variables] = @reference_variables.to_a.join ',' end end docdup.to_xml end
@return [true, false] the validity of the scenario. Will be false if errors were encountered while building the scenario from a manifest
# File lib/sippy_cup/scenario.rb, line 121 def valid? @errors.size.zero? end
Convenience method to wait for an answer from the called party
This sets expectations for optional SIP 100, 180 and 183, followed by a required 200 and sending the acknowledgement.
@param [Hash] opts A set of options to modify the expectations
# File lib/sippy_cup/scenario.rb, line 432 def wait_for_answer(opts = {}) receive_trying opts receive_ringing opts receive_progress opts receive_answer opts ack_answer opts end
Shortcut to set an expectation for a BYE and acknowledge it when received
@param [Hash] opts A set of options to modify the expectation
# File lib/sippy_cup/scenario.rb, line 616 def wait_for_hangup(opts = {}) receive_bye(opts) ack_bye(opts) end
Private Instance Methods
# File lib/sippy_cup/scenario.rb, line 790 def compile_media raise "Media not started" unless @media @media.compile! end
# File lib/sippy_cup/scenario.rb, line 758 def doc @doc ||= begin Nokogiri::XML::Builder.new do |xml| xml.scenario name: @scenario_options[:name] do @scenario_node = xml.parent end end.doc end end
# File lib/sippy_cup/scenario.rb, line 875 def handle_response(code, opts) optional_recv opts.merge(response: code) end
# File lib/sippy_cup/scenario.rb, line 870 def optional_recv(opts) opts[:optional] = true if opts[:optional].nil? recv opts end
# File lib/sippy_cup/scenario.rb, line 773 def parse_args(args) if args[:dtmf_mode] @dtmf_mode = args[:dtmf_mode].to_sym raise ArgumentError, "dtmf_mode must be rfc2833 or info" unless [:rfc2833, :info].include?(@dtmf_mode) else @dtmf_mode = :rfc2833 end @from_user = args[:from_user] || "sipp" args[:to] ||= args[:to_user] if args.has_key?(:to_user) if args[:to] @to_user, @to_domain = args[:to].to_s.split('@') end @to_domain ||= "[remote_ip]" end
TODO: SIPS support?
# File lib/sippy_cup/scenario.rb, line 744 def parse_user(user) user.slice! 0, 4 if user =~ /sip:/ user = user.split(":")[0] user, domain = user.split("@") domain ||= "[remote_ip]" [user, domain] end
# File lib/sippy_cup/scenario.rb, line 879 def partition_table(name, min, max, interval) range = Range.new(min, max).step interval partition_table = Nokogiri::XML::Node.new name, doc partition_table[:value] = range.inject{ |n,m| "#{n},#{m}"} scenario_node << partition_table end
# File lib/sippy_cup/scenario.rb, line 842 def pause(msec) pause = Nokogiri::XML::Node.new 'pause', doc pause['milliseconds'] = msec.to_i scenario_node << pause end
# File lib/sippy_cup/scenario.rb, line 860 def recv(opts = {}, &block) raise ArgumentError, "Receive must include either a response or a request" unless opts.keys.include?(:response) || opts.keys.include?(:request) recv = Nokogiri::XML::Node.new 'recv', doc opts.each do |k,v| recv[k.to_s] = v end yield recv if block_given? scenario_node << recv end
# File lib/sippy_cup/scenario.rb, line 812 def register_auth(domain, user, password) <<-AUTH REGISTER sip:#{domain} SIP/2.0 Via: SIP/2.0/[transport] #{@adv_ip}:[local_port];branch=[branch] From: <sip:#{user}@#{domain}>;tag=[call_number] To: <sip:#{user}@#{domain}> Call-ID: [call_id] CSeq: [cseq] REGISTER Contact: <sip:#{@from_user}@#{@adv_ip}:[local_port];transport=[transport]> Max-Forwards: 20 Expires: 3600 [authentication username=#{user} password=#{password}] User-Agent: #{USER_AGENT} Content-Length: 0 AUTH end
# File lib/sippy_cup/scenario.rb, line 795 def register_message(domain, user) <<-BODY REGISTER sip:#{domain} SIP/2.0 Via: SIP/2.0/[transport] #{@adv_ip}:[local_port];branch=[branch] From: <sip:#{user}@#{domain}>;tag=[call_number] To: <sip:#{user}@#{domain}> Call-ID: [call_id] CSeq: [cseq] REGISTER Contact: <sip:#{@from_user}@#{@adv_ip}:[local_port];transport=[transport]> Max-Forwards: 10 Expires: 120 User-Agent: #{USER_AGENT} Content-Length: 0 BODY end
# File lib/sippy_cup/scenario.rb, line 768 def scenario_node doc @scenario_node end
# File lib/sippy_cup/scenario.rb, line 848 def send(msg, opts = {}) send = Nokogiri::XML::Node.new 'send', doc opts.each do |k,v| send[k.to_s] = v end send << "\n" send << Nokogiri::XML::CDATA.new(doc, msg) send << "\n" #Newlines are required before and after CDATA so SIPp will parse properly yield send if block_given? scenario_node << send end
Split a string into space-delimited components, optionally allowing quoted groups Example: cars “cats and dogs” fish 'hammers' => [“cars”, “cats and dogs”, “fish”, “hammers”]
# File lib/sippy_cup/scenario.rb, line 754 def split_quoted_string(args) args.to_s.scan(/'.+?'|".+?"|[^ ]+/).map { |s| s.gsub /^['"]|['"]$/, '' } end
# File lib/sippy_cup/scenario.rb, line 830 def start_media @media = Media.new '127.0.0.255', 55555, '127.255.255.255', 44444 nop = doc.create_element('nop') { |nop| nop << doc.create_element('action') { |action| action << doc.create_element('exec') } } @media_nodes << nop scenario_node << nop end
# File lib/sippy_cup/scenario.rb, line 739 def to_addr @to_addr ||= "[service]@#{@to_domain}:[remote_port]" end