Browse Source

Merge branch 'release-0.26'

release-0.26 v0.26.0
Simon Eisenmann 9 years ago
parent
commit
3dfd030b0b
  1. 2
      .gitignore
  2. 9
      .travis.yml
  3. 3
      AUTHORS
  4. 238
      CHANGELOG.md
  5. 4
      ChangeLog
  6. 11
      Godeps
  7. 79
      Makefile.am
  8. 2
      README.md
  9. 4
      configure.ac
  10. 2
      debian/gbp.conf
  11. 12
      dependencies.tsv
  12. 20
      doc/REST-API.txt
  13. 4
      go/buffercache/buffercache.go
  14. 49
      go/channelling/api.go
  15. 196
      go/channelling/api/api.go
  16. 98
      go/channelling/api/api_test.go
  17. 43
      go/channelling/api/handle_authentication.go
  18. 68
      go/channelling/api/handle_chat.go
  19. 43
      go/channelling/api/handle_conference.go
  20. 48
      go/channelling/api/handle_hello.go
  21. 35
      go/channelling/api/handle_room.go
  22. 53
      go/channelling/api/handle_self.go
  23. 60
      go/channelling/api/handle_sessions.go
  24. 36
      go/channelling/api/handle_users.go
  25. 41
      go/channelling/bus.go
  26. 306
      go/channelling/bus_manager.go
  27. 49
      go/channelling/client.go
  28. 26
      go/channelling/clientstats.go
  29. 20
      go/channelling/codec.go
  30. 2
      go/channelling/common_test.go
  31. 43
      go/channelling/config.go
  32. 27
      go/channelling/connection.go
  33. 4
      go/channelling/contact.go
  34. 27
      go/channelling/contact_manager.go
  35. 2
      go/channelling/context.go
  36. 42
      go/channelling/data.go
  37. 72
      go/channelling/hub.go
  38. 22
      go/channelling/imagecache.go
  39. 266
      go/channelling/pipeline.go
  40. 244
      go/channelling/pipeline_manager.go
  41. 14
      go/channelling/room_manager.go
  42. 4
      go/channelling/room_manager_test.go
  43. 27
      go/channelling/roomworker.go
  44. 2
      go/channelling/roomworker_test.go
  45. 2
      go/channelling/server/api.go
  46. 63
      go/channelling/server/config.go
  47. 98
      go/channelling/server/pipelines.go
  48. 6
      go/channelling/server/rooms.go
  49. 19
      go/channelling/server/sessions.go
  50. 12
      go/channelling/server/stats.go
  51. 2
      go/channelling/server/tls.go
  52. 8
      go/channelling/server/tokens.go
  53. 56
      go/channelling/server/users.go
  54. 144
      go/channelling/session.go
  55. 75
      go/channelling/session_manager.go
  56. 60
      go/channelling/sessionattestation.go
  57. 26
      go/channelling/sessioncreator.go
  58. 26
      go/channelling/sessionstore.go
  59. 29
      go/channelling/sessiontoken.go
  60. 29
      go/channelling/sessionupdate.go
  61. 37
      go/channelling/sink.go
  62. 2
      go/channelling/stats_manager.go
  63. 17
      go/channelling/tickets.go
  64. 7
      go/channelling/tokenprovider.go
  65. 26
      go/channelling/turndata.go
  66. 29
      go/channelling/unicaster.go
  67. 12
      go/channelling/user.go
  68. 26
      go/channelling/userstore.go
  69. 140
      go/natsconnection/natsconnection.go
  70. 32
      go/randomstring/randomstring.go
  71. 14
      server.conf.in
  72. 329
      src/app/spreed-webrtc-server/channelling_api.go
  73. 56
      src/app/spreed-webrtc-server/handler_image.go
  74. 30
      src/app/spreed-webrtc-server/handler_main.go
  75. 91
      src/app/spreed-webrtc-server/handler_room.go
  76. 79
      src/app/spreed-webrtc-server/handler_sandbox.go
  77. 59
      src/app/spreed-webrtc-server/handler_wellknown.go
  78. 27
      src/app/spreed-webrtc-server/handler_ws.go
  79. 272
      src/app/spreed-webrtc-server/main.go
  80. 17
      src/app/spreed-webrtc-server/utils.go
  81. 12
      static/js/controllers/uicontroller.js
  82. 53
      static/js/services/playpromise.js
  83. 9
      static/js/services/services.js
  84. 12
      static/js/services/videolayout.js

2
.gitignore vendored

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
/src/github.com
/src/golang.struktur.de
/*.pprof
/server.conf
/server.conf*
/*.log
/changelog*.txt
/src/styles/.sass-cache

9
.travis.yml

@ -3,16 +3,13 @@ @@ -3,16 +3,13 @@
language: go
go:
- 1.1
- 1.2
- 1.3
- 1.4
- 1.5
- 1.6
env:
- GEM_HOME=/var/lib/gems/1.9.1 USE_GODEPS=0
- GEM_HOME=/var/lib/gems/1.9.1 USE_GODEPS=1
- GEM_HOME=/var/lib/gems/1.9.1
before_install:
- sudo add-apt-repository -y ppa:chris-lea/node.js
@ -23,19 +20,17 @@ install: @@ -23,19 +20,17 @@ install:
- sudo gem1.9.1 install compass
- sudo gem1.9.1 install scss-lint
- npm install
- if [ "$USE_GODEPS" = "1" ]; then wget https://raw.githubusercontent.com/pote/gpm/v1.3.2/bin/gpm && chmod +x gpm && sudo mv gpm /usr/local/bin; fi
script:
- ./autogen.sh
- ./configure
- if [ "$USE_GODEPS" = "0" ]; then make get; fi
- if [ "$USE_GODEPS" = "1" ]; then make gpm; fi
- make styleshint
# TODO(fancycode): enable styleslint once all styles have been fixed
# - make styleslint
- make styles
- make jshint
- make javascript
- make goget
- make test
- make binary
- make build-i18n

3
AUTHORS

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
Simon Eisenmann <simon@struktur.de>
Joachim Bauch <bauch@struktur.de>
Evan Theurer <theurer@struktur.de>
Leon Klingele <klingele@struktur.de>
Translation:
Curt Frisemo <curt.frisemo@spreed.com>
Curt Frisemo <curt.frisemo@spreed.com>

238
debian/changelog → CHANGELOG.md

@ -1,16 +1,85 @@ @@ -1,16 +1,85 @@
spreed-webrtc-server (0.24.13) trusty; urgency=medium
## 0.26.0
* Remove go-tip from travis until it works again.
* Update Dockerfile to Xenial base and install pinned Go dependencies (fixes #278).
* Update README.md
## 0.25.5
* Improve misleading log when pipelines API is enabled.
* Pipeline API is now optional and disabled by default.
* Update to Xenial base and install pinned Go dependencies (fixing #278).
* Added default sink.
* Added to and from userid to sink/pipeline API.
* Hide pipelines web API behind a configuration flag.
* Implement NATS sink outbound encoding.
* Extended Pipeline manager to support Sink creation.
* Implement sessionCreate via NATS.
* Implemented pipeline for Offer, Candidate and Bye.
## 0.25.4
* Wrap nats connections as reference.
* Updated release build logic, so it works better with packaging.
## 0.25.3
* Updated change log.
## 0.25.2
* Support promise based play as defined in Chrome 50 - see https://developers.google.com/web/updates/2016/03/play-returns-promise
* Always disable web worker for PDF.js and no longer rely on PDF.js catching the execption when the worker cannot be started, fixing Firefox 45+.
* Moved NATS connecting helpers to own submodule.
* Added outbound ringer timeout (35s).
* Fixed make target name.
* Refactored structure of Go source code to module and binary.
* Reconnect NATS forever and log NATS connection events.
* Implemented service discovery .well-known endpoint at /.well-known/spreed-configuration
* Removed obsolete file.
## 0.25.1
-- Simon Eisenmann <simon@struktur.de> Tue, 29 Mar 2016 13:40:35 +0200
* Use new changelog to retrieve VERSION.
* Added support for default and override config.
* Removed obsolete file.
spreed-webrtc-server (0.24.12) trusty; urgency=medium
## 0.25.0
* Added hints how CHANGELOG.md is created.
* Use markdown for changelog.
* Removed own debian folder, to avoid conflicts for packagers.
* Trigger NATS events non blocking through buffered channel.
* Split "release" target into binary and assets.
* Split "install" target into binary and assets. This way packaging can later move the static assets to a separate package.
* Brought back mediaDevices wrapper for gUM for Firefox >= 38 fixing #263 and #264.
* Added Go 1.6.
* Fixed tests to reflect busManager changes.
* Added startup bus event and a NATS client id.
* Removed auth bus event in favour of session bus event.
* Added docstrings and cleaned up code.
* Validate Offer and Answer content, so only events without _token key are triggered as channelling event to bus.
* Added support for NATS pub/sub messaging to trigger channeling events for external services.
* Added Leon to authors.
* cryptoRand.Int / pseudoRand.Intn to generate random integer. Previous way was modulo-biased
* Add missing characters to random string function, so we use the full upper+lowercase alphabet
* Avoid using LDFLAGS as this might be set to unexpected values in environment.
* Require a golang version of at least 1.3.0.
* Only run TravisCI builds against go1.3 and tip.
## 0.24.12
* Brought back mediaDevices wrapper for gUM for Firefox >= 38 fixing #263 and #264.
-- Simon Eisenmann <simon@struktur.de> Tue, 01 Mar 2016 17:31:11 +0100
spreed-webrtc-server (0.24.11) trusty; urgency=medium
## 0.24.11
* Stop waiting on video early if first video track is enabled but muted.
* Use sh shebang instead of bash to be less Linux specific (#244).
@ -23,18 +92,16 @@ spreed-webrtc-server (0.24.11) trusty; urgency=medium @@ -23,18 +92,16 @@ spreed-webrtc-server (0.24.11) trusty; urgency=medium
* Firefox 44 has fixed gUM permission indicator, so limiting workaround to 43 and lower.
* Restrict VP9 experiment to Chrome >= 48.
-- Simon Eisenmann <simon@struktur.de> Thu, 18 Feb 2016 11:05:49 +0100
spreed-webrtc-server (0.24.10) trusty; urgency=medium
## 0.24.10
* Avoid to break when there is no mediaDevices.
* Added compatibility fix for Chrome 38 which stopped working when called from Chrome 46+ (Munge remote offer UDP/TLS/RTP/SAVPF to RTP/SAVPF).
* Only stop user media automatically, when all tracks have ended.
* Stop waiting on video early if first video track is enabled but muted.
-- Simon Eisenmann <simon@struktur.de> Fri, 15 Jan 2016 12:57:13 +0100
spreed-webrtc-server (0.24.9) trusty; urgency=medium
## 0.24.9
* Added support for Firefox 43 API changes.
* Use mediaDevices API to enumarate input devices to avoid deprecation warning in Chrome.
@ -47,30 +114,26 @@ spreed-webrtc-server (0.24.9) trusty; urgency=medium @@ -47,30 +114,26 @@ spreed-webrtc-server (0.24.9) trusty; urgency=medium
* Added travis to test Go 1.5 compatibility.
* ODF and PDF presentations now have a white background to avoid issues with files with have no background on their own.
-- Simon Eisenmann <simon@struktur.de> Tue, 05 Jan 2016 14:48:27 +0100
spreed-webrtc-server (0.24.8) trusty; urgency=medium
## 0.24.8
* Avoid to scale up screen sharing when sharing not full screen.
* Avoid to scale up screen sharing when sharing not full screen.
-- Simon Eisenmann <simon@struktur.de> Thu, 13 Aug 2015 16:21:22 +0200
spreed-webrtc-server (0.24.7) trusty; urgency=medium
## 0.24.7
* Fixed a problem where Chrome did not apply screen sharing constraints correctly and screen sharing was using a low resolution.
* Fixed a problem where sounds used as interval could not be disabled.
* Fixed a problem where sounds used as interval could not be disabled.
* Added window.showCiphers helper for testing WebRTC stats API.
-- Simon Eisenmann <simon@struktur.de> Thu, 13 Aug 2015 16:01:41 +0200
spreed-webrtc-server (0.24.6) trusty; urgency=medium
## 0.24.6
* Make travis run 'make test'.
* Disable notifications on Android Chrome (see https://code.google.com/p/chromium/issues/detail?id=481856).
* Disable notifications on Android Chrome (see https://code.google.com/p/chromium/issues/detail?id=481856).
-- Simon Eisenmann <simon@struktur.de> Mon, 10 Aug 2015 17:33:46 +0200
spreed-webrtc-server (0.24.5) trusty; urgency=medium
## 0.24.5
* Updated ua-parser to 0.7.9.
* Fixed errors in unit tests.
@ -82,18 +145,16 @@ spreed-webrtc-server (0.24.5) trusty; urgency=medium @@ -82,18 +145,16 @@ spreed-webrtc-server (0.24.5) trusty; urgency=medium
* Fixed a problem where streams could not be started when they were disabled when call was started and server has renegotiation disabled.
* Fixed a problem where the renegotiation shrine was ignored.
-- Simon Eisenmann <simon@struktur.de> Fri, 07 Aug 2015 16:08:14 +0200
spreed-webrtc-server (0.24.4) precise; urgency=low
## 0.24.4
* Updated German translations.
* Fixed invalid experimental constraints.
* Avoid to handle the main room as global room.
* Avoid to handle the main room as global room.
-- Simon Eisenmann <simon@struktur.de> Wed, 29 Jul 2015 16:31:06 +0200
## 0.24.3
spreed-webrtc-server (0.24.3) precise; urgency=low
* Removed deprecated API to fix Chromium 47 compatibility.
* Improved UI usability for smaller devices.
* Increased the width of buddy list and chat.
@ -108,15 +169,13 @@ spreed-webrtc-server (0.24.3) precise; urgency=low @@ -108,15 +169,13 @@ spreed-webrtc-server (0.24.3) precise; urgency=low
* Fixed a problem where screen sharing streams were not cleaned up.
* Added support for custom type dialogs.
-- Simon Eisenmann <simon@struktur.de> Tue, 28 Jul 2015 19:22:28 +0200
spreed-webrtc-server (0.24.2) precise; urgency=low
## 0.24.2
* Fixed javascript load order, so compiled scripts load properly.
* Fixed javascript load order, so compiled scripts load properly.
-- Simon Eisenmann <simon@struktur.de> Fri, 03 Jul 2015 15:15:18 +0200
spreed-webrtc-server (0.24.1) precise; urgency=low
## 0.24.1
* Load sandboxes on demand, generated by server.
* ODF and PDF sandboxes now use CSP from HTTP response header.
@ -124,9 +183,8 @@ spreed-webrtc-server (0.24.1) precise; urgency=low @@ -124,9 +183,8 @@ spreed-webrtc-server (0.24.1) precise; urgency=low
* Sandbox iframes are now always created on demand.
* Don't return users twice in "Welcome" from global room.
-- Simon Eisenmann <simon@struktur.de> Fri, 03 Jul 2015 11:43:56 +0200
spreed-webrtc-server (0.24.0) precise; urgency=low
## 0.24.0
* Added hover actions on buddy picture in group chat.
* Jed.js was updated to 1.1.0 including API update for translations.
@ -165,17 +223,15 @@ spreed-webrtc-server (0.24.0) precise; urgency=low @@ -165,17 +223,15 @@ spreed-webrtc-server (0.24.0) precise; urgency=low
* Styles were split up, so they can be built seperately.
* Fixed a problem, where Chrome thought it already had an offer.
-- Simon Eisenmann <simon@struktur.de> Tue, 16 Jun 2015 22:50:46 +0200
spreed-webrtc-server (0.23.8) precise; urgency=low
## 0.23.8
* Session subscriptions now notify close both ways.
* Reenable chat rooms on certain conditions related to peer connectivity.
* Fixed an issue where replaced sessions cannot receive data from contacts in other rooms.
-- Simon Eisenmann <simon@struktur.de> Wed, 08 Apr 2015 17:24:59 +0200
spreed-webrtc-server (0.23.7) precise; urgency=low
## 0.23.7
* Updated SCSS to match coding style.
* Updated sjcl.js to 1.0.2.
@ -189,9 +245,8 @@ spreed-webrtc-server (0.23.7) precise; urgency=low @@ -189,9 +245,8 @@ spreed-webrtc-server (0.23.7) precise; urgency=low
* Channeling API now discards all incoming messages larger than 1MB.
* Video component now corretly exits from full screen in all cases.
-- Simon Eisenmann <simon@struktur.de> Thu, 05 Mar 2015 18:00:55 +0100
spreed-webrtc-server (0.23.6) precise; urgency=low
## 0.23.6
* Fixed Youtube module.
* Contacts is now a module and can be disabled in server configuration.
@ -203,16 +258,14 @@ spreed-webrtc-server (0.23.6) precise; urgency=low @@ -203,16 +258,14 @@ spreed-webrtc-server (0.23.6) precise; urgency=low
* Text.js was updated.
* CPU overuse detection (Chrome) is no longe experimental and now enabled by default.
-- Simon Eisenmann <simon@struktur.de> Fri, 20 Feb 2015 18:30:16 +0100
spreed-webrtc-server (0.23.5) precise; urgency=low
## 0.23.5
* No longer install config file in install target of Makefile. We leave it to the packaging.
* Sessions are no longer cleaned up when another connection replaced the session and a stale connection gets disconnected after that.
-- Simon Eisenmann <simon@struktur.de> Wed, 11 Feb 2015 11:16:47 +0100
spreed-webrtc-server (0.23.4) precise; urgency=low
## 0.23.4
* Cleanup of README.
* Fixed a problem where videos were not sized correctly.
@ -226,9 +279,8 @@ spreed-webrtc-server (0.23.4) precise; urgency=low @@ -226,9 +279,8 @@ spreed-webrtc-server (0.23.4) precise; urgency=low
* Updated require.js and r.js to 2.1.5.
* Fixed room reset when default room is disabled.
-- Simon Eisenmann <simon@struktur.de> Mon, 02 Feb 2015 17:18:53 +0100
spreed-webrtc-server (0.23.3) precise; urgency=low
## 0.23.3
* Improved room bar room change and leave buttons.
* Never hide room bar completely.
@ -245,22 +297,19 @@ spreed-webrtc-server (0.23.3) precise; urgency=low @@ -245,22 +297,19 @@ spreed-webrtc-server (0.23.3) precise; urgency=low
* Avoid showing settings automatically when not connected or still in authorizing phase.
* Added some missing CSS classes to allow easier UI mods.
-- Simon Eisenmann <simon@struktur.de> Fri, 19 Dec 2014 17:15:10 +0100
spreed-webrtc-server (0.23.2) precise; urgency=low
## 0.23.2
* Do not build combined Javascript in strict mode to avoid compatibility issues.
* Do not build combined Javascript in strict mode to avoid compatibility issues.
-- Simon Eisenmann <simon@struktur.de> Wed, 10 Dec 2014 17:18:17 +0100
spreed-webrtc-server (0.23.1) precise; urgency=low
## 0.23.1
* Fixed strict mode on release compile.
* Fixed prefix support of make install.
-- Simon Eisenmann <simon@struktur.de> Wed, 10 Dec 2014 14:03:15 +0100
spreed-webrtc-server (0.23.0) precise; urgency=low
## 0.23.0
* Added support for renegotation in web client (disabled).
* Rooms were refactored to be able to confirm joins.
@ -286,9 +335,8 @@ spreed-webrtc-server (0.23.0) precise; urgency=low @@ -286,9 +335,8 @@ spreed-webrtc-server (0.23.0) precise; urgency=low
* Missed call toast now always is shown.
* Improved toast notification styles.
-- Simon Eisenmann <simon@struktur.de> Tue, 09 Dec 2014 15:45:52 +0100
spreed-webrtc-server (0.22.8) precise; urgency=low
## 0.22.8
* Removed opacity transition from chat pane to avoid compositing issues.
* Fixed timeout of usermedia test.
@ -297,9 +345,8 @@ spreed-webrtc-server (0.22.8) precise; urgency=low @@ -297,9 +345,8 @@ spreed-webrtc-server (0.22.8) precise; urgency=low
* Make sure to stop stream after testing usermedia.
* Several small UI alignment issues.
-- Simon Eisenmann <simon@struktur.de> Tue, 11 Nov 2014 18:37:40 +0100
spreed-webrtc-server (0.22.7) precise; urgency=low
## 0.22.7
* Fixed a typo in getStreamById api.
* Roombar visibility is now controlled by layout.
@ -309,9 +356,8 @@ spreed-webrtc-server (0.22.7) precise; urgency=low @@ -309,9 +356,8 @@ spreed-webrtc-server (0.22.7) precise; urgency=low
* Fixed an issue where incoming chat messages failed when getting called from the sender at the same time.
* No longer use dpkg-parsechangelog on configure.
-- Simon Eisenmann <simon@struktur.de> Mon, 03 Nov 2014 11:02:53 +0100
spreed-webrtc-server (0.22.6) precise; urgency=low
## 0.22.6
* Added missing gear to remove streams from peer connections.
* FireFox no longer shows remove videos multiple times.
@ -319,19 +365,17 @@ spreed-webrtc-server (0.22.6) precise; urgency=low @@ -319,19 +365,17 @@ spreed-webrtc-server (0.22.6) precise; urgency=low
* Added a bunch of experimental audio and video settings (disabled by default).
* Added an option to automatically use same device for output as is used for input (Windows only and enabled by default).
-- Simon Eisenmann <simon@struktur.de> Thu, 23 Oct 2014 14:17:54 +0200
spreed-webrtc-server (0.22.5) precise; urgency=low
## 0.22.5
* Fixed an issue where the own video was not showing in democrazy layout.
* Own video is no longer delayed in democrazy layout.
-- Simon Eisenmann <simon@struktur.de> Fri, 26 Sep 2014 22:29:35 +0200
spreed-webrtc-server (0.22.4) precise; urgency=low
## 0.22.4
* Optimized Makefile and cleaned up building.
* WebODF was updated to 0.5.4.
* WebODF was updated to ## 0.5.4.
* Video layout 'classroom' has been added.
* Video layout 'smally' is now using black background.
* Several smaller layout improvements.
@ -341,9 +385,8 @@ spreed-webrtc-server (0.22.4) precise; urgency=low @@ -341,9 +385,8 @@ spreed-webrtc-server (0.22.4) precise; urgency=low
* Video layout 'onepeople' is now selectable als "Large view".
* The own audio level indicator is now visible again.
-- Simon Eisenmann <simon@struktur.de> Fri, 26 Sep 2014 16:06:23 +0200
spreed-webrtc-server (0.22.3) precise; urgency=low
## 0.22.3
* Enable 1080p capturing for Chrome 38+.
* Added option to use 8 FPS video capturing.
@ -355,23 +398,20 @@ spreed-webrtc-server (0.22.3) precise; urgency=low @@ -355,23 +398,20 @@ spreed-webrtc-server (0.22.3) precise; urgency=low
* Fixed fast reenable of local video (added timeout).
* Fixed issue where a failed peer connection did hangup the whole conference.
* Added video layout self portrait.
* Fixed call state resurrection when there was a heartbeat timeout.
* Fixed call state resurrection when there was a heartbeat timeout.
-- Simon Eisenmann <simon@struktur.de> Fri, 19 Sep 2014 11:04:42 +0200
spreed-webrtc-server (0.22.2) precise; urgency=low
## 0.22.2
* Fixed room join after reconnect.
-- Simon Eisenmann <simon@struktur.de> Tue, 09 Sep 2014 11:00:08 +0200
spreed-webrtc-server (0.22.1) precise; urgency=low
## 0.22.1
* Fixed load of local stored date when not logged in.
* Fixed load of local stored date when not logged in.
-- Simon Eisenmann <simon@struktur.de> Fri, 05 Sep 2014 17:42:48 +0200
spreed-webrtc-server (0.22.0) precise; urgency=low
## 0.22.0
* WebODF was updated to 0.5.2.
* Multiple bugfixes and improvements to YouTube player.
@ -380,7 +420,7 @@ spreed-webrtc-server (0.22.0) precise; urgency=low @@ -380,7 +420,7 @@ spreed-webrtc-server (0.22.0) precise; urgency=low
* Added support for promises during initialization code.
* Added support for plugin provided translations.
* Stream lined reconnects.
* Improved status update performance and avoid to do
* Improved status update performance and avoid to do
several during connect/authentication phase.
* Increased timeout to wait for remote video.
* Screen sharing extension waiter timeout fixed.
@ -404,9 +444,8 @@ spreed-webrtc-server (0.22.0) precise; urgency=low @@ -404,9 +444,8 @@ spreed-webrtc-server (0.22.0) precise; urgency=low
* Fixed scaling of contact images.
* Fixed chat room resume when it was previously deleted.
-- Simon Eisenmann <simon@struktur.de> Fri, 05 Sep 2014 16:50:52 +0200
spreed-webrtc-server (0.21.0) precise; urgency=low
## 0.21.0
* The language is now available in appData service.
* Implemented YouTube video sharing.
@ -424,9 +463,8 @@ spreed-webrtc-server (0.21.0) precise; urgency=low @@ -424,9 +463,8 @@ spreed-webrtc-server (0.21.0) precise; urgency=low
* Contacts can now be removed in contact manager.
* Various other bug fixes.
-- Simon Eisenmann <simon@struktur.de> Fri, 25 Jul 2014 17:53:34 +0200
spreed-webrtc-server (0.20.0) precise; urgency=low
## 0.20.0
* Added presentation mode.
* Added geolocation sharing in chat.
@ -438,17 +476,15 @@ spreed-webrtc-server (0.20.0) precise; urgency=low @@ -438,17 +476,15 @@ spreed-webrtc-server (0.20.0) precise; urgency=low
* Added ES5 detection on startup.
* Implemented a contact manager.
-- Simon Eisenmann <longsleep@redemption.intranet.struktur.de> Fri, 11 Jul 2014 19:12:00 +0200
spreed-webrtc-server (0.19.1) precise; urgency=low
## 0.19.1
* Added Dockerfile.
* Updates to compile time dependencies.
* Session data no longer overwrites contact data.
-- Simon Eisenmann <simon@struktur.de> Mon, 30 Jun 2014 19:18:17 +0200
spreed-webrtc-server (0.19.0) precise; urgency=low
## 0.19.0
* Implemented authenticated sessions.
* Implemented contacts.
@ -466,25 +502,22 @@ spreed-webrtc-server (0.19.0) precise; urgency=low @@ -466,25 +502,22 @@ spreed-webrtc-server (0.19.0) precise; urgency=low
* Multiple bug fixes.
* Improved build system autoconf detections.
-- Simon Eisenmann <simon@struktur.de> Mon, 23 Jun 2014 15:44:30 +0200
spreed-webrtc-server (0.18.1) precise; urgency=low
## 0.18.1
* Added autoconf/automake support.
* Build SCSS compressed in release mode.
* Build SCSS compressed in release mode.
-- Simon Eisenmann <simon@struktur.de> Tue, 27 May 2014 11:16:22 +0200
spreed-webrtc-server (0.18.0) precise; urgency=low
## 0.18.0
* The project is now named Spreed WebRTC. All reference to the old
name Spreed Speak Freely have been replaced.
* Cleanup of Javascript code to match coding guide lines.
* Added various new targets to make to check javascript and scss code.
* Added various new targets to make to check javascript and scss code.
-- Simon Eisenmann <simon@struktur.de> Fri, 23 May 2014 10:46:51 +0200
spreed-webrtc-server (0.17.5) precise; urgency=low
## 0.17.5
* Implemented server side support for user authentication and authorization.
* Added an REST API end point (see docs).
@ -504,9 +537,8 @@ spreed-webrtc-server (0.17.5) precise; urgency=low @@ -504,9 +537,8 @@ spreed-webrtc-server (0.17.5) precise; urgency=low
* Added support to specify the default language by URL parameter (?lang=en).
* Added support for .webp images as buddy images.
-- Simon Eisenmann <simon@struktur.de> Thu, 22 May 2014 17:49:16 +0200
spreed-webrtc-server (0.17.4) precise; urgency=low
## 0.17.4
* Updated Japanese translation.
* Allow Makefile variables CONFIG_FILE and CONFIG_PATH.
@ -522,9 +554,8 @@ spreed-webrtc-server (0.17.4) precise; urgency=low @@ -522,9 +554,8 @@ spreed-webrtc-server (0.17.4) precise; urgency=low
* Use new websocket.Upgraded API.
* No longer hang up on reload when not confirmed.
-- Simon Eisenmann <simon@struktur.de> Thu, 24 Apr 2014 17:59:05 +0200
spreed-webrtc-server (0.17.3) precise; urgency=low
## 0.17.3
* Buddy images are now loaded with seperate URL calls.
* Updated Korean (ko) language.
@ -537,9 +568,8 @@ spreed-webrtc-server (0.17.3) precise; urgency=low @@ -537,9 +568,8 @@ spreed-webrtc-server (0.17.3) precise; urgency=low
* Refactored video layout renderer in seperate service.
* Implemented alternaitve conference view (not enabled yet).
-- Simon Eisenmann <simon@struktur.de> Wed, 16 Apr 2014 17:41:13 +0200
spreed-webrtc-server (0.17.2) precise; urgency=low
## 0.17.2
* Fixed timeouts when there was a disconnect.
* Use sleepy as submodule from external source.
@ -560,9 +590,8 @@ spreed-webrtc-server (0.17.2) precise; urgency=low @@ -560,9 +590,8 @@ spreed-webrtc-server (0.17.2) precise; urgency=low
* Fixed Javascript code injection with room names.
* Show current room name in title.
-- Simon Eisenmann <simon@struktur.de> Fri, 11 Apr 2014 19:42:10 +0200
spreed-webrtc-server (0.17.1) precise; urgency=low
## 0.17.1
* Added translations for Korean and Chinese.
* Multiple updates to 3rd party js libraries.
@ -570,9 +599,8 @@ spreed-webrtc-server (0.17.1) precise; urgency=low @@ -570,9 +599,8 @@ spreed-webrtc-server (0.17.1) precise; urgency=low
* Bootstrap update to 3.1.1.
* No longer disconnect ongoing calls on websocket disconnect.
-- Simon Eisenmann <simon@struktur.de> Fri, 04 Apr 2014 18:46:56 +0200
spreed-webrtc-server (0.17.0) precise; urgency=low
## 0.17.0
* TURN user names now use expiration time stamp. This fixes compatibility
with latest TURN REST specification and requires a reasonably recent
@ -586,17 +614,15 @@ spreed-webrtc-server (0.17.0) precise; urgency=low @@ -586,17 +614,15 @@ spreed-webrtc-server (0.17.0) precise; urgency=low
* Use strong random number generator.
* Support configuring pprof HTTP server.
-- Simon Eisenmann <simon@struktur.de> Fri, 28 Mar 2014 16:48:46 +0100
spreed-webrtc-server (0.16.1) precise; urgency=low
## 0.16.1
* Implemented chat session control UI.
* Layout controller refactorization.
* Chat UI bugfixes.
-- Simon Eisenmann <simon@struktur.de> Fri, 21 Mar 2014 11:46:10 +0100
spreed-webrtc-server (0.16.0) precise; urgency=low
## 0.16.0
* Chat UI improvements.
* Screen sharing is now a scroll pane and no longer scaled down.
@ -609,11 +635,9 @@ spreed-webrtc-server (0.16.0) precise; urgency=low @@ -609,11 +635,9 @@ spreed-webrtc-server (0.16.0) precise; urgency=low
numer of cpus for GOMAXPROCS per default.
* Added server helper for stats and profiling.
-- Simon Eisenmann <simon@struktur.de> Mon, 17 Mar 2014 18:35:08 +0100
spreed-webrtc-server (0.15.0) precise; urgency=low
## 0.15.0
* Initial public release.
-- Simon Eisenmann <simon@struktur.de> Thu, 13 Feb 2014 16:14:05 +0100

4
ChangeLog

@ -1 +1,3 @@ @@ -1 +1,3 @@
See debian/changelog or https://github.com/strukturag/spreed-webrtc for further information.
See CHANGELOG.md or https://github.com/strukturag/spreed-webrtc for further information.
The changes are generated with git like `git log v0.24.12..HEAD --no-merges --format=" * %s"` and then manually formatted and added to CHANGELOG.md.

11
Godeps

@ -1,11 +0,0 @@ @@ -1,11 +0,0 @@
github.com/gorilla/context 215affda49addc4c8ef7e2534915df2c8c35c6cd
github.com/gorilla/mux ba336c9cfb43552c90de6cb2ceedd3271c747558
github.com/gorilla/securecookie aeade84400a85c6875264ae51c7a56ecdcb61751
github.com/gorilla/websocket 6eb6ad425a89d9da7a5549bc6da8f79ba5c17844
github.com/longsleep/pkac 0.0.1
github.com/satori/go.uuid afe1e2ddf0f05b7c29d388a3f8e76cb15c2231ca
github.com/strukturag/goacceptlanguageparser goacceptlanguageparser_v100
github.com/strukturag/httputils httputils_v012
github.com/strukturag/phoenix phoenix_v0133
github.com/strukturag/sloth v0.9.2
github.com/dlintw/goconf dcc070983490608a14480e3bf943bad464785df5

79
Makefile.am

@ -22,11 +22,14 @@ AUTOMAKE_OPTIONS = -Wno-portability @@ -22,11 +22,14 @@ AUTOMAKE_OPTIONS = -Wno-portability
ACLOCAL_AMFLAGS = -I m4
EXENAME := spreed-webrtc-server
GOPKG := github.com/strukturag/spreed-webrtc
GOPATH := "$(CURDIR)/vendor:$(CURDIR)"
CONFIG_FILE ?= spreed-webrtc-server.conf
CONFIG_PATH ?= /etc
GOBUILDFLAGS ?=
GOTESTFLAGS ?=
SYSTEM_GOPATH = /usr/share/gocode/src/
BIN ?= @prefix@/sbin
SHARE ?= @prefix@/share/spreed-webrtc-server
@ -47,24 +50,26 @@ build: get binary assets @@ -47,24 +50,26 @@ build: get binary assets
gopath:
@echo GOPATH=$(GOPATH)
if READONLY_VENDOR_GOPATH
export GOPATH = $(DIST):$(CURDIR)
get: $(DIST)
ln -sf $(VENDOR_GOPATH)/src -t $(DIST)
else
export GOPATH = $(VENDOR_GOPATH):$(CURDIR)
get:
endif
$(GO) get app/...
goget:
if [ -z "$(DEB_BUILDING)" ]; then GOPATH=$(GOPATH) go get github.com/rogpeppe/godeps; fi
if [ -z "$(DEB_BUILDING)" ]; then GOPATH=$(GOPATH) $(CURDIR)/vendor/bin/godeps -u dependencies.tsv; fi
mkdir -p $(shell dirname "$(CURDIR)/vendor/src/$(GOPKG)")
rm -f $(CURDIR)/vendor/src/$(GOPKG)
ln -sfn $(PWD) $(CURDIR)/vendor/src/$(GOPKG)
getupdate: vendorclean get
get: goget
gpm:
@if [ "$(GPM)" = "" ]; then echo "Command 'gpm' not found"; exit 1; fi
$(GPM) install
gogetupdate: govendorclean goget
dependencies.tsv:
set -e ;\
TMP=$$(mktemp -d) ;\
cp -r $(CURDIR)/vendor $$TMP ;\
GOPATH=$$TMP/vendor:$(CURDIR) $(CURDIR)/vendor/bin/godeps $(GOPKG)/src/app/spreed-webrtc-server ./go/... > $(CURDIR)/dependencies.tsv ;\
rm -rf $$TMP
binary:
$(GO) build $(GOBUILDFLAGS) -o bin/$(EXENAME) -ldflags '$(LDFLAGS)' app/$(EXENAME)
GOPATH=$(GOPATH) $(GO) build $(GOBUILDFLAGS) -o bin/$(EXENAME) -ldflags '$(INTERNALLDFLAGS)' app/$(EXENAME)
binaryrace: GOBUILDFLAGS := $(GOBUILDFLAGS) -race
binaryrace: binary
@ -72,16 +77,23 @@ binaryrace: binary @@ -72,16 +77,23 @@ binaryrace: binary
binaryall: GOBUILDFLAGS := $(GOBUILDFLAGS) -a
binaryall: binary
fmt:
$(GO) fmt app/...
gofmt:
GOPATH=$(GOPATH) $(GO) fmt app/... ./go/...
fmt: gofmt
test:
GOPATH=$(GOPATH) $(GO) test -v $(GOTESTFLAGS) app/... ./go/...
dist_gopath: $(DIST_SRC)
find $(SYSTEM_GOPATH) -mindepth 1 -maxdepth 1 -type d \
-exec ln -sf {} $(DIST_SRC) \;
test: get
$(GO) test $(GOTESTFLAGS) app/...
assets: javascript fonts
releaseassets: RJSFLAGS = generateSourceMaps=false preserveLicenseComments=true
releaseassets: assets
release-assets: RJSFLAGS = generateSourceMaps=false preserveLicenseComments=true
release-assets: assets
fonts:
$(MKDIR_P) $(CURDIR)/static/fonts
@ -119,14 +131,20 @@ extract-i18n: @@ -119,14 +131,20 @@ extract-i18n:
update-i18n:
cd $(CURDIR)/src/i18n && $(MAKE) update
release: LDFLAGS = -X main.version $(PACKAGE_VERSION) -X main.defaultConfig $(CONFIG_PATH)/$(CONFIG_FILE)
release: OUTPUT = $(DIST_BIN)
release: $(DIST_BIN) binary releaseassets
release-binary: GOPATH = "$(DIST):$(CURDIR)/vendor:$(CURDIR)"
release-binary: INTERNALLDFLAGS = -X main.version $(PACKAGE_VERSION) -X main.defaultConfig $(CONFIG_PATH)/$(CONFIG_FILE)
release-binary: OUTPUT = $(DIST_BIN)
release-binary: dist_gopath $(DIST_BIN) binary
release: release-binary release-assets
install:
install-binary:
@echo "Installing binaries to: $(DESTDIR)$(BIN)"
@echo "Installing static resources to: $(DESTDIR)$(SHARE)"
$(INSTALL) -d $(DESTDIR)$(BIN)
$(INSTALL) bin/$(EXENAME) $(DESTDIR)$(BIN)
install-assets:
@echo "Installing static resources to: $(DESTDIR)$(SHARE)"
$(INSTALL) -d $(DESTDIR)$(SHARE)/www/html
$(INSTALL) -d $(DESTDIR)$(SHARE)/www/html/sandboxes
$(INSTALL) -d $(DESTDIR)$(SHARE)/www/static
@ -137,7 +155,6 @@ install: @@ -137,7 +155,6 @@ install:
$(INSTALL) -d $(DESTDIR)$(SHARE)/www/static/css
$(INSTALL) -d $(DESTDIR)$(SHARE)/www/static/js/libs/pdf
$(INSTALL) -d $(DESTDIR)$(SHARE)/www/static/js/sandboxes
$(INSTALL) bin/$(EXENAME) $(DESTDIR)$(BIN)
$(INSTALL) html/*.html $(DESTDIR)$(SHARE)/www/html
$(INSTALL) html/sandboxes/*.html $(DESTDIR)$(SHARE)/www/html/sandboxes
$(INSTALL) static/img/* $(DESTDIR)$(SHARE)/www/static/img
@ -151,6 +168,8 @@ install: @@ -151,6 +168,8 @@ install:
$(INSTALL) -D static/js/libs/webodf.js $(DESTDIR)$(SHARE)/www/static/js/libs/webodf.js
$(INSTALL) $(OUTPUT_JS)/sandboxes/*.js $(DESTDIR)$(SHARE)/www/static/js/sandboxes
install: install-binary install-assets
clean:
$(GO) clean -i -r app/... 2>/dev/null || true
rm -rf $(CURDIR)/static/fonts
@ -160,13 +179,13 @@ clean: @@ -160,13 +179,13 @@ clean:
distclean: clean
rm -rf $(DIST)
vendorclean:
govendorclean:
rm -rf vendor/*
pristine: distclean vendorclean
pristine: distclean govendorclean
rm -f server.conf
$(DIST):
$(DIST_SRC):
$(MKDIR_P) $@
$(DIST_BIN):
@ -181,4 +200,4 @@ tarball: distclean release install @@ -181,4 +200,4 @@ tarball: distclean release install
cp server.conf.in $(TARPATH)/loader
tar czf $(DIST)/$(PACKAGE_NAME)-$(PACKAGE_VERSION)_$(BUILD_OS)_$(BUILD_ARCH).tar.gz -C $(DIST) $(PACKAGE_NAME)-$(PACKAGE_VERSION)
.PHONY: clean distclean vendorclean pristine get getupdate build javascript fonts styles release releasetest dist_gopath install gopath binary binaryrace binaryall tarball assets
.PHONY: clean distclean govendorclean pristine goget gogetupdate build javascript fonts styles release release-binary dist_gopath install install-binary install-assets gopath binary binaryrace binaryall tarball assets dependencies.tsv

2
README.md

@ -9,7 +9,7 @@ The latest source of Spreed WebRTC can be found on [GitHub](https://github.com/s @@ -9,7 +9,7 @@ The latest source of Spreed WebRTC can be found on [GitHub](https://github.com/s
## Build prerequisites
- [Go](http://golang.org) >= 1.1.0
- [Go](http://golang.org) >= 1.3.0
- [NodeJS](http://nodejs.org/) >= 0.6.0
- [autoconf](http://www.gnu.org/software/autoconf/)
- [automake](http://www.gnu.org/software/automake/)

4
configure.ac

@ -33,7 +33,7 @@ AC_SUBST(I18N_COPYRIGHT) @@ -33,7 +33,7 @@ AC_SUBST(I18N_COPYRIGHT)
AC_SUBST(I18N_BUG_ADDRESS)
# minimum required versions
GO_VERSION_MIN=1.1
GO_VERSION_MIN=1.3
NODEJS_VERSION_MIN=0.6.0
NODEJS_VERSION_STYLES_MIN=0.10.0
SASS_VERSION_MIN=3.3.0
@ -202,7 +202,7 @@ else @@ -202,7 +202,7 @@ else
fi
AC_SUBST(NODEJS_SUPPORT_PO2JSON)
VERSION=`head -n1 $PWD/debian/changelog | $AWK -F'[[()]]' '{print $2}'`
VERSION=`head -n1 $PWD/CHANGELOG.md | $AWK '{print $2}'`
PACKAGE_VERSION="$VERSION"
PACKAGE_STRING="$PACKAGE_NAME $PACKAGE_VERSION"
AC_DEFINE_UNQUOTED(VERSION, ["$VERSION"], [Version number of package])

2
debian/gbp.conf vendored

@ -1,2 +0,0 @@ @@ -1,2 +0,0 @@
[DEFAULT]
debian-tag = v%(version)s

12
dependencies.tsv

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
github.com/dlintw/goconf git dcc070983490608a14480e3bf943bad464785df5 2012-02-28T08:26:10Z
github.com/gorilla/context git 215affda49addc4c8ef7e2534915df2c8c35c6cd 2014-12-17T16:02:51Z
github.com/gorilla/mux git ba336c9cfb43552c90de6cb2ceedd3271c747558 2015-07-17T15:03:03Z
github.com/gorilla/securecookie git aeade84400a85c6875264ae51c7a56ecdcb61751 2015-07-16T23:32:44Z
github.com/gorilla/websocket git 6eb6ad425a89d9da7a5549bc6da8f79ba5c17844 2015-07-14T14:06:27Z
github.com/longsleep/pkac git 68bf8859f58dd84332ee41c07eba357fb3818ba3 2014-05-01T18:13:13Z
github.com/nats-io/nats git 355b5b97e0842dc94f1106729aa88e33e06317ca 2015-12-09T21:13:14Z
github.com/satori/go.uuid git afe1e2ddf0f05b7c29d388a3f8e76cb15c2231ca 2015-06-15T02:45:37Z
github.com/strukturag/goacceptlanguageparser git 68066e68c2940059aadc6e19661610cf428b6647 2014-02-13T13:31:23Z
github.com/strukturag/httputils git afbf05c71ac03ee7989c96d033a9571ba4ded468 2014-07-02T01:35:33Z
github.com/strukturag/phoenix git c3429c4e93588d848606263a7f96f91c90e43178 2016-03-02T12:52:52Z
github.com/strukturag/sloth git 74a8bcf67368de59baafe5d3e17aee9875564cfc 2015-04-22T08:59:42Z
1 github.com/dlintw/goconf git dcc070983490608a14480e3bf943bad464785df5 2012-02-28T08:26:10Z
2 github.com/gorilla/context git 215affda49addc4c8ef7e2534915df2c8c35c6cd 2014-12-17T16:02:51Z
3 github.com/gorilla/mux git ba336c9cfb43552c90de6cb2ceedd3271c747558 2015-07-17T15:03:03Z
4 github.com/gorilla/securecookie git aeade84400a85c6875264ae51c7a56ecdcb61751 2015-07-16T23:32:44Z
5 github.com/gorilla/websocket git 6eb6ad425a89d9da7a5549bc6da8f79ba5c17844 2015-07-14T14:06:27Z
6 github.com/longsleep/pkac git 68bf8859f58dd84332ee41c07eba357fb3818ba3 2014-05-01T18:13:13Z
7 github.com/nats-io/nats git 355b5b97e0842dc94f1106729aa88e33e06317ca 2015-12-09T21:13:14Z
8 github.com/satori/go.uuid git afe1e2ddf0f05b7c29d388a3f8e76cb15c2231ca 2015-06-15T02:45:37Z
9 github.com/strukturag/goacceptlanguageparser git 68066e68c2940059aadc6e19661610cf428b6647 2014-02-13T13:31:23Z
10 github.com/strukturag/httputils git afbf05c71ac03ee7989c96d033a9571ba4ded468 2014-07-02T01:35:33Z
11 github.com/strukturag/phoenix git c3429c4e93588d848606263a7f96f91c90e43178 2016-03-02T12:52:52Z
12 github.com/strukturag/sloth git 74a8bcf67368de59baafe5d3e17aee9875564cfc 2015-04-22T08:59:42Z

20
doc/REST-API.txt

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
Spreed WebRTC REST API v1.0.0
===============================================
(c)2014 struktur AG
(c)2016 struktur AG
The server provides a REST api end point to provide functionality outside the
the channeling API or without a established web socket connection.
@ -13,12 +13,26 @@ API or there was a problem while JSON encoding. @@ -13,12 +13,26 @@ API or there was a problem while JSON encoding.
Some end points of this API require a existing session from the channeling
API, incuding the private secret session ID. To get these it is sufficient
to connect to the channeling API with a websocket connection. The server
sends the session ID (Id) and secure session ID (Sid) within the Self
sends the session ID (Id) and secure session ID (Sid) within the Self
document after connection was established.
Available end points with request methods and content-type:
/.well-known/spreed-configuration
The well-known end points can be used to discover the base addresses for
other end points. Spreed WebRTC provides this for compatibility with
larger setups so clients using this can find the Spreed WebRTC end point.
GET application/x-www-form-urlencoded
No parameters.
Response 200:
{
"spreed-webrtc_endpoint": "http://localhost:8093"
}
/api/v1/config
The config end points returns the server configuration. As it is available
@ -178,4 +192,4 @@ https://github.com/strukturag/spreed-webrtc @@ -178,4 +192,4 @@ https://github.com/strukturag/spreed-webrtc
For questions, contact mailto:opensource@struktur.de.
(c)2014 struktur AG
(c)2016 struktur AG

4
src/app/spreed-webrtc-server/buffercache.go → go/buffercache/buffercache.go vendored

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package buffercache
import (
"bytes"
@ -161,7 +161,7 @@ func (cache *bufferCache) Wrap(data []byte) Buffer { @@ -161,7 +161,7 @@ func (cache *bufferCache) Wrap(data []byte) Buffer {
return &directBuffer{refcnt: 1, cache: cache, buf: bytes.NewBuffer(data)}
}
func readAll(dest Buffer, r io.Reader) error {
func ReadAll(dest Buffer, r io.Reader) error {
var err error
defer func() {
e := recover()

49
go/channelling/api.go

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
type ChannellingAPI interface {
OnConnect(*Client, *Session) (interface{}, error)
OnDisconnect(*Client, *Session)
OnIncoming(Sender, *Session, *DataIncoming) (interface{}, error)
}
type ChannellingAPIConsumer interface {
SetChannellingAPI(ChannellingAPI)
GetChannellingAPI() ChannellingAPI
}
type channellingAPIConsumer struct {
ChannellingAPI ChannellingAPI
}
func NewChannellingAPIConsumer() ChannellingAPIConsumer {
return &channellingAPIConsumer{}
}
func (c *channellingAPIConsumer) SetChannellingAPI(api ChannellingAPI) {
c.ChannellingAPI = api
}
func (c *channellingAPIConsumer) GetChannellingAPI() ChannellingAPI {
return c.ChannellingAPI
}

196
go/channelling/api/api.go

@ -0,0 +1,196 @@ @@ -0,0 +1,196 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package api
import (
"log"
"github.com/strukturag/spreed-webrtc/go/channelling"
)
const (
maxConferenceSize = 100
apiVersion = 1.4 // Keep this in sync with CHANNELING-API docs.Hand
)
type channellingAPI struct {
RoomStatusManager channelling.RoomStatusManager
SessionEncoder channelling.SessionEncoder
SessionManager channelling.SessionManager
StatsCounter channelling.StatsCounter
ContactManager channelling.ContactManager
TurnDataCreator channelling.TurnDataCreator
Unicaster channelling.Unicaster
BusManager channelling.BusManager
PipelineManager channelling.PipelineManager
config *channelling.Config
}
// New creates and initializes a new ChannellingAPI using
// various other services for initialization. It is intended to handle
// incoming and outgoing channeling API events from clients.
func New(config *channelling.Config,
roomStatus channelling.RoomStatusManager,
sessionEncoder channelling.SessionEncoder,
sessionManager channelling.SessionManager,
statsCounter channelling.StatsCounter,
contactManager channelling.ContactManager,
turnDataCreator channelling.TurnDataCreator,
unicaster channelling.Unicaster,
busManager channelling.BusManager,
pipelineManager channelling.PipelineManager) channelling.ChannellingAPI {
return &channellingAPI{
roomStatus,
sessionEncoder,
sessionManager,
statsCounter,
contactManager,
turnDataCreator,
unicaster,
busManager,
pipelineManager,
config,
}
}
func (api *channellingAPI) OnConnect(client *channelling.Client, session *channelling.Session) (interface{}, error) {
api.Unicaster.OnConnect(client, session)
self, err := api.HandleSelf(session)
if err == nil {
api.BusManager.Trigger(channelling.BusManagerConnect, session.Id, "", nil, nil)
}
return self, err
}
func (api *channellingAPI) OnDisconnect(client *channelling.Client, session *channelling.Session) {
api.Unicaster.OnDisconnect(client, session)
api.BusManager.Trigger(channelling.BusManagerDisconnect, session.Id, "", nil, nil)
}
func (api *channellingAPI) OnIncoming(sender channelling.Sender, session *channelling.Session, msg *channelling.DataIncoming) (interface{}, error) {
var pipeline *channelling.Pipeline
switch msg.Type {
case "Self":
return api.HandleSelf(session)
case "Hello":
if msg.Hello == nil {
return nil, channelling.NewDataError("bad_request", "message did not contain Hello")
}
return api.HandleHello(session, msg.Hello, sender)
case "Offer":
if msg.Offer == nil || msg.Offer.Offer == nil {
log.Println("Received invalid offer message.", msg)
break
}
if _, ok := msg.Offer.Offer["_token"]; !ok {
pipeline = api.PipelineManager.GetPipeline(channelling.PipelineNamespaceCall, sender, session, msg.Offer.To)
// Trigger offer event when offer has no token, so this is
// not triggered for peerxfer and peerscreenshare offers.
api.BusManager.Trigger(channelling.BusManagerOffer, session.Id, msg.Offer.To, nil, pipeline)
}
session.Unicast(msg.Offer.To, msg.Offer, pipeline)
case "Candidate":
if msg.Candidate == nil || msg.Candidate.Candidate == nil {
log.Println("Received invalid candidate message.", msg)
break
}
pipeline = api.PipelineManager.GetPipeline(channelling.PipelineNamespaceCall, sender, session, msg.Candidate.To)
session.Unicast(msg.Candidate.To, msg.Candidate, pipeline)
case "Answer":
if msg.Answer == nil || msg.Answer.Answer == nil {
log.Println("Received invalid answer message.", msg)
break
}
if _, ok := msg.Answer.Answer["_token"]; !ok {
pipeline = api.PipelineManager.GetPipeline(channelling.PipelineNamespaceCall, sender, session, msg.Answer.To)
// Trigger answer event when answer has no token. so this is
// not triggered for peerxfer and peerscreenshare answers.
api.BusManager.Trigger(channelling.BusManagerAnswer, session.Id, msg.Answer.To, nil, pipeline)
}
session.Unicast(msg.Answer.To, msg.Answer, pipeline)
case "Users":
return api.HandleUsers(session)
case "Authentication":
if msg.Authentication == nil || msg.Authentication.Authentication == nil {
return nil, channelling.NewDataError("bad_request", "message did not contain Authentication")
}
return api.HandleAuthentication(session, msg.Authentication.Authentication)
case "Bye":
if msg.Bye == nil {
log.Println("Received invalid bye message.", msg)
break
}
pipeline = api.PipelineManager.GetPipeline(channelling.PipelineNamespaceCall, sender, session, msg.Bye.To)
api.BusManager.Trigger(channelling.BusManagerBye, session.Id, msg.Bye.To, nil, pipeline)
session.Unicast(msg.Bye.To, msg.Bye, pipeline)
if pipeline != nil {
pipeline.Close()
}
case "Status":
if msg.Status == nil {
log.Println("Received invalid status message.", msg)
break
}
//log.Println("Status", msg.Status)
session.Update(&channelling.SessionUpdate{Types: []string{"Status"}, Status: msg.Status.Status})
session.BroadcastStatus()
case "Chat":
if msg.Chat == nil || msg.Chat.Chat == nil {
log.Println("Received invalid chat message.", msg)
break
}
api.HandleChat(session, msg.Chat)
case "Conference":
if msg.Conference == nil {
log.Println("Received invalid conference message.", msg)
break
}
api.HandleConference(session, msg.Conference)
case "Alive":
return msg.Alive, nil
case "Sessions":
if msg.Sessions == nil || msg.Sessions.Sessions == nil {
return nil, channelling.NewDataError("bad_request", "message did not contain Sessions")
}
return api.HandleSessions(session, msg.Sessions.Sessions)
case "Room":
if msg.Room == nil {
return nil, channelling.NewDataError("bad_request", "message did not contain Room")
}
return api.HandleRoom(session, msg.Room)
default:
log.Println("OnText unhandled message type", msg.Type)
}
return nil, nil
}

98
src/app/spreed-webrtc-server/channelling_api_test.go → go/channelling/api/api_test.go

@ -19,50 +19,59 @@ @@ -19,50 +19,59 @@
*
*/
package main
package api
import (
"errors"
"fmt"
"testing"
"github.com/gorilla/securecookie"
"github.com/strukturag/spreed-webrtc/go/buffercache"
"github.com/strukturag/spreed-webrtc/go/channelling"
)
type fakeClient struct {
}
func (fake *fakeClient) Send(_ Buffer) {
func (fake *fakeClient) Index() uint64 {
return 0
}
func (fake *fakeClient) Send(_ buffercache.Buffer) {
}
type fakeRoomManager struct {
joinedRoomID string
leftRoomID string
roomUsers []*DataSession
roomUsers []*channelling.DataSession
joinedID string
joinError error
leftID string
broadcasts []interface{}
updatedRoom *DataRoom
updatedRoom *channelling.DataRoom
updateError error
}
func (fake *fakeRoomManager) RoomUsers(session *Session) []*DataSession {
func (fake *fakeRoomManager) RoomUsers(session *channelling.Session) []*channelling.DataSession {
return fake.roomUsers
}
func (fake *fakeRoomManager) JoinRoom(id, roomName, roomType string, _ *DataRoomCredentials, session *Session, sessionAuthenticated bool, _ Sender) (*DataRoom, error) {
func (fake *fakeRoomManager) JoinRoom(id, roomName, roomType string, _ *channelling.DataRoomCredentials, session *channelling.Session, sessionAuthenticated bool, _ channelling.Sender) (*channelling.DataRoom, error) {
fake.joinedID = id
return &DataRoom{Name: roomName, Type: roomType}, fake.joinError
return &channelling.DataRoom{Name: roomName, Type: roomType}, fake.joinError
}
func (fake *fakeRoomManager) LeaveRoom(roomID, sessionID string) {
fake.leftID = roomID
}
func (fake *fakeRoomManager) Broadcast(_, _ string, outgoing *DataOutgoing) {
func (fake *fakeRoomManager) Broadcast(_, _ string, outgoing *channelling.DataOutgoing) {
fake.broadcasts = append(fake.broadcasts, outgoing.Data)
}
func (fake *fakeRoomManager) UpdateRoom(_ *Session, _ *DataRoom) (*DataRoom, error) {
func (fake *fakeRoomManager) UpdateRoom(_ *channelling.Session, _ *channelling.DataRoom) (*channelling.DataRoom, error) {
return fake.updatedRoom, fake.updateError
}
@ -73,22 +82,22 @@ func (fake *fakeRoomManager) MakeRoomID(roomName, roomType string) string { @@ -73,22 +82,22 @@ func (fake *fakeRoomManager) MakeRoomID(roomName, roomType string) string {
return fmt.Sprintf("%s:%s", roomType, roomName)
}
func NewTestChannellingAPI() (ChannellingAPI, *fakeClient, *Session, *fakeRoomManager) {
func NewTestChannellingAPI() (channelling.ChannellingAPI, *fakeClient, *channelling.Session, *fakeRoomManager) {
apiConsumer := channelling.NewChannellingAPIConsumer()
client, roomManager := &fakeClient{}, &fakeRoomManager{}
session := &Session{
attestations: sessionNonces,
Broadcaster: roomManager,
RoomStatusManager: roomManager,
}
session.attestation = &SessionAttestation{s: session}
return NewChannellingAPI(nil, roomManager, nil, nil, nil, nil, nil, nil), client, session, roomManager
sessionNonces := securecookie.New(securecookie.GenerateRandomKey(64), nil)
session := channelling.NewSession(nil, nil, roomManager, roomManager, nil, sessionNonces, "", "")
busManager := channelling.NewBusManager(apiConsumer, "", false, "")
api := New(nil, roomManager, nil, nil, nil, nil, nil, nil, busManager, nil)
apiConsumer.SetChannellingAPI(api)
return api, client, session, roomManager
}
func Test_ChannellingAPI_OnIncoming_HelloMessage_JoinsTheSelectedRoom(t *testing.T) {
roomID, roomName, ua := "Room:foobar", "foobar", "unit tests"
api, client, session, roomManager := NewTestChannellingAPI()
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomName, Ua: ua}})
api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomName, Ua: ua}})
if roomManager.joinedID != roomID {
t.Errorf("Expected to have joined room %v, but got %v", roomID, roomManager.joinedID)
@ -98,7 +107,7 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_JoinsTheSelectedRoom(t *testing @@ -98,7 +107,7 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_JoinsTheSelectedRoom(t *testing
t.Fatalf("Expected 1 broadcast, but got %d", broadcastCount)
}
dataSession, ok := roomManager.broadcasts[0].(*DataSession)
dataSession, ok := roomManager.broadcasts[0].(*channelling.DataSession)
if !ok {
t.Fatal("Expected a session data broadcast")
}
@ -112,8 +121,8 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_LeavesAnyPreviouslyJoinedRooms( @@ -112,8 +121,8 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_LeavesAnyPreviouslyJoinedRooms(
roomID, roomName := "Room:foobar", "foobar"
api, client, session, roomManager := NewTestChannellingAPI()
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomName}})
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: "baz"}})
api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomName}})
api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: "baz"}})
if roomManager.leftID != roomID {
t.Errorf("Expected to have left room %v, but got %v", roomID, roomManager.leftID)
@ -123,7 +132,7 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_LeavesAnyPreviouslyJoinedRooms( @@ -123,7 +132,7 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_LeavesAnyPreviouslyJoinedRooms(
t.Fatalf("Expected 3 broadcasts, but got %d", broadcastCount)
}
dataSession, ok := roomManager.broadcasts[1].(*DataSession)
dataSession, ok := roomManager.broadcasts[1].(*channelling.DataSession)
if !ok {
t.Fatal("Expected a session data broadcast")
}
@ -137,7 +146,7 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_DoesNotJoinIfNotPermitted(t *te @@ -137,7 +146,7 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_DoesNotJoinIfNotPermitted(t *te
api, client, session, roomManager := NewTestChannellingAPI()
roomManager.joinError = errors.New("Can't enter this room")
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{}})
api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{}})
if broadcastCount := len(roomManager.broadcasts); broadcastCount != 0 {
t.Fatalf("Expected no broadcasts, but got %d", broadcastCount)
@ -147,14 +156,14 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_DoesNotJoinIfNotPermitted(t *te @@ -147,14 +156,14 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_DoesNotJoinIfNotPermitted(t *te
func Test_ChannellingAPI_OnIncoming_HelloMessage_RespondsWithAWelcome(t *testing.T) {
roomID := "a-room"
api, client, session, roomManager := NewTestChannellingAPI()
roomManager.roomUsers = []*DataSession{&DataSession{}}
roomManager.roomUsers = []*channelling.DataSession{&channelling.DataSession{}}
reply, err := api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomID}})
reply, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomID}})
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
welcome, ok := reply.(*DataWelcome)
welcome, ok := reply.(*channelling.DataWelcome)
if !ok {
t.Fatalf("Expected response %#v to be a Welcome", reply)
}
@ -174,9 +183,9 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_RespondsWithAWelcome(t *testing @@ -174,9 +183,9 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_RespondsWithAWelcome(t *testing
func Test_ChannellingAPI_OnIncoming_HelloMessage_RespondsWithAnErrorIfTheRoomCannotBeJoined(t *testing.T) {
api, client, session, roomManager := NewTestChannellingAPI()
roomManager.joinError = NewDataError("bad_join", "")
roomManager.joinError = channelling.NewDataError("bad_join", "")
_, err := api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{}})
_, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{}})
assertDataError(t, err, "bad_join")
}
@ -184,19 +193,19 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_RespondsWithAnErrorIfTheRoomCan @@ -184,19 +193,19 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_RespondsWithAnErrorIfTheRoomCan
func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpdatedRoom(t *testing.T) {
roomName := "foo"
api, client, session, roomManager := NewTestChannellingAPI()
roomManager.updatedRoom = &DataRoom{Name: "FOO"}
roomManager.updatedRoom = &channelling.DataRoom{Name: "FOO"}
_, err := api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomName}})
_, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomName}})
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
reply, err := api.OnIncoming(client, session, &DataIncoming{Type: "Room", Room: &DataRoom{Name: roomName}})
reply, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Room", Room: &channelling.DataRoom{Name: roomName}})
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
room, ok := reply.(*DataRoom)
room, ok := reply.(*channelling.DataRoom)
if !ok {
t.Fatalf("Expected response message to be a Room")
}
@ -209,7 +218,7 @@ func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpda @@ -209,7 +218,7 @@ func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpda
t.Fatalf("Expected 1 broadcasts, but got %d", broadcastCount)
}
if _, ok := roomManager.broadcasts[1].(*DataRoom); !ok {
if _, ok := roomManager.broadcasts[1].(*channelling.DataRoom); !ok {
t.Fatal("Expected a room data broadcast")
}
}
@ -217,13 +226,30 @@ func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpda @@ -217,13 +226,30 @@ func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpda
func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAnErrorIfUpdatingTheRoomFails(t *testing.T) {
roomName := "foo"
api, client, session, roomManager := NewTestChannellingAPI()
roomManager.updateError = NewDataError("a_room_error", "")
roomManager.updateError = channelling.NewDataError("a_room_error", "")
_, err := api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomName}})
_, err := api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Hello", Hello: &channelling.DataHello{Id: roomName}})
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
_, err = api.OnIncoming(client, session, &DataIncoming{Type: "Room", Room: &DataRoom{Name: roomName}})
_, err = api.OnIncoming(client, session, &channelling.DataIncoming{Type: "Room", Room: &channelling.DataRoom{Name: roomName}})
assertDataError(t, err, "a_room_error")
}
func assertDataError(t *testing.T, err error, code string) {
if err == nil {
t.Error("Expected an error, but none was returned")
return
}
dataError, ok := err.(*channelling.DataError)
if !ok {
t.Errorf("Expected error %#v to be a *DataError", err)
return
}
if code != dataError.Code {
t.Errorf("Expected error code to be %v, but was %v", code, dataError.Code)
}
}

