Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
galene
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
galene
Commits
c7c3c9c6
Commit
c7c3c9c6
authored
Oct 26, 2021
by
Juliusz Chroboczek
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Export group status in .status.json.
parent
5e39c3a2
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
132 additions
and
56 deletions
+132
-56
README.PROTOCOL
README.PROTOCOL
+42
-19
group/group.go
group/group.go
+30
-14
rtpconn/webclient.go
rtpconn/webclient.go
+4
-19
static/galene.js
static/galene.js
+17
-2
webserver/webserver.go
webserver/webserver.go
+39
-2
No files found.
README.PROTOCOL
View file @
c7c3c9c6
# Galène's protocol
Galène uses a symmetric, asynchronous protocol. In client-server
usage, some messages are only sent in the client to server or in the
server to client direction.
## Message syntax
All messages are sent as JSON objects. All fields except `type` are
optional; however, there are some fields that are common across multiple
message types.
- `type`, the type of the message;
- `kind`, the subtype of the message;
- `id`, the id of the object being manipulated;
- `source`, the client-id of the originating client;
- `username`, the username of the originating client;
- `dest`, the client-id of the destination client;
- `privileged`, set by the server to indicate that the originating client
had the `op` privilege at the time it sent the message.
## Data structures
### Group
...
...
@@ -41,6 +22,48 @@ exactly one peer connection (PC) (multiple streams in a single PC are not
allowed). The offerer is also the RTP sender (i.e. all tracks sent by the
offerer are of type `sendonly`).
Galène uses a symmetric, asynchronous protocol. In client-server
usage, some messages are only sent in the client to server or in the
server to client direction.
## Before connecting
Before it connects and joins a group, a client may perform an HTTP GET
request on the URL `/public-groups.json`. This yields a JSON array of
objects, one for each group that has been marked public in its
configuration file. Each object has the following fields:
- `name`: the group's name
- `displayName` (optional): a longer version of the name used for display;
- `description` (optional): a user-readable description.
- `locked`: true if the group is locked;
- `clientCount`: the number of clients currently in the group.
A client may also fetch the URL `/group/name/.status.json` to retrieve the
status of a single group. If the group has not been marked as public,
then the fields `locked` and `clientCount` are omitted.
## Connecting
The client connects to the websocket at `/ws`. Galene uses a symmetric,
asynchronous protocol: there are no requests and responses, and most
messages may be sent by either peer.
## Message syntax
All messages are sent as JSON objects. All fields except `type` are
optional; however, there are some fields that are common across multiple
message types:
- `type`, the type of the message;
- `kind`, the subtype of the message;
- `id`, the id of the object being manipulated;
- `source`, the client-id of the originating client;
- `username`, the username of the originating client;
- `dest`, the client-id of the destination client;
- `privileged`, set by the server to indicate that the originating client
had the `op` privilege at the time when it sent the message.
## Establishing and maintaining a connection
The peer establishing the connection (the WebSocket client) sends
...
...
group/group.go
View file @
c7c3c9c6
...
...
@@ -112,6 +112,12 @@ func (g *Group) Description() *Description {
return
g
.
description
}
func
(
g
*
Group
)
ClientCount
()
int
{
g
.
mu
.
Lock
()
defer
g
.
mu
.
Unlock
()
return
len
(
g
.
clients
)
}
func
(
g
*
Group
)
EmptyTime
()
time
.
Duration
{
g
.
mu
.
Lock
()
defer
g
.
mu
.
Unlock
()
...
...
@@ -1052,27 +1058,37 @@ func (desc *Description) GetPermission(group string, creds ClientCredentials) (C
return
p
,
ErrNotAuthorised
}
type
Public
struct
{
type
Status
struct
{
Name
string
`json:"name"`
DisplayName
string
`json:"displayName,omitempty"`
Description
string
`json:"description,omitempty"`
Locked
bool
`json:"locked,omitempty"`
ClientCount
int
`json:"clientCount"`
ClientCount
*
int
`json:"clientCount,omitempty"`
}
func
GetStatus
(
g
*
Group
,
authentified
bool
)
Status
{
desc
:=
g
.
Description
()
d
:=
Status
{
Name
:
g
.
name
,
DisplayName
:
desc
.
DisplayName
,
Description
:
desc
.
Description
,
}
if
authentified
||
desc
.
Public
{
// these are considered private information
locked
,
_
:=
g
.
Locked
()
count
:=
g
.
ClientCount
()
d
.
Locked
=
locked
d
.
ClientCount
=
&
count
}
return
d
}
func
GetPublic
()
[]
Public
{
gs
:=
make
([]
Public
,
0
)
func
GetPublic
()
[]
Status
{
gs
:=
make
([]
Status
,
0
)
Range
(
func
(
g
*
Group
)
bool
{
desc
:=
g
.
Description
()
if
desc
.
Public
{
locked
,
_
:=
g
.
Locked
()
gs
=
append
(
gs
,
Public
{
Name
:
g
.
name
,
DisplayName
:
desc
.
DisplayName
,
Description
:
desc
.
Description
,
Locked
:
locked
,
ClientCount
:
len
(
g
.
clients
),
})
if
g
.
Description
()
.
Public
{
gs
=
append
(
gs
,
GetStatus
(
g
,
false
))
}
return
true
})
...
...
rtpconn/webclient.go
View file @
c7c3c9c6
...
...
@@ -111,7 +111,7 @@ type clientMessage struct {
Password
string
`json:"password,omitempty"`
Privileged
bool
`json:"privileged,omitempty"`
Permissions
*
group
.
ClientPermissions
`json:"permissions,omitempty"`
Status
map
[
string
]
interface
{}
`json:"status,omitempty"`
Status
interface
{}
`json:"status,omitempty"`
Group
string
`json:"group,omitempty"`
Value
interface
{}
`json:"value,omitempty"`
NoEcho
bool
`json:"noecho,omitempty"`
...
...
@@ -813,21 +813,6 @@ func (c *webClient) PushConn(g *group.Group, id string, up conn.Up, tracks []con
return
nil
}
func
getGroupStatus
(
g
*
group
.
Group
)
map
[
string
]
interface
{}
{
status
:=
make
(
map
[
string
]
interface
{})
if
locked
,
message
:=
g
.
Locked
();
locked
{
if
message
==
""
{
status
[
"locked"
]
=
true
}
else
{
status
[
"locked"
]
=
message
}
}
if
dn
:=
g
.
Description
()
.
DisplayName
;
dn
!=
""
{
status
[
"displayName"
]
=
dn
}
return
status
}
func
readMessage
(
conn
*
websocket
.
Conn
,
m
*
clientMessage
)
error
{
err
:=
conn
.
SetReadDeadline
(
time
.
Now
()
.
Add
(
15
*
time
.
Second
))
if
err
!=
nil
{
...
...
@@ -1120,11 +1105,11 @@ func handleAction(c *webClient, a interface{}) error {
Status
:
a
.
status
,
})
case
joinedAction
:
var
status
map
[
string
]
interface
{}
var
status
interface
{}
if
a
.
group
!=
""
{
g
:=
group
.
Get
(
a
.
group
)
if
g
!=
nil
{
status
=
g
etGroupStatus
(
g
)
status
=
g
roup
.
GetStatus
(
g
,
true
)
}
}
perms
:=
c
.
permissions
...
...
@@ -1149,7 +1134,7 @@ func handleAction(c *webClient, a interface{}) error {
Group
:
g
.
Name
(),
Username
:
c
.
username
,
Permissions
:
&
perms
,
Status
:
g
etGroupStatus
(
g
),
Status
:
g
roup
.
GetStatus
(
g
,
true
),
RTCConfiguration
:
ice
.
ICEConfiguration
(),
})
if
!
c
.
permissions
.
Present
{
...
...
static/galene.js
View file @
c7c3c9c6
...
...
@@ -26,6 +26,9 @@ let group;
/** @type {ServerConnection} */
let
serverConnection
;
/** @type {Object} */
let
groupStatus
=
{};
/**
* @typedef {Object} userpass
* @property {string} username
...
...
@@ -2149,6 +2152,7 @@ async function gotJoined(kind, group, perms, status, message) {
return
;
case
'
join
'
:
case
'
change
'
:
groupStatus
=
status
;
setTitle
((
status
&&
status
.
displayName
)
||
capitalise
(
group
));
displayUsername
();
setButtonsVisibility
();
...
...
@@ -3095,11 +3099,22 @@ async function serverConnect() {
}
}
function
start
()
{
async
function
start
()
{
group
=
decodeURIComponent
(
location
.
pathname
.
replace
(
/^
\/[
a-z
]
*
\/
/
,
''
).
replace
(
/
\/
$/
,
''
)
);
setTitle
(
capitalise
(
group
));
/** @type {Object} */
try
{
let
r
=
await
fetch
(
"
.status.json
"
)
if
(
!
r
.
ok
)
throw
new
Error
(
`
${
r
.
status
}
${
r
.
statusText
}
`
);
groupStatus
=
await
r
.
json
()
}
catch
(
e
)
{
console
.
error
(
e
);
return
;
}
setTitle
(
groupStatus
.
displayName
||
capitalise
(
group
));
addFilters
();
setMediaChoices
(
false
).
then
(
e
=>
reflectSettings
());
...
...
webserver/webserver.go
View file @
c7c3c9c6
...
...
@@ -278,7 +278,11 @@ func groupHandler(w http.ResponseWriter, r *http.Request) {
return
}
mungeHeader
(
w
)
if
strings
.
HasSuffix
(
r
.
URL
.
Path
,
"/.status.json"
)
{
groupStatusHandler
(
w
,
r
)
return
}
name
:=
parseGroupName
(
"/group/"
,
r
.
URL
.
Path
)
if
name
==
""
{
notFound
(
w
)
...
...
@@ -290,7 +294,7 @@ func groupHandler(w http.ResponseWriter, r *http.Request) {
if
os
.
IsNotExist
(
err
)
{
notFound
(
w
)
}
else
{
log
.
Printf
(
"
addGroup
: %v"
,
err
)
log
.
Printf
(
"
group.Add
: %v"
,
err
)
http
.
Error
(
w
,
"Internal server error"
,
http
.
StatusInternalServerError
)
}
...
...
@@ -308,9 +312,42 @@ func groupHandler(w http.ResponseWriter, r *http.Request) {
return
}
mungeHeader
(
w
)
serveFile
(
w
,
r
,
filepath
.
Join
(
StaticRoot
,
"galene.html"
))
}
func
groupStatusHandler
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
path
:=
path
.
Dir
(
r
.
URL
.
Path
)
name
:=
parseGroupName
(
"/group/"
,
path
)
if
name
==
""
{
notFound
(
w
)
return
}
g
,
err
:=
group
.
Add
(
name
,
nil
)
if
err
!=
nil
{
if
os
.
IsNotExist
(
err
)
{
notFound
(
w
)
}
else
{
http
.
Error
(
w
,
"Internal server error"
,
http
.
StatusInternalServerError
)
}
return
}
d
:=
group
.
GetStatus
(
g
,
false
)
w
.
Header
()
.
Set
(
"content-type"
,
"application/json"
)
w
.
Header
()
.
Set
(
"cache-control"
,
"no-cache"
)
if
r
.
Method
==
"HEAD"
{
return
}
e
:=
json
.
NewEncoder
(
w
)
e
.
Encode
(
d
)
return
}
func
publicHandler
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
w
.
Header
()
.
Set
(
"content-type"
,
"application/json"
)
w
.
Header
()
.
Set
(
"cache-control"
,
"no-cache"
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment