One type of information that an SRT caller can share with an SRT listener during a connection negotiation
is the "Stream ID". This is a string of maximum 512 characters set on the caller
side. It can be retrieved at the listener side on the newly accepted socket
through a socket option (see SRTO_STREAMID
in SRT API Socket Options).
Only caller-listener connections are supported.
As of SRT version 1.3.3 a callback can be registered on the listener socket for an application to make decisions on incoming caller connections. This callback, among others, is provided with the value of Stream ID from the incoming connection. Based on this information, the application can accept or reject the connection, select the desired data stream, or set an appropriate passphrase for the connection.
There are two target use-cases of the Stream ID:
- identify the file name of a stream that is about to be sent (simple use case);
- identify a user, the purpose of the connection (receive or send), the resources, and more (advanced use case).
The Stream ID can be provided as free-form value, especially when targeting the simple use case. However, there is a recommended convention so that all SRT users speak the same language. The intent of the convention is to:
- promote readability and consistency among free-form names
- interpret some typical data in the key-value style
In short,
SRTO_STREAMID
is designed for a caller (client) to be able to identify itself, and to state its intent (send/receive, live/file, etc.).srt_listen_callback(...)
function is used by a listener (server) to check what a caller (client) has provided inSRTO_STREAMID
before the connection is established. For example, the listener (server) can check if it knows the user and set the corresponding passphrase for a connection to be accepted.- Even if
srt_listen_callback(...)
accepts the connection, SRT will still have one more step to check the PASSPHRASE, and reject on mismatch. If a correct passphrase is not provided by the client (caller), the request from caller will be rejected by SRT library (not application or programmer).
Note! srt_listen_callback(...)
can't check the passphrase directly for security reasons.
The only way to make the app check the passphrase is to set the passphrase on the socket by using the SRTO_PASSPHRASE
option. This lets SRT to reject connection on mismatch.
The Stream ID uses UTF-8 encoding.
The recommended syntax starts with the characters known as an executable specification in POSIX: #!
.
The next character defines the format used for the following key-value pair syntax.
At the moment, there is only one supported syntax identified by :
and described below.
Everything that comes after a syntax identifier is further referenced as the content of the Stream ID.
The content starts with a :
or {
character identifying its format:
:
: comma-separated key-value pairs with no nesting,{
: a nested block with one or several key-value pairs that must end with a}
character. Nesting means that multiple level brace-enclosed parts are allowed.
The form of the key-value pair is
key1=value1,key2=value2,...
Beside the general syntax, there are several top-level keys treated as standard
keys. All single letter key definitions, including those not listed in this section,
are reserved for future use. Users can additionally use custom key definitions
with user_*
or companyname_*
prefixes, where user
and companyname
are
to be replaced with an actual user or company name.
The existing key values must not be extended, and must not differ from those described in this section.
The following keys are standard:
u
: User Name, or authorization name, that is expected to control which password should be used for the connection. The application should interpret it to distinguish which user should be used by the listener party to set up the password.r
: Resource Name identifies the name of the resource and facilitates selection should the listener party be able to serve multiple resources.h
: Host Name identifies the hostname of the resource. For example, to request a stream with the URIsomehost.com/videos/querry.php?vid=366
thehostname
field should havesomehost.com
, and the resource name can havevideos/querry.php?vid=366
or simply366
. Note that this is still a key to be specified explicitly. Support tools that apply simplifications and URI extraction are expected to insert only the host portion of the URI here.s
: Session ID is a temporary resource identifier negotiated with the server, used just for verification. This is a one-shot identifier, invalidated after the first use. The expected usage is when details for the resource and authorization are negotiated over a separate connection first, and then the session ID is used here alone.t
: Type specifies the purpose of the connection. Several standard types are defined, but users may extend the use:stream
(default, if not specified): for exchanging the user-specified payload for an application-defined purposefile
: for transmitting a file, wherer
is the filenameauth
: for exchanging sensible data. Ther
value states its purpose. No specific possible values for that are known so far (FUTURE USE]
m
: Mode expected for this connection:request
(default): the caller wants to receive the streampublish
: the caller wants to send the stream databidirectional
: bidirectional data exchange is expected
Note that m
is not required in the case where you don't use streamid
to
distinguish authorization or resources, and your caller is expected to send the
data. This is only for cases where the listener can handle various purposes of the
connection and is therefore required to know what the caller is attempting to do.
Examples:
#!::u=admin,r=bluesbrothers1_hi
This specifies the username and the resource name of the stream to be served to the caller.
#!::u=johnny,t=file,m=publish,r=results.csv
This specifies that the file is expected to be transmitted from the caller to
the listener and its name is results.csv
.
The listener callback handler is also able to decide about rejecting the
incoming connection. In a normal situation, the rejection code is predefined
as SRT_REJ_RESOURCE
. The handler can, however, set its own rejection
code. There are two number spaces intended for this purpose (as the range
below SRT_REJC_PREDEFINED
is reserved for internal codes):
-
SRT_REJC_PREDEFINED
and above: predefined errors. Errors from this range (that is, belowSRT_REJC_USERDEFINED
) have their definitions provided in theaccess_control.h
public header file. The intention is that applications using these codes understand the situation described by these codes standard way. -
SRT_REJC_USERDEFINED
and above: to be freely defined by the application. Codes from this range can be only understood if each application knows the code definitions of the other. These codes should be used only after making sure that both applications understood them.
The intention for the predefined codes is to be consistent with the HTTP standard codes. Therefore the following sub-ranges are used:
- 0 - 99: Reserved for unique SRT-specific codes (unused by HTTP)
- 100 - 399: Info, Success and Redirection in HTTP, unused in SRT
- 400 - 599: Client and server errors in HTTP, adopted by SRT
- 600 - 999: unused in SRT
Such a code can be set by using the srt_setrejectreason
function.
The SRT-specific codes are:
This code should be set by the callback handler in the beginning in case the application needs to be informed that the callback handler actually has interpreted the incoming connection, but hasn't set a more appropriate code describing the situation.
Indicates there was a key specified in the StreamID string that this application
doesn't support. Note that it's not obligatory for the application to
react this way - it may chose to ignore unknown keys completely, or
to have some keys in the ignore list (which it won't interpret, but tolerate)
while rejecting any others. It is also up to the application
to decide to return this specific error, or more generally report
the syntax error with SRT_REJX_BAD_REQUEST
.
The resource type designates a file, and the path either has the wrong syntax
or is not found. In the case where t=file
, the path should be specified under
the r
key, and the file specified there must be able to be saved this way.
It's up to the application to decide how to treat this path, how to parse it,
and what this path specifically means. For the r
key, the application should
at least handle the single filename, and have storage space available to save
it (provided a file of the same name does not already exist there). The
application should decide whether and how to handle all other situations (like
directory path, special markers in the path to be interpreted by the
application, etc.), or to report this error.
The host specified in the h
key cannot be identified. The h
key is
generally for a situation when you have multiple DNS names for a host,
so an application may want to extract the name from the URI and set it
to h
key so that the application can distinguish the request also by
the target host name. The application may however limit the number of
recognized services by host name to some predefined names and not
handle the others, even if this is properly resolved by DNS. In this
case it should report this error.
The other error codes are HTTP codes adopted for SRT:
General syntax error. This can be reported in any case when parsing the StreamID contents failed, or it cannot be properly interpreted.
Authentication failed, which makes the client unauthorized to access the
resource. This error, however, confirms that the syntax is correct and
the resource has been properly identified. Note that this cannot be
reported when you use a simple user-password authentication
method because in this case the password is verified only after the
listener callback handler accepts the connection. This error is rather
intended to be reported in case of t=auth
when the authentication
process has generated some valid session ID, but then the session
connection has specified a resource that is not within the frames
of that authentication.
The server is too heavily loaded to process your request, or you have exceeded credits for accessing the service and the resource. In HTTP the description mentions payment for a service, but it is also used by some services for general "credit" management for a client. In SRT it should be used when your service is doing any kind of credit management to limit access to selected clients that "have" enough credit, even if the credit is something the client can recharge itself, or that can be granted depending on available service resources.
Access denied to the resource for any reason. This error is
independent of an authorization or authentication error (as reported
by SRT_REJX_UNAUTHORIZED
). The application can decide which
is more appropriate. This error is usually intended for
a resource that should only be accessed after a successful
authorization over a separate auth-only connection, where the query
in StreamID has correctly specified the resource identity and mode,
but the session ID (in the s
key) is either (a) not specified, or (b) does
specify a valid session, but the authorization region for this
session does not embrace the specified resource.
The resource specified in the r
key (in combination with the h
key)
is not found at this time. This error should be only reported if the
information about resource accessibility is allowed to be publicly
visible. Otherwise the application might report authorization
errors.
The mode specified in the m
key in StreamID is not supported for this request.
This may apply to read-only or write-only resources, as well for when interactive
(bidirectional) access is not valid for a resource.
Applies when the parameters specified in SocketID cannot be satisfied for the
requested resource, or when m=publish
but the data format is not acceptable.
This is a general error reporting an unsupported format for data that appears to
be wrong when sending, or a restriction on the data format (as specified in the
details of the resource specification) such that it cannot be provided
when receiving.
The resource being accessed (as specified by r
and h
keys) is locked for
modification. This error should only be reported for m=publish
when the
resource being accessed is read-only because another client (not necessarily
connected through SRT):
- is currently publishing into this resource
- has reserved this resource ID for publishing
Note that this error should be reported when there is no other reason for having a problem accessing the resource.
The media type is not supported by the application. The media type is
specified in the t
key. The currently standard types are
stream
, file
and auth
. An application may extend this list, and
is not obliged to support all of the standard types.
The resource being accessed is locked against any access. This is similar to
SRT_REJX_CONFLICT
, but in this case the resource is locked for reading
and writing. This is for when the resource should be shown as existing and
available to the client, but access is temporarily blocked.
The dependent entity for the request is not present. In this case the
dependent entity is the session, which should be specified in the s
key. This means that the specified session ID is nonexistent or it
has already expired.
Internal server error. This is for a general case when a request has been correctly verified, with no related problems found, but an unexpected error occurs after the processing of the request has started.
The request was correctly recognized, but the current software version
of the service (be it SRT or any other software component) doesn't
support it. This should be reported for a case, when some features to
be specified in the StreamID request are supposed to be supported in a
predictable future, but the current version of the server does not
support it, or the support for this feature in this version has been
temporarily blocked. This shouldn't be reported for existing features that are
being deprecated, or older features that are no longer supported
(for this case the general SRT_REJX_BAD_REQUEST
is more appropriate).
The server acts as a gateway and the target endpoint rejected the connection. The reason the connection was rejected is unspecified. The gateway cannot forward the original rejection code from the target endpoint because this would suggest the error was on the gateway itself. Use this error with some other mechanism to report the original target error, if possible.
The service is down for maintenance. This can only be reported when the service has been temporarily replaced by a stub that is only reporting this error, while the real service is down for maintenance.
Application version not supported. This can refer to an application feature that is unsupported (possibly from an older SRT version), or to a feature that is no longer supported because of backward compatibility requirements.
The data stream cannot be archived due to a lack of storage space. This is reported when a request to send a file or a live stream to be archived is unsuccessful. Note that the length of a file transmission is usually pre-declared, so this error can be reported early. It can also be reported when the stream is of undefined length, and there is no more storage space available.
An example of Stream ID functionality and the listener callback can be
found under tests/test_listen_callback.cpp
.
A listener can register a callback to be called in the middle of accepting a new socket connection:
srt_listen(server_sock, 5);
srt_listen_callback(server_sock, &SrtTestListenCallback, NULL);
A callback function has to be implemented by the upstream application. In the
example below, the function tries to interpret the Stream ID value first according
to the Access Control guidelines and to extract the username from the u
key.
Otherwise it falls back to a free-form specified username. Depending on the user,
it sets the appropriate password for the expected connection so that it can be
rejected if the password isn't correct. If the user isn't found in the
database (passwd
map) the function itself rejects the connection. Note that
this can be done by both returning -1 and by throwing an exception.
int SrtTestListenCallback(void* opaq, SRTSOCKET ns, int hsversion,
const struct sockaddr* peeraddr, const char* streamid)
{
using namespace std;
// opaq is used to pass some further chained callbacks
// To reject a connection attempt, return -1.
static const map<string, string> passwd {
{"admin", "thelocalmanager"},
{"user", "verylongpassword"}
};
// Try the "standard interpretation" with username at key u
string username;
static const char stdhdr [] = "#!::";
uint32_t* pattern = (uint32_t*)stdhdr;
bool found = -1;
// Extract a username from the StreamID:
if (strlen(streamid) > 4 && *(uint32_t*)streamid == *pattern)
{
vector<string> items;
Split(streamid+4, ',', back_inserter(items));
for (auto& i: items)
{
vector<string> kv;
Split(i, '=', back_inserter(kv));
if (kv.size() == 2 && kv[0] == "u")
{
username = kv[1];
found = true;
}
}
if (!found)
{
cerr << "TEST: USER NOT FOUND, returning false.\n";
return -1;
}
}
else
{
// By default the whole streamid is username
username = streamid;
}
// When the username of the client is known, the passphrase can be set
// on the socket being accepted (SRTSOCKET ns).
// The remaining part of the SRT handshaking process will check the
// passphrase of the client and accept or reject the connection.
// When not found, it will throw an exception
cerr << "TEST: Accessing user '" << username << "', might throw if not found\n";
string exp_pw = passwd.at(username);
cerr << "TEST: Setting password '" << exp_pw << "' as per user '" << username << "'\n";
srt_setsockflag(ns, SRTO_PASSPHRASE, exp_pw.c_str(), exp_pw.size());
return 0;
}