43
go/channelling/api/handle_authentication.go

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package api
import (
"log"
"github.com/strukturag/spreed-webrtc/go/channelling"
)
func (api *channellingAPI) HandleAuthentication(session *channelling.Session, st *channelling.SessionToken) (*channelling.DataSelf, error) {
if err := api.SessionManager.Authenticate(session, st, ""); err != nil {
log.Println("Authentication failed", err, st.Userid, st.Nonce)
return nil, err
}
log.Println("Authentication success", session.Userid())
self, err := api.HandleSelf(session)
if err == nil {
session.BroadcastStatus()
}
return self, err
}

68
go/channelling/api/handle_chat.go

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package api
import (
"log"
"time"
"github.com/strukturag/spreed-webrtc/go/channelling"
)
func (api *channellingAPI) HandleChat(session *channelling.Session, chat *channelling.DataChat) {
// TODO(longsleep): Limit sent chat messages per incoming connection.
msg := chat.Chat
to := chat.To
if !msg.NoEcho {
session.Unicast(session.Id, chat, nil)
}
msg.Time = time.Now().Format(time.RFC3339)
if to == "" {
// TODO(longsleep): Check if chat broadcast is allowed.
if session.Hello {
api.StatsCounter.CountBroadcastChat()
session.Broadcast(chat)
}
} else {
if msg.Status != nil {
if msg.Status.ContactRequest != nil {
if !api.config.WithModule("contacts") {
return
}
if err := api.ContactManager.ContactrequestHandler(session, to, msg.Status.ContactRequest); err != nil {
log.Println("Ignoring invalid contact request.", err)
return
}
msg.Status.ContactRequest.Userid = session.Userid()
}
} else {
api.StatsCounter.CountUnicastChat()
}
session.Unicast(to, chat, nil)
if msg.Mid != "" {
// Send out delivery confirmation status chat message.
session.Unicast(session.Id, &channelling.DataChat{To: to, Type: "Chat", Chat: &channelling.DataChatMessage{Mid: msg.Mid, Status: &channelling.DataChatStatus{State: "sent"}}}, nil)
}
}
}

43
go/channelling/api/handle_conference.go

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package api
import (
"log"
"github.com/strukturag/spreed-webrtc/go/channelling"
)
func (api *channellingAPI) HandleConference(session *channelling.Session, conference *channelling.DataConference) {
// Check conference maximum size.
if len(conference.Conference) > maxConferenceSize {
log.Println("Refusing to create conference above limit.", len(conference.Conference))
return
}
// Send conference update to anyone.
for _, id := range conference.Conference {
if id != session.Id {
session.Unicast(id, conference, nil)
}
}
}

48
go/channelling/api/handle_hello.go

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package api
import (
"github.com/strukturag/spreed-webrtc/go/channelling"
)
func (api *channellingAPI) HandleHello(session *channelling.Session, hello *channelling.DataHello, sender channelling.Sender) (*channelling.DataWelcome, error) {
// TODO(longsleep): Filter room id and user agent.
session.Update(&channelling.SessionUpdate{Types: []string{"Ua"}, Ua: hello.Ua})
// Compatibily for old clients.
roomName := hello.Name
if roomName == "" {
roomName = hello.Id
}
room, err := session.JoinRoom(roomName, hello.Type, hello.Credentials, sender)
if err != nil {
return nil, err
}
return &channelling.DataWelcome{
Type: "Welcome",
Room: room,
Users: api.RoomStatusManager.RoomUsers(session),
}, nil
}

35
go/channelling/api/handle_room.go

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package api
import (
"github.com/strukturag/spreed-webrtc/go/channelling"
)
func (api *channellingAPI) HandleRoom(session *channelling.Session, room *channelling.DataRoom) (*channelling.DataRoom, error) {
room, err := api.RoomStatusManager.UpdateRoom(session, room)
if err == nil {
session.Broadcast(room)
}
return room, err
}

53
go/channelling/api/handle_self.go

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package api
import (
"log"
"github.com/strukturag/spreed-webrtc/go/channelling"
)
func (api *channellingAPI) HandleSelf(session *channelling.Session) (*channelling.DataSelf, error) {
token, err := api.SessionEncoder.EncodeSessionToken(session)
if err != nil {
log.Println("Error in OnRegister", err)
return nil, err
}
log.Println("Created new session token", len(token), token)
self := &channelling.DataSelf{
Type: "Self",
Id: session.Id,
Sid: session.Sid,
Userid: session.Userid(),
Suserid: api.SessionEncoder.EncodeSessionUserID(session),
Token: token,
Version: api.config.Version,
ApiVersion: apiVersion,
Turn: api.TurnDataCreator.CreateTurnData(session),
Stun: api.config.StunURIs,
}
api.BusManager.Trigger(channelling.BusManagerSession, session.Id, session.Userid(), nil, nil)
return self, nil
}

60
go/channelling/api/handle_sessions.go

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package api
import (
"github.com/strukturag/spreed-webrtc/go/channelling"
)
func (api *channellingAPI) HandleSessions(session *channelling.Session, sessions *channelling.DataSessionsRequest) (*channelling.DataSessions, error) {
switch sessions.Type {
case "contact":
if !api.config.WithModule("contacts") {
return nil, channelling.NewDataError("contacts_not_enabled", "incoming contacts session request with contacts disabled")
}
userID, err := api.ContactManager.GetContactID(session, sessions.Token)
if err != nil {
return nil, err
}
return &channelling.DataSessions{
Type: "Sessions",
Users: api.SessionManager.GetUserSessions(session, userID),
Sessions: sessions,
}, nil
case "session":
id, err := session.DecodeAttestation(sessions.Token)
if err != nil {
return nil, channelling.NewDataError("bad_attestation", err.Error())
}
session, ok := api.Unicaster.GetSession(id)
if !ok {
return nil, channelling.NewDataError("no_such_session", "cannot retrieve session")
}
return &channelling.DataSessions{
Type: "Sessions",
Users: []*channelling.DataSession{session.Data()},
Sessions: sessions,
}, nil
default:
return nil, channelling.NewDataError("bad_request", "unknown sessions request type")
}
}

36
go/channelling/api/handle_users.go

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package api
import (
"github.com/strukturag/spreed-webrtc/go/channelling"
)
func (api *channellingAPI) HandleUsers(session *channelling.Session) (sessions *channelling.DataSessions, err error) {
if session.Hello {
sessions = &channelling.DataSessions{Type: "Users", Users: api.RoomStatusManager.RoomUsers(session)}
} else {
err = channelling.NewDataError("not_in_room", "Cannot list users without a current room")
}
return
}

41
go/channelling/bus.go

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
type SessionCreateRequest struct {
Id string
Session *DataSession
Room *DataRoom
SetAsDefault bool
}
type DataSink struct {
SubjectOut string `json:subject_out"`
SubjectIn string `json:subject_in"`
}
type DataSinkOutgoing struct {
Outgoing *DataOutgoing
ToUserid string
FromUserid string
Pipe string `json:",omitempty"`
}

306
go/channelling/bus_manager.go

