sipx 0.0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sipx/__init__.py +299 -0
- sipx/_depends.py +219 -0
- sipx/_events.py +337 -0
- sipx/_routing.py +126 -0
- sipx/_types.py +265 -0
- sipx/_uri.py +219 -0
- sipx/_utils.py +187 -0
- sipx/_version.py +3 -0
- sipx/client/__init__.py +4 -0
- sipx/client/_async.py +640 -0
- sipx/client/_base.py +184 -0
- sipx/client/_sync.py +1172 -0
- sipx/contrib/__init__.py +57 -0
- sipx/contrib/_fastapi.py +169 -0
- sipx/contrib/_isup.py +740 -0
- sipx/contrib/_sipi.py +258 -0
- sipx/contrib/_sipi_br.py +383 -0
- sipx/contrib/_stt_whisper.py +28 -0
- sipx/contrib/_tts_google.py +38 -0
- sipx/contrib/ivr/__init__.py +5 -0
- sipx/contrib/ivr/_async.py +93 -0
- sipx/contrib/ivr/_models.py +55 -0
- sipx/contrib/ivr/_sync.py +90 -0
- sipx/dns/__init__.py +5 -0
- sipx/dns/_async.py +122 -0
- sipx/dns/_models.py +19 -0
- sipx/dns/_sync.py +124 -0
- sipx/fsm/__init__.py +13 -0
- sipx/fsm/_manager.py +452 -0
- sipx/fsm/_models.py +386 -0
- sipx/fsm/_timer.py +142 -0
- sipx/main.py +239 -0
- sipx/media/__init__.py +48 -0
- sipx/media/_async.py +426 -0
- sipx/media/_audio.py +103 -0
- sipx/media/_codecs.py +258 -0
- sipx/media/_dtmf.py +242 -0
- sipx/media/_generators.py +225 -0
- sipx/media/_opus.py +111 -0
- sipx/media/_pyaudio.py +192 -0
- sipx/media/_rtp.py +365 -0
- sipx/media/_session.py +233 -0
- sipx/media/_stt.py +39 -0
- sipx/media/_tts.py +72 -0
- sipx/media/audio/__init__.py +27 -0
- sipx/media/codecs/__init__.py +4 -0
- sipx/media/dtmf/__init__.py +17 -0
- sipx/media/rtp/__init__.py +4 -0
- sipx/media/session/__init__.py +4 -0
- sipx/models/__init__.py +60 -0
- sipx/models/_auth.py +811 -0
- sipx/models/_body.py +1420 -0
- sipx/models/_header.py +535 -0
- sipx/models/_message.py +965 -0
- sipx/server/__init__.py +6 -0
- sipx/server/_async.py +116 -0
- sipx/server/_base.py +156 -0
- sipx/server/_sync.py +234 -0
- sipx/session/__init__.py +13 -0
- sipx/session/_subscription.py +315 -0
- sipx/session/_timer.py +375 -0
- sipx/transports/__init__.py +66 -0
- sipx/transports/_base.py +235 -0
- sipx/transports/_tcp.py +572 -0
- sipx/transports/_tls.py +683 -0
- sipx/transports/_udp.py +461 -0
- sipx/transports/_utils.py +28 -0
- sipx/transports/_ws.py +286 -0
- sipx-0.0.4.dist-info/METADATA +319 -0
- sipx-0.0.4.dist-info/RECORD +73 -0
- sipx-0.0.4.dist-info/WHEEL +5 -0
- sipx-0.0.4.dist-info/entry_points.txt +2 -0
- sipx-0.0.4.dist-info/top_level.txt +1 -0
sipx/__init__.py
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""
|
|
2
|
+
sipx — Modern SIP library for Python.
|
|
3
|
+
|
|
4
|
+
Quick Start:
|
|
5
|
+
>>> import sipx
|
|
6
|
+
>>> r = sipx.register("sip:alice@pbx.com", auth=("alice", "secret"))
|
|
7
|
+
>>> r = sipx.options("sip:pbx.com")
|
|
8
|
+
|
|
9
|
+
With Client:
|
|
10
|
+
>>> from sipx import Client, Events, on, SDPBody
|
|
11
|
+
>>>
|
|
12
|
+
>>> class MyEvents(Events):
|
|
13
|
+
... @on('INVITE', status=200)
|
|
14
|
+
... def on_call(self, request, response, context):
|
|
15
|
+
... print("Call accepted!")
|
|
16
|
+
...
|
|
17
|
+
>>> with Client() as client:
|
|
18
|
+
... client.auth = ("alice", "secret")
|
|
19
|
+
... client.events = MyEvents()
|
|
20
|
+
... r = client.invite("sip:bob@pbx.com", body=SDPBody.audio("10.0.0.1", 8000).to_string())
|
|
21
|
+
|
|
22
|
+
Server with DI:
|
|
23
|
+
>>> from sipx import SIPServer, Request, Response, SDPBody, FromHeader, SDP
|
|
24
|
+
>>> from typing import Annotated
|
|
25
|
+
>>>
|
|
26
|
+
>>> server = SIPServer(port=5060)
|
|
27
|
+
>>> @server.invite
|
|
28
|
+
... def on_invite(request: Request, caller: Annotated[str, FromHeader]) -> Response:
|
|
29
|
+
... return Response(200)
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
from typing import Optional
|
|
35
|
+
|
|
36
|
+
# ============================================================================
|
|
37
|
+
# Client / Server
|
|
38
|
+
# ============================================================================
|
|
39
|
+
|
|
40
|
+
from .client import Client, AsyncClient
|
|
41
|
+
from .server import SIPServer
|
|
42
|
+
|
|
43
|
+
# ============================================================================
|
|
44
|
+
# Events
|
|
45
|
+
# ============================================================================
|
|
46
|
+
|
|
47
|
+
from ._events import Events, EventContext, event_handler, on
|
|
48
|
+
|
|
49
|
+
# ============================================================================
|
|
50
|
+
# Models
|
|
51
|
+
# ============================================================================
|
|
52
|
+
|
|
53
|
+
from .models import (
|
|
54
|
+
SIPMessage,
|
|
55
|
+
Request,
|
|
56
|
+
Response,
|
|
57
|
+
MessageParser,
|
|
58
|
+
Headers,
|
|
59
|
+
HeaderParser,
|
|
60
|
+
HeaderContainer,
|
|
61
|
+
MessageBody,
|
|
62
|
+
RawBody,
|
|
63
|
+
SDPBody,
|
|
64
|
+
BodyParser,
|
|
65
|
+
Auth,
|
|
66
|
+
SipAuthCredentials,
|
|
67
|
+
AuthMethod,
|
|
68
|
+
DigestAuth,
|
|
69
|
+
DigestChallenge,
|
|
70
|
+
DigestCredentials,
|
|
71
|
+
Challenge,
|
|
72
|
+
Credentials,
|
|
73
|
+
AuthParser,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# ============================================================================
|
|
77
|
+
# DI Extractors
|
|
78
|
+
# ============================================================================
|
|
79
|
+
|
|
80
|
+
from ._depends import (
|
|
81
|
+
Extractor,
|
|
82
|
+
FromHeader,
|
|
83
|
+
ToHeader,
|
|
84
|
+
CallID,
|
|
85
|
+
CSeqValue,
|
|
86
|
+
ViaValue,
|
|
87
|
+
SDP,
|
|
88
|
+
Source,
|
|
89
|
+
Header,
|
|
90
|
+
AutoRTP,
|
|
91
|
+
resolve_handler,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# ============================================================================
|
|
95
|
+
# URI
|
|
96
|
+
# ============================================================================
|
|
97
|
+
|
|
98
|
+
from ._uri import SipURI
|
|
99
|
+
from .session import AsyncSessionTimer, SessionTimer, SessionTimerConfig
|
|
100
|
+
from ._routing import RouteSet
|
|
101
|
+
from .dns import SipResolver, ResolvedTarget
|
|
102
|
+
from .session import AsyncSubscription, Subscription, SubscriptionState
|
|
103
|
+
|
|
104
|
+
# ============================================================================
|
|
105
|
+
# FSM
|
|
106
|
+
# ============================================================================
|
|
107
|
+
|
|
108
|
+
from .fsm import AsyncTimerManager, Dialog, StateManager, TimerManager, Transaction
|
|
109
|
+
|
|
110
|
+
# ============================================================================
|
|
111
|
+
# Transport
|
|
112
|
+
# ============================================================================
|
|
113
|
+
|
|
114
|
+
from .transports import (
|
|
115
|
+
BaseTransport,
|
|
116
|
+
AsyncBaseTransport,
|
|
117
|
+
TransportAddress,
|
|
118
|
+
TransportConfig,
|
|
119
|
+
TransportError,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# ============================================================================
|
|
123
|
+
# Types
|
|
124
|
+
# ============================================================================
|
|
125
|
+
|
|
126
|
+
from ._types import (
|
|
127
|
+
DialogState,
|
|
128
|
+
TransactionState,
|
|
129
|
+
TransactionType,
|
|
130
|
+
HeaderTypes,
|
|
131
|
+
ConnectionError,
|
|
132
|
+
ReadError,
|
|
133
|
+
WriteError,
|
|
134
|
+
TimeoutError,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# ============================================================================
|
|
138
|
+
# Version
|
|
139
|
+
# ============================================================================
|
|
140
|
+
|
|
141
|
+
from ._version import __version__
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# ============================================================================
|
|
145
|
+
# One-liner functions (httpx-style)
|
|
146
|
+
# ============================================================================
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def register(
|
|
150
|
+
aor: str,
|
|
151
|
+
*,
|
|
152
|
+
auth: Optional[tuple[str, str]] = None,
|
|
153
|
+
transport: str = "UDP",
|
|
154
|
+
expires: int = 3600,
|
|
155
|
+
) -> Response:
|
|
156
|
+
"""Register with a SIP server.
|
|
157
|
+
|
|
158
|
+
>>> r = sipx.register("sip:alice@pbx.com", auth=("alice", "secret"))
|
|
159
|
+
"""
|
|
160
|
+
with Client(transport=transport, auto_auth=bool(auth)) as client:
|
|
161
|
+
if auth:
|
|
162
|
+
client.auth = auth
|
|
163
|
+
return client.register(aor, expires=expires)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def options(
|
|
167
|
+
uri: str,
|
|
168
|
+
*,
|
|
169
|
+
auth: Optional[tuple[str, str]] = None,
|
|
170
|
+
transport: str = "UDP",
|
|
171
|
+
) -> Response:
|
|
172
|
+
"""Query server capabilities.
|
|
173
|
+
|
|
174
|
+
>>> r = sipx.options("sip:pbx.com")
|
|
175
|
+
"""
|
|
176
|
+
with Client(transport=transport, auto_auth=bool(auth)) as client:
|
|
177
|
+
if auth:
|
|
178
|
+
client.auth = auth
|
|
179
|
+
return client.options(uri)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def call(
|
|
183
|
+
uri: str,
|
|
184
|
+
*,
|
|
185
|
+
auth: Optional[tuple[str, str]] = None,
|
|
186
|
+
transport: str = "UDP",
|
|
187
|
+
body: Optional[str] = None,
|
|
188
|
+
) -> Response:
|
|
189
|
+
"""Make a SIP call (INVITE).
|
|
190
|
+
|
|
191
|
+
>>> r = sipx.call("sip:100@pbx.com", auth=("alice", "secret"))
|
|
192
|
+
"""
|
|
193
|
+
with Client(transport=transport, auto_auth=bool(auth)) as client:
|
|
194
|
+
if auth:
|
|
195
|
+
client.auth = auth
|
|
196
|
+
return client.invite(to_uri=uri, body=body)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def send(
|
|
200
|
+
uri: str,
|
|
201
|
+
content: str = "",
|
|
202
|
+
*,
|
|
203
|
+
auth: Optional[tuple[str, str]] = None,
|
|
204
|
+
transport: str = "UDP",
|
|
205
|
+
) -> Response:
|
|
206
|
+
"""Send a SIP MESSAGE.
|
|
207
|
+
|
|
208
|
+
>>> r = sipx.send("sip:bob@pbx.com", "Hello!", auth=("alice", "secret"))
|
|
209
|
+
"""
|
|
210
|
+
with Client(transport=transport, auto_auth=bool(auth)) as client:
|
|
211
|
+
if auth:
|
|
212
|
+
client.auth = auth
|
|
213
|
+
return client.message(to_uri=uri, content=content)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# ============================================================================
|
|
217
|
+
# Public API
|
|
218
|
+
# ============================================================================
|
|
219
|
+
|
|
220
|
+
__all__ = [
|
|
221
|
+
# ------ Main API ------
|
|
222
|
+
"Client",
|
|
223
|
+
"AsyncClient",
|
|
224
|
+
"SIPServer",
|
|
225
|
+
"SipURI",
|
|
226
|
+
"AsyncSessionTimer",
|
|
227
|
+
"SessionTimer",
|
|
228
|
+
"SessionTimerConfig",
|
|
229
|
+
"RouteSet",
|
|
230
|
+
"SipResolver",
|
|
231
|
+
"ResolvedTarget",
|
|
232
|
+
"AsyncSubscription",
|
|
233
|
+
"Subscription",
|
|
234
|
+
"SubscriptionState",
|
|
235
|
+
"Events",
|
|
236
|
+
"EventContext",
|
|
237
|
+
"event_handler",
|
|
238
|
+
"on",
|
|
239
|
+
"__version__",
|
|
240
|
+
# ------ One-liners ------
|
|
241
|
+
"register",
|
|
242
|
+
"options",
|
|
243
|
+
"call",
|
|
244
|
+
"send",
|
|
245
|
+
# ------ Auth ------
|
|
246
|
+
"Auth",
|
|
247
|
+
"SipAuthCredentials",
|
|
248
|
+
# ------ Models ------
|
|
249
|
+
"Request",
|
|
250
|
+
"Response",
|
|
251
|
+
"SDPBody",
|
|
252
|
+
"Headers",
|
|
253
|
+
# ------ DI Extractors ------
|
|
254
|
+
"Extractor",
|
|
255
|
+
"FromHeader",
|
|
256
|
+
"ToHeader",
|
|
257
|
+
"CallID",
|
|
258
|
+
"CSeqValue",
|
|
259
|
+
"ViaValue",
|
|
260
|
+
"SDP",
|
|
261
|
+
"Source",
|
|
262
|
+
"Header",
|
|
263
|
+
"AutoRTP",
|
|
264
|
+
# ------ Transport ------
|
|
265
|
+
"BaseTransport",
|
|
266
|
+
"TransportAddress",
|
|
267
|
+
"TransportConfig",
|
|
268
|
+
"TransportError",
|
|
269
|
+
# ------ Advanced ------
|
|
270
|
+
"SIPMessage",
|
|
271
|
+
"MessageParser",
|
|
272
|
+
"HeaderParser",
|
|
273
|
+
"HeaderContainer",
|
|
274
|
+
"MessageBody",
|
|
275
|
+
"RawBody",
|
|
276
|
+
"BodyParser",
|
|
277
|
+
"AuthMethod",
|
|
278
|
+
"DigestAuth",
|
|
279
|
+
"DigestChallenge",
|
|
280
|
+
"DigestCredentials",
|
|
281
|
+
"Challenge",
|
|
282
|
+
"Credentials",
|
|
283
|
+
"AuthParser",
|
|
284
|
+
"AsyncBaseTransport",
|
|
285
|
+
"StateManager",
|
|
286
|
+
"AsyncTimerManager",
|
|
287
|
+
"TimerManager",
|
|
288
|
+
"Transaction",
|
|
289
|
+
"Dialog",
|
|
290
|
+
"TransactionState",
|
|
291
|
+
"DialogState",
|
|
292
|
+
"TransactionType",
|
|
293
|
+
"HeaderTypes",
|
|
294
|
+
"ConnectionError",
|
|
295
|
+
"ReadError",
|
|
296
|
+
"WriteError",
|
|
297
|
+
"TimeoutError",
|
|
298
|
+
"resolve_handler",
|
|
299
|
+
]
|
sipx/_depends.py
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dependency injection extractors for SIP server handlers.
|
|
3
|
+
|
|
4
|
+
Provides an Extractor ABC and built-in extractors that resolve handler
|
|
5
|
+
parameters from SIP requests via ``typing.Annotated`` metadata, similar
|
|
6
|
+
to FastAPI's ``Depends``.
|
|
7
|
+
|
|
8
|
+
Example::
|
|
9
|
+
|
|
10
|
+
from typing import Annotated
|
|
11
|
+
from sipx._depends import FromHeader, SDP, AutoRTP
|
|
12
|
+
|
|
13
|
+
@server.invite
|
|
14
|
+
def on_invite(
|
|
15
|
+
request: Request,
|
|
16
|
+
caller: Annotated[str, FromHeader()],
|
|
17
|
+
sdp: Annotated[SDPBody, SDP()],
|
|
18
|
+
rtp: Annotated[RTPSession, AutoRTP(port=19000)],
|
|
19
|
+
) -> Response:
|
|
20
|
+
...
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import typing
|
|
26
|
+
from abc import ABC, abstractmethod
|
|
27
|
+
from typing import Any
|
|
28
|
+
|
|
29
|
+
from ._utils import logger
|
|
30
|
+
from .models._message import Request
|
|
31
|
+
from ._types import TransportAddress
|
|
32
|
+
|
|
33
|
+
_log = logger.getChild("di")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# ============================================================================
|
|
37
|
+
# Base Extractor
|
|
38
|
+
# ============================================================================
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Extractor(ABC):
|
|
42
|
+
"""Abstract base class for dependency injection extractors.
|
|
43
|
+
|
|
44
|
+
Subclass and implement ``extract`` to create custom extractors
|
|
45
|
+
usable with ``typing.Annotated``.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
@abstractmethod
|
|
49
|
+
def extract(self, request: Request, source: TransportAddress) -> Any:
|
|
50
|
+
"""Extract a value from a SIP request and transport source."""
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def resolve_handler(
|
|
55
|
+
cls, handler: Any, request: Request, source: TransportAddress
|
|
56
|
+
) -> Any:
|
|
57
|
+
"""Inspect handler type hints and inject dependencies.
|
|
58
|
+
|
|
59
|
+
Resolves ``Annotated[X, <Extractor>]`` metadata, ``Request`` and
|
|
60
|
+
``TransportAddress`` type hints. Falls back to ``handler(request, source)``.
|
|
61
|
+
"""
|
|
62
|
+
import inspect
|
|
63
|
+
|
|
64
|
+
_log.debug("Resolving handler %s", getattr(handler, "__name__", handler))
|
|
65
|
+
hints = typing.get_type_hints(handler, include_extras=True)
|
|
66
|
+
kwargs: dict[str, Any] = {}
|
|
67
|
+
|
|
68
|
+
for name, hint in hints.items():
|
|
69
|
+
if name == "return":
|
|
70
|
+
continue
|
|
71
|
+
if hint is Request or (
|
|
72
|
+
hasattr(hint, "__name__") and hint.__name__ == "Request"
|
|
73
|
+
):
|
|
74
|
+
kwargs[name] = request
|
|
75
|
+
continue
|
|
76
|
+
if hint is TransportAddress:
|
|
77
|
+
kwargs[name] = source
|
|
78
|
+
continue
|
|
79
|
+
if hasattr(hint, "__metadata__"):
|
|
80
|
+
for meta in hint.__metadata__:
|
|
81
|
+
if isinstance(meta, cls):
|
|
82
|
+
_log.debug("Extracting %s via %s", name, type(meta).__name__)
|
|
83
|
+
kwargs[name] = meta.extract(request, source)
|
|
84
|
+
break
|
|
85
|
+
if isinstance(meta, type) and issubclass(meta, cls):
|
|
86
|
+
_log.debug("Extracting %s via %s()", name, meta.__name__)
|
|
87
|
+
kwargs[name] = meta().extract(request, source)
|
|
88
|
+
break
|
|
89
|
+
|
|
90
|
+
if not kwargs:
|
|
91
|
+
return handler(request, source)
|
|
92
|
+
|
|
93
|
+
sig = inspect.signature(handler)
|
|
94
|
+
positional = [p.name for p in sig.parameters.values() if p.name != "return"]
|
|
95
|
+
for i, name in enumerate(positional):
|
|
96
|
+
if name not in kwargs:
|
|
97
|
+
if i == 0:
|
|
98
|
+
kwargs[name] = request
|
|
99
|
+
elif i == 1:
|
|
100
|
+
kwargs[name] = source
|
|
101
|
+
|
|
102
|
+
return handler(**kwargs)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# ============================================================================
|
|
106
|
+
# Built-in Extractors
|
|
107
|
+
# ============================================================================
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class FromHeader(Extractor):
|
|
111
|
+
"""Extract the ``From`` header value."""
|
|
112
|
+
|
|
113
|
+
def extract(self, request: Request, source: TransportAddress) -> str | None:
|
|
114
|
+
return request.from_header
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class ToHeader(Extractor):
|
|
118
|
+
"""Extract the ``To`` header value."""
|
|
119
|
+
|
|
120
|
+
def extract(self, request: Request, source: TransportAddress) -> str | None:
|
|
121
|
+
return request.to_header
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class CallID(Extractor):
|
|
125
|
+
"""Extract the ``Call-ID`` header value."""
|
|
126
|
+
|
|
127
|
+
def extract(self, request: Request, source: TransportAddress) -> str | None:
|
|
128
|
+
return request.call_id
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class CSeqValue(Extractor):
|
|
132
|
+
"""Extract the ``CSeq`` header value."""
|
|
133
|
+
|
|
134
|
+
def extract(self, request: Request, source: TransportAddress) -> str | None:
|
|
135
|
+
return request.cseq
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class ViaValue(Extractor):
|
|
139
|
+
"""Extract the ``Via`` header value."""
|
|
140
|
+
|
|
141
|
+
def extract(self, request: Request, source: TransportAddress) -> str | None:
|
|
142
|
+
return request.via
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class SDP(Extractor):
|
|
146
|
+
"""Extract the lazily-parsed SDP body (``SDPBody`` or ``None``)."""
|
|
147
|
+
|
|
148
|
+
def extract(self, request: Request, source: TransportAddress) -> Any:
|
|
149
|
+
return request.body
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class Source(Extractor):
|
|
153
|
+
"""Extract the ``TransportAddress`` the request arrived from."""
|
|
154
|
+
|
|
155
|
+
def extract(self, request: Request, source: TransportAddress) -> TransportAddress:
|
|
156
|
+
return source
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class Header(Extractor):
|
|
160
|
+
"""Extract an arbitrary header by name.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
name: The header name to look up.
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
def __init__(self, name: str) -> None:
|
|
167
|
+
self.name = name
|
|
168
|
+
|
|
169
|
+
def extract(self, request: Request, source: TransportAddress) -> str | None:
|
|
170
|
+
return request.headers.get(self.name)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class AutoRTP(Extractor):
|
|
174
|
+
"""Create an ``RTPSession`` from the request SDP automatically.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
port: Local RTP port to bind.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
def __init__(self, port: int) -> None:
|
|
181
|
+
self.port = port
|
|
182
|
+
|
|
183
|
+
def extract(self, request: Request, source: TransportAddress) -> Any:
|
|
184
|
+
from .media._rtp import RTPSession
|
|
185
|
+
from .models._body import SDPBody
|
|
186
|
+
|
|
187
|
+
body = request.body
|
|
188
|
+
if isinstance(body, SDPBody):
|
|
189
|
+
return RTPSession.from_sdp(body, source.host, self.port)
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# ============================================================================
|
|
194
|
+
# Handler Resolver
|
|
195
|
+
# ============================================================================
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def resolve_handler(handler: Any, request: Request, source: TransportAddress) -> Any:
|
|
199
|
+
"""Shortcut for Extractor.resolve_handler()."""
|
|
200
|
+
return Extractor.resolve_handler(handler, request, source)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
# ============================================================================
|
|
204
|
+
# Exports
|
|
205
|
+
# ============================================================================
|
|
206
|
+
|
|
207
|
+
__all__ = [
|
|
208
|
+
"Extractor",
|
|
209
|
+
"FromHeader",
|
|
210
|
+
"ToHeader",
|
|
211
|
+
"CallID",
|
|
212
|
+
"CSeqValue",
|
|
213
|
+
"ViaValue",
|
|
214
|
+
"SDP",
|
|
215
|
+
"Source",
|
|
216
|
+
"Header",
|
|
217
|
+
"AutoRTP",
|
|
218
|
+
"resolve_handler",
|
|
219
|
+
]
|