Commit b55e531a authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Encode group location in the status.json file.

parent 4bc873a5
......@@ -28,31 +28,33 @@ 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:
The client needs to know the location of the group, the (user-visible) URL
at which the group is found. This may be obtained either by explicit
configuration by the user, or by parsing the `/public-groups.json` file
which may contain an array of group statuses (see below).
A client then performs an HTTP GET request on the file `.status.json` at
the group's location. This yields a single JSON object, which contains
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.
- `location`: the group's location
- `endpoint`: the URL of the server's WebSocket endpoint
- `displayName`: a longer version of the name used for display;
- `description`: a user-readable description;
- `authServer`: the URL of the authentication server, if any;
- `authPortal`: the uRL of the authentication portal, if any;
- `locked`: true if the group is locked;
- `clientCount`: the number of clients currently in the group.
If token-based authorisation is in use for the group, then the dictionary
contains the following additional field:
- `authServer`: the URL of the authorisation server.
All fields are optional except `name`, `location` and `endpoint`.
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.
The client connects to the websocket at the URL obtained at the previous
step. Galene uses a symmetric, asynchronous protocol: there are no
requests and responses, and most messages may be sent by either peer.
## Message syntax
......
......@@ -1177,6 +1177,7 @@ func (desc *Description) GetPermission(group string, creds ClientCredentials) (s
type Status struct {
Name string `json:"name"`
Location string `json:"location"`
Endpoint string `json:"endpoint"`
DisplayName string `json:"displayName,omitempty"`
Description string `json:"description,omitempty"`
......@@ -1186,10 +1187,40 @@ type Status struct {
ClientCount *int `json:"clientCount,omitempty"`
}
func (g *Group) Status(authentified bool, endpoint string) Status {
// Status returns a group's status.
// Base is the base URL for groups; if omitted, then both the Location and
// Endpoint members are omitted from the result.
func (g *Group) Status(authentified bool, base string) Status {
desc := g.Description()
var location, endpoint string
if base != "" {
burl, err := url.Parse(base)
if err == nil {
wss := "wss"
if burl.Scheme == "http" {
wss = "ws"
}
l := url.URL{
Scheme: burl.Scheme,
Host: burl.Host,
Path: path.Join(burl.Path, g.name) + "/",
}
location = l.String()
e := url.URL{
Scheme: wss,
Host: burl.Host,
Path: "/ws",
}
endpoint = e.String()
} else {
log.Printf("Couldn't parse base URL %v", base)
}
}
d := Status{
Name: g.name,
Location: location,
Endpoint: endpoint,
DisplayName: desc.DisplayName,
AuthServer: desc.AuthServer,
......@@ -1207,11 +1238,11 @@ func (g *Group) Status(authentified bool, endpoint string) Status {
return d
}
func GetPublic() []Status {
func GetPublic(base string) []Status {
gs := make([]Status, 0)
Range(func(g *Group) bool {
if g.Description().Public {
gs = append(gs, g.Status(false, ""))
gs = append(gs, g.Status(false, base))
}
return true
})
......
......@@ -42,7 +42,7 @@ func TestGroup(t *testing.T) {
t.Errorf("Expected [], got %v", subs)
}
if public := GetPublic(); len(public) != 1 || public[0].Name != "group/subgroup" {
if public := GetPublic(""); len(public) != 1 || public[0].Name != "group/subgroup" {
t.Errorf("Expected group/subgroup, got %v", public)
}
}
......
......@@ -110,7 +110,7 @@ async function listPublicGroups() {
let td = document.createElement('td');
let a = document.createElement('a');
a.textContent = group.displayName || group.name;
a.href = '/group/' + group.name + '/';
a.href = group.location;
td.appendChild(a);
tr.appendChild(td);
let td2 = document.createElement('td');
......
......@@ -332,9 +332,18 @@ func groupHandler(w http.ResponseWriter, r *http.Request) {
serveFile(w, r, filepath.Join(StaticRoot, "galene.html"))
}
func groupBase(r *http.Request) string {
base := url.URL{
Scheme: r.URL.Scheme,
Host: r.Host,
Path: "/group/",
}
return base.String()
}
func groupStatusHandler(w http.ResponseWriter, r *http.Request) {
path := path.Dir(r.URL.Path)
name := parseGroupName("/group/", path)
pth := path.Dir(r.URL.Path)
name := parseGroupName("/group/", pth)
if name == "" {
notFound(w)
return
......@@ -351,16 +360,7 @@ func groupStatusHandler(w http.ResponseWriter, r *http.Request) {
return
}
scheme := "wss"
if Insecure {
scheme = "ws"
}
endpoint := url.URL{
Scheme: scheme,
Host: r.Host,
Path: "/ws",
}
d := g.Status(false, endpoint.String())
d := g.Status(false, groupBase(r))
w.Header().Set("content-type", "application/json")
w.Header().Set("cache-control", "no-cache")
......@@ -380,7 +380,7 @@ func publicHandler(w http.ResponseWriter, r *http.Request) {
return
}
g := group.GetPublic()
g := group.GetPublic(groupBase(r))
e := json.NewEncoder(w)
e.Encode(g)
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment