Freeswitch: mod_curl

Приложение mod_curl позволяет выполнять HTTP запросы и получать данные в ответ.
Вывод может быть, как простым текстом, так и JSON объектом.

Setup & Configuration

Для того чтобы использовать mod_curl, вы должны скомпилоировать и загрузить соответствующий модуль. раскомментируйте в /usr/src/freeswitch/modules.conf:

#applications/mod_curl 

Перекомпилируйте Freeswtich:

make 
make install 

И добавьте модуль в автозагрузку в файле modules.conf.xml из директории /usr/local/freeswitch/conf/autoload_configs:

<load module="mod_curl"/> 

Для модуля mod_curl не существует отдельного конфига.

Загрузите модуль:

fs_cli> load mod_curl

 

2020-03-19 16:33:47.528923 [CONSOLE] switch_loadable_module.c:1804 Successfully Loaded [mod_curl]
2020-03-19 16:33:47.528923 [NOTICE] switch_loadable_module.c:350 Adding Application 'curl'
2020-03-19 16:33:47.528923 [NOTICE] switch_loadable_module.c:350 Adding Application 'curl_sendfile'
2020-03-19 16:33:47.528923 [NOTICE] switch_loadable_module.c:412 Adding API Function 'curl'
2020-03-19 16:33:47.528923 [NOTICE] switch_loadable_module.c:412 Adding API Function 'curl_sendfile'

mod_curl предоставляет команды для вызова, как через API, так и через диалплан:

  • curl - сделать запрос и получить какие-то данные с сервера.
  • curl_sendfile - передать данные на cервер и получить отчет, если требуется.

Application

Синтаксис приложения curl:

<action application="curl" data="url [headers|json] [get|head|post [url_encode_data]]"/> 

Приложение устанавливает переменные curl_response_data и curl_response_code.
curl_response_data может содержать headers/body или json если запрошено.

<action application="curl" data="http://www.google.com"/> 
<action application="info"/> 
<action application="curl" data="http://www.google.com headers"/> 
<action application="info"/> 
<action application="curl" data="http://www.google.com json"/> 
<action application="info"/> 

Синтаксис curl_sendfile может принимать одну из двух следующих форм,
все данные в application data:

<action application="curl_sendfile" data="<url> <filename_post_name=/path/to/filename [nopost|foo1=bar1&foo2=bar2&...fooN=barN [event|none [uuid|identifier]]]"/> 

или с предварительно заданными переменными канала:

<action application="set" data="curl_sendfile_report=event"/> 
<action application="set" data="curl_sendfile_url=http://www.mydomain.com/test_files.php"/> 
<action application="set" data="curl_sendfile_filename_element=myFile"/> 
<action application="set" data="curl_sendfile_filename=/tmp/somefile.dmp"/> 
<action application="set" data="curl_sendfile_extrapost=foo1=bar1&foo2=bar2&testing=a%20pain%20in%20the%20rear"/> 
<action application="set" data="_sendfile_identifier=1234567890"/> 
<action application="curl_sendfile"/> 

Необходимо применять urlencode для переменных канала _url, _filename, _extrapost или data = "".
Требуется указывать nopost, если нет дополнительных данных или дополнительные параметры передаются в url. Если вы укажете uuid в качестве идентификатора, приложение использует uuid текущего сеанса.

CLI / API

Примеры использования команд API через fs_cli:

curl url [headers|json|content-type <mime-type>|connect-timeout <seconds>|timeout <seconds>] [get|head|post|delete|put [data]] 

Вызов из командной строки FS:

curl https://nominatim.openstreetmap.org/?format=xml&addressdetails=1&q=london&format=json&limit=1&pretty=1

Вывод:

[
    {
        "place_id": 100200,
        "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
        "osm_type": "node",
        "osm_id": 107775,
        "boundingbox": [
            "51.3473219",
            "51.6673219",
            "-0.2876474",
            "0.0323526"
        ],
        "lat": "51.5073219",
        "lon": "-0.1276474",
        "display_name": "London, Greater London, England, SW1A 2DX, United Kingdom",
        "class": "place",
        "type": "city",
        "importance": 0.9307827616237295,
        "icon": "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png",
        "address": {
            "city": "London",
            "state_district": "Greater London",
            "state": "England",
            "postcode": "SW1A 2DX",
            "country": "United Kingdom",
            "country_code": "gb"
        }
    }
]
Headers
curl https://nominatim.openstreetmap.org/?format=xml&addressdetails=1&q=london&format=json&limit=1&pretty=1 headers 

Вывод:

headers

headers

HTTP/1.1 200 OK
Date: Fri, 20 Mar 2020 08:36:25 GMT
Server: Apache/2.4.29 (Ubuntu)
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: OPTIONS,GET
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Expect-CT: max-age=0, report-uri="https://openstreetmap.report-uri.com/r/d/ct/reportOnly"
Upgrade: h2
Connection: Upgrade, close
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8

[
    {
        "place_id": 100200,
        "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
        "osm_type": "node",
        "osm_id": 107775,
        "boundingbox": [
            "51.3473219",
            "51.6673219",
            "-0.2876474",
            "0.0323526"
        ],
        "lat": "51.5073219",
        "lon": "-0.1276474",
        "display_name": "London, Greater London, England, SW1A 2DX, United Kingdom",
        "class": "place",
        "type": "city",
        "importance": 0.9307827616237295,
        "icon": "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png",
        "address": {
            "city": "London",
            "state_district": "Greater London",
            "state": "England",
            "postcode": "SW1A 2DX",
            "country": "United Kingdom",
            "country_code": "gb"
        }
    }
]

json
curl https://nominatim.openstreetmap.org/?format=xml&addressdetails=1&q=london&format=json&limit=1&pretty=1 json 

Вывод:

json

json

{"status_code":"200","body":"[\n    {\n        \"place_id\": 100200,\n        \"licence\": \"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\n        \"osm_type\": \"node\",\n        \"osm_id\": 107775,\n        \"boundingbox\": [\n            \"51.3473219\",\n            \"51.6673219\",\n            \"-0.2876474\",\n            \"0.0323526\"\n        ],\n        \"lat\": \"51.5073219\",\n        \"lon\": \"-0.1276474\",\n        \"display_name\": \"London, Greater London, England, SW1A 2DX, United Kingdom\",\n        \"class\": \"place\",\n        \"type\": \"city\",\n        \"importance\": 0.9307827616237295,\n        \"icon\": \"https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png\",\n        \"address\": {\n            \"city\": \"London\",\n            \"state_district\": \"Greater London\",\n            \"state\": \"England\",\n            \"postcode\": \"SW1A 2DX\",\n            \"country\": \"United Kingdom\",\n            \"country_code\": \"gb\"\n        }\n    }\n]","version":"HTTP/1.1","phrase":"OK","headers":[{"key":"Date","value":"Fri, 20 Mar 2020 08:41:54 GMT"},{"key":"Server","value":"Apache/2.4.29 (Ubuntu)"},{"key":"Access-Control-Allow-Origin","value":"*"},{"key":"Access-Control-Allow-Methods","value":"OPTIONS,GET"},{"key":"Strict-Transport-Security","value":"max-age=31536000; includeSubDomains; preload"},{"key":"Expect-CT","value":"max-age=0, report-uri=\"https://openstreetmap.report-uri.com/r/d/ct/reportOnly\""},{"key":"Upgrade","value":"h2"},{"key":"Connection","value":"Upgrade, close"},{"key":"Transfer-Encoding","value":"chunked"},{"key":"Content-Type","value":"application/json; charset=UTF-8"}]}
2020-03-20 11:41:53.608877 [DEBUG] mod_curl.c:192 method: get, url: https://nominatim.openstreetmap.org/?format=xml&addressdetails=1&q=london&format=json&limit=1&pretty=1, content-type: (null)
2020-03-20 11:41:53.608877 [WARNING] mod_curl.c:206 Not verifying TLS cert for https://nominatim.openstreetmap.org/?format=xml&addressdetails=1&q=london&format=json&limit=1&pretty=1; connection is not secure

 