@ -0,0 +1,306 @@ @@ -0,0 +1,306 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
import (
"errors"
"fmt"
"log"
"sync"
"time"
"github.com/nats-io/nats"
"github.com/strukturag/spreed-webrtc/go/natsconnection"
)
const (
BusManagerStartup = "startup"
BusManagerOffer = "offer"
BusManagerAnswer = "answer"
BusManagerBye = "bye"
BusManagerConnect = "connect"
BusManagerDisconnect = "disconnect"
BusManagerSession = "session"
)
// A BusManager provides the API to interact with a bus.
type BusManager interface {
ChannellingAPIConsumer
Start()
Publish(subject string, v interface{}) error
Request(subject string, v interface{}, vPtr interface{}, timeout time.Duration) error
Trigger(name, from, payload string, data interface{}, pipeline *Pipeline) error
Subscribe(subject string, cb nats.Handler) (*nats.Subscription, error)
BindRecvChan(subject string, channel interface{}) (*nats.Subscription, error)
BindSendChan(subject string, channel interface{}) error
PrefixSubject(string) string
CreateSink(string) Sink
}
// A BusTrigger is a container to serialize trigger events
// for the bus backend.
type BusTrigger struct {
Id string
Name string
From string
Payload string `json:",omitempty"`
Data interface{} `json:",omitempty"`
Pipeline string `json:",omitempty"`
}
// BusSubjectTrigger returns the bus subject for trigger payloads.
func BusSubjectTrigger(prefix, suffix string) string {
return fmt.Sprintf("%s.%s", prefix, suffix)
}
// NewBusManager creates and initializes a new BusMager with the
// provided flags for NATS support. It is intended to connect the
// backend bus with a easy to use API to send and receive bus data.
func NewBusManager(apiConsumer ChannellingAPIConsumer, id string, useNats bool, subjectPrefix string) BusManager {
var b BusManager
var err error
if useNats {
b, err = newNatsBus(apiConsumer, id, subjectPrefix)
if err == nil {
log.Println("NATS bus connected")
} else {
log.Println("Error connecting NATS bus", err)
b = &noopBus{apiConsumer, id}
}
} else {
b = &noopBus{apiConsumer, id}
}
return b
}
type noopBus struct {
ChannellingAPIConsumer
id string
}
func (bus *noopBus) Start() {
// noop
}
func (bus *noopBus) Publish(subject string, v interface{}) error {
return nil
}
func (bus *noopBus) Request(subject string, v interface{}, vPtr interface{}, timeout time.Duration) error {
return nil
}
func (bus *noopBus) Trigger(name, from, payload string, data interface{}, pipeline *Pipeline) error {
return nil
}
func (bus *noopBus) PrefixSubject(subject string) string {
return subject
}
func (bus *noopBus) BindRecvChan(subject string, channel interface{}) (*nats.Subscription, error) {
return nil, nil
}
func (bus *noopBus) BindSendChan(subject string, channel interface{}) error {
return nil
}
func (bus *noopBus) Subscribe(subject string, cb nats.Handler) (*nats.Subscription, error) {
return nil, nil
}
func (bus *noopBus) CreateSink(id string) Sink {
return nil
}
type natsBus struct {
ChannellingAPIConsumer
id string
prefix string
ec *natsconnection.EncodedConnection
triggerQueue chan *busQueueEntry
}
func newNatsBus(apiConsumer ChannellingAPIConsumer, id, prefix string) (*natsBus, error) {
ec, err := natsconnection.EstablishJSONEncodedConnection(nil)
if err != nil {
return nil, err
}
if prefix == "" {
prefix = "channelling.trigger"
}
// Create buffered channel for outbound NATS data.
triggerQueue := make(chan *busQueueEntry, 50)
return &natsBus{apiConsumer, id, prefix, ec, triggerQueue}, nil
}
func (bus *natsBus) Start() {
// Start go routine to process outbount NATS publishing.
go chPublish(bus.ec, bus.triggerQueue)
bus.Trigger(BusManagerStartup, bus.id, "", nil, nil)
}
func (bus *natsBus) Publish(subject string, v interface{}) error {
return bus.ec.Publish(subject, v)
}
func (bus *natsBus) Request(subject string, v interface{}, vPtr interface{}, timeout time.Duration) error {
return bus.ec.Request(subject, v, vPtr, timeout)
}
func (bus *natsBus) Trigger(name, from, payload string, data interface{}, pipeline *Pipeline) (err error) {
trigger := &BusTrigger{
Id: bus.id,
Name: name,
From: from,
Payload: payload,
Data: data,
}
if pipeline != nil {
trigger.Pipeline = pipeline.GetID()
}
entry := &busQueueEntry{BusSubjectTrigger(bus.prefix, name), trigger}
select {
case bus.triggerQueue <- entry:
// sent ok
default:
log.Println("Failed to queue NATS event - queue full?")
err = errors.New("NATS trigger queue full")
}
return err
}
func (bus *natsBus) PrefixSubject(sub string) string {
return fmt.Sprintf("%s.%s", bus.prefix, sub)
}
func (bus *natsBus) Subscribe(subject string, cb nats.Handler) (*nats.Subscription, error) {
return bus.ec.Subscribe(subject, cb)
}
func (bus *natsBus) BindRecvChan(subject string, channel interface{}) (*nats.Subscription, error) {
return bus.ec.BindRecvChan(subject, channel)
}
func (bus *natsBus) BindSendChan(subject string, channel interface{}) error {
return bus.ec.BindSendChan(subject, channel)
}
func (bus *natsBus) CreateSink(id string) (sink Sink) {
sink = newNatsSink(bus, id)
return
}
type busQueueEntry struct {
subject string
data interface{}
}
func chPublish(ec *natsconnection.EncodedConnection, channel chan (*busQueueEntry)) {
for {
entry := <-channel
err := ec.Publish(entry.subject, entry.data)
if err != nil {
log.Println("Failed to publish to NATS", entry.subject, err)
}
}
}
type natsSink struct {
sync.RWMutex
id string
bm BusManager
closed bool
SubjectOut string
SubjectIn string
sub *nats.Subscription
sendQueue chan *DataSinkOutgoing
}
func newNatsSink(bm BusManager, id string) *natsSink {
sink := &natsSink{
id: id,
bm: bm,
SubjectOut: bm.PrefixSubject(fmt.Sprintf("sink.%s.out", id)),
SubjectIn: bm.PrefixSubject(fmt.Sprintf("sink.%s.in", id)),
}
sink.sendQueue = make(chan *DataSinkOutgoing, 100)
bm.BindSendChan(sink.SubjectOut, sink.sendQueue)
return sink
}
func (sink *natsSink) Write(outgoing *DataSinkOutgoing) (err error) {
if sink.Enabled() {
log.Println("Sending via NATS sink", sink.SubjectOut, outgoing)
sink.sendQueue <- outgoing
}
return err
}
func (sink *natsSink) Enabled() bool {
sink.RLock()
defer sink.RUnlock()
return sink.closed == false
}
func (sink *natsSink) Close() {
sink.Lock()
defer sink.Unlock()
if sink.sub != nil {
err := sink.sub.Unsubscribe()
if err != nil {
log.Println("Failed to unsubscribe NATS sink", err)
} else {
sink.sub = nil
}
}
sink.closed = true
}
func (sink *natsSink) Export() *DataSink {
return &DataSink{
SubjectOut: sink.SubjectOut,
SubjectIn: sink.SubjectIn,
}
}
func (sink *natsSink) BindRecvChan(channel interface{}) (*nats.Subscription, error) {
sink.Lock()
defer sink.Unlock()
if sink.sub != nil {
sink.sub.Unsubscribe()
sink.sub = nil
}
sub, err := sink.bm.BindRecvChan(sink.SubjectIn, channel)
if err != nil {
return nil, err
}
sink.sub = sub
return sub, nil
}

49
src/app/spreed-webrtc-server/client.go → go/channelling/client.go