POST и GET запросы.

curl https://asterisk-pbx.ru/wiki/freeswitch/start?do=backlink
curl https://asterisk-pbx.ru/wiki/freeswitch/start post do=backlink 

Совместно с headers или JSON:

curl https://asterisk-pbx.ru/wiki/start?id=freeswitch%20curl&do=search json
curl https://asterisk-pbx.ru/wiki/start headers post id=freeswitch%20mod_curl&do=search

Синтаксис вызвова API - curl_sendfile:

[api/bgapi] curl_sendfile <url> <filenameParamName=filepath> [nopost|postparam1=foo&postparam2=bar... [event|stream|both|none  [identifier ]]] 

<url>: Вызываемый URL REST запроса. Должен содержать необходимые GET параметры и URL вызваемого скрипта, который будет обрабатывать параметры. <filenameParamName=filepath> : Пара ключ=значение. Ключ это желаемое имя элемента формы. Значение - полный путь к файлу который мы хотим передать, связанный с элементом.

nopost|postparam1=foo&postparam2=bar… : Если требуется задать доплнительный набор параметров, вы можете назначить urlencoded:

fs_cli -x 'url_encode param1=foo&param2=bar'
param1%3Dfoo1%26param2%3Dfoo2

набор вида ключ=значение так, как вы бы это сделали в GET запросе. Если передача дополнительных элементов не требуется, просто укажите nopost.

event|stream|both|none : Данный параметр определяет, где отображается вывод полученный после REST запроса. Если указано event, это присоединит пользовательское (CUSTOM) событие, помеченное как curl_sendfile::ack и отправит его:

curl_sendfile::ack

curl_sendfile::ack

Event-Subclass: curl_sendfile::ack
Event-Name: CUSTOM
Core-UUID: 618d9603-a67b-4e06-acec-29f3c5ceb0d5
FreeSWITCH-Hostname: fs.ru.net
FreeSWITCH-Switchname: fs.ru.net
FreeSWITCH-IPv4: 91.xxx.xxx.xxx
FreeSWITCH-IPv6: 2a03%3A80c0%3A1%3A155%3A%3A
Event-Date-Local: 2020-03-20%2014%3A32%3A49
Event-Date-GMT: Fri,%2020%20Mar%202020%2011%3A32%3A49%20GMT
Event-Date-Timestamp: 1584703969269012
Event-Calling-File: mod_curl.c
Event-Calling-Function: http_sendfile_success_report
Event-Calling-Line-Number: 494
Event-Sequence: 419369
Filename: /usr/local/freeswitch/recordings/997b6b21-9637-4a97-9940-b3b33ee4e454.ogg
File-Access: Success
REST-HTTP-Code: 401
Content-Length: 137

{"error_code":"UNAUTHORIZED","error_message":"rpc error: code = Unauthenticated desc = IAM token or API key has to be passed in request"}Content-Length: 1509
Content-Type: text/event-plain


Если вы укажете stream, он будет выведен в активную сессию, какой бы метод ни использовался для выполнения вызова.
Если both, то оба вида будут применены.
Если none, то ничего кроме +HTTP_STATUS_CODE Ok или -HTTP_STATUS_CODE Err отображено не будет.

identifier: Это произвольный идентификатор, который вы можете использовать для собственных нужд.
Он будет отображен как заголовок события - Command-Execution-Identifier:

...
Event-Sequence: 419544
Command-Execution-Identifier: FOOO
Filename: /usr/local/freeswitch/recordings/997b6b21-9637-4a97-9940-b3b33ee4e454.ogg
File-Access: Success
REST-HTTP-Code: 200
Content-Length: 137
...
Чтобы identifier был корректно обработан, должны быть заданы два предыдущих «необязательных» параметра, иначе парсер не сможет обработать его.

… nopost none identifier

Lua Usage

Хорошей практикой является проверка session:ready() перед любыми длительными вызовами функций, такими как HTTP-запрос, чтобы ваш сценарий прекратил работу и освободил свои ресурсы как можно скорее, если вызов потерпел неудачу.

 

GET запрос:

session:execute("curl", "http://www.myhost.com/?name1=value1&name2=value2") 
curl_response_code = session:getVariable("curl_response_code") 
curl_response = session:getVariable("curl_response_data") 

POST запрос:

session:execute("curl", "http://www.myhost.com/ post name1=value1&name2=value2") 
curl_response_code = session:getVariable("curl_response_code") 
curl_response = session:getVariable("curl_response_data") 

Установить таймаут в секундах:

session:setVariable("curl_timeout", "10") 
session:execute("curl", "http://www.myhost.com/ post name1=value1&name2=value2") 

Также возможно делать вызов через API интерфейс:

api = freeswitch.API(); 
get_response = api:execute("curl", "http://www.myhost.com/?name1=value1&name2=value2") 
post_response = api:execute("curl", "http://www.myhost.com/ post name1=value1&name2=value2") 

Все приведенный выше примеры, должны передаваться, как URL encoded.

 

Submit:

first  = "a short value" 
second = "a slightly longer value" 

as:

first=a%20short%20value&second=a%20slightly%20longer%20value 

Пример Urlencode для формирования GET/POST запроса:

-- Lua uriencode function
--
-- Can take a table, or a string of comma separated values.
-- Examples:
-- > print(uriencode("this=is,a=/test/,string='quotes'"))
-- a=%2Ftest%2F&string=%27quotes%27&this=is
-- > print(uriencode({this="is", a="/test/", string="'quotes'"}))
-- a=%2Ftest%2F&string=%27quotes%27&this=is
--
function uriencode(vals)
 
    function escape (s)
        s = string.gsub(
            s,
            '([\r\n"#%%&+:;<=>?@^`{|}%\\%[%]%(%)$!~,/\'])',
            function (c)
                return '%'..string.format("%02X", string.byte(c));
            end
        );
        s = string.gsub(s, "%s", "+");
        return s;
    end
 
    function encode (t)
        local s = "";
        for k , v in pairs(t) do
            s = s .. "&" .. escape(k) .. "=" .. escape(v);
        end
        return string.sub(s, 2);
    end
 
    if type(vals) == 'table' then
        return encode(vals);
    else
        local t = {};
        for k, v in  string.gmatch(vals, ",?([^=]+)=([^,]+)") do
            t[k]=v;
        end
        return encode(t);
    end
end

(или  CGILua's urlcode)

Если вы хотите передать запрос базовой аутентификации, то используейте форму:

 local auth_url = "http://username:password@mysecure_web_service.com" 
 local response = api:execute("curl", auth_url) 

Source

https://freeswitch.org/confluence/display/FREESWITCH/mod_curl

Feedback

<action application="curl" data="http://localhost/faxapp content-type 'application/JSON' post {"session_id":"9dea8e0d880ec5b0450c2a96766b87","source":"1234XXXXXXX","destination":"56475XXXXXXX"}"

Вышеприведенный пример потерпит неудачу из-за двойных кавычек.

Данная форма записи позволит избежать этого:

<action application="set" data='post={"session_id":"9dea8e0d880ec5b0450c2a96766b87","source":"1234XXXXXXX","destination":"56475XXXXXXX"}' inline="true"/>
<action application="curl" data="http://localhost/faxapp content-type 'application/json' post ${post}" inline="true"/>
  • freeswitch/mod/mod_curl.txt
  • Последние изменения: 2020/04/11