@ -19,36 +19,35 @@ @@ -19,36 +19,35 @@
*
*/
package main
package channelling
import (
"log"
"github.com/strukturag/spreed-webrtc/go/buffercache"
)
type Sender interface {
Send(Buffer)
}
type Client interface {
Sender
Session() *Session
Index() uint64
Close()
ReplaceAndClose(Client)
Send(buffercache.Buffer)
}
type client struct {
Codec
ChannellingAPI
type Client struct {
Connection
session *Session
Codec
ChannellingAPI ChannellingAPI
session *Session
}
func NewClient(codec Codec, api ChannellingAPI, session *Session) *client {
return &client{codec, api, nil, session}
func NewClient(codec Codec, api ChannellingAPI, session *Session) *Client {
return &Client{
Codec: codec,
ChannellingAPI: api,
session: session,
}
}
func (client *client) OnConnect(conn Connection) {
func (client *Client) OnConnect(conn Connection) {
client.Connection = conn
if reply, err := client.ChannellingAPI.OnConnect(client, client.session); err == nil {
client.reply("", reply)
@ -57,38 +56,38 @@ func (client *client) OnConnect(conn Connection) { @@ -57,38 +56,38 @@ func (client *client) OnConnect(conn Connection) {
}
}
func (client *client) OnDisconnect() {
func (client *Client) OnDisconnect() {
client.session.Close()
client.ChannellingAPI.OnDisconnect(client, client.session)
}
func (client *client) OnText(b Buffer) {
incoming, err := client.DecodeIncoming(b)
func (client *Client) OnText(b buffercache.Buffer) {
incoming, err := client.Codec.DecodeIncoming(b)
if err != nil {
log.Println("OnText error while processing incoming message", err)
return
}
if reply, err := client.OnIncoming(client, client.session, incoming); err != nil {
if reply, err := client.ChannellingAPI.OnIncoming(client, client.session, incoming); err != nil {
client.reply(incoming.Iid, err)
} else if reply != nil {
client.reply(incoming.Iid, reply)
}
}
func (client *client) reply(iid string, m interface{}) {
func (client *Client) reply(iid string, m interface{}) {
outgoing := &DataOutgoing{From: client.session.Id, Iid: iid, Data: m}
if b, err := client.EncodeOutgoing(outgoing); err == nil {
client.Send(b)
if b, err := client.Codec.EncodeOutgoing(outgoing); err == nil {
client.Connection.Send(b)
b.Decref()
}
}
func (client *client) Session() *Session {
func (client *Client) Session() *Session {
return client.session
}
func (client *client) ReplaceAndClose(oldClient Client) {
func (client *Client) ReplaceAndClose(oldClient *Client) {
oldSession := oldClient.Session()
client.session.Replace(oldSession)
go func() {

26
go/channelling/clientstats.go

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
type ClientStats interface {
ClientInfo(details bool) (int, map[string]*DataSession, map[string]string)
}

20
src/app/spreed-webrtc-server/incoming_codec.go → go/channelling/codec.go

@ -19,43 +19,45 @@ @@ -19,43 +19,45 @@
*
*/
package main
package channelling
import (
"bytes"
"encoding/json"
"errors"
"log"
"github.com/strukturag/spreed-webrtc/go/buffercache"
)
type IncomingDecoder interface {
DecodeIncoming(Buffer) (*DataIncoming, error)
DecodeIncoming(buffercache.Buffer) (*DataIncoming, error)
}
type OutgoingEncoder interface {
EncodeOutgoing(*DataOutgoing) (Buffer, error)
EncodeOutgoing(*DataOutgoing) (buffercache.Buffer, error)
}
type Codec interface {
NewBuffer() Buffer
NewBuffer() buffercache.Buffer
IncomingDecoder
OutgoingEncoder
}
type incomingCodec struct {
buffers BufferCache
buffers buffercache.BufferCache
incomingLimit int
}
func NewCodec(incomingLimit int) Codec {
return &incomingCodec{NewBufferCache(1024, bytes.MinRead), incomingLimit}
return &incomingCodec{buffercache.NewBufferCache(1024, bytes.MinRead), incomingLimit}
}
func (codec incomingCodec) NewBuffer() Buffer {
func (codec incomingCodec) NewBuffer() buffercache.Buffer {
return codec.buffers.New()
}
func (codec incomingCodec) DecodeIncoming(b Buffer) (*DataIncoming, error) {
func (codec incomingCodec) DecodeIncoming(b buffercache.Buffer) (*DataIncoming, error) {
length := b.GetBuffer().Len()
if length > codec.incomingLimit {
return nil, errors.New("Incoming message size limit exceeded")
@ -64,7 +66,7 @@ func (codec incomingCodec) DecodeIncoming(b Buffer) (*DataIncoming, error) { @@ -64,7 +66,7 @@ func (codec incomingCodec) DecodeIncoming(b Buffer) (*DataIncoming, error) {
return incoming, json.Unmarshal(b.Bytes(), incoming)
}
func (codec incomingCodec) EncodeOutgoing(outgoing *DataOutgoing) (Buffer, error) {
func (codec incomingCodec) EncodeOutgoing(outgoing *DataOutgoing) (buffercache.Buffer, error) {
b := codec.NewBuffer()
if err := json.NewEncoder(b).Encode(outgoing); err != nil {
log.Println("Error while encoding JSON", err)

2
src/app/spreed-webrtc-server/common_test.go → go/channelling/common_test.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package channelling
import (
"testing"

43
go/channelling/config.go

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
package channelling
import (
"net/http"
)
type Config struct {
Title string // Title
Ver string `json:"-"` // Version (not exported to Javascript)
S string // Static URL prefix with version
B string // Base URL
Token string // Server token
Renegotiation bool // Renegotiation flag
StunURIs []string // STUN server URIs
TurnURIs []string // TURN server URIs
Tokens bool // True when we got a tokens file
Version string // Server version number
UsersEnabled bool // Flag if users are enabled
UsersAllowRegistration bool // Flag if users can register
UsersMode string // Users mode string
DefaultRoomEnabled bool // Flag if default room ("") is enabled
Plugin string // Plugin to load
AuthorizeRoomCreation bool // Whether a user account is required to create rooms
AuthorizeRoomJoin bool // Whether a user account is required to join rooms
Modules []string // List of enabled modules
ModulesTable map[string]bool `json:"-"` // Map of enabled modules
GlobalRoomID string `json:"-"` // Id of the global room (not exported to Javascript)
ContentSecurityPolicy string `json:"-"` // HTML content security policy
ContentSecurityPolicyReportOnly string `json:"-"` // HTML content security policy in report only mode
RoomTypeDefault string `json:"-"` // New rooms default to this type
}
func (config *Config) WithModule(m string) bool {
if val, ok := config.ModulesTable[m]; ok && val {
return true
}
return false
}
func (config *Config) Get(request *http.Request) (int, interface{}, http.Header) {
return 200, config, http.Header{"Content-Type": {"application/json; charset=utf-8"}}
}

27
src/app/spreed-webrtc-server/connection.go → go/channelling/connection.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package channelling
import (
"container/list"
@ -28,6 +28,8 @@ import ( @@ -28,6 +28,8 @@ import (
"sync"
"time"
"github.com/strukturag/spreed-webrtc/go/buffercache"
"github.com/gorilla/websocket"
)
@ -55,17 +57,17 @@ const ( @@ -55,17 +57,17 @@ const (
type Connection interface {
Index() uint64
Send(Buffer)
Send(buffercache.Buffer)
Close()
readPump()
writePump()
ReadPump()
WritePump()
}
type ConnectionHandler interface {
NewBuffer() Buffer
NewBuffer() buffercache.Buffer
OnConnect(Connection)
OnDisconnect()
OnText(Buffer)
OnText(buffercache.Buffer)
}
type connection struct {
@ -116,7 +118,7 @@ func (c *connection) Close() { @@ -116,7 +118,7 @@ func (c *connection) Close() {
break
}
c.queue.Remove(head)
message := head.Value.(Buffer)
message := head.Value.(buffercache.Buffer)
message.Decref()
}
c.condition.Signal()
@ -124,7 +126,7 @@ func (c *connection) Close() { @@ -124,7 +126,7 @@ func (c *connection) Close() {
}
// readPump pumps messages from the websocket connection to the hub.
func (c *connection) readPump() {
func (c *connection) ReadPump() {
c.ws.SetReadLimit(maxMessageSize)
c.ws.SetReadDeadline(time.Now().Add(pongWait))
c.ws.SetPongHandler(func(string) error {
@ -161,7 +163,7 @@ func (c *connection) readPump() { @@ -161,7 +163,7 @@ func (c *connection) readPump() {
times.PushBack(now)
message := c.handler.NewBuffer()
err = readAll(message, r)
err = buffercache.ReadAll(message, r)
if err != nil {
message.Decref()
break
@ -176,7 +178,7 @@ func (c *connection) readPump() { @@ -176,7 +178,7 @@ func (c *connection) readPump() {
}
// Write message to outbound queue.
func (c *connection) Send(message Buffer) {
func (c *connection) Send(message buffercache.Buffer) {
c.mutex.Lock()
defer c.mutex.Unlock()
if c.isClosed {
@ -190,11 +192,10 @@ func (c *connection) Send(message Buffer) { @@ -190,11 +192,10 @@ func (c *connection) Send(message Buffer) {
message.Incref()
c.queue.PushBack(message)
c.condition.Signal()
}
// writePump pumps messages from the queue to the websocket connection.
func (c *connection) writePump() {
func (c *connection) WritePump() {
var timer *time.Timer
ping := false
@ -232,7 +233,7 @@ func (c *connection) writePump() { @@ -232,7 +233,7 @@ func (c *connection) writePump() {
break
}
c.queue.Remove(head)
message := head.Value.(Buffer)
message := head.Value.(buffercache.Buffer)
if ping {
// Send ping.
ping = false

4
src/app/spreed-webrtc-server/contact.go → go/channelling/contact.go

@ -19,9 +19,7 @@ @@ -19,9 +19,7 @@
*
*/
package main
import ()
package channelling
type Contact struct {
A string

27
go/channelling/contact_manager.go

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
type ContactManager interface {
ContactrequestHandler(*Session, string, *DataContactRequest) error
GetContactID(*Session, string) (string, error)
}

2
src/app/spreed-webrtc-server/context.go → go/channelling/context.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package channelling
type Context struct {
App string // Main client script

42
src/app/spreed-webrtc-server/channelling.go → go/channelling/data.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package channelling
type DataError struct {
Type string
@ -63,7 +63,7 @@ type DataRoom struct { @@ -63,7 +63,7 @@ type DataRoom struct {
type DataOffer struct {
Type string
To string
Offer interface{}
Offer map[string]interface{}
}
type DataCandidate struct {
@ -75,7 +75,7 @@ type DataCandidate struct { @@ -75,7 +75,7 @@ type DataCandidate struct {
type DataAnswer struct {
Type string
To string
Answer interface{}
Answer map[string]interface{}
}
type DataSelf struct {
@ -182,27 +182,27 @@ type DataAutoCall struct { @@ -182,27 +182,27 @@ type DataAutoCall struct {
type DataIncoming struct {
Type string
Hello *DataHello
Offer *DataOffer
Candidate *DataCandidate
Answer *DataAnswer
Bye *DataBye
Status *DataStatus
Chat *DataChat
Conference *DataConference
Alive *DataAlive
Authentication *DataAuthentication
Sessions *DataSessions
Room *DataRoom
Iid string `json:",omitempty"`
Hello *DataHello `json:",omitempty"`
Offer *DataOffer `json:",omitempty"`
Candidate *DataCandidate `json:",omitempty"`
Answer *DataAnswer `json:",omitempty"`
Bye *DataBye `json:",omitempty"`
Status *DataStatus `json:",omitempty"`
Chat *DataChat `json:",omitempty"`
Conference *DataConference `json:",omitempty"`
Alive *DataAlive `json:",omitempty"`
Authentication *DataAuthentication `json:",omitempty"`
Sessions *DataSessions `json:",omitempty"`
Room *DataRoom `json:",omitempty"`
Iid string `json:",omitempty"`
}
type DataOutgoing struct {
Data interface{}
From string
To string
Iid string `json:",omitempty"`
A string `json:",omitempty"`
Data interface{} `json:",omitempty"`
From string `json:",omitempty"`
To string `json:",omitempty"`
Iid string `json:",omitempty"`
A string `json:",omitempty"`
}
type DataSessions struct {

72
src/app/spreed-webrtc-server/hub.go → go/channelling/hub.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package channelling
import (
"crypto/aes"
@ -36,35 +36,9 @@ import ( @@ -36,35 +36,9 @@ import (
)
const (
turnTTL = 3600 // XXX(longsleep): Add to config file.
maxBroadcastPerSecond = 1000
maxUsersLength = 5000
turnTTL = 3600 // XXX(longsleep): Add to config file.
)
type SessionStore interface {
GetSession(id string) (session *Session, ok bool)
}
type Unicaster interface {
SessionStore
OnConnect(Client, *Session)
OnDisconnect(Client, *Session)
Unicast(to string, outgoing *DataOutgoing)
}
type ContactManager interface {
contactrequestHandler(*Session, string, *DataContactRequest) error
getContactID(*Session, string) (string, error)
}
type TurnDataCreator interface {
CreateTurnData(*Session) *DataTurn
}
type ClientStats interface {
ClientInfo(details bool) (int, map[string]*DataSession, map[string]string)
}
type Hub interface {
ClientStats
Unicaster
@ -74,7 +48,7 @@ type Hub interface { @@ -74,7 +48,7 @@ type Hub interface {
type hub struct {
OutgoingEncoder
clients map[string]Client
clients map[string]*Client
config *Config
turnSecret []byte
mutex sync.RWMutex
@ -82,10 +56,9 @@ type hub struct { @@ -82,10 +56,9 @@ type hub struct {
}
func NewHub(config *Config, sessionSecret, encryptionSecret, turnSecret []byte, encoder OutgoingEncoder) Hub {
h := &hub{
OutgoingEncoder: encoder,
clients: make(map[string]Client),
clients: make(map[string]*Client),
config: config,
turnSecret: turnSecret,
}
@ -94,8 +67,8 @@ func NewHub(config *Config, sessionSecret, encryptionSecret, turnSecret []byte, @@ -94,8 +67,8 @@ func NewHub(config *Config, sessionSecret, encryptionSecret, turnSecret []byte,
h.contacts.MaxAge(0) // Forever
h.contacts.HashFunc(sha256.New)
h.contacts.BlockFunc(aes.NewCipher)
return h
return h
}
func (h *hub) ClientInfo(details bool) (clientCount int, sessions map[string]*DataSession, connections map[string]string) {
@ -119,7 +92,6 @@ func (h *hub) ClientInfo(details bool) (clientCount int, sessions map[string]*Da @@ -119,7 +92,6 @@ func (h *hub) ClientInfo(details bool) (clientCount int, sessions map[string]*Da
}
func (h *hub) CreateTurnData(session *Session) *DataTurn {
// Create turn data credentials for shared secret auth with TURN
// server. See http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
// and https://code.google.com/p/rfc5766-turn-server/ REST API auth
@ -136,20 +108,21 @@ func (h *hub) CreateTurnData(session *Session) *DataTurn { @@ -136,20 +108,21 @@ func (h *hub) CreateTurnData(session *Session) *DataTurn {
user := fmt.Sprintf("%d:%s", expiration, id)
foo.Write([]byte(user))
password := base64.StdEncoding.EncodeToString(foo.Sum(nil))
return &DataTurn{user, password, turnTTL, h.config.TurnURIs}
return &DataTurn{user, password, turnTTL, h.config.TurnURIs}
}
func (h *hub) GetSession(id string) (session *Session, ok bool) {
var client Client
var client *Client
client, ok = h.GetClient(id)
if ok {
session = client.Session()
}
return
}
func (h *hub) OnConnect(client Client, session *Session) {
func (h *hub) OnConnect(client *Client, session *Session) {
h.mutex.Lock()
log.Printf("Created client %d with id %s\n", client.Index(), session.Id)
// Register connection or replace existing one.
@ -161,7 +134,7 @@ func (h *hub) OnConnect(client Client, session *Session) { @@ -161,7 +134,7 @@ func (h *hub) OnConnect(client Client, session *Session) {
h.mutex.Unlock()
}
func (h *hub) OnDisconnect(client Client, session *Session) {
func (h *hub) OnDisconnect(client *Client, session *Session) {
h.mutex.Lock()
if ec, ok := h.clients[session.Id]; ok {
if ec == client {
@ -174,26 +147,32 @@ func (h *hub) OnDisconnect(client Client, session *Session) { @@ -174,26 +147,32 @@ func (h *hub) OnDisconnect(client Client, session *Session) {
h.mutex.Unlock()
}
func (h *hub) GetClient(sessionID string) (client Client, ok bool) {
func (h *hub) GetClient(sessionID string) (client *Client, ok bool) {
h.mutex.RLock()
client, ok = h.clients[sessionID]
h.mutex.RUnlock()
return
}
func (h *hub) Unicast(to string, outgoing *DataOutgoing) {
if message, err := h.EncodeOutgoing(outgoing); err == nil {
client, ok := h.GetClient(to)
if !ok {
log.Println("Unicast To not found", to)
func (h *hub) Unicast(to string, outgoing *DataOutgoing, pipeline *Pipeline) {
client, ok := h.GetClient(to)
if pipeline != nil {
if complete := pipeline.FlushOutgoing(h, client, to, outgoing); complete {
return
}
}
if !ok {
log.Println("Unicast To not found", to)
return
}
if message, err := h.EncodeOutgoing(outgoing); err == nil {
client.Send(message)
message.Decref()
}
}
func (h *hub) getContactID(session *Session, token string) (userid string, err error) {
func (h *hub) GetContactID(session *Session, token string) (userid string, err error) {
contact := &Contact{}
err = h.contacts.Decode("contact", token, contact)
if err != nil {
@ -210,11 +189,11 @@ func (h *hub) getContactID(session *Session, token string) (userid string, err e @@ -210,11 +189,11 @@ func (h *hub) getContactID(session *Session, token string) (userid string, err e
if userid == "" {
err = fmt.Errorf("Ignoring foreign contact token", contact.A, contact.B)
}
return
}
func (h *hub) contactrequestHandler(session *Session, to string, cr *DataContactRequest) error {
func (h *hub) ContactrequestHandler(session *Session, to string, cr *DataContactRequest) error {
var err error
if cr.Success {
@ -274,5 +253,4 @@ func (h *hub) contactrequestHandler(session *Session, to string, cr *DataContact @@ -274,5 +253,4 @@ func (h *hub) contactrequestHandler(session *Session, to string, cr *DataContact
}
return err
}

22
src/app/spreed-webrtc-server/images.go → go/channelling/imagecache.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package channelling
import (
"bytes"
@ -43,11 +43,25 @@ type Image struct { @@ -43,11 +43,25 @@ type Image struct {
data []byte
}
func (img *Image) LastChangeID() string {
return img.lastChangeId
}
func (img *Image) LastChange() time.Time {
return img.lastChange
}
func (img *Image) MimeType() string {
return img.mimetype
}
func (img *Image) Reader() *bytes.Reader {
return bytes.NewReader(img.data)
}
type ImageCache interface {
Update(sessionId string, image string) string
Get(imageId string) *Image
Delete(sessionId string)
}
@ -136,6 +150,7 @@ func (self *imageCache) Update(sessionId string, image string) string { @@ -136,6 +150,7 @@ func (self *imageCache) Update(sessionId string, image string) string {
if ok {
result += "/" + filename
}
return result
}
@ -143,6 +158,7 @@ func (self *imageCache) Get(imageId string) *Image { @@ -143,6 +158,7 @@ func (self *imageCache) Get(imageId string) *Image {
self.mutex.RLock()
image := self.images[imageId]
self.mutex.RUnlock()
return image
}

266
go/channelling/pipeline.go

@ -0,0 +1,266 @@ @@ -0,0 +1,266 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
import (
"bytes"
"encoding/json"
"errors"
"log"
"sync"
"time"
"github.com/strukturag/spreed-webrtc/go/buffercache"
)
type PipelineFeedLine struct {
Seq int
Msg *DataOutgoing
}
type Pipeline struct {
PipelineManager PipelineManager
mutex sync.RWMutex
namespace string
id string
from *Session
to *Session
expires *time.Time
data []*DataSinkOutgoing
sink Sink
recvQueue chan *DataIncoming
closed bool
}
func NewPipeline(manager PipelineManager,
namespace string,
id string,
from *Session,
duration time.Duration) *Pipeline {
pipeline := &Pipeline{
PipelineManager: manager,
namespace: namespace,
id: id,
from: from,
recvQueue: make(chan *DataIncoming, 100),
}
go pipeline.receive()
pipeline.Refresh(duration)
return pipeline
}
func (pipeline *Pipeline) receive() {
// TODO(longsleep): Call to ToSession() should be avoided because it locks.
api := pipeline.PipelineManager.GetChannellingAPI()
for data := range pipeline.recvQueue {
_, err := api.OnIncoming(nil, pipeline.ToSession(), data)
if err != nil {
// TODO(longsleep): Handle reply and error.
log.Println("Pipeline receive incoming error", err)
}
}
log.Println("Pipeline receive done")
}
func (pipeline *Pipeline) GetID() string {
return pipeline.id
}
func (pipeline *Pipeline) Refresh(duration time.Duration) {
pipeline.mutex.Lock()
pipeline.refresh(duration)
pipeline.mutex.Unlock()
}
func (pipeline *Pipeline) refresh(duration time.Duration) {
expiration := time.Now().Add(duration)
pipeline.expires = &expiration
}
func (pipeline *Pipeline) Add(msg *DataSinkOutgoing) *Pipeline {
msg.Pipe = pipeline.id
pipeline.mutex.Lock()
pipeline.data = append(pipeline.data, msg)
pipeline.refresh(30 * time.Second)
pipeline.mutex.Unlock()
return pipeline
}
func (pipeline *Pipeline) Send(b buffercache.Buffer) {
// Noop.
}
func (pipeline *Pipeline) Index() uint64 {
return 0
}
func (pipeline *Pipeline) Close() {
pipeline.mutex.Lock()
if !pipeline.closed {
pipeline.expires = nil
if pipeline.sink != nil {
pipeline.sink = nil
}
close(pipeline.recvQueue)
pipeline.closed = true
log.Println("Closed pipeline")
}
pipeline.mutex.Unlock()
}
func (pipeline *Pipeline) Expired() bool {
var expired bool
pipeline.mutex.RLock()
if pipeline.expires == nil {
expired = true
} else {
expired = pipeline.expires.Before(time.Now())
}
pipeline.mutex.RUnlock()
return expired
}
func (pipeline *Pipeline) FromSession() *Session {
pipeline.mutex.RLock()
defer pipeline.mutex.RUnlock()
return pipeline.from
}
func (pipeline *Pipeline) ToSession() *Session {
pipeline.mutex.RLock()
defer pipeline.mutex.RUnlock()
return pipeline.to
}
func (pipeline *Pipeline) JSONFeed(since, limit int) ([]byte, error) {
pipeline.mutex.RLock()
var lineRaw []byte
var line *PipelineFeedLine
var buffer bytes.Buffer
var err error
data := pipeline.data[since:]
count := 0
for seq, msg := range data {
line = &PipelineFeedLine{
Seq: seq + since,
Msg: msg.Outgoing,
}
lineRaw, err = json.Marshal(line)
if err != nil {
return nil, err
}
buffer.Write(lineRaw)
buffer.WriteString("\n")
count++
if limit > 0 && count >= limit {
break
}
}
pipeline.mutex.RUnlock()
return buffer.Bytes(), nil
}
func (pipeline *Pipeline) FlushOutgoing(hub Hub, client *Client, to string, outgoing *DataOutgoing) bool {
//log.Println("Flush outgoing via pipeline", to, client == nil)
if client == nil {
sinkOutgoing := &DataSinkOutgoing{
Outgoing: outgoing,
}
pipeline.mutex.Lock()
sink := pipeline.sink
toSession := pipeline.to
fromSession := pipeline.from
for {
if sink != nil && sink.Enabled() {
// Sink it.
pipeline.mutex.Unlock()
break
}
sink, toSession = pipeline.PipelineManager.FindSinkAndSession(to)
if sink != nil {
pipeline.to = toSession
err := pipeline.attach(sink)
if err == nil {
pipeline.mutex.Unlock()
// Create incoming receiver.
sink.BindRecvChan(pipeline.recvQueue)
// Sink it.
break
}
}
// Not pipelined, do nothing.
pipeline.mutex.Unlock()
break
}
if fromSession != nil {
sinkOutgoing.FromUserid = fromSession.Userid()
}
if toSession != nil {
sinkOutgoing.ToUserid = toSession.Userid()
}
pipeline.Add(sinkOutgoing)
if sink != nil {
// Pipelined, sink data.
sink.Write(sinkOutgoing)
return true
}
}
return false
}
func (pipeline *Pipeline) Attach(sink Sink) error {
pipeline.mutex.Lock()
defer pipeline.mutex.Unlock()
// Sink existing data first.
log.Println("Attach sink to pipeline", pipeline.id)
err := pipeline.attach(sink)
if err == nil {
for _, msg := range pipeline.data {
log.Println("Flushing pipeline to sink after attach", len(pipeline.data))
sink.Write(msg)
}
}
return err
}
func (pipeline *Pipeline) attach(sink Sink) error {
if pipeline.sink != nil {
return errors.New("pipeline already attached to sink")
}
pipeline.sink = sink
return nil
}

244
go/channelling/pipeline_manager.go

@ -0,0 +1,244 @@ @@ -0,0 +1,244 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
import (
"fmt"
"log"
"sync"
"time"
)
const (
PipelineNamespaceCall = "call"
)
type PipelineManager interface {
BusManager
SessionStore
UserStore
SessionCreator
GetPipelineByID(id string) (pipeline *Pipeline, ok bool)
GetPipeline(namespace string, sender Sender, session *Session, to string) *Pipeline
FindSinkAndSession(to string) (Sink, *Session)
}
type pipelineManager struct {
BusManager
SessionStore
UserStore
SessionCreator
mutex sync.RWMutex
pipelineTable map[string]*Pipeline
sessionTable map[string]*Session
sessionByBusIDTable map[string]*Session
sessionSinkTable map[string]Sink
duration time.Duration
defaultSinkID string
enabled bool
}
func NewPipelineManager(busManager BusManager, sessionStore SessionStore, userStore UserStore, sessionCreator SessionCreator) PipelineManager {
plm := &pipelineManager{
BusManager: busManager,
SessionStore: sessionStore,
UserStore: userStore,
SessionCreator: sessionCreator,
pipelineTable: make(map[string]*Pipeline),
sessionTable: make(map[string]*Session),
sessionByBusIDTable: make(map[string]*Session),
sessionSinkTable: make(map[string]Sink),
duration: 60 * time.Second,
}
return plm
}
func (plm *pipelineManager) Start() {
plm.enabled = true
plm.start()
plm.Subscribe("channelling.session.create", plm.sessionCreate)
plm.Subscribe("channelling.session.close", plm.sessionClose)
}
func (plm *pipelineManager) cleanup() {
plm.mutex.Lock()
for id, pipeline := range plm.pipelineTable {
if pipeline.Expired() {
pipeline.Close()
delete(plm.pipelineTable, id)
}
}
plm.mutex.Unlock()
}
func (plm *pipelineManager) start() {
c := time.Tick(30 * time.Second)
go func() {
for _ = range c {
plm.cleanup()
}
}()
}
func (plm *pipelineManager) sessionCreate(subject, reply string, msg *SessionCreateRequest) {
log.Println("sessionCreate via NATS", subject, reply, msg)
if msg.Session == nil || msg.Id == "" {
return
}
var sink Sink
plm.mutex.Lock()
session, ok := plm.sessionByBusIDTable[msg.Id]
if ok {
// Remove existing session with same ID.
delete(plm.sessionTable, session.Id)
sink, _ = plm.sessionSinkTable[session.Id]
delete(plm.sessionSinkTable, session.Id)
session.Close()
}
session = plm.CreateSession(nil, "")
plm.sessionByBusIDTable[msg.Id] = session
plm.sessionTable[session.Id] = session
if sink == nil {
sink = plm.CreateSink(msg.Id)
log.Println("Created NATS sink", msg.Id)
}
if reply != "" {
// Always reply with our sink data
plm.Publish(reply, sink.Export())
}
plm.sessionSinkTable[session.Id] = sink
if msg.SetAsDefault {
plm.defaultSinkID = session.Id
log.Println("Using NATS sink as default session", session.Id)
}
plm.mutex.Unlock()
if msg.Session.Status != nil {
session.Status = msg.Session.Status
}
if msg.Session.Userid != "" {
session.SetUseridFake(msg.Session.Userid)
}
if msg.Room != nil {
room, err := session.JoinRoom(msg.Room.Name, msg.Room.Type, msg.Room.Credentials, nil)
log.Println("Joined NATS session to room", room, err)
}
session.BroadcastStatus()
}
func (plm *pipelineManager) sessionClose(subject, reply string, id string) {
log.Println("sessionClose via NATS", subject, reply, id)
if id == "" {
return
}
plm.mutex.Lock()
session, ok := plm.sessionByBusIDTable[id]
if ok {
delete(plm.sessionByBusIDTable, id)
delete(plm.sessionTable, session.Id)
if sink, ok := plm.sessionSinkTable[session.Id]; ok {
delete(plm.sessionSinkTable, session.Id)
sink.Close()
}
}
plm.mutex.Unlock()
if ok {
session.Close()
}
}
func (plm *pipelineManager) GetPipelineByID(id string) (*Pipeline, bool) {
plm.mutex.RLock()
pipeline, ok := plm.pipelineTable[id]
plm.mutex.RUnlock()
return pipeline, ok
}
func (plm *pipelineManager) PipelineID(namespace string, sender Sender, session *Session, to string) string {
return fmt.Sprintf("%s.%s.%s", namespace, session.Id, to)
}
func (plm *pipelineManager) GetPipeline(namespace string, sender Sender, session *Session, to string) *Pipeline {
if !plm.enabled {
return nil
}
id := plm.PipelineID(namespace, sender, session, to)
plm.mutex.Lock()
pipeline, ok := plm.pipelineTable[id]
if ok {
// Refresh. We do not care if the pipeline is expired.
pipeline.Refresh(plm.duration)
plm.mutex.Unlock()
return pipeline
}
log.Println("Creating pipeline", namespace, id)
pipeline = NewPipeline(plm, namespace, id, session, plm.duration)
plm.pipelineTable[id] = pipeline
plm.mutex.Unlock()
return pipeline
}
func (plm *pipelineManager) FindSinkAndSession(to string) (sink Sink, session *Session) {
plm.mutex.RLock()
var found bool
if sink, found = plm.sessionSinkTable[to]; found {
session, _ = plm.sessionTable[to]
plm.mutex.RUnlock()
if sink.Enabled() {
log.Println("Pipeline sink found via manager", sink)
return sink, session
}
} else {
plm.mutex.RUnlock()
}
if plm.defaultSinkID != "" && to != plm.defaultSinkID {
// Keep target to while returning a the default sink.
log.Println("Find sink via default sink ID", plm.defaultSinkID)
sink, _ = plm.FindSinkAndSession(plm.defaultSinkID)
if sink != nil {
if session, found = plm.GetSession(to); found {
return
}
}
}
return nil, nil
}

14
src/app/spreed-webrtc-server/room_manager.go → go/channelling/room_manager.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package channelling
import (
"fmt"
@ -65,8 +65,8 @@ func NewRoomManager(config *Config, encoder OutgoingEncoder) RoomManager { @@ -65,8 +65,8 @@ func NewRoomManager(config *Config, encoder OutgoingEncoder) RoomManager {
OutgoingEncoder: encoder,
roomTable: make(map[string]RoomWorker),
}
if config.globalRoomID != "" {
rm.globalRoomID = rm.MakeRoomID(config.globalRoomID, "")
if config.GlobalRoomID != "" {
rm.globalRoomID = rm.MakeRoomID(config.GlobalRoomID, "")
}
rm.defaultRoomID = rm.MakeRoomID("", "")
return rm
@ -111,7 +111,8 @@ func (rooms *roomManager) UpdateRoom(session *Session, room *DataRoom) (*DataRoo @@ -111,7 +111,8 @@ func (rooms *roomManager) UpdateRoom(session *Session, room *DataRoom) (*DataRoo
return room, roomWorker.Update(room)
}
// Set default room type if room was not found.
room.Type = rooms.roomTypeDefault
room.Type = rooms.RoomTypeDefault
// TODO(lcooper): We should almost certainly return an error in this case.
return room, nil
}
@ -147,6 +148,7 @@ func (rooms *roomManager) RoomInfo(includeSessions bool) (count int, sessionInfo @@ -147,6 +148,7 @@ func (rooms *roomManager) RoomInfo(includeSessions bool) (count int, sessionInfo
sessionInfo[roomid] = room.SessionIDs()
}
}
return
}
@ -154,6 +156,7 @@ func (rooms *roomManager) Get(roomID string) (room RoomWorker, ok bool) { @@ -154,6 +156,7 @@ func (rooms *roomManager) Get(roomID string) (room RoomWorker, ok bool) {
rooms.RLock()
room, ok = rooms.roomTable[roomID]
rooms.RUnlock()
return
}
@ -211,7 +214,8 @@ func (rooms *roomManager) GlobalUsers() []*roomUser { @@ -211,7 +214,8 @@ func (rooms *roomManager) GlobalUsers() []*roomUser {
func (rooms *roomManager) MakeRoomID(roomName, roomType string) string {
if roomType == "" {
roomType = rooms.roomTypeDefault
roomType = rooms.RoomTypeDefault
}
return fmt.Sprintf("%s:%s", roomType, roomName)
}

4
src/app/spreed-webrtc-server/room_manager_test.go → go/channelling/room_manager_test.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package channelling
import (
"testing"
@ -27,7 +27,7 @@ import ( @@ -27,7 +27,7 @@ import (
func NewTestRoomManager() (RoomManager, *Config) {
config := &Config{
roomTypeDefault: "Room",
RoomTypeDefault: "Room",
}
return NewRoomManager(config, nil), config
}

27
src/app/spreed-webrtc-server/roomworker.go → go/channelling/roomworker.go

@ -19,18 +19,21 @@ @@ -19,18 +19,21 @@
*
*/
package main
package channelling
import (
"crypto/subtle"
"log"
"sync"
"time"
"github.com/strukturag/spreed-webrtc/go/buffercache"
)
const (
roomMaxWorkers = 10000
roomExpiryDuration = 60 * time.Second
maxUsersLength = 5000
)
type RoomWorker interface {
@ -39,7 +42,7 @@ type RoomWorker interface { @@ -39,7 +42,7 @@ type RoomWorker interface {
Users() []*roomUser
Update(*DataRoom) error
GetUsers() []*DataSession
Broadcast(sessionID string, buf Buffer)
Broadcast(sessionID string, buf buffercache.Buffer)
Join(*DataRoomCredentials, *Session, Sender) (*DataRoom, error)
Leave(sessionID string)
}
@ -68,7 +71,6 @@ type roomUser struct { @@ -68,7 +71,6 @@ type roomUser struct {
}
func NewRoomWorker(manager *roomManager, roomID, roomName, roomType string, credentials *DataRoomCredentials) RoomWorker {
log.Printf("Creating worker for room '%s'\n", roomID)
r := &roomWorker{
@ -91,11 +93,9 @@ func NewRoomWorker(manager *roomManager, roomID, roomName, roomType string, cred @@ -91,11 +93,9 @@ func NewRoomWorker(manager *roomManager, roomID, roomName, roomType string, cred
})
return r
}
func (r *roomWorker) Start() {
// Main blocking worker.
L:
for {
@ -122,7 +122,6 @@ L: @@ -122,7 +122,6 @@ L:
r.timer.Stop()
close(r.workers)
//fmt.Println("Exit worker", r.Id)
}
func (r *roomWorker) SessionIDs() []string {
@ -132,23 +131,22 @@ func (r *roomWorker) SessionIDs() []string { @@ -132,23 +131,22 @@ func (r *roomWorker) SessionIDs() []string {
for id := range r.users {
sessions = append(sessions, id)
}
return sessions
}
func (r *roomWorker) Users() []*roomUser {
r.mutex.RLock()
defer r.mutex.RUnlock()
users := make([]*roomUser, 0, len(r.users))
for _, user := range r.users {
users = append(users, user)
}
return users
return users
}
func (r *roomWorker) Run(f func()) bool {
select {
case r.workers <- f:
return true
@ -156,7 +154,6 @@ func (r *roomWorker) Run(f func()) bool { @@ -156,7 +154,6 @@ func (r *roomWorker) Run(f func()) bool {
log.Printf("Room worker channel full or closed '%s'\n", r.id)
return false
}
}
func (r *roomWorker) Update(room *DataRoom) error {
@ -178,6 +175,7 @@ func (r *roomWorker) Update(room *DataRoom) error { @@ -178,6 +175,7 @@ func (r *roomWorker) Update(room *DataRoom) error {
fault <- nil
}
r.Run(worker)
return <-fault
}
@ -223,13 +221,12 @@ func (r *roomWorker) GetUsers() []*DataSession { @@ -223,13 +221,12 @@ func (r *roomWorker) GetUsers() []*DataSession {
return <-out
}
func (r *roomWorker) Broadcast(sessionID string, message Buffer) {
func (r *roomWorker) Broadcast(sessionID string, message buffercache.Buffer) {
worker := func() {
r.mutex.RLock()
for id, user := range r.users {
if id == sessionID {
// Skip broadcast to self.
if id == sessionID || user.Sender == nil {
// Skip broadcast to self or non existing sender.
continue
}
//fmt.Printf("%s\n", m.Message)
@ -241,7 +238,6 @@ func (r *roomWorker) Broadcast(sessionID string, message Buffer) { @@ -241,7 +238,6 @@ func (r *roomWorker) Broadcast(sessionID string, message Buffer) {
message.Incref()
r.Run(worker)
}
type joinResult struct {
@ -280,6 +276,7 @@ func (r *roomWorker) Join(credentials *DataRoomCredentials, session *Session, se @@ -280,6 +276,7 @@ func (r *roomWorker) Join(credentials *DataRoomCredentials, session *Session, se
}
r.Run(worker)
result := <-results
return result.DataRoom, result.error
}

2
src/app/spreed-webrtc-server/roomworker_test.go → go/channelling/roomworker_test.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package channelling
import (
"testing"

2
src/app/spreed-webrtc-server/api.go → go/channelling/server/api.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package server
import ()

63
src/app/spreed-webrtc-server/config.go → go/channelling/server/config.go

@ -19,44 +19,20 @@ @@ -19,44 +19,20 @@
*
*/
package main
package server
import (
"fmt"
"github.com/strukturag/phoenix"
"log"
"net/http"
"strings"
"time"
)
type Config struct {
Title string // Title
ver string // Version (not exported to Javascript)
S string // Static URL prefix with version
B string // Base URL
Token string // Server token
Renegotiation bool // Renegotiation flag
StunURIs []string // STUN server URIs
TurnURIs []string // TURN server URIs
Tokens bool // True when we got a tokens file
Version string // Server version number
UsersEnabled bool // Flag if users are enabled
UsersAllowRegistration bool // Flag if users can register
UsersMode string // Users mode string
DefaultRoomEnabled bool // Flag if default room ("") is enabled
Plugin string // Plugin to load
AuthorizeRoomCreation bool // Whether a user account is required to create rooms
AuthorizeRoomJoin bool // Whether a user account is required to join rooms
Modules []string // List of enabled modules
modulesTable map[string]bool // Map of enabled modules
globalRoomID string // Id of the global room (not exported to Javascript)
contentSecurityPolicy string // HTML content security policy
contentSecurityPolicyReportOnly string // HTML content security policy in report only mode
roomTypeDefault string // New rooms default to this type
}
"github.com/strukturag/spreed-webrtc/go/channelling"
func NewConfig(container phoenix.Container, tokens bool) *Config {
"github.com/strukturag/phoenix"
)
func NewConfig(container phoenix.Container, tokens bool) *channelling.Config {
ver := container.GetStringDefault("app", "ver", "")
version := container.Version()
@ -107,9 +83,9 @@ func NewConfig(container phoenix.Container, tokens bool) *Config { @@ -107,9 +83,9 @@ func NewConfig(container phoenix.Container, tokens bool) *Config {
}
log.Println("Enabled modules:", modules)
return &Config{
return &channelling.Config{
Title: container.GetStringDefault("app", "title", "Spreed WebRTC"),
ver: ver,
Ver: ver,
S: fmt.Sprintf("static/ver=%s", ver),
B: basePath,
Token: serverToken,
@ -126,27 +102,14 @@ func NewConfig(container phoenix.Container, tokens bool) *Config { @@ -126,27 +102,14 @@ func NewConfig(container phoenix.Container, tokens bool) *Config {
AuthorizeRoomCreation: container.GetBoolDefault("app", "authorizeRoomCreation", false),
AuthorizeRoomJoin: container.GetBoolDefault("app", "authorizeRoomJoin", false),
Modules: modules,
modulesTable: modulesTable,
globalRoomID: container.GetStringDefault("app", "globalRoom", ""),
contentSecurityPolicy: container.GetStringDefault("app", "contentSecurityPolicy", ""),
contentSecurityPolicyReportOnly: container.GetStringDefault("app", "contentSecurityPolicyReportOnly", ""),
roomTypeDefault: "Room",
ModulesTable: modulesTable,
GlobalRoomID: container.GetStringDefault("app", "globalRoom", ""),
ContentSecurityPolicy: container.GetStringDefault("app", "contentSecurityPolicy", ""),
ContentSecurityPolicyReportOnly: container.GetStringDefault("app", "contentSecurityPolicyReportOnly", ""),
RoomTypeDefault: "Room",
}
}
func (config *Config) Get(request *http.Request) (int, interface{}, http.Header) {
return 200, config, http.Header{"Content-Type": {"application/json; charset=utf-8"}}
}
func (config *Config) WithModule(m string) bool {
if val, ok := config.modulesTable[m]; ok && val {
return true
}
return false
}
// Helper function to clean up string arrays.
func trimAndRemoveDuplicates(data *[]string) {
found := make(map[string]bool)

98
go/channelling/server/pipelines.go

@ -0,0 +1,98 @@ @@ -0,0 +1,98 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package server
import (
"encoding/json"
"net/http"
"strconv"
"github.com/strukturag/spreed-webrtc/go/channelling"
"github.com/gorilla/mux"
)
type Pipelines struct {
channelling.PipelineManager
API channelling.ChannellingAPI
}
func (pipelines *Pipelines) Get(request *http.Request) (int, interface{}, http.Header) {
vars := mux.Vars(request)
id, ok := vars["id"]
if !ok {
return http.StatusNotFound, "", nil
}
pipeline, ok := pipelines.GetPipelineByID(id)
if !ok {
return http.StatusNotFound, "", nil
}
since := 0
limit := 0
if sinceParam := request.Form.Get("since"); sinceParam != "" {
since, _ = strconv.Atoi(sinceParam)
}
if limitParam := request.Form.Get("limit"); limitParam != "" {
limit, _ = strconv.Atoi(limitParam)
}
result, err := pipeline.JSONFeed(since, limit)
if err != nil {
return http.StatusInternalServerError, err.Error(), nil
}
return http.StatusOK, result, nil
}
func (pipelines *Pipelines) Post(request *http.Request) (int, interface{}, http.Header) {
vars := mux.Vars(request)
id, ok := vars["id"]
if !ok {
return http.StatusNotFound, "", nil
}
pipeline, ok := pipelines.GetPipelineByID(id)
if !ok {
return http.StatusNotFound, "", nil
}
var incoming channelling.DataIncoming
dec := json.NewDecoder(request.Body)
if err := dec.Decode(&incoming); err != nil {
return http.StatusBadRequest, err.Error(), nil
}
result := &channelling.DataOutgoing{
From: pipeline.FromSession().Id,
Iid: incoming.Iid,
}
reply, err := pipelines.API.OnIncoming(pipeline, pipeline.ToSession(), &incoming)
if err == nil {
result.Data = reply
} else {
result.Data = err
}
return http.StatusOK, result, nil
}

6
src/app/spreed-webrtc-server/rooms.go → go/channelling/server/rooms.go

@ -19,11 +19,13 @@ @@ -19,11 +19,13 @@
*
*/
package main
package server
import (
"fmt"
"net/http"
"github.com/strukturag/spreed-webrtc/go/randomstring"
)
type Room struct {
@ -36,7 +38,7 @@ type Rooms struct { @@ -36,7 +38,7 @@ type Rooms struct {
func (rooms *Rooms) Post(request *http.Request) (int, interface{}, http.Header) {
name := NewRandomString(11)
name := randomstring.NewRandomString(11)
return 200, &Room{name, fmt.Sprintf("/%s", name)}, http.Header{"Content-Type": {"application/json"}}
}

19
src/app/spreed-webrtc-server/sessions.go → go/channelling/server/sessions.go

@ -19,14 +19,17 @@ @@ -19,14 +19,17 @@
*
*/
package main
package server
import (
"encoding/json"
"errors"
"github.com/gorilla/mux"
"log"
"net/http"
"github.com/strukturag/spreed-webrtc/go/channelling"
"github.com/gorilla/mux"
)
type SessionNonce struct {
@ -43,9 +46,9 @@ type SessionNonceRequest struct { @@ -43,9 +46,9 @@ type SessionNonceRequest struct {
}
type Sessions struct {
SessionValidator
SessionStore
users *Users
channelling.SessionValidator
channelling.SessionStore
Users *Users
}
// Patch is used to add a userid to a given session (login).
@ -87,8 +90,8 @@ func (sessions *Sessions) Patch(request *http.Request) (int, interface{}, http.H @@ -87,8 +90,8 @@ func (sessions *Sessions) Patch(request *http.Request) (int, interface{}, http.H
var userid string
// Validate with users handler.
if sessions.users.handler != nil {
userid, err = sessions.users.handler.Validate(&snr, request)
if sessions.Users.handler != nil {
userid, err = sessions.Users.handler.Validate(&snr, request)
if err != nil {
error = true
log.Println("Session patch failed - users validation failed.", err)
@ -107,7 +110,7 @@ func (sessions *Sessions) Patch(request *http.Request) (int, interface{}, http.H @@ -107,7 +110,7 @@ func (sessions *Sessions) Patch(request *http.Request) (int, interface{}, http.H
if !error {
// FIXME(longsleep): Not running this might reveal error state with a timing attack.
if session, ok := sessions.GetSession(snr.Id); ok {
nonce, err = session.Authorize(sessions.Realm(), &SessionToken{Id: snr.Id, Sid: snr.Sid, Userid: userid})
nonce, err = session.Authorize(sessions.Realm(), &channelling.SessionToken{Id: snr.Id, Sid: snr.Sid, Userid: userid})
} else {
err = errors.New("no such session")
}

12
src/app/spreed-webrtc-server/stats.go → go/channelling/server/stats.go

@ -19,21 +19,23 @@ @@ -19,21 +19,23 @@
*
*/
package main
package server
import (
"net/http"
"runtime"
"time"
"github.com/strukturag/spreed-webrtc/go/channelling"
)
type Stat struct {
details bool
Runtime *RuntimeStat `json:"runtime"`
Hub *HubStat `json:"hub"`
Runtime *RuntimeStat `json:"runtime"`
Hub *channelling.HubStat `json:"hub"`
}
func NewStat(details bool, statsGenerator StatsGenerator) *Stat {
func NewStat(details bool, statsGenerator channelling.StatsGenerator) *Stat {
stat := &Stat{
details: details,
Runtime: &RuntimeStat{},
@ -69,7 +71,7 @@ func (stat *RuntimeStat) Read() { @@ -69,7 +71,7 @@ func (stat *RuntimeStat) Read() {
}
type Stats struct {
StatsGenerator
channelling.StatsGenerator
}
func (stats *Stats) Get(request *http.Request) (int, interface{}, http.Header) {

2
src/app/spreed-webrtc-server/tls.go → go/channelling/server/tls.go

@ -32,7 +32,7 @@ @@ -32,7 +32,7 @@
*
*/
package main
package server
import (
"crypto"

8
src/app/spreed-webrtc-server/tokens.go → go/channelling/server/tokens.go

@ -19,12 +19,14 @@ @@ -19,12 +19,14 @@
*
*/
package main
package server
import (
"log"
"net/http"
"strings"
"github.com/strukturag/spreed-webrtc/go/channelling"
)
type Token struct {
@ -33,7 +35,7 @@ type Token struct { @@ -33,7 +35,7 @@ type Token struct {
}
type Tokens struct {
provider TokenProvider
Provider channelling.TokenProvider
}
func (tokens Tokens) Post(request *http.Request) (int, interface{}, http.Header) {
@ -44,7 +46,7 @@ func (tokens Tokens) Post(request *http.Request) (int, interface{}, http.Header) @@ -44,7 +46,7 @@ func (tokens Tokens) Post(request *http.Request) (int, interface{}, http.Header)
return 413, NewApiError("auth_too_large", "Auth too large"), http.Header{"Content-Type": {"application/json"}}
}
valid := tokens.provider(strings.ToLower(auth))
valid := tokens.Provider(strings.ToLower(auth))
if valid != "" {
log.Printf("Good incoming token request: %s\n", auth)

56
src/app/spreed-webrtc-server/users.go → go/channelling/server/users.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package server
import (
"crypto"
@ -34,15 +34,18 @@ import ( @@ -34,15 +34,18 @@ import (
"encoding/pem"
"errors"
"fmt"
"github.com/longsleep/pkac"
"github.com/satori/go.uuid"
"github.com/strukturag/phoenix"
"log"
"math/big"
"net/http"
"strconv"
"strings"
"time"
"github.com/strukturag/spreed-webrtc/go/channelling"
"github.com/longsleep/pkac"
"github.com/satori/go.uuid"
"github.com/strukturag/phoenix"
)
var (
@ -290,41 +293,27 @@ func (un *UserNonce) Response() (int, interface{}, http.Header) { @@ -290,41 +293,27 @@ func (un *UserNonce) Response() (int, interface{}, http.Header) {
}
type Users struct {
SessionValidator
SessionManager
SessionStore
channelling.SessionStore
channelling.SessionValidator
channelling.SessionManager
realm string
handler UsersHandler
}
func NewUsers(sessionStore SessionStore, sessionValidator SessionValidator, sessionManager SessionManager, mode, realm string, runtime phoenix.Runtime) *Users {
func NewUsers(sessionStore channelling.SessionStore, sessionValidator channelling.SessionValidator, sessionManager channelling.SessionManager, mode, realm string, runtime phoenix.Runtime) *Users {
var users = &Users{
sessionStore,
sessionValidator,
sessionManager,
sessionStore,
realm,
nil,
}
// Create handler based on mode.
var handler UsersHandler
var err error
// Create handler based on mode.
if handler, err = users.createHandler(mode, runtime); handler != nil && err == nil {
users.handler = handler
// Register handler Get.
sessionManager.RetrieveUsersWith(func(request *http.Request) (userid string, err error) {
userid, err = handler.Get(request)
if err != nil {
log.Printf("Failed to get userid from handler: %s", err)
} else {
if userid != "" {
log.Printf("Users handler get success: %s\n", userid)
}
}
return
})
log.Printf("Enabled users handler '%s'\n", mode)
} else if err != nil {
log.Printf("Failed to enable handler '%s': %s\n", mode, err)
@ -419,6 +408,21 @@ func (users *Users) createHandler(mode string, runtime phoenix.Runtime) (handler @@ -419,6 +408,21 @@ func (users *Users) createHandler(mode string, runtime phoenix.Runtime) (handler
}
func (users *Users) GetUserID(request *http.Request) (userid string, err error) {
if users.handler == nil {
return
}
userid, err = users.handler.Get(request)
if err != nil {
log.Printf("Failed to get userid from handler: %s", err)
} else {
if userid != "" {
log.Printf("Users handler get success: %s\n", userid)
}
}
return
}
// Post is used to create new userids for this server.
func (users *Users) Post(request *http.Request) (int, interface{}, http.Header) {
@ -462,8 +466,8 @@ func (users *Users) Post(request *http.Request) (int, interface{}, http.Header) @@ -462,8 +466,8 @@ func (users *Users) Post(request *http.Request) (int, interface{}, http.Header)
nonce string
err error
)
if session, ok := users.GetSession(snr.Id); ok {
nonce, err = session.Authorize(users.Realm(), &SessionToken{Id: snr.Id, Sid: snr.Sid, Userid: userid})
if session, ok := users.SessionStore.GetSession(snr.Id); ok {
nonce, err = session.Authorize(users.Realm(), &channelling.SessionToken{Id: snr.Id, Sid: snr.Sid, Userid: userid})
} else {
err = errors.New("no such session")
}

144
src/app/spreed-webrtc-server/session.go → go/channelling/session.go

@ -19,47 +19,54 @@ @@ -19,47 +19,54 @@
*
*/
package main
package channelling
import (
"fmt"
"github.com/gorilla/securecookie"
"strings"
"sync"
"time"
"github.com/gorilla/securecookie"
)
var sessionNonces *securecookie.SecureCookie
type Session struct {
SessionManager
Unicaster
Broadcaster
RoomStatusManager
buddyImages ImageCache
Id string
Sid string
Ua string
UpdateRev uint64
Status interface{}
Nonce string
Prio int
Hello bool
Roomid string
mutex sync.RWMutex
userid string
fake bool
stamp int64
attestation *SessionAttestation
attestations *securecookie.SecureCookie
subscriptions map[string]*Session
subscribers map[string]*Session
disconnected bool
replaced bool
SessionManager SessionManager
Unicaster Unicaster
Broadcaster Broadcaster
RoomStatusManager RoomStatusManager
buddyImages ImageCache
Id string
Sid string
Ua string
UpdateRev uint64
Status interface{}
Nonce string
Prio int
Hello bool
Roomid string
mutex sync.RWMutex
userid string
fake bool
stamp int64
attestation *SessionAttestation
attestations *securecookie.SecureCookie
subscriptions map[string]*Session
subscribers map[string]*Session
disconnected bool
replaced bool
}
func NewSession(manager SessionManager, unicaster Unicaster, broadcaster Broadcaster, rooms RoomStatusManager, buddyImages ImageCache, attestations *securecookie.SecureCookie, id, sid string) *Session {
func NewSession(manager SessionManager,
unicaster Unicaster,
broadcaster Broadcaster,
rooms RoomStatusManager,
buddyImages ImageCache,
attestations *securecookie.SecureCookie,
id,
sid string) *Session {
session := &Session{
SessionManager: manager,
Unicaster: unicaster,
@ -75,8 +82,8 @@ func NewSession(manager SessionManager, unicaster Unicaster, broadcaster Broadca @@ -75,8 +82,8 @@ func NewSession(manager SessionManager, unicaster Unicaster, broadcaster Broadca
subscribers: make(map[string]*Session),
}
session.NewAttestation()
return session
return session
}
func (s *Session) authenticated() (authenticated bool) {
@ -152,6 +159,7 @@ func (s *Session) JoinRoom(roomName, roomType string, credentials *DataRoomCrede @@ -152,6 +159,7 @@ func (s *Session) JoinRoom(roomName, roomType string, credentials *DataRoomCrede
} else {
s.Hello = false
}
return room, err
}
@ -186,7 +194,7 @@ func (s *Session) BroadcastStatus() { @@ -186,7 +194,7 @@ func (s *Session) BroadcastStatus() {
s.mutex.RUnlock()
}
func (s *Session) Unicast(to string, m interface{}) {
func (s *Session) Unicast(to string, m interface{}, pipeline *Pipeline) {
s.mutex.RLock()
outgoing := &DataOutgoing{
From: s.Id,
@ -196,7 +204,7 @@ func (s *Session) Unicast(to string, m interface{}) { @@ -196,7 +204,7 @@ func (s *Session) Unicast(to string, m interface{}) {
}
s.mutex.RUnlock()
s.Unicaster.Unicast(to, outgoing)
s.Unicaster.Unicast(to, outgoing, pipeline)
}
func (s *Session) Close() {
@ -227,12 +235,12 @@ func (s *Session) Close() { @@ -227,12 +235,12 @@ func (s *Session) Close() {
}
for _, session := range s.subscribers {
s.Unicaster.Unicast(session.Id, outgoing)
s.Unicaster.Unicast(session.Id, outgoing, nil)
}
for _, session := range s.subscriptions {
session.RemoveSubscriber(s.Id)
s.Unicaster.Unicast(session.Id, outgoing)
s.Unicaster.Unicast(session.Id, outgoing, nil)
}
s.SessionManager.DestroySession(s.Id, s.userid)
@ -248,7 +256,6 @@ func (s *Session) Close() { @@ -248,7 +256,6 @@ func (s *Session) Close() {
}
func (s *Session) Replace(oldSession *Session) {
oldSession.mutex.Lock()
if oldSession.disconnected {
oldSession.mutex.Unlock()
@ -265,7 +272,6 @@ func (s *Session) Replace(oldSession *Session) { @@ -265,7 +272,6 @@ func (s *Session) Replace(oldSession *Session) {
// Mark old session as replaced.
oldSession.replaced = true
oldSession.mutex.Unlock()
}
func (s *Session) Update(update *SessionUpdate) uint64 {
@ -301,11 +307,9 @@ func (s *Session) Update(update *SessionUpdate) uint64 { @@ -301,11 +307,9 @@ func (s *Session) Update(update *SessionUpdate) uint64 {
s.UpdateRev++
return s.UpdateRev
}
func (s *Session) Authorize(realm string, st *SessionToken) (string, error) {
s.mutex.Lock()
defer s.mutex.Unlock()
@ -324,11 +328,9 @@ func (s *Session) Authorize(realm string, st *SessionToken) (string, error) { @@ -324,11 +328,9 @@ func (s *Session) Authorize(realm string, st *SessionToken) (string, error) {
}
return s.Nonce, err
}
func (s *Session) Authenticate(realm string, st *SessionToken, userid string) error {
s.mutex.Lock()
defer s.mutex.Unlock()
@ -352,12 +354,11 @@ func (s *Session) Authenticate(realm string, st *SessionToken, userid string) er @@ -352,12 +354,11 @@ func (s *Session) Authenticate(realm string, st *SessionToken, userid string) er
s.userid = userid
s.stamp = time.Now().Unix()
s.UpdateRev++
return nil
return nil
}
func (s *Session) Token() *SessionToken {
s.mutex.RLock()
defer s.mutex.RUnlock()
@ -365,7 +366,6 @@ func (s *Session) Token() *SessionToken { @@ -365,7 +366,6 @@ func (s *Session) Token() *SessionToken {
}
func (s *Session) Data() *DataSession {
s.mutex.RLock()
defer s.mutex.RUnlock()
@ -378,25 +378,21 @@ func (s *Session) Data() *DataSession { @@ -378,25 +378,21 @@ func (s *Session) Data() *DataSession {
Prio: s.Prio,
stamp: s.stamp,
}
}
func (s *Session) Userid() (userid string) {
s.mutex.RLock()
userid = s.userid
s.mutex.RUnlock()
return
return
}
func (s *Session) SetUseridFake(userid string) {
s.mutex.Lock()
s.userid = userid
s.fake = true
s.mutex.Unlock()
}
func (s *Session) NewAttestation() {
@ -406,58 +402,14 @@ func (s *Session) NewAttestation() { @@ -406,58 +402,14 @@ func (s *Session) NewAttestation() {
s.attestation.Update()
}
func (s *Session) UpdateAttestation() {
func (s *Session) UpdateAttestation() (string, error) {
s.mutex.Lock()
s.attestation.Update()
s.mutex.Unlock()
}
type SessionUpdate struct {
Types []string
Ua string
Prio int
Status interface{}
}
type SessionToken struct {
Id string // Public session id.
Sid string // Secret session id.
Userid string // Public user id.
Nonce string `json:"Nonce,omitempty"` // User autentication nonce.
}
type SessionAttestation struct {
refresh int64
token string
s *Session
}
func (sa *SessionAttestation) Update() (string, error) {
token, err := sa.Encode()
if err == nil {
sa.token = token
sa.refresh = time.Now().Unix() + 180 // expires after 3 minutes
}
return token, err
}
func (sa *SessionAttestation) Token() (token string) {
if sa.refresh < time.Now().Unix() {
token, _ = sa.Update()
} else {
token = sa.token
}
return
}
func (sa *SessionAttestation) Encode() (string, error) {
return sa.s.attestations.Encode("attestation", sa.s.Id)
defer s.mutex.Unlock()
return s.attestation.Update()
}
func (sa *SessionAttestation) Decode(token string) (string, error) {
var id string
err := sa.s.attestations.Decode("attestation", token, &id)
return id, err
func (s *Session) DecodeAttestation(token string) (string, error) {
return s.attestation.Decode(token)
}
func init() {

75
src/app/spreed-webrtc-server/session_manager.go → go/channelling/session_manager.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package channelling
import (
"crypto/sha256"
@ -35,11 +35,13 @@ type UserStats interface { @@ -35,11 +35,13 @@ type UserStats interface {
type SessionManager interface {
UserStats
RetrieveUsersWith(func(*http.Request) (string, error))
CreateSession(*http.Request) *Session
SessionStore
UserStore
SessionCreator
DestroySession(sessionID, userID string)
Authenticate(*Session, *SessionToken, string) error
GetUserSessions(session *Session, id string) []*DataSession
DecodeSessionToken(token string) (st *SessionToken)
}
type sessionManager struct {
@ -48,12 +50,13 @@ type sessionManager struct { @@ -48,12 +50,13 @@ type sessionManager struct {
Unicaster
Broadcaster
RoomStatusManager
buddyImages ImageCache
config *Config
userTable map[string]*User
fakesessionTable map[string]*Session
useridRetriever func(*http.Request) (string, error)
attestations *securecookie.SecureCookie
buddyImages ImageCache
config *Config
userTable map[string]*User
sessionTable map[string]*Session
sessionByUserIDTable map[string]*Session
useridRetriever func(*http.Request) (string, error)
attestations *securecookie.SecureCookie
}
func NewSessionManager(config *Config, tickets Tickets, unicaster Unicaster, broadcaster Broadcaster, rooms RoomStatusManager, buddyImages ImageCache, sessionSecret []byte) SessionManager {
@ -67,6 +70,7 @@ func NewSessionManager(config *Config, tickets Tickets, unicaster Unicaster, bro @@ -67,6 +70,7 @@ func NewSessionManager(config *Config, tickets Tickets, unicaster Unicaster, bro
config,
make(map[string]*User),
make(map[string]*Session),
make(map[string]*Session),
nil,
nil,
}
@ -89,32 +93,35 @@ func (sessionManager *sessionManager) UserInfo(details bool) (userCount int, use @@ -89,32 +93,35 @@ func (sessionManager *sessionManager) UserInfo(details bool) (userCount int, use
users[userid] = user.Data()
}
}
return
}
func (sessionManager *sessionManager) RetrieveUsersWith(retriever func(*http.Request) (string, error)) {
sessionManager.useridRetriever = retriever
// GetSession returns the client-less sessions created directly by sessionManager.
func (sessionManager *sessionManager) GetSession(id string) (*Session, bool) {
sessionManager.RLock()
defer sessionManager.RUnlock()
session, ok := sessionManager.sessionTable[id]
return session, ok
}
func (sessionManager *sessionManager) CreateSession(request *http.Request) *Session {
request.ParseForm()
token := request.FormValue("t")
st := sessionManager.DecodeSessionToken(token)
var userid string
if sessionManager.config.UsersEnabled {
if sessionManager.useridRetriever != nil {
userid, _ = sessionManager.useridRetriever(request)
if userid == "" {
userid = st.Userid
}
}
}
func (sessionManager *sessionManager) GetUser(id string) (*User, bool) {
sessionManager.RLock()
defer sessionManager.RUnlock()
user, ok := sessionManager.userTable[id]
return user, ok
}
func (sessionManager *sessionManager) CreateSession(st *SessionToken, userid string) *Session {
if st == nil {
st = sessionManager.DecodeSessionToken("")
}
session := NewSession(sessionManager, sessionManager.Unicaster, sessionManager.Broadcaster, sessionManager.RoomStatusManager, sessionManager.buddyImages, sessionManager.attestations, st.Id, st.Sid)
if userid != "" {
// XXX(lcooper): Should errors be handled here?
// Errors are ignored here, session is returned without userID when auth failed.
sessionManager.Authenticate(session, st, userid)
}
@ -127,10 +134,15 @@ func (sessionManager *sessionManager) DestroySession(sessionID, userID string) { @@ -127,10 +134,15 @@ func (sessionManager *sessionManager) DestroySession(sessionID, userID string) {
}
sessionManager.Lock()
user, ok := sessionManager.userTable[userID]
if ok && user.RemoveSession(sessionID) {
if user, ok := sessionManager.userTable[userID]; ok && user.RemoveSession(sessionID) {
delete(sessionManager.userTable, userID)
}
if _, ok := sessionManager.sessionTable[sessionID]; ok {
delete(sessionManager.sessionTable, sessionID)
}
if session, ok := sessionManager.sessionByUserIDTable[userID]; ok && session.Id == sessionID {
delete(sessionManager.sessionByUserIDTable, sessionID)
}
sessionManager.Unlock()
}
@ -149,6 +161,7 @@ func (sessionManager *sessionManager) Authenticate(session *Session, st *Session @@ -149,6 +161,7 @@ func (sessionManager *sessionManager) Authenticate(session *Session, st *Session
}
sessionManager.Unlock()
user.AddSession(session)
return nil
}
@ -163,12 +176,13 @@ func (sessionManager *sessionManager) GetUserSessions(session *Session, userid s @@ -163,12 +176,13 @@ func (sessionManager *sessionManager) GetUserSessions(session *Session, userid s
if !ok {
// No user. Create fake session.
sessionManager.Lock()
session, ok := sessionManager.fakesessionTable[userid]
session, ok := sessionManager.sessionByUserIDTable[userid]
if !ok {
st := sessionManager.FakeSessionToken(userid)
session = NewSession(sessionManager, sessionManager.Unicaster, sessionManager.Broadcaster, sessionManager.RoomStatusManager, sessionManager.buddyImages, sessionManager.attestations, st.Id, st.Sid)
session.SetUseridFake(st.Userid)
sessionManager.fakesessionTable[userid] = session
sessionManager.sessionByUserIDTable[userid] = session
sessionManager.sessionTable[session.Id] = session
}
sessionManager.Unlock()
users = make([]*DataSession, 1, 1)
@ -177,5 +191,6 @@ func (sessionManager *sessionManager) GetUserSessions(session *Session, userid s @@ -177,5 +191,6 @@ func (sessionManager *sessionManager) GetUserSessions(session *Session, userid s
// Add sessions for foreign user.
users = user.SubscribeSessions(session)
}
return
}

60
go/channelling/sessionattestation.go

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
import (
"time"
)
type SessionAttestation struct {
refresh int64
token string
s *Session
}
func (sa *SessionAttestation) Update() (string, error) {
token, err := sa.Encode()
if err == nil {
sa.token = token
sa.refresh = time.Now().Unix() + 180 // expires after 3 minutes
}
return token, err
}
func (sa *SessionAttestation) Token() (token string) {
if sa.refresh < time.Now().Unix() {
token, _ = sa.Update()
} else {
token = sa.token
}
return
}
func (sa *SessionAttestation) Encode() (string, error) {
return sa.s.attestations.Encode("attestation", sa.s.Id)
}
func (sa *SessionAttestation) Decode(token string) (string, error) {
var id string
err := sa.s.attestations.Decode("attestation", token, &id)
return id, err
}

26
go/channelling/sessioncreator.go

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
type SessionCreator interface {
CreateSession(st *SessionToken, userid string) *Session
}

26
go/channelling/sessionstore.go

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
type SessionStore interface {
GetSession(id string) (session *Session, ok bool)
}

29
go/channelling/sessiontoken.go

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
type SessionToken struct {
Id string // Public session id.
Sid string // Secret session id.
Userid string // Public user id.
Nonce string `json:"Nonce,omitempty"` // User autentication nonce.
}

29
go/channelling/sessionupdate.go

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
type SessionUpdate struct {
Types []string
Ua string
Prio int
Status interface{}
}

37
go/channelling/sink.go

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
import (
"github.com/nats-io/nats"
)
// Sink connects a Pipeline with end points in both directions by
// getting attached to a Pipeline.
type Sink interface {
// Write sends outgoing data on the sink
Write(*DataSinkOutgoing) error
Enabled() bool
Close()
Export() *DataSink
BindRecvChan(channel interface{}) (*nats.Subscription, error)
}

2
src/app/spreed-webrtc-server/stats_manager.go → go/channelling/stats_manager.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package channelling
import (
"sync/atomic"

17
src/app/spreed-webrtc-server/tickets.go → go/channelling/tickets.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package channelling
import (
"crypto/aes"
@ -29,6 +29,8 @@ import ( @@ -29,6 +29,8 @@ import (
"fmt"
"log"
"github.com/strukturag/spreed-webrtc/go/randomstring"
"github.com/gorilla/securecookie"
)
@ -86,7 +88,7 @@ func (tickets *tickets) DecodeSessionToken(token string) (st *SessionToken) { @@ -86,7 +88,7 @@ func (tickets *tickets) DecodeSessionToken(token string) (st *SessionToken) {
}
if st == nil || err != nil {
sid := NewRandomString(32)
sid := randomstring.NewRandomString(32)
id, _ := tickets.Encode("id", sid)
st = &SessionToken{Id: id, Sid: sid}
log.Println("Created new session id", id)
@ -94,13 +96,12 @@ func (tickets *tickets) DecodeSessionToken(token string) (st *SessionToken) { @@ -94,13 +96,12 @@ func (tickets *tickets) DecodeSessionToken(token string) (st *SessionToken) {
return
}
func (tickets *tickets) FakeSessionToken(userid string) *SessionToken {
st := &SessionToken{}
st.Sid = fmt.Sprintf("fake-%s", NewRandomString(27))
st.Id, _ = tickets.Encode("id", st.Sid)
st.Userid = userid
func (tickets *tickets) FakeSessionToken(userid string) (st *SessionToken) {
sid := fmt.Sprintf("fake-%s", randomstring.NewRandomString(27))
id, _ := tickets.Encode("id", sid)
st = &SessionToken{Id: id, Sid: sid, Userid: userid}
log.Println("Created new fake session id", st.Id)
return st
return
}
func (tickets *tickets) ValidateSession(id, sid string) bool {

7
src/app/spreed-webrtc-server/tokenprovider.go → go/channelling/tokenprovider.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package channelling
import (
"encoding/csv"
@ -47,11 +47,11 @@ func (tf *TokenFile) ReloadIfModified() error { @@ -47,11 +47,11 @@ func (tf *TokenFile) ReloadIfModified() error {
tf.Info = info
tf.Reload()
}
return nil
}
func reloadRokens(tf *TokenFile) {
r, err := os.Open(tf.Path)
if err != nil {
panic(err)
@ -71,11 +71,9 @@ func reloadRokens(tf *TokenFile) { @@ -71,11 +71,9 @@ func reloadRokens(tf *TokenFile) {
for _, record := range records {
tf.Tokens[strings.ToLower(record[0])] = true
}
}
func TokenFileProvider(filename string) TokenProvider {
tf := &TokenFile{Path: filename}
tf.Reload = func() { reloadRokens(tf) }
return func(token string) string {
@ -86,5 +84,4 @@ func TokenFileProvider(filename string) TokenProvider { @@ -86,5 +84,4 @@ func TokenFileProvider(filename string) TokenProvider {
}
return token
}
}

26
go/channelling/turndata.go

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
type TurnDataCreator interface {
CreateTurnData(*Session) *DataTurn
}

29
go/channelling/unicaster.go

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
type Unicaster interface {
SessionStore
OnConnect(*Client, *Session)
OnDisconnect(*Client, *Session)
Unicast(to string, outgoing *DataOutgoing, pipeline *Pipeline)
}

12
src/app/spreed-webrtc-server/user.go → go/channelling/user.go

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
package main
package channelling
import (
"log"
@ -34,13 +34,12 @@ type User struct { @@ -34,13 +34,12 @@ type User struct {
}
func NewUser(id string) *User {
user := &User{
Id: id,
sessionTable: make(map[string]*Session),
}
return user
return user
}
// AddSession adds a session to the session table and returns true if
@ -54,6 +53,7 @@ func (u *User) AddSession(s *Session) bool { @@ -54,6 +53,7 @@ func (u *User) AddSession(s *Session) bool {
first = true
}
u.mutex.Unlock()
return first
}
@ -68,12 +68,14 @@ func (u *User) RemoveSession(sessionID string) bool { @@ -68,12 +68,14 @@ func (u *User) RemoveSession(sessionID string) bool {
last = true
}
u.mutex.Unlock()
return last
}
func (u *User) Data() *DataUser {
u.mutex.RLock()
defer u.mutex.RUnlock()
return &DataUser{
Id: u.Id,
Sessions: len(u.sessionTable),
@ -81,7 +83,6 @@ func (u *User) Data() *DataUser { @@ -81,7 +83,6 @@ func (u *User) Data() *DataUser {
}
func (u *User) SubscribeSessions(from *Session) []*DataSession {
sessions := make([]*DataSession, 0, len(u.sessionTable))
u.mutex.RLock()
defer u.mutex.RUnlock()
@ -91,8 +92,8 @@ func (u *User) SubscribeSessions(from *Session) []*DataSession { @@ -91,8 +92,8 @@ func (u *User) SubscribeSessions(from *Session) []*DataSession {
sessions = append(sessions, session.Data())
}
sort.Sort(ByPrioAndStamp(sessions))
return sessions
return sessions
}
type ByPrioAndStamp []*DataSession
@ -112,5 +113,6 @@ func (a ByPrioAndStamp) Less(i, j int) bool { @@ -112,5 +113,6 @@ func (a ByPrioAndStamp) Less(i, j int) bool {
if a[i].Prio == a[j].Prio {
return a[i].stamp < a[j].stamp
}
return false
}

26
go/channelling/userstore.go

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
type UserStore interface {
GetUser(id string) (user *User, ok bool)
}

140
go/natsconnection/natsconnection.go

@ -0,0 +1,140 @@ @@ -0,0 +1,140 @@
package natsconnection
import (
"errors"
"log"
"time"
"github.com/nats-io/nats"
)
// DefaultNatsEstablishTimeout is the default timeout for
// calls to EstablishNatsConnection.
var DefaultEstablishTimeout = 60 * time.Second
// DefaultNatsURL is the default NATS server URL used for
// calls to NewConnection and EstablishConnection.
var DefaultURL = nats.DefaultURL
// Connection implements the wrapped nats.Conn.
type Connection struct {
*nats.Conn
}
// EncodedConnection implements the wrapped nats.EncodedConn.
type EncodedConnection struct {
*nats.EncodedConn
}
// NewConnection creates a connetion to the default NATS server
// and tries to establish the connection. It returns the connection
// and any connection error encountered.
func NewConnection() (*Connection, error) {
opts := &nats.Options{
Url: DefaultURL,
AllowReconnect: true,
MaxReconnect: -1, // Reconnect forever.
ReconnectWait: nats.DefaultReconnectWait,
Timeout: nats.DefaultTimeout,
PingInterval: nats.DefaultPingInterval,
MaxPingsOut: nats.DefaultMaxPingOut,
SubChanLen: nats.DefaultMaxChanLen,
ClosedCB: func(conn *nats.Conn) {
log.Println("NATS connection closed")
},
DisconnectedCB: func(conn *nats.Conn) {
log.Println("NATS disconnected")
},
ReconnectedCB: func(conn *nats.Conn) {
log.Println("NATS reconnected")
},
AsyncErrorCB: func(conn *nats.Conn, sub *nats.Subscription, err error) {
log.Println("NATS async error", sub, err)
},
}
nc, err := opts.Connect()
if err != nil {
return nil, err
}
return &Connection{nc}, nil
}
// NewJSONEncodedConnection creates a JSON-encoded connetion to the
// default NATS server and tries to establish the connection. It
// returns the JSON-encoded connection and any connection error
// encountered.
func NewJSONEncodedConnection() (*EncodedConnection, error) {
nc, err := NewConnection()
if err != nil {
return nil, err
}
ec, err := nats.NewEncodedConn(nc.Conn, nats.JSON_ENCODER)
if err != nil {
return nil, err
}
return &EncodedConnection{ec}, nil
}
// EstablishConnection is a blocking way to create and establish
// connection to the default NATS server. The function will only return
// after a timeout has reached or a connection has been established. It
// returns the connection and and any timeout error encountered.
func EstablishConnection(timeout *time.Duration) (*Connection, error) {
if timeout == nil {
timeout = &DefaultEstablishTimeout
}
connch := make(chan *Connection, 1)
errch := make(chan error, 1)
go func() {
notify := true
for {
nc, err := NewConnection()
if err == nil {
connch <- nc
break
}
switch err {
case nats.ErrTimeout:
fallthrough
case nats.ErrNoServers:
if notify {
notify = false
log.Println("Waiting for NATS server to become available")
}
time.Sleep(1 * time.Second)
continue
default:
errch <- err
break
}
}
}()
select {
case conn := <-connch:
return conn, nil
case err := <-errch:
return nil, err
case <-time.After(*timeout):
return nil, errors.New("NATS connection: timeout")
}
}
// EstablishJSONEncodedConnection is a blocking way to create and establish
// JSON-encoded connection to the default NATS server. The function will
// only return after a timeout has reached or a connection has been
// established. It returns the JSON-encoded connection and and any timeout
// error encountered.
func EstablishJSONEncodedConnection(timeout *time.Duration) (*EncodedConnection, error) {
nc, err := EstablishConnection(timeout)
if err != nil {
return nil, err
}
ec, err := nats.NewEncodedConn(nc.Conn, nats.JSON_ENCODER)
if err != nil {
return nil, err
}
return &EncodedConnection{ec}, nil
}

32
src/app/spreed-webrtc-server/random.go → go/randomstring/randomstring.go

@ -19,34 +19,38 @@ @@ -19,34 +19,38 @@
*
*/
package main
package randomstring
import (
"crypto/rand"
"math/big"
pseudoRand "math/rand"
"time"
)
const (
dict = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW0123456789"
dict = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
)
func NewRandomString(length int) string {
func newRandomInt(max *big.Int) int {
rand, err := rand.Int(rand.Reader, max)
if err != nil {
// Fallback to pseudo-random
return pseudoRand.Intn(int(max.Int64()))
}
return int(rand.Int64())
}
// NewRandomString returns a alphanumeric random string with
// the specified length using crypto/rand with fallback to
// math/rand on error.
func NewRandomString(length int) string {
buf := make([]byte, length)
_, err := rand.Read(buf)
if err != nil {
// fallback to pseudo-random
for i := 0; i < length; i++ {
buf[i] = dict[pseudoRand.Intn(len(dict))]
}
} else {
for i := 0; i < length; i++ {
buf[i] = dict[int(buf[i])%len(dict)]
}
max := big.NewInt(int64(len(dict)))
for i := 0; i < length; i++ {
buf[i] = dict[newRandomInt(max)]
}
return string(buf)
}
func init() {

14
server.conf.in

@ -88,6 +88,8 @@ encryptionSecret = tne-default-encryption-block-key @@ -88,6 +88,8 @@ encryptionSecret = tne-default-encryption-block-key
; Whether a user account is required to create a room. This only has an effect
; if user accounts are enabled. Optional, defaults to false.
;authorizeRoomCreation = false
; Wether the pipelines API should be enabled. Optional, defaults to false.
;pipelinesEnabled = false
; Server token is a public random string which is used to enhance security of
; server generated security tokens. When the serverToken is changed all existing
; nonces become invalid. Use 32 or 64 characters (eg. 16 or 32 byte hex).
@ -194,3 +196,15 @@ enabled = false @@ -194,3 +196,15 @@ enabled = false
; enable userid creation/registration. Users are created according the settings
; of the currently configured mode (see above).
;allowRegistration = false
[nats]
; Set to true, to enable triggering channelling events via NATS
;channelling_trigger = false
;channelling_trigger_subject = channelling.trigger
; NATS server URL
;url = nats://127.0.0.1:4222
; NATS connect establish timeout in seconds
;establishTimeout = 60
; Use client_id to distinguish between multipe servers. The value is sent
; together with every NATS request. Defaults to empty.
;client_id =

329
src/app/spreed-webrtc-server/channelling_api.go

@ -1,329 +0,0 @@ @@ -1,329 +0,0 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package main
import (
"log"
"time"
)
const (
maxConferenceSize = 100
apiVersion = 1.4 // Keep this in sync with CHANNELING-API docs.Hand
)
type ChannellingAPI interface {
OnConnect(Client, *Session) (interface{}, error)
OnDisconnect(Client, *Session)
OnIncoming(Sender, *Session, *DataIncoming) (interface{}, error)
}
type channellingAPI struct {
*Config
RoomStatusManager
SessionEncoder
SessionManager
StatsCounter
ContactManager
TurnDataCreator
Unicaster
}
func NewChannellingAPI(config *Config, roomStatus RoomStatusManager, sessionEncoder SessionEncoder, sessionManager SessionManager, statsCounter StatsCounter, contactManager ContactManager, turnDataCreator TurnDataCreator, unicaster Unicaster) ChannellingAPI {
return &channellingAPI{
config,
roomStatus,
sessionEncoder,
sessionManager,
statsCounter,
contactManager,
turnDataCreator,
unicaster,
}
}
func (api *channellingAPI) OnConnect(client Client, session *Session) (interface{}, error) {
api.Unicaster.OnConnect(client, session)
return api.HandleSelf(session)
}
func (api *channellingAPI) OnDisconnect(client Client, session *Session) {
api.Unicaster.OnDisconnect(client, session)
}
func (api *channellingAPI) OnIncoming(sender Sender, session *Session, msg *DataIncoming) (interface{}, error) {
switch msg.Type {
case "Self":
return api.HandleSelf(session)
case "Hello":
if msg.Hello == nil {
return nil, NewDataError("bad_request", "message did not contain Hello")
}
return api.HandleHello(session, msg.Hello, sender)
case "Offer":
if msg.Offer == nil {
log.Println("Received invalid offer message.", msg)
break
}
// TODO(longsleep): Validate offer
session.Unicast(msg.Offer.To, msg.Offer)
case "Candidate":
if msg.Candidate == nil {
log.Println("Received invalid candidate message.", msg)
break
}
// TODO(longsleep): Validate candidate
session.Unicast(msg.Candidate.To, msg.Candidate)
case "Answer":
if msg.Answer == nil {
log.Println("Received invalid answer message.", msg)
break
}
// TODO(longsleep): Validate Answer
session.Unicast(msg.Answer.To, msg.Answer)
case "Users":
return api.HandleUsers(session)
case "Authentication":
if msg.Authentication == nil || msg.Authentication.Authentication == nil {
return nil, NewDataError("bad_request", "message did not contain Authentication")
}
return api.HandleAuthentication(session, msg.Authentication.Authentication)
case "Bye":
if msg.Bye == nil {
log.Println("Received invalid bye message.", msg)
break
}
session.Unicast(msg.Bye.To, msg.Bye)
case "Status":
if msg.Status == nil {
log.Println("Received invalid status message.", msg)
break
}
//log.Println("Status", msg.Status)
session.Update(&SessionUpdate{Types: []string{"Status"}, Status: msg.Status.Status})
session.BroadcastStatus()
case "Chat":
if msg.Chat == nil || msg.Chat.Chat == nil {
log.Println("Received invalid chat message.", msg)
break
}
api.HandleChat(session, msg.Chat)
case "Conference":
if msg.Conference == nil {
log.Println("Received invalid conference message.", msg)
break
}
api.HandleConference(session, msg.Conference)
case "Alive":
return msg.Alive, nil
case "Sessions":
if msg.Sessions == nil || msg.Sessions.Sessions == nil {
return nil, NewDataError("bad_request", "message did not contain Sessions")
}
return api.HandleSessions(session, msg.Sessions.Sessions)
case "Room":
if msg.Room == nil {
return nil, NewDataError("bad_request", "message did not contain Room")
}
return api.HandleRoom(session, msg.Room)
default:
log.Println("OnText unhandled message type", msg.Type)
}
return nil, nil
}
func (api *channellingAPI) HandleSelf(session *Session) (*DataSelf, error) {
token, err := api.EncodeSessionToken(session)
if err != nil {
log.Println("Error in OnRegister", err)
return nil, err
}
log.Println("Created new session token", len(token), token)
self := &DataSelf{
Type: "Self",
Id: session.Id,
Sid: session.Sid,
Userid: session.Userid(),
Suserid: api.EncodeSessionUserID(session),
Token: token,
Version: api.Version,
ApiVersion: apiVersion,
Turn: api.CreateTurnData(session),
Stun: api.StunURIs,
}
return self, nil
}
func (api *channellingAPI) HandleHello(session *Session, hello *DataHello, sender Sender) (*DataWelcome, error) {
// TODO(longsleep): Filter room id and user agent.
session.Update(&SessionUpdate{Types: []string{"Ua"}, Ua: hello.Ua})
// Compatibily for old clients.
roomName := hello.Name
if roomName == "" {
roomName = hello.Id
}
room, err := session.JoinRoom(roomName, hello.Type, hello.Credentials, sender)
if err != nil {
return nil, err
}
return &DataWelcome{
Type: "Welcome",
Room: room,
Users: api.RoomUsers(session),
}, nil
}
func (api *channellingAPI) HandleUsers(session *Session) (sessions *DataSessions, err error) {
if session.Hello {
sessions = &DataSessions{Type: "Users", Users: api.RoomUsers(session)}
} else {
err = NewDataError("not_in_room", "Cannot list users without a current room")
}
return
}
func (api *channellingAPI) HandleAuthentication(session *Session, st *SessionToken) (*DataSelf, error) {
if err := api.Authenticate(session, st, ""); err != nil {
log.Println("Authentication failed", err, st.Userid, st.Nonce)
return nil, err
}
log.Println("Authentication success", session.Userid())
self, err := api.HandleSelf(session)
if err == nil {
session.BroadcastStatus()
}
return self, err
}
func (api *channellingAPI) HandleChat(session *Session, chat *DataChat) {
// TODO(longsleep): Limit sent chat messages per incoming connection.
msg := chat.Chat
to := chat.To
if !msg.NoEcho {
session.Unicast(session.Id, chat)
}
msg.Time = time.Now().Format(time.RFC3339)
if to == "" {
// TODO(longsleep): Check if chat broadcast is allowed.
if session.Hello {
api.CountBroadcastChat()
session.Broadcast(chat)
}
} else {
if msg.Status != nil {
if msg.Status.ContactRequest != nil {
if !api.Config.WithModule("contacts") {
return
}
if err := api.contactrequestHandler(session, to, msg.Status.ContactRequest); err != nil {
log.Println("Ignoring invalid contact request.", err)
return
}
msg.Status.ContactRequest.Userid = session.Userid()
}
} else {
api.CountUnicastChat()
}
session.Unicast(to, chat)
if msg.Mid != "" {
// Send out delivery confirmation status chat message.
session.Unicast(session.Id, &DataChat{To: to, Type: "Chat", Chat: &DataChatMessage{Mid: msg.Mid, Status: &DataChatStatus{State: "sent"}}})
}
}
}
func (api *channellingAPI) HandleConference(session *Session, conference *DataConference) {
// Check conference maximum size.
if len(conference.Conference) > maxConferenceSize {
log.Println("Refusing to create conference above limit.", len(conference.Conference))
return
}
// Send conference update to anyone.
for _, id := range conference.Conference {
if id != session.Id {
session.Unicast(id, conference)
}
}
}
func (api *channellingAPI) HandleSessions(session *Session, sessions *DataSessionsRequest) (*DataSessions, error) {
switch sessions.Type {
case "contact":
if !api.Config.WithModule("contacts") {
return nil, NewDataError("contacts_not_enabled", "incoming contacts session request with contacts disabled")
}
userID, err := api.getContactID(session, sessions.Token)
if err != nil {
return nil, err
}
return &DataSessions{
Type: "Sessions",
Users: api.GetUserSessions(session, userID),
Sessions: sessions,
}, nil
case "session":
id, err := session.attestation.Decode(sessions.Token)
if err != nil {
return nil, NewDataError("bad_attestation", err.Error())
}
session, ok := api.GetSession(id)
if !ok {
return nil, NewDataError("no_such_session", "cannot retrieve session")
}
return &DataSessions{
Type: "Sessions",
Users: []*DataSession{session.Data()},
Sessions: sessions,
}, nil
default:
return nil, NewDataError("bad_request", "unknown sessions request type")
}
}
func (api *channellingAPI) HandleRoom(session *Session, room *DataRoom) (*DataRoom, error) {
room, err := api.UpdateRoom(session, room)
if err == nil {
session.Broadcast(room)
}
return room, err
}

56
src/app/spreed-webrtc-server/handler_image.go

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package main
import (
"net/http"
"strconv"
"time"
"github.com/strukturag/spreed-webrtc/go/channelling"
"github.com/gorilla/mux"
)
func makeImageHandler(buddyImages channelling.ImageCache, expires time.Duration) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
image := buddyImages.Get(vars["imageid"])
if image == nil {
http.Error(w, "Unknown image", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", image.MimeType())
w.Header().Set("ETag", image.LastChangeID())
age := time.Now().Sub(image.LastChange())
if age >= time.Second {
w.Header().Set("Age", strconv.Itoa(int(age.Seconds())))
}
if expires >= time.Second {
w.Header().Set("Expires", time.Now().Add(expires).Format(time.RFC1123))
w.Header().Set("Cache-Control", "public, no-transform, max-age="+strconv.Itoa(int(expires.Seconds())))
}
http.ServeContent(w, r, "", image.LastChange(), image.Reader())
}
}

30
src/app/spreed-webrtc-server/handler_main.go

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package main
import (
"net/http"
)
func mainHandler(w http.ResponseWriter, r *http.Request) {
handleRoomView("", w, r)
}

91
src/app/spreed-webrtc-server/handler_room.go

@ -0,0 +1,91 @@ @@ -0,0 +1,91 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package main
import (
"net/http"
"github.com/strukturag/spreed-webrtc/go/channelling"
"github.com/gorilla/mux"
)
func roomHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
handleRoomView(vars["room"], w, r)
}
func handleRoomView(room string, w http.ResponseWriter, r *http.Request) {
var err error
w.Header().Set("Content-Type", "text/html; charset=UTF-8")
w.Header().Set("Expires", "-1")
w.Header().Set("Cache-Control", "private, max-age=0")
csp := false
if config.ContentSecurityPolicy != "" {
w.Header().Set("Content-Security-Policy", config.ContentSecurityPolicy)
csp = true
}
if config.ContentSecurityPolicyReportOnly != "" {
w.Header().Set("Content-Security-Policy-Report-Only", config.ContentSecurityPolicyReportOnly)
csp = true
}
scheme := "http"
// Detect if the request was made with SSL.
ssl := r.TLS != nil
proto, ok := r.Header["X-Forwarded-Proto"]
if ok {
ssl = proto[0] == "https"
scheme = "https"
}
// Get languages from request.
langs := getRequestLanguages(r, []string{})
if len(langs) == 0 {
langs = append(langs, "en")
}
// Prepare context to deliver to HTML..
context := &channelling.Context{Cfg: config, App: "main", Host: r.Host, Scheme: scheme, Ssl: ssl, Csp: csp, Languages: langs, Room: room}
// Get URL parameters.
r.ParseForm()
// Check if incoming request is a crawler which supports AJAX crawling.
// See https://developers.google.com/webmasters/ajax-crawling/docs/getting-started for details.
if _, ok := r.Form["_escaped_fragment_"]; ok {
// Render crawlerPage template..
err = templates.ExecuteTemplate(w, "crawlerPage", &context)
} else {
// Render mainPage template.
err = templates.ExecuteTemplate(w, "mainPage", &context)
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

79
src/app/spreed-webrtc-server/handler_sandbox.go

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package main
import (
"fmt"
"net/http"
"net/url"
"github.com/strukturag/spreed-webrtc/go/channelling"
"github.com/gorilla/mux"
)
func sandboxHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
// NOTE(longsleep): origin_scheme is window.location.protocol (eg. https:, http:).
originURL, err := url.Parse(fmt.Sprintf("%s//%s", vars["origin_scheme"], vars["origin_host"]))
if err != nil || originURL.Scheme == "" || originURL.Host == "" {
http.Error(w, "Invalid origin path", http.StatusBadRequest)
return
}
origin := fmt.Sprintf("%s://%s", originURL.Scheme, originURL.Host)
handleSandboxView(vars["sandbox"], origin, w, r)
}
func handleSandboxView(sandbox string, origin string, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=UTF-8")
w.Header().Set("Expires", "-1")
w.Header().Set("Cache-Control", "private, max-age=0")
sandboxTemplateName := fmt.Sprintf("%s_sandbox.html", sandbox)
// Prepare context to deliver to HTML..
if t := templates.Lookup(sandboxTemplateName); t != nil {
// CSP support for sandboxes.
var csp string
switch sandbox {
case "odfcanvas":
csp = fmt.Sprintf("default-src 'none'; script-src %s; img-src data: blob:; style-src 'unsafe-inline'", origin)
case "pdfcanvas":
csp = fmt.Sprintf("default-src 'none'; script-src %s 'unsafe-eval'; img-src 'self' data: blob:; style-src 'unsafe-inline'", origin)
default:
csp = "default-src 'none'"
}
w.Header().Set("Content-Security-Policy", csp)
// Prepare context to deliver to HTML..
context := &channelling.Context{Cfg: config, Origin: origin, Csp: true}
err := t.Execute(w, &context)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
} else {
http.Error(w, "404 Unknown Sandbox", http.StatusNotFound)
}
}

59
src/app/spreed-webrtc-server/handler_wellknown.go

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package main
import (
"encoding/json"
"net/http"
"net/url"
"strings"
)
func wellKnownHandler(w http.ResponseWriter, r *http.Request) {
// Detect if the request was made with SSL.
ssl := r.TLS != nil
scheme := "http"
proto, ok := r.Header["X-Forwarded-Proto"]
if ok {
ssl = proto[0] == "https"
}
if ssl {
scheme = "https"
}
// Construct our URL.
url := url.URL{
Scheme: scheme,
Host: r.Host,
Path: strings.TrimSuffix(config.B, "/"),
}
doc := &map[string]string{
"spreed-webrtc_endpoint": url.String(),
}
data, err := json.MarshalIndent(doc, "", " ")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
w.Header().Set("Content-Type", "application/json")
w.Write(data)
}

27
src/app/spreed-webrtc-server/ws.go → src/app/spreed-webrtc-server/handler_ws.go

@ -25,6 +25,9 @@ import ( @@ -25,6 +25,9 @@ import (
"log"
"net/http"
"github.com/strukturag/spreed-webrtc/go/channelling"
"github.com/strukturag/spreed-webrtc/go/channelling/server"
"github.com/gorilla/websocket"
)
@ -50,7 +53,7 @@ var ( @@ -50,7 +53,7 @@ var (
}
)
func makeWSHandler(connectionCounter ConnectionCounter, sessionManager SessionManager, codec Codec, channellingAPI ChannellingAPI) http.HandlerFunc {
func makeWSHandler(connectionCounter channelling.ConnectionCounter, sessionManager channelling.SessionManager, codec channelling.Codec, channellingAPI channelling.ChannellingAPI, users *server.Users) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Validate incoming request.
if r.Method != "GET" {
@ -67,13 +70,25 @@ func makeWSHandler(connectionCounter ConnectionCounter, sessionManager SessionMa @@ -67,13 +70,25 @@ func makeWSHandler(connectionCounter ConnectionCounter, sessionManager SessionMa
return
}
r.ParseForm()
token := r.FormValue("t")
st := sessionManager.DecodeSessionToken(token)
var userid string
if users != nil {
userid, _ = users.GetUserID(r)
if userid == "" {
userid = st.Userid
}
}
// Create a new connection instance.
session := sessionManager.CreateSession(r)
client := NewClient(codec, channellingAPI, session)
conn := NewConnection(connectionCounter.CountConnection(), ws, client)
session := sessionManager.CreateSession(st, userid)
client := channelling.NewClient(codec, channellingAPI, session)
conn := channelling.NewConnection(connectionCounter.CountConnection(), ws, client)
// Start pumps (readPump blocks).
go conn.writePump()
conn.readPump()
go conn.WritePump()
conn.ReadPump()
}
}

272
src/app/spreed-webrtc-server/main.go

@ -22,197 +22,40 @@ @@ -22,197 +22,40 @@
package main
import (
"bytes"
"crypto/rand"
"encoding/hex"
"flag"
"fmt"
"github.com/gorilla/mux"
"github.com/strukturag/goacceptlanguageparser"
"github.com/strukturag/httputils"
"github.com/strukturag/phoenix"
"github.com/strukturag/sloth"
"html/template"
"log"
"net/http"
_ "net/http/pprof"
"net/url"
"os"
"path"
"path/filepath"
goruntime "runtime"
"strconv"
"strings"
"syscall"
"time"
"github.com/strukturag/spreed-webrtc/go/channelling"
"github.com/strukturag/spreed-webrtc/go/channelling/api"
"github.com/strukturag/spreed-webrtc/go/channelling/server"
"github.com/strukturag/spreed-webrtc/go/natsconnection"
"github.com/gorilla/mux"
"github.com/strukturag/httputils"
"github.com/strukturag/phoenix"
"github.com/strukturag/sloth"
)
var version = "unreleased"
var defaultConfig = "./server.conf"
var templates *template.Template
var config *Config
// Helper to retrieve languages from request.
func getRequestLanguages(r *http.Request, supportedLanguages []string) []string {
acceptLanguageHeader, ok := r.Header["Accept-Language"]
var langs []string
if ok {
langs = goacceptlanguageparser.ParseAcceptLanguage(acceptLanguageHeader[0], supportedLanguages)
}
return langs
}
func mainHandler(w http.ResponseWriter, r *http.Request) {
handleRoomView("", w, r)
}
func roomHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
handleRoomView(vars["room"], w, r)
}
func sandboxHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
// NOTE(longsleep): origin_scheme is window.location.protocol (eg. https:, http:).
originURL, err := url.Parse(fmt.Sprintf("%s//%s", vars["origin_scheme"], vars["origin_host"]))
if err != nil || originURL.Scheme == "" || originURL.Host == "" {
http.Error(w, "Invalid origin path", http.StatusBadRequest)
return
}
origin := fmt.Sprintf("%s://%s", originURL.Scheme, originURL.Host)
handleSandboxView(vars["sandbox"], origin, w, r)
}
func makeImageHandler(buddyImages ImageCache, expires time.Duration) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
image := buddyImages.Get(vars["imageid"])
if image == nil {
http.Error(w, "Unknown image", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", image.mimetype)
w.Header().Set("ETag", image.lastChangeId)
age := time.Now().Sub(image.lastChange)
if age >= time.Second {
w.Header().Set("Age", strconv.Itoa(int(age.Seconds())))
}
if expires >= time.Second {
w.Header().Set("Expires", time.Now().Add(expires).Format(time.RFC1123))
w.Header().Set("Cache-Control", "public, no-transform, max-age="+strconv.Itoa(int(expires.Seconds())))
}
http.ServeContent(w, r, "", image.lastChange, bytes.NewReader(image.data))
}
}
func handleRoomView(room string, w http.ResponseWriter, r *http.Request) {
var err error
w.Header().Set("Content-Type", "text/html; charset=UTF-8")
w.Header().Set("Expires", "-1")
w.Header().Set("Cache-Control", "private, max-age=0")
csp := false
if config.contentSecurityPolicy != "" {
w.Header().Set("Content-Security-Policy", config.contentSecurityPolicy)
csp = true
}
if config.contentSecurityPolicyReportOnly != "" {
w.Header().Set("Content-Security-Policy-Report-Only", config.contentSecurityPolicyReportOnly)
csp = true
}
scheme := "http"
// Detect if the request was made with SSL.
ssl := r.TLS != nil
proto, ok := r.Header["X-Forwarded-Proto"]
if ok {
ssl = proto[0] == "https"
scheme = "https"
}
// Get languages from request.
langs := getRequestLanguages(r, []string{})
if len(langs) == 0 {
langs = append(langs, "en")
}
// Prepare context to deliver to HTML..
context := &Context{Cfg: config, App: "main", Host: r.Host, Scheme: scheme, Ssl: ssl, Csp: csp, Languages: langs, Room: room}
// Get URL parameters.
r.ParseForm()
// Check if incoming request is a crawler which supports AJAX crawling.
// See https://developers.google.com/webmasters/ajax-crawling/docs/getting-started for details.
if _, ok := r.Form["_escaped_fragment_"]; ok {
// Render crawlerPage template..
err = templates.ExecuteTemplate(w, "crawlerPage", &context)
} else {
// Render mainPage template.
err = templates.ExecuteTemplate(w, "mainPage", &context)
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func handleSandboxView(sandbox string, origin string, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=UTF-8")
w.Header().Set("Expires", "-1")
w.Header().Set("Cache-Control", "private, max-age=0")
sandboxTemplateName := fmt.Sprintf("%s_sandbox.html", sandbox)
// Prepare context to deliver to HTML..
if t := templates.Lookup(sandboxTemplateName); t != nil {
// CSP support for sandboxes.
var csp string
switch sandbox {
case "odfcanvas":
csp = fmt.Sprintf("default-src 'none'; script-src %s; img-src data: blob:; style-src 'unsafe-inline'", origin)
case "pdfcanvas":
csp = fmt.Sprintf("default-src 'none'; script-src %s 'unsafe-eval'; img-src 'self' data: blob:; style-src 'unsafe-inline'", origin)
default:
csp = "default-src 'none'"
}
w.Header().Set("Content-Security-Policy", csp)
// Prepare context to deliver to HTML..
context := &Context{Cfg: config, Origin: origin, Csp: true}
err := t.Execute(w, &context)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
} else {
http.Error(w, "404 Unknown Sandbox", http.StatusNotFound)
}
}
var config *channelling.Config
func runner(runtime phoenix.Runtime) error {
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
rootFolder, err := runtime.GetString("http", "root")
@ -245,6 +88,11 @@ func runner(runtime phoenix.Runtime) error { @@ -245,6 +88,11 @@ func runner(runtime phoenix.Runtime) error {
}()
}
pipelinesEnabled, err := runtime.GetBool("app", "pipelinesEnabled")
if err != nil {
pipelinesEnabled = false
}
var sessionSecret []byte
sessionSecretString, err := runtime.GetString("app", "sessionSecret")
if err != nil {
@ -300,14 +148,29 @@ func runner(runtime phoenix.Runtime) error { @@ -300,14 +148,29 @@ func runner(runtime phoenix.Runtime) error {
}
}
var tokenProvider TokenProvider
var tokenProvider channelling.TokenProvider
if tokenFile != "" {
log.Printf("Using token authorization from %s\n", tokenFile)
tokenProvider = TokenFileProvider(tokenFile)
tokenProvider = channelling.TokenFileProvider(tokenFile)
}
// Nats pub/sub supports.
natsChannellingTrigger, _ := runtime.GetBool("nats", "channelling_trigger")
natsChannellingTriggerSubject, _ := runtime.GetString("nats", "channelling_trigger_subject")
if natsURL, err := runtime.GetString("nats", "url"); err == nil {
if natsURL != "" {
natsconnection.DefaultURL = natsURL
}
}
if natsEstablishTimeout, err := runtime.GetInt("nats", "establishTimeout"); err == nil {
if natsEstablishTimeout != 0 {
natsconnection.DefaultEstablishTimeout = time.Duration(natsEstablishTimeout) * time.Second
}
}
natsClientId, _ := runtime.GetString("nats", "client_id")
// Load remaining configuration items.
config = NewConfig(runtime, tokenProvider != nil)
config = server.NewConfig(runtime, tokenProvider != nil)
// Load templates.
templates = template.New("")
@ -400,14 +263,23 @@ func runner(runtime phoenix.Runtime) error { @@ -400,14 +263,23 @@ func runner(runtime phoenix.Runtime) error {
}
// Prepare services.
buddyImages := NewImageCache()
codec := NewCodec(incomingCodecLimit)
roomManager := NewRoomManager(config, codec)
hub := NewHub(config, sessionSecret, encryptionSecret, turnSecret, codec)
tickets := NewTickets(sessionSecret, encryptionSecret, computedRealm)
sessionManager := NewSessionManager(config, tickets, hub, roomManager, roomManager, buddyImages, sessionSecret)
statsManager := NewStatsManager(hub, roomManager, sessionManager)
channellingAPI := NewChannellingAPI(config, roomManager, tickets, sessionManager, statsManager, hub, hub, hub)
apiConsumer := channelling.NewChannellingAPIConsumer()
buddyImages := channelling.NewImageCache()
codec := channelling.NewCodec(incomingCodecLimit)
roomManager := channelling.NewRoomManager(config, codec)
hub := channelling.NewHub(config, sessionSecret, encryptionSecret, turnSecret, codec)
tickets := channelling.NewTickets(sessionSecret, encryptionSecret, computedRealm)
sessionManager := channelling.NewSessionManager(config, tickets, hub, roomManager, roomManager, buddyImages, sessionSecret)
statsManager := channelling.NewStatsManager(hub, roomManager, sessionManager)
busManager := channelling.NewBusManager(apiConsumer, natsClientId, natsChannellingTrigger, natsChannellingTriggerSubject)
pipelineManager := channelling.NewPipelineManager(busManager, sessionManager, sessionManager, sessionManager)
// Create API.
channellingAPI := api.New(config, roomManager, tickets, sessionManager, statsManager, hub, hub, hub, busManager, pipelineManager)
apiConsumer.SetChannellingAPI(channellingAPI)
// Start bus.
busManager.Start()
// Add handlers.
r.HandleFunc("/", httputils.MakeGzipHandler(mainHandler))
@ -415,32 +287,36 @@ func runner(runtime phoenix.Runtime) error { @@ -415,32 +287,36 @@ func runner(runtime phoenix.Runtime) error {
r.Handle("/static/{path:.*}", http.StripPrefix(config.B, httputils.FileStaticServer(http.Dir(rootFolder))))
r.Handle("/robots.txt", http.StripPrefix(config.B, http.FileServer(http.Dir(path.Join(rootFolder, "static")))))
r.Handle("/favicon.ico", http.StripPrefix(config.B, http.FileServer(http.Dir(path.Join(rootFolder, "static", "img")))))
r.Handle("/ws", makeWSHandler(statsManager, sessionManager, codec, channellingAPI))
// Simple room handler.
r.HandleFunc("/{room}", httputils.MakeGzipHandler(roomHandler))
r.HandleFunc("/.well-known/spreed-configuration", wellKnownHandler)
// Sandbox handler.
r.HandleFunc("/sandbox/{origin_scheme}/{origin_host}/{sandbox}.html", httputils.MakeGzipHandler(sandboxHandler))
// Add API end points.
api := sloth.NewAPI()
api.SetMux(r.PathPrefix("/api/v1/").Subrouter())
api.AddResource(&Rooms{}, "/rooms")
api.AddResource(config, "/config")
api.AddResourceWithWrapper(&Tokens{tokenProvider}, httputils.MakeGzipHandler, "/tokens")
// Add RESTful API end points.
rest := sloth.NewAPI()
rest.SetMux(r.PathPrefix("/api/v1/").Subrouter())
rest.AddResource(&server.Rooms{}, "/rooms")
rest.AddResource(config, "/config")
rest.AddResourceWithWrapper(&server.Tokens{tokenProvider}, httputils.MakeGzipHandler, "/tokens")
var users *server.Users
if config.UsersEnabled {
// Create Users handler.
users := NewUsers(hub, tickets, sessionManager, config.UsersMode, serverRealm, runtime)
api.AddResource(&Sessions{tickets, hub, users}, "/sessions/{id}/")
users = server.NewUsers(hub, tickets, sessionManager, config.UsersMode, serverRealm, runtime)
rest.AddResource(&server.Sessions{tickets, hub, users}, "/sessions/{id}/")
if config.UsersAllowRegistration {
api.AddResource(users, "/users")
rest.AddResource(users, "/users")
}
}
if statsEnabled {
api.AddResourceWithWrapper(&Stats{statsManager}, httputils.MakeGzipHandler, "/stats")
rest.AddResourceWithWrapper(&server.Stats{statsManager}, httputils.MakeGzipHandler, "/stats")
log.Println("Stats are enabled!")
}
if pipelinesEnabled {
pipelineManager.Start()
rest.AddResource(&server.Pipelines{pipelineManager, channellingAPI}, "/pipelines/{id}")
log.Println("Pipelines API is enabled!")
}
// Add extra/static support if configured and exists.
if extraFolder != "" {
@ -451,6 +327,12 @@ func runner(runtime phoenix.Runtime) error { @@ -451,6 +327,12 @@ func runner(runtime phoenix.Runtime) error {
}
}
// Finally add websocket handler.
r.Handle("/ws", makeWSHandler(statsManager, sessionManager, codec, channellingAPI, users))
// Simple room handler.
r.HandleFunc("/{room}", httputils.MakeGzipHandler(roomHandler))
// Map everything else to a room when it is a GET.
rooms := r.PathPrefix("/").Methods("GET").Subrouter()
rooms.HandleFunc("/{room:.*}", httputils.MakeGzipHandler(roomHandler))
@ -459,7 +341,9 @@ func runner(runtime phoenix.Runtime) error { @@ -459,7 +341,9 @@ func runner(runtime phoenix.Runtime) error {
}
func boot() error {
defaultConfigPath := flag.String("dc", "", "Default configuration file.")
configPath := flag.String("c", defaultConfig, "Configuration file.")
overrideConfigPath := flag.String("oc", "", "Override configuration file.")
logPath := flag.String("l", "", "Log file, defaults to stderr.")
showVersion := flag.Bool("v", false, "Display version number and exit.")
memprofile := flag.String("memprofile", "", "Write memory profile to this file.")
@ -476,7 +360,9 @@ func boot() error { @@ -476,7 +360,9 @@ func boot() error {
}
return phoenix.NewServer("server", version).
DefaultConfig(defaultConfigPath).
Config(configPath).
OverrideConfig(overrideConfigPath).
Log(logPath).
CpuProfile(cpuprofile).
MemProfile(memprofile).

17
src/app/spreed-webrtc-server/utils.go

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
package main
import (
"net/http"
"github.com/strukturag/goacceptlanguageparser"
)
// Helper to retrieve languages from request.
func getRequestLanguages(r *http.Request, supportedLanguages []string) []string {
acceptLanguageHeader, ok := r.Header["Accept-Language"]
var langs []string
if ok {
langs = goacceptlanguageparser.ParseAcceptLanguage(acceptLanguageHeader[0], supportedLanguages)
}
return langs
}

12
static/js/controllers/uicontroller.js

@ -232,6 +232,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web @@ -232,6 +232,7 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web
var pickupTimeout = null;
var autoAcceptTimeout = null;
var ringerTimeout = null;
$scope.updateAutoAccept = function(id, from) {
if (id) {
@ -726,8 +727,19 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web @@ -726,8 +727,19 @@ define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'web
$scope.$on("status", function(event, status) {
if (status === "connecting" && dialerEnabled) {
dialer.start();
// Start accept timeout.
ringerTimeout = $timeout(function() {
console.log("Ringer timeout reached.");
mediaStream.webrtc.doHangup("ringertimeout");
$scope.$emit("notification", "pickuptimeout", {
reason: 'pickuptimeout',
from: $scope.dialing
});
}, 35000);
} else {
dialer.stop();
$timeout.cancel(ringerTimeout);
ringerTimeout = null;
}
safeApply($scope, function(scope) {
var old = $scope.status;

53
static/js/services/playpromise.js

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
"use strict";
define(["underscore"], function(_) {
function noopThen() {
// Automatic playback started.
}
function noopCatch(error) {
// Automatic playback failed.
}
// playPromise
return function() {
return function(elem, thenFunc, catchFunc) {
// Starting with Chome 50 play() returns a promise.
// https://developers.google.com/web/updates/2016/03/play-returns-promise
var playPromise = elem.play()
if (playPromise !== undefined) {
if (!thenFunc) {
thenFunc = noopThen;
}
if (!catchFunc) {
catchFunc = noopCatch;
}
playPromise.then(thenFunc).catch(catchFunc);
} else {
if (thenFunc) {
_.defer(thenFunc);
}
}
return playPromise;
}
};
});

9
static/js/services/services.js

@ -70,7 +70,8 @@ define([ @@ -70,7 +70,8 @@ define([
'services/mediadevices',
'services/sandbox',
'services/dummystream',
'services/usermedia'], function(_,
'services/usermedia',
'services/playpromise'], function(_,
desktopNotify,
playSound,
safeApply,
@ -118,7 +119,8 @@ modules, @@ -118,7 +119,8 @@ modules,
mediaDevices,
sandbox,
dummyStream,
userMedia) {
userMedia,
playPromise) {
var services = {
desktopNotify: desktopNotify,
@ -168,7 +170,8 @@ userMedia) { @@ -168,7 +170,8 @@ userMedia) {
mediaDevices: mediaDevices,
sandbox: sandbox,
dummyStream: dummyStream,
userMedia: userMedia
userMedia: userMedia,
playPromise: playPromise
};
var initialize = function(angModule) {

12
static/js/services/videolayout.js

@ -59,7 +59,7 @@ define(["jquery", "underscore", "modernizr", "injectCSS"], function($, _, Modern @@ -59,7 +59,7 @@ define(["jquery", "underscore", "modernizr", "injectCSS"], function($, _, Modern
var objectFitSupport = Modernizr["object-fit"] && true;
// videoLayout
return ["$window", function($window) {
return ["$window", "playPromise", function($window, playPromise) {
// Invisible layout (essentially shows nothing).
var Invisible = function(container, scope, controller) {};
@ -189,7 +189,7 @@ define(["jquery", "underscore", "modernizr", "injectCSS"], function($, _, Modern @@ -189,7 +189,7 @@ define(["jquery", "underscore", "modernizr", "injectCSS"], function($, _, Modern
var $mini = $(scope.mini);
this.miniParent = $mini.parent();
$mini.prependTo(scope.remoteVideos);
$mini.find("video")[0].play();
playPromise($mini.find("video")[0]);
this.countSelfAsRemote = true;
}
Democrazy.prototype = Object.create(OnePeople.prototype);
@ -199,7 +199,7 @@ define(["jquery", "underscore", "modernizr", "injectCSS"], function($, _, Modern @@ -199,7 +199,7 @@ define(["jquery", "underscore", "modernizr", "injectCSS"], function($, _, Modern
OnePeople.prototype.close.call(this, container, scope, controller);
var $mini = $(scope.mini);
$mini.appendTo(this.miniParent);
$mini.find("video")[0].play();
playPromise($mini.find("video")[0]);
this.miniParent = null;
};
@ -232,12 +232,12 @@ define(["jquery", "underscore", "modernizr", "injectCSS"], function($, _, Modern @@ -232,12 +232,12 @@ define(["jquery", "underscore", "modernizr", "injectCSS"], function($, _, Modern
if (this.big) {
// Add old video back.
this.big.insertAfter(remoteVideo);
this.big.find("video")[0].play();
playPromise(this.big.find("video")[0]);
}
this.big = remoteVideo;
remoteVideo.appendTo(this.bigVideo);
remoteVideo.find("video")[0].play();
playPromise(remoteVideo.find("video")[0]);
};
@ -292,7 +292,7 @@ define(["jquery", "underscore", "modernizr", "injectCSS"], function($, _, Modern @@ -292,7 +292,7 @@ define(["jquery", "underscore", "modernizr", "injectCSS"], function($, _, Modern
this.closed = true;
if (this.big) {
this.remoteVideos.append(this.big);
this.big.find("video")[0].play();
playPromise(this.big.find("video")[0]);
}
this.big = null;
this.bigVideo.remove()

Loading…
Cancel
Save