Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
b24ad687df | |||
dd1c6b7968 | |||
224263afad | |||
d2dd96dcd3 | |||
48d58f4cc2 | |||
19a665d61d | |||
d5f6a5f2cb | |||
a17886e300 | |||
3af9423871 | |||
5a6c798e93 | |||
02e8b407c3 | |||
e44298c172 | |||
8df09ddb11 | |||
bd59534bac | |||
0679ad31a7 | |||
83ecd84d78 | |||
055a73fb05 | |||
584a6b632f | |||
d90b08f45e | |||
7934c926d9 | |||
|
8364e9a146 | ||
|
ab89f5f93f | ||
|
29bcf75470 | ||
|
fcddbf85b0 |
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,6 +1,2 @@
|
|||||||
nmdc-webfrontend
|
clientpack/**
|
||||||
nmdc-webfrontend.exe
|
node_modules/**
|
||||||
nmdc-webfrontend.conf
|
|
||||||
clientpack/
|
|
||||||
_dist/
|
|
||||||
node_modules/
|
|
12
.hgignore
Normal file
12
.hgignore
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
mode:regex
|
||||||
|
|
||||||
|
\.exe$
|
||||||
|
^nmdc-webfrontend\.conf$
|
||||||
|
^clientpack/
|
||||||
|
^node_modules/
|
||||||
|
|
||||||
|
^_dist/
|
||||||
|
^vendor/
|
||||||
|
|
||||||
|
^nmdc-webfrontend$
|
||||||
|
^nmdc-webfrontend\.exe$
|
11
.hgtags
Normal file
11
.hgtags
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
769fad81e3f8db8f7e5f5c164656a382a169d735 v1.0.0
|
||||||
|
9ed95938d809a8226aca529e34b655e6d8c8c379 v1.0.1
|
||||||
|
46fe533682419c8a519836ac95b5575053aa0fa8 v1.0.2
|
||||||
|
a2c92b262f339f82eb01c8d92dda252a27432255 v1.1.0
|
||||||
|
d14041daa7bbbd37ea2ff47aa978b9595af67ca3 v1.1.1
|
||||||
|
7278eb0d067d8ed2a653de6a1feeeb7f76fb9891 v1.1.2
|
||||||
|
6cbd9d59630372c0dff430e3dc6c1fbd7dcee734 v1.1.3
|
||||||
|
76c178b8f27ec894e79b8f73649fcb3e45a73729 v1.1.4
|
||||||
|
0eeab5594ba4d683e6a268ef971675c73b226bd0 v1.2.0
|
||||||
|
c8cd84947e4516215e50639914adfaecf350fe91 v1.2.1
|
||||||
|
0c6b957de43252f89688ba73c4857f6d912b2912 v1.2.2
|
23
.vscode/tasks.json
vendored
Normal file
23
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "0.1.0",
|
||||||
|
"command": "npm",
|
||||||
|
"isShellCommand": true,
|
||||||
|
"showOutput": "always",
|
||||||
|
"suppressTaskName": true,
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"taskName": "install",
|
||||||
|
"args": ["install"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"taskName": "update",
|
||||||
|
"args": ["update"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"taskName": "test",
|
||||||
|
"args": ["run", "test"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -17,7 +17,7 @@ type Config struct {
|
|||||||
|
|
||||||
Hub struct {
|
Hub struct {
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
Port int `json:"port,omitempty"`
|
Port int `json:"port"`
|
||||||
Tag string `json:"tag"`
|
Tag string `json:"tag"`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
33
Gopkg.lock
generated
Normal file
33
Gopkg.lock
generated
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "code.ivysaur.me/libnmdc"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "21847db9fdc1b983285de44e789cd9e921bdf1bb"
|
||||||
|
version = "v0.14.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/googollee/go-engine.io"
|
||||||
|
packages = [".","message","parser","polling","transport","websocket"]
|
||||||
|
revision = "80ae0e43aca17b4c5a6834999d0f2eaa16b9afda"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/googollee/go-socket.io"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "5447e71f36d394766bf855d5714a487596809f0d"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/gorilla/websocket"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
|
||||||
|
version = "v1.2.0"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
inputs-digest = "928468c40a3a664870459e1d5a1fa97fcbc45802fdd498ebf89bcffe0b44ec6e"
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
@ -21,6 +21,10 @@
|
|||||||
# version = "2.4.0"
|
# version = "2.4.0"
|
||||||
|
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "code.ivysaur.me/libnmdc"
|
||||||
|
version = "0.14.0"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/cxmcc/tiger"
|
name = "github.com/googollee/go-socket.io"
|
41
Makefile
41
Makefile
@ -2,22 +2,24 @@
|
|||||||
|
|
||||||
BINNAME=nmdc-webfrontend
|
BINNAME=nmdc-webfrontend
|
||||||
|
|
||||||
VERSION=1.3.1
|
VERSION=1.2.3
|
||||||
|
|
||||||
GOFLAGS=-a \
|
GOFLAGS=-a \
|
||||||
-ldflags "-s -w -X main.VERSION=$(BINNAME)/$(VERSION)" \
|
-ldflags "-s -w -X main.VERSION=$(BINNAME)/$(VERSION)" \
|
||||||
-gcflags "-trimpath ${GOPATH}" \
|
-gcflags "-trimpath ${GOPATH}" \
|
||||||
-asmflags "-trimpath ${GOPATH}"
|
-asmflags "-trimpath ${GOPATH}"
|
||||||
|
|
||||||
SOURCES=client/ go.mod go.sum Makefile Config.go main.go nmdc-webfrontend.conf.SAMPLE
|
.PHONY: all dist deps clean clean-deps
|
||||||
|
|
||||||
.PHONY: all deps clean
|
all: $(BINNAME) $(BINNAME).exe
|
||||||
|
|
||||||
all: $(BINNAME)-$(VERSION)-win32.7z $(BINNAME)-$(VERSION)-linux64.tar.xz $(BINNAME)-$(VERSION)-src.tar.xz
|
dist: $(BINNAME)-$(VERSION)-win32.7z $(BINNAME)-$(VERSION)-linux64.tar.xz $(BINNAME)-$(VERSION)-src.zip
|
||||||
|
|
||||||
deps:
|
deps:
|
||||||
npm install -g less uglify-js less-plugin-clean-css html-minifier
|
npm i
|
||||||
go get -u github.com/jteeuwen/go-bindata/...
|
go get -u github.com/jteeuwen/go-bindata/...
|
||||||
|
go get -u github.com/golang/dep/cmd/dep
|
||||||
|
$(GOPATH)/bin/dep ensure
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f ./$(BINNAME)
|
rm -f ./$(BINNAME)
|
||||||
@ -25,22 +27,21 @@ clean:
|
|||||||
rm -fr ./clientpack
|
rm -fr ./clientpack
|
||||||
rm -f ./bindata.go
|
rm -f ./bindata.go
|
||||||
|
|
||||||
|
clean-deps:
|
||||||
|
rm -fr ./vendor
|
||||||
|
rm -fr ./node_modules
|
||||||
|
|
||||||
bindata.go: client client/*
|
bindata.go: client client/*
|
||||||
rm -fr ./clientpack
|
mkdir -p clientpack
|
||||||
cp -r ./client ./clientpack
|
cp client/favicon.ico client/apple-touch-icon.png clientpack/
|
||||||
( echo ';(function() {' ; cat clientpack/dcwebui.js ; echo '})();' ) | uglifyjs -o clientpack/dcwebui.min.js -c -m --ie8
|
npm run webpack
|
||||||
lessc --clean-css clientpack/dcwebui.css clientpack/dcwebui.min.css
|
cat client/index.htm \
|
||||||
cat clientpack/index.htm \
|
| sed -e '/bundle.js/{i <script>' -e 'r clientpack/bundle.min.js' -e 'a </script>' -e 'd}' \
|
||||||
| sed -e '/dcwebui.css/{i <style>' -e 'r clientpack/dcwebui.min.css' -e 'a </style>' -e 'd}' \
|
> clientpack/index.htm
|
||||||
| sed -e '/dcwebui.js/{i <script>' -e 'r clientpack/dcwebui.min.js' -e 'a </script>' -e 'd}' \
|
|
||||||
| sed -e '/socket.io-1.7.2.js/{i <script>' -e 'r clientpack/socket.io-1.7.2.js' -e 'a </script>' -e 'd}' \
|
|
||||||
> clientpack/index.packed.htm
|
|
||||||
mv clientpack/index.packed.htm clientpack/index.htm
|
|
||||||
html-minifier --collapse-whitespace -o clientpack/index.min.htm clientpack/index.htm
|
html-minifier --collapse-whitespace -o clientpack/index.min.htm clientpack/index.htm
|
||||||
mv clientpack/index.min.htm clientpack/index.htm
|
mv clientpack/index.min.htm clientpack/index.htm
|
||||||
rm ./clientpack/*.js
|
rm clientpack/bundle.min.js
|
||||||
rm ./clientpack/*.css
|
$(GOPATH)/bin/go-bindata -nomemcopy -nometadata -prefix clientpack clientpack
|
||||||
go-bindata -nomemcopy -nometadata -prefix clientpack clientpack
|
|
||||||
|
|
||||||
$(BINNAME).exe: bindata.go *.go
|
$(BINNAME).exe: bindata.go *.go
|
||||||
GOARCH=386 GOOS=windows go build $(GOFLAGS) -o $(BINNAME).exe
|
GOARCH=386 GOOS=windows go build $(GOFLAGS) -o $(BINNAME).exe
|
||||||
@ -54,5 +55,5 @@ $(BINNAME)-$(VERSION)-win32.7z: $(BINNAME).exe nmdc-webfrontend.conf.SAMPLE
|
|||||||
$(BINNAME)-$(VERSION)-linux64.tar.xz: $(BINNAME) nmdc-webfrontend.conf.SAMPLE
|
$(BINNAME)-$(VERSION)-linux64.tar.xz: $(BINNAME) nmdc-webfrontend.conf.SAMPLE
|
||||||
XZ_OPT='-9' tar caf "$(BINNAME)-$(VERSION)-linux64.tar.xz" $(BINNAME) nmdc-webfrontend.conf.SAMPLE --owner=0 --group=0
|
XZ_OPT='-9' tar caf "$(BINNAME)-$(VERSION)-linux64.tar.xz" $(BINNAME) nmdc-webfrontend.conf.SAMPLE --owner=0 --group=0
|
||||||
|
|
||||||
$(BINNAME)-$(VERSION)-src.tar.xz: $(SOURCES)
|
$(BINNAME)-$(VERSION)-src.zip:
|
||||||
XZ_OPT='-9' tar caf "$(BINNAME)-$(VERSION)-src.tar.xz" $(SOURCES) --owner=0 --group=0
|
hg archive "$(BINNAME)-$(VERSION)-src.zip" || git archive -o "$(BINNAME)-$(VERSION)-src.zip" HEAD
|
||||||
|
165
README.md
165
README.md
@ -1,165 +0,0 @@
|
|||||||
# nmdc-webfrontend
|
|
||||||
|
|
||||||
A web interface to an NMDC/ADC hub.
|
|
||||||
|
|
||||||
Chat in real-time on your NMDC/ADC hub using a web browser.
|
|
||||||
|
|
||||||
## Relationship to other projects
|
|
||||||
|
|
||||||
This project forks and deprecates my earlier `dcwebui2` project since Go seems to use less memory than node.
|
|
||||||
|
|
||||||
This project supercedes [dcwebui](https://code.ivysaur.me/dcwebui/), [flexdc](https://code.ivysaur.me/flexdc/), and [dcwebui2](https://code.ivysaur.me/dcwebui2/).
|
|
||||||
|
|
||||||
### Upgrading from `dcwebui2`
|
|
||||||
|
|
||||||
- The configuration file content is identical between `nmdc-webfrontend` 1.0.0 and `dcwebui2` 1.3.0, but please now ensure it's valid JSON instead of arbitrary javascript. This means no assignment, use double-quoted strings, and no comments.
|
|
||||||
- Future changes to the configuration file since `nmdc-webfrontend` 1.0.0 are backward compatible (see the changelog for more details).
|
|
||||||
|
|
||||||
## Developing
|
|
||||||
|
|
||||||
1. Install Go (>= 1.11), Node.js, NPM, and 7-Zip (`p7zip-full` on Debian)
|
|
||||||
2. Download the source code
|
|
||||||
- `git clone https://git.ivysaur.me/code.ivysaur.me/nmdc-webfrontend.git `; or
|
|
||||||
- `go get code.ivysaur.me/nmdc-webfrontend` ; or
|
|
||||||
- download and extract a source archive
|
|
||||||
3. Install dependencies: `sudo make deps`
|
|
||||||
4. Build: `make`
|
|
||||||
5. Optional: Set `web.external_webroot: true` in the config file for unminified development
|
|
||||||
|
|
||||||
## Changelog
|
|
||||||
|
|
||||||
2017-11-26 1.3.0
|
|
||||||
- Update libnmdc to 0.16 (adds ADC hub support)
|
|
||||||
- Configuration: The `hub.port` property is now optional. You can specify a full URI in the `hub.address` property instead.
|
|
||||||
- [⬇️ nmdc-webfrontend-1.3.0-win32.7z](https://git.ivysaur.me/attachments/76e6b425-d34d-4451-815b-98a6e0685a19) *(1.44 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.3.0-src.tar.xz](https://git.ivysaur.me/attachments/a362dd86-2999-4753-adac-830f1a1a64ac) *(64.81 KiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.3.0-linux64.tar.xz](https://git.ivysaur.me/attachments/877fc7c9-0df8-4514-9147-7f102f221d49) *(1.69 MiB)*
|
|
||||||
|
|
||||||
2017-11-14 1.2.3
|
|
||||||
- Update libnmdc to 0.15
|
|
||||||
- [⬇️ nmdc-webfrontend-1.2.3-win32.7z](https://git.ivysaur.me/attachments/02c33e38-89ef-4cbd-b8a2-80cbd6077520) *(1.41 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.2.3-src.tar.xz](https://git.ivysaur.me/attachments/e3e7599d-4805-4e39-a45e-d7f68cc32f09) *(64.72 KiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.2.3-linux64.tar.xz](https://git.ivysaur.me/attachments/c5a06c20-e14a-4cfd-b830-7b41a52074c1) *(1.66 MiB)*
|
|
||||||
|
|
||||||
2017-10-28 1.2.2
|
|
||||||
- Enhancement: Simplify build process
|
|
||||||
- Fix an issue with closing PM tabs
|
|
||||||
- [⬇️ nmdc-webfrontend-1.2.2-win32.7z](https://git.ivysaur.me/attachments/37f3a7f4-637b-44d7-9575-5a721a504ce5) *(1.41 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.2.2-src.tar.xz](https://git.ivysaur.me/attachments/265ed79c-901f-43e7-9746-99b45a28311b) *(64.72 KiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.2.2-linux64.tar.xz](https://git.ivysaur.me/attachments/74ebeaaa-a2ef-4b9c-948f-8c11522d5ab0) *(1.66 MiB)*
|
|
||||||
|
|
||||||
2017-10-16 1.2.1
|
|
||||||
- Enhancement: Increase scrollback buffer size
|
|
||||||
- Fix an issue with missing `contented` upload link once logged in
|
|
||||||
- Fix a cosmetic issue with `(0)` appearing in page title
|
|
||||||
- [⬇️ nmdc-webfrontend-1.2.1-win32.7z](https://git.ivysaur.me/attachments/a99d5d16-968f-4b02-954a-e22314ae9d64) *(1.39 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.2.1-src.tar.xz](https://git.ivysaur.me/attachments/e9c3dd9f-c0b1-4913-8315-3a39c809155c) *(65.74 KiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.2.1-linux64.tar.xz](https://git.ivysaur.me/attachments/bf3dc821-182b-4911-a46f-72efbc4310f8) *(1.64 MiB)*
|
|
||||||
|
|
||||||
2017-10-15 1.2.0
|
|
||||||
- Feature: Add `contented` integration (set `app.contented_server` in config file)
|
|
||||||
- Fix a cosmetic issue with the menu icon on devices without unicode font coverage
|
|
||||||
- [⬇️ nmdc-webfrontend-1.2.0-win32.7z](https://git.ivysaur.me/attachments/48c194e1-6463-4449-a8d4-e2b1cef2a522) *(1.39 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.2.0-src.tar.xz](https://git.ivysaur.me/attachments/ceba8731-b0e6-483e-8282-360a0a584304) *(65.69 KiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.2.0-linux64.tar.xz](https://git.ivysaur.me/attachments/6082cb13-0e62-4fa8-95c6-1b7d7f7a3272) *(1.64 MiB)*
|
|
||||||
|
|
||||||
2017-02-11 1.1.4
|
|
||||||
- Update libnmdc to 0.14
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.4-win64.7z](https://git.ivysaur.me/attachments/b104b02c-9469-4b8d-be9f-3fbafecb4edc) *(1.36 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.4-win32.7z](https://git.ivysaur.me/attachments/dd0a9bab-6da6-45d4-a48f-51c9621e9c55) *(1.25 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.4-src.tar.xz](https://git.ivysaur.me/attachments/cc0be981-bad7-401e-aace-ac6d8d9a56dd) *(65.53 KiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.4-linux64.tar.xz](https://git.ivysaur.me/attachments/7cd26e52-9ea7-4dcc-91b3-273bc0a3eda4) *(1.50 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.4-linux32.tar.xz](https://git.ivysaur.me/attachments/15895b23-a582-4d59-91da-36f6e6145dfe) *(1.41 MiB)*
|
|
||||||
|
|
||||||
2017-02-11 1.1.3
|
|
||||||
- Feature: Display user IP address on hover, if available
|
|
||||||
- Enhancement: Allow clicking on popup notifications
|
|
||||||
- Enhancement: Only show 'popups enabled' notification when enabling for the first time, not persistent page load
|
|
||||||
- Update libnmdc to 0.13
|
|
||||||
- Fix a cosmetic issue with not displaying non-zero user share sizes
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.3-win64.7z](https://git.ivysaur.me/attachments/d7b551fb-0dd4-4c38-bbe2-513c67311752) *(1.36 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.3-win32.7z](https://git.ivysaur.me/attachments/33b90056-bff3-488c-bf01-bcaf7f114176) *(1.25 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.3-src.tar.xz](https://git.ivysaur.me/attachments/0390f266-3663-48b5-9216-4a3e41623017) *(65.50 KiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.3-linux64.tar.xz](https://git.ivysaur.me/attachments/8ab5ac7c-e8d2-47c0-886a-90bfaf011efd) *(1.50 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.3-linux32.tar.xz](https://git.ivysaur.me/attachments/229ef410-19a1-4fa8-8625-473bf486b40f) *(1.41 MiB)*
|
|
||||||
|
|
||||||
2017-02-06 1.1.2
|
|
||||||
- Autodetect 'extern', no need to include it in config files
|
|
||||||
- Enhancement: Remove redundant request, for a faster page load
|
|
||||||
- Display server version number in log file and in response headers
|
|
||||||
- Fix a cosmetic issue with spacing around user count in page title
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.2-win64.7z](https://git.ivysaur.me/attachments/96c0305e-dc3e-45c6-9567-5eaa461551ec) *(1.36 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.2-win32.7z](https://git.ivysaur.me/attachments/27095754-735c-4e24-8cff-60bdbbe60b34) *(1.25 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.2-src.tar.xz](https://git.ivysaur.me/attachments/5b9d177e-f79d-4afd-a97c-6d563ba0e7af) *(65.42 KiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.2-linux64.tar.xz](https://git.ivysaur.me/attachments/a2325dc5-992e-4d45-a622-c516e41a3187) *(1.50 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.2-linux32.tar.xz](https://git.ivysaur.me/attachments/e2dee266-1b9d-4837-9536-98b1922eefa0) *(1.41 MiB)*
|
|
||||||
|
|
||||||
2017-02-06 1.1.1
|
|
||||||
- Fix an issue with malformed content in minified build
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.1-win64.7z](https://git.ivysaur.me/attachments/412f04e2-08f5-4a12-b933-43ea00164fb2) *(1.36 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.1-win32.7z](https://git.ivysaur.me/attachments/52753dca-3dd6-4668-a785-2d5807d27e63) *(1.25 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.1-src.tar.xz](https://git.ivysaur.me/attachments/e9beee17-8368-4d21-b33f-9e3fa2e11c74) *(65.39 KiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.1-linux64.tar.xz](https://git.ivysaur.me/attachments/e93a09fc-7d0c-4b2a-a706-dddd51d9426d) *(1.50 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.1-linux32.tar.xz](https://git.ivysaur.me/attachments/bb9daa45-1bdb-492d-9d55-5fc39a046433) *(1.41 MiB)*
|
|
||||||
|
|
||||||
2017-02-06 1.1.0
|
|
||||||
- Feature: Remember last username/password for login; remember last "show joins/parts" status
|
|
||||||
- Feature: Display user details on hover (description, email, client tag, share size)
|
|
||||||
- Feature: Optional desktop notifications for background PMs (not possible in incognito)
|
|
||||||
- Feature: Automatically reconnect with the same username/password if connection was lost
|
|
||||||
- Feature: Re-enter last message (Ctrl+Up, Ctrl+Down)
|
|
||||||
- Feature: Set custom date/time format (Minutes, Seconds, Full), remembered for next session
|
|
||||||
- Feature: Clickable magnet links
|
|
||||||
- Feature: Display unread main-chat message count in the page title if the window is inactive
|
|
||||||
- Feature: Add warning message when closing tab while still connected (optional preference, disabled by default, will be remembered).
|
|
||||||
- Feature: Admin option to load a custom favicon (set `web.custom_favicon=true` and place a `favicon.ico` in the current directory)
|
|
||||||
- Feature: Admin option to use external web resources (set `web.external_webroot=true` and use the /client/ directory)
|
|
||||||
- Enhancement: Higher resolution favicon
|
|
||||||
- Enhancement: Display operators in green in the user list
|
|
||||||
- Enhancement: Enable spellcheck for text input once logged in
|
|
||||||
- Enhancement: Prevent sending referrer to remote URLs
|
|
||||||
- Enhancement: Display joins/parts and connection/disconnection messages in PM tabs
|
|
||||||
- Enhancement: Support Shift+Tab to autocomplete backward
|
|
||||||
- Enhancement: Support unread status for the main tab
|
|
||||||
- Enhancement: Improve page load time via minification
|
|
||||||
- Remove unused options from the config file
|
|
||||||
- Update socket.io to 1.7.2
|
|
||||||
- Update libnmdc to 0.12
|
|
||||||
- Add margin between bottom of the text area and the text input box
|
|
||||||
- Fix a cosmetic issue with collapsing consecutive spaces in posted messages
|
|
||||||
- Fix a cosmetic issue with text size adjustment on mobile devices
|
|
||||||
- Fix a cosmetic issue with clearing the screen on reconnection
|
|
||||||
- Fix a cosmetic issue with not clearing the userlist on certain types of network error
|
|
||||||
- Fix a cosmetic issue with closed PM tabs reappearing in some cases
|
|
||||||
- Fix a cosmetic issue with marking all PM tabs as read when switching to a single one
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.0-win64.7z](https://git.ivysaur.me/attachments/a88be3f1-a20f-4ae8-8723-fb538e321a73) *(1.36 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.0-win32.7z](https://git.ivysaur.me/attachments/09326166-ef9e-4901-91f9-ad3b0fff156f) *(1.25 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.0-src.tar.xz](https://git.ivysaur.me/attachments/6d83cf30-3436-4c5d-a6e4-715703af7ce7) *(65.39 KiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.0-linux64.tar.xz](https://git.ivysaur.me/attachments/a9baf26c-1756-49d9-a8aa-720f01d72194) *(1.50 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.1.0-linux32.tar.xz](https://git.ivysaur.me/attachments/00e5a0a2-4b3f-4276-aefe-56046707a14b) *(1.41 MiB)*
|
|
||||||
|
|
||||||
2016-11-29 1.0.2
|
|
||||||
- Rebuild with libnmdc 0.11
|
|
||||||
- Fix an issue with not setting a version in the client tag
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.2-win64.7z](https://git.ivysaur.me/attachments/6b84047d-9a4c-45fe-985b-5321f629f806) *(1.34 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.2-win32.7z](https://git.ivysaur.me/attachments/1505b8fa-83e1-469d-8c77-acee3711e26f) *(1.23 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.2-src.tar.xz](https://git.ivysaur.me/attachments/faff2112-d191-4ee8-a572-553bc4a25be8) *(54.28 KiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.2-linux64.tar.xz](https://git.ivysaur.me/attachments/c319c140-8bed-4225-a953-e99940ba9732) *(1.48 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.2-linux32.tar.xz](https://git.ivysaur.me/attachments/a55fc471-5d80-49ad-8670-67cfb8c6175c) *(1.39 MiB)*
|
|
||||||
|
|
||||||
2016-10-08 1.0.1
|
|
||||||
- Fix an issue with backward compatibility with `dcwebui2` configuration file format
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.1-win64.7z](https://git.ivysaur.me/attachments/d5ed658d-1ef3-4bd8-b2de-3d9415322f57) *(1.35 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.1-win32.7z](https://git.ivysaur.me/attachments/546bd5f9-3742-498f-a1cb-85690c84ddc2) *(1.23 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.1-src.tar.xz](https://git.ivysaur.me/attachments/5ec729d7-d914-4b7e-ade3-21988c203652) *(54.25 KiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.1-linux64.tar.xz](https://git.ivysaur.me/attachments/d7bf1c9c-ab2c-4c61-b7d8-7b6233ae059f) *(1.48 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.1-linux32.tar.xz](https://git.ivysaur.me/attachments/d5265319-81e5-4f3d-95f7-a6e46c607490) *(1.39 MiB)*
|
|
||||||
|
|
||||||
2016-10-08 1.0.0
|
|
||||||
- Port `dcwebui2` from Node.js (Javascript) to Go
|
|
||||||
- Fix a cosmetic issue with not clearing userlist on disconnection
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.0-win64.7z](https://git.ivysaur.me/attachments/3b7386c8-1445-4590-9779-af9870ea542a) *(1.34 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.0-win32.7z](https://git.ivysaur.me/attachments/9ad9bd21-4152-462c-9ddc-b584917058e5) *(1.23 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.0-src.tar.xz](https://git.ivysaur.me/attachments/d2745543-671c-42b5-b334-2dba1ee8726c) *(54.23 KiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.0-linux64.tar.xz](https://git.ivysaur.me/attachments/6102edf8-471b-4793-8877-c747f5ac5680) *(1.48 MiB)*
|
|
||||||
- [⬇️ nmdc-webfrontend-1.0.0-linux32.tar.xz](https://git.ivysaur.me/attachments/5a4bcb39-41e4-4db9-80ff-c0ea04912b06) *(1.39 MiB)*
|
|
104
_dist/README.txt
Normal file
104
_dist/README.txt
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
A web interface to an NMDC hub.
|
||||||
|
|
||||||
|
Chat in real-time on your NMDC hub using a web browser. This project forks and deprecates my earlier `dcwebui2` project since Go seems to use less memory than node.
|
||||||
|
|
||||||
|
Written in Golang
|
||||||
|
|
||||||
|
Tags: nmdc
|
||||||
|
|
||||||
|
This project supercedes [entry=dcwebui]dcwebui[/entry], [entry=flexdc]flexdc[/entry], and [entry=dcwebui2]dcwebui2[/entry].
|
||||||
|
|
||||||
|
=UPGRADING FROM DCWEBUI2=
|
||||||
|
|
||||||
|
- The configuration file content is identical between nmdc-webfrontend 1.0.0 and dcwebui2 1.3.0, but please now ensure it's valid JSON instead of arbitrary javascript. This means no assignment, use double-quoted strings, and no comments.
|
||||||
|
- Future changes to the configuration file since nmdc-webfrontend 1.0.0 are backward compatible (see the changelog for more details).
|
||||||
|
|
||||||
|
=DEVELOPING=
|
||||||
|
|
||||||
|
[go-get]code.ivysaur.me/nmdc-webfrontend git https://git.ivysaur.me/code.ivysaur.me/nmdc-webfrontend.git[/go-get]
|
||||||
|
|
||||||
|
1. Install Go, Node.js, NPM, and 7-Zip (`p7zip-full` on Debian)
|
||||||
|
2. Set up your `$GOPATH`
|
||||||
|
3. Download the source code
|
||||||
|
- `git clone https://git.ivysaur.me/code.ivysaur.me/nmdc-webfrontend.git `; or
|
||||||
|
- `go get code.ivysaur.me/nmdc-webfrontend` ; or
|
||||||
|
- download and extract a source archive
|
||||||
|
4. Install dependencies: `sudo make deps`
|
||||||
|
5. Build: `make`
|
||||||
|
6. Optional: Set `web.external_webroot: true` in the config file for unminified development
|
||||||
|
|
||||||
|
=CHANGELOG=
|
||||||
|
|
||||||
|
2017-10-28 1.2.2
|
||||||
|
- Enhancement: Simplify build process
|
||||||
|
- Fix an issue with closing PM tabs
|
||||||
|
|
||||||
|
2017-10-16 1.2.1
|
||||||
|
- Enhancement: Increase scrollback buffer size
|
||||||
|
- Fix an issue with missing `contented` upload link once logged in
|
||||||
|
- Fix a cosmetic issue with `(0)` appearing in page title
|
||||||
|
|
||||||
|
2017-10-15 1.2.0
|
||||||
|
- Feature: Add `contented` integration (set `app.contented_server` in config file)
|
||||||
|
- Fix a cosmetic issue with the menu icon on devices without unicode font coverage
|
||||||
|
|
||||||
|
2017-02-11 1.1.4
|
||||||
|
- Update libnmdc to 0.14
|
||||||
|
|
||||||
|
2017-02-11 1.1.3
|
||||||
|
- Feature: Display user IP address on hover, if available
|
||||||
|
- Enhancement: Allow clicking on popup notifications
|
||||||
|
- Enhancement: Only show 'popups enabled' notification when enabling for the first time, not persistent page load
|
||||||
|
- Update libnmdc to 0.13
|
||||||
|
- Fix a cosmetic issue with not displaying non-zero user share sizes
|
||||||
|
|
||||||
|
2017-02-06 1.1.2
|
||||||
|
- Autodetect 'extern', no need to include it in config files
|
||||||
|
- Enhancement: Remove redundant request, for a faster page load
|
||||||
|
- Display server version number in log file and in response headers
|
||||||
|
- Fix a cosmetic issue with spacing around user count in page title
|
||||||
|
|
||||||
|
2017-02-06 1.1.1
|
||||||
|
- Fix an issue with malformed content in minified build
|
||||||
|
|
||||||
|
2017-02-06 1.1.0
|
||||||
|
- Feature: Remember last username/password for login; remember last "show joins/parts" status
|
||||||
|
- Feature: Display user details on hover (description, email, client tag, share size)
|
||||||
|
- Feature: Optional desktop notifications for background PMs (not possible in incognito)
|
||||||
|
- Feature: Automatically reconnect with the same username/password if connection was lost
|
||||||
|
- Feature: Re-enter last message (Ctrl+Up, Ctrl+Down)
|
||||||
|
- Feature: Set custom date/time format (Minutes, Seconds, Full), remembered for next session
|
||||||
|
- Feature: Clickable magnet links
|
||||||
|
- Feature: Display unread main-chat message count in the page title if the window is inactive
|
||||||
|
- Feature: Add warning message when closing tab while still connected (optional preference, disabled by default, will be remembered).
|
||||||
|
- Feature: Admin option to load a custom favicon (set `web.custom_favicon=true` and place a `favicon.ico` in the current directory)
|
||||||
|
- Feature: Admin option to use external web resources (set `web.external_webroot=true` and use the /client/ directory)
|
||||||
|
- Enhancement: Higher resolution favicon
|
||||||
|
- Enhancement: Display operators in green in the user list
|
||||||
|
- Enhancement: Enable spellcheck for text input once logged in
|
||||||
|
- Enhancement: Prevent sending referrer to remote URLs
|
||||||
|
- Enhancement: Display joins/parts and connection/disconnection messages in PM tabs
|
||||||
|
- Enhancement: Support Shift+Tab to autocomplete backward
|
||||||
|
- Enhancement: Support unread status for the main tab
|
||||||
|
- Enhancement: Improve page load time via minification
|
||||||
|
- Remove unused options from the config file
|
||||||
|
- Update socket.io to 1.7.2
|
||||||
|
- Update libnmdc to 0.12
|
||||||
|
- Add margin between bottom of the text area and the text input box
|
||||||
|
- Fix a cosmetic issue with collapsing consecutive spaces in posted messages
|
||||||
|
- Fix a cosmetic issue with text size adjustment on mobile devices
|
||||||
|
- Fix a cosmetic issue with clearing the screen on reconnection
|
||||||
|
- Fix a cosmetic issue with not clearing the userlist on certain types of network error
|
||||||
|
- Fix a cosmetic issue with closed PM tabs reappearing in some cases
|
||||||
|
- Fix a cosmetic issue with marking all PM tabs as read when switching to a single one
|
||||||
|
|
||||||
|
2016-11-29 1.0.2
|
||||||
|
- Rebuild with libnmdc 0.11
|
||||||
|
- Fix an issue with not setting a version in the client tag
|
||||||
|
|
||||||
|
2016-10-08 1.0.1
|
||||||
|
- Fix an issue with backward compatibility with `dcwebui2` configuration file format
|
||||||
|
|
||||||
|
2016-10-08 1.0.0
|
||||||
|
- Port `dcwebui2` from Node.js (Javascript) to Go
|
||||||
|
- Fix a cosmetic issue with not clearing userlist on disconnection
|
File diff suppressed because one or more lines are too long
@ -51,15 +51,17 @@ html,body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* WiiU placements */
|
/* WiiU placements */
|
||||||
.navigator-wiiu .placement-mid {
|
.navigator-wiiu {
|
||||||
|
.placement-mid {
|
||||||
bottom:120px;
|
bottom:120px;
|
||||||
}
|
}
|
||||||
.navigator-wiiu .placement-bottom {
|
.placement-bottom {
|
||||||
height:120px;
|
height:120px;
|
||||||
}
|
}
|
||||||
.navigator-wiiu .placement-panel {
|
.placement-panel {
|
||||||
bottom:120px;
|
bottom:120px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Menu button */
|
/* Menu button */
|
||||||
.menubutton {
|
.menubutton {
|
||||||
@ -75,14 +77,17 @@ html,body {
|
|||||||
height:18px;
|
height:18px;
|
||||||
width:18px;
|
width:18px;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
}
|
|
||||||
.menubutton svg {
|
svg {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
}
|
}
|
||||||
.menubutton:hover {
|
|
||||||
|
&:hover {
|
||||||
background:white;
|
background:white;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Menu list */
|
/* Menu list */
|
||||||
.menu {
|
.menu {
|
||||||
background:#DDD;
|
background:#DDD;
|
||||||
@ -97,20 +102,24 @@ html,body {
|
|||||||
z-index:10; /* above placement-panel */
|
z-index:10; /* above placement-panel */
|
||||||
|
|
||||||
overflow-y:auto;
|
overflow-y:auto;
|
||||||
}
|
|
||||||
.menu ul {
|
ul {
|
||||||
margin:0;
|
margin:0;
|
||||||
padding:0;
|
padding:0;
|
||||||
}
|
}
|
||||||
.menu li {
|
|
||||||
|
li {
|
||||||
list-style-type:none;
|
list-style-type:none;
|
||||||
padding:6px;
|
padding:6px;
|
||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
}
|
|
||||||
.menu li:hover {
|
&:hover {
|
||||||
background:#333;
|
background:#333;
|
||||||
color:white;
|
color:white;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Tabs */
|
/* Tabs */
|
||||||
.tabbar {
|
.tabbar {
|
||||||
position:relative;
|
position:relative;
|
||||||
@ -132,16 +141,20 @@ html,body {
|
|||||||
border-right:1px solid lightgrey;
|
border-right:1px solid lightgrey;
|
||||||
margin-top:2px;
|
margin-top:2px;
|
||||||
padding:0 8px;
|
padding:0 8px;
|
||||||
}
|
|
||||||
.tabitem:hover {
|
&:hover {
|
||||||
border-top:2px solid #EEE;
|
border-top:2px solid #EEE;
|
||||||
}
|
}
|
||||||
.tabitem.selected {
|
|
||||||
|
&.selected {
|
||||||
border-top:2px solid darkgreen;
|
border-top:2px solid darkgreen;
|
||||||
}
|
}
|
||||||
.tabitem.unread {
|
|
||||||
|
&.unread {
|
||||||
background:lightyellow;
|
background:lightyellow;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.tab-label {
|
.tab-label {
|
||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
}
|
}
|
||||||
@ -170,12 +183,14 @@ html,body {
|
|||||||
-webkit-transition:0.1s linear all;
|
-webkit-transition:0.1s linear all;
|
||||||
-moz-transition:0.1s linear all;
|
-moz-transition:0.1s linear all;
|
||||||
transition:0.1s linear all;
|
transition:0.1s linear all;
|
||||||
}
|
|
||||||
.tab-closer:hover {
|
&:hover {
|
||||||
background:#FFDDDD;
|
background:#FFDDDD;
|
||||||
border:1px solid red;
|
border:1px solid red;
|
||||||
color:black;
|
color:black;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
.content {
|
.content {
|
||||||
display:block;
|
display:block;
|
||||||
@ -241,7 +256,10 @@ html,body {
|
|||||||
}
|
}
|
||||||
#submit-container {
|
#submit-container {
|
||||||
position:absolute;
|
position:absolute;
|
||||||
top:0;bottom:0;right:0;width:32px;
|
top:0;
|
||||||
|
bottom:0;
|
||||||
|
right:0;
|
||||||
|
width:32px;
|
||||||
}
|
}
|
||||||
#btsubmit {
|
#btsubmit {
|
||||||
width:100%;
|
width:100%;
|
||||||
@ -265,8 +283,8 @@ html,body {
|
|||||||
-webkit-border-radius: 12px;
|
-webkit-border-radius: 12px;
|
||||||
-moz-border-radius: 12px;
|
-moz-border-radius: 12px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
}
|
|
||||||
.ul-large li {
|
li {
|
||||||
color: #333;
|
color: #333;
|
||||||
font: bold 15px Geneva, Arial, Helvetica, sans-serif;
|
font: bold 15px Geneva, Arial, Helvetica, sans-serif;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
@ -279,8 +297,9 @@ html,body {
|
|||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
display: block;
|
display: block;
|
||||||
padding: 15px 5px 5px 10px;
|
padding: 15px 5px 5px 10px;
|
||||||
}
|
cursor:pointer;
|
||||||
.ul-large li:first-child {
|
|
||||||
|
&:first-child {
|
||||||
border-top: 0;
|
border-top: 0;
|
||||||
-webkit-border-top-left-radius: 12px;
|
-webkit-border-top-left-radius: 12px;
|
||||||
-webkit-border-top-right-radius: 12px;
|
-webkit-border-top-right-radius: 12px;
|
||||||
@ -289,7 +308,8 @@ html,body {
|
|||||||
border-top-left-radius: 12px;
|
border-top-left-radius: 12px;
|
||||||
border-top-right-radius: 12px;
|
border-top-right-radius: 12px;
|
||||||
}
|
}
|
||||||
.ul-large li:last-child {
|
|
||||||
|
&:last-child {
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
-webkit-border-bottom-left-radius: 12px;
|
-webkit-border-bottom-left-radius: 12px;
|
||||||
-webkit-border-bottom-right-radius: 12px;
|
-webkit-border-bottom-right-radius: 12px;
|
||||||
@ -298,9 +318,7 @@ html,body {
|
|||||||
border-bottom-left-radius: 12px;
|
border-bottom-left-radius: 12px;
|
||||||
border-bottom-right-radius: 12px;
|
border-bottom-right-radius: 12px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.ul-large li {
|
|
||||||
cursor:pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Text */
|
/* Text */
|
||||||
@ -368,8 +386,8 @@ html,body {
|
|||||||
.ul-mini {
|
.ul-mini {
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
margin-top:4px;
|
margin-top:4px;
|
||||||
}
|
|
||||||
.ul-mini li {
|
li {
|
||||||
list-style-type:none;
|
list-style-type:none;
|
||||||
min-height:13px;
|
min-height:13px;
|
||||||
line-height:13px;
|
line-height:13px;
|
||||||
@ -378,10 +396,12 @@ html,body {
|
|||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
|
|
||||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAANCAYAAACQN/8FAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABh0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzT7MfTgAAAZJJREFUKJFtz71rE2EAgPHnrknbXBKkLWhx0DhoWsEOxk0taMGlQ7cimElK5uroH5DFSUQ3KQpV2qlQEDJ4mKFQ0MVS6kfN1dQP2ss1ydW79y6Xu3udBG36zL/lUaSUVKtVqes6hmGgaRqlUulUoVAw+ScV4P2bFfrEPhnF4+v2NuVy+QFHSjiNXWV2cgzh+dRqX1hsNflcr5/tgcJ14larSdDxCYIOuWEV0c3+7oEAxk4NANu2GUnFFIt3FnvgydxFZb9hSRmFCCGwmjb3i3OVo1AFyF2bZm/PxDRN4rDDo4WHt4+F77RfmKNZ1MQA9ukTPPeXXt14OvXita5f+guVJ+uPZ1brlZUJI08qG/F26AP9chDXO8RpeyzNvMyMnc+7fembmU9WYJFXL7M1YtBNhqRIEYURh1Gb/u+DH69fubqRaLQOcAccHDy++TW0pEaATzcOkFHMxs/NCQD13oV50laaH/EuQdhBhAIRClzF/W9GkVIC8Gx54dZafa3SDmxc1SEdZxgfHmdu+m7y3Jlc+AextbuG6q1RSQAAAABJRU5ErkJggg==') no-repeat 0 0;
|
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAANCAYAAACQN/8FAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABh0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzT7MfTgAAAZJJREFUKJFtz71rE2EAgPHnrknbXBKkLWhx0DhoWsEOxk0taMGlQ7cimElK5uroH5DFSUQ3KQpV2qlQEDJ4mKFQ0MVS6kfN1dQP2ss1ydW79y6Xu3udBG36zL/lUaSUVKtVqes6hmGgaRqlUulUoVAw+ScV4P2bFfrEPhnF4+v2NuVy+QFHSjiNXWV2cgzh+dRqX1hsNflcr5/tgcJ14larSdDxCYIOuWEV0c3+7oEAxk4NANu2GUnFFIt3FnvgydxFZb9hSRmFCCGwmjb3i3OVo1AFyF2bZm/PxDRN4rDDo4WHt4+F77RfmKNZ1MQA9ukTPPeXXt14OvXita5f+guVJ+uPZ1brlZUJI08qG/F26AP9chDXO8RpeyzNvMyMnc+7fembmU9WYJFXL7M1YtBNhqRIEYURh1Gb/u+DH69fubqRaLQOcAccHDy++TW0pEaATzcOkFHMxs/NCQD13oV50laaH/EuQdhBhAIRClzF/W9GkVIC8Gx54dZafa3SDmxc1SEdZxgfHmdu+m7y3Jlc+AextbuG6q1RSQAAAABJRU5ErkJggg==') no-repeat 0 0;
|
||||||
}
|
|
||||||
.ul-mini li:hover {
|
&:hover {
|
||||||
background-color:#EEE;
|
background-color:#EEE;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Display userlist alongside main chat for wide screens */
|
/* Display userlist alongside main chat for wide screens */
|
||||||
|
|
@ -1,29 +1,37 @@
|
|||||||
/* dcwebui.js */
|
/* dcwebui.js */
|
||||||
|
|
||||||
|
import "./dcwebui.less"; // for webpack
|
||||||
|
import * as io from 'socket.io-client'
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display value when loading a saved password
|
||||||
|
*/
|
||||||
var SENTINEL_PASSWORD = "************";
|
var SENTINEL_PASSWORD = "************";
|
||||||
var CHAT_SCROLLBACK_LIMIT = 200; // Once over 2x $limit, the first $limit will be trimmed off the list
|
|
||||||
|
/**
|
||||||
|
* Number of lines of chat to keep in the scroll area.
|
||||||
|
* Once there are over 2x $limit lines, the first $limit lines will be trimmed off the list
|
||||||
|
*/
|
||||||
|
var CHAT_SCROLLBACK_LIMIT = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our externally-accessible URL
|
||||||
|
*/
|
||||||
var EXTERN_ROOT = window.location.protocol + "//" + window.location.host + "/";
|
var EXTERN_ROOT = window.location.protocol + "//" + window.location.host + "/";
|
||||||
|
|
||||||
var el = function(s) {
|
// Help out the braindead minifier, use these functions instead
|
||||||
// There used to be a querySelectorAll implementation, but, better that we don't have
|
var document_getElementById = function(x) { return document.getElementById(x); }
|
||||||
// potentially-incompatible implementations if this one does actually work.
|
var document_getElementsByClassName = function(x) { return document.getElementsByClassName(x); }
|
||||||
// i'm not writing a selector engine...
|
var document_createElement = function(x) { return document.createElement(x); }
|
||||||
if (! s.length) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s[0] === '#') {
|
/**
|
||||||
return document.getElementById(s.slice(1)); // single element
|
* Encode a string for NMDC
|
||||||
} else if (s[0] === '.') {
|
*
|
||||||
return document.getElementsByClassName(s.slice(1)); // multiple elements
|
* @param str
|
||||||
} else {
|
*/
|
||||||
return document.getElementsByTagName(s); // multiple elements
|
var nmdc_escape = function(str: string): string {
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var nmdc_escape = function(str) {
|
|
||||||
return (
|
return (
|
||||||
(''+str).length
|
(''+str).length
|
||||||
? (''+str).replace(/&/g,'&').replace(/\|/g,'|').replace(/\$/g,'$')
|
? (''+str).replace(/&/g,'&').replace(/\|/g,'|').replace(/\$/g,'$')
|
||||||
@ -31,14 +39,24 @@ var nmdc_escape = function(str) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
var hesc = function(s) {
|
/**
|
||||||
|
* Encode a string for HTML
|
||||||
|
*
|
||||||
|
* @param s
|
||||||
|
*/
|
||||||
|
var hesc = function(s: string): string {
|
||||||
var filter = {
|
var filter = {
|
||||||
'&': '&', '<': '<', '>': '>', '"': '"', '\'': '''
|
'&': '&', '<': '<', '>': '>', '"': '"', '\'': '''
|
||||||
};
|
};
|
||||||
return s.toString().replace(/[&<>'"]/g, function(s) { return filter[s]; });
|
return s.toString().replace(/[&<>'"]/g, function(s) { return filter[s]; });
|
||||||
};
|
};
|
||||||
|
|
||||||
var fmtBytes = function(b) {
|
/**
|
||||||
|
* Format a number of bytes as a human-readable string
|
||||||
|
*
|
||||||
|
* @param b
|
||||||
|
*/
|
||||||
|
var fmtBytes = function(b: number): string {
|
||||||
if (b == 0) {
|
if (b == 0) {
|
||||||
return '(nothing)';
|
return '(nothing)';
|
||||||
}
|
}
|
||||||
@ -49,12 +67,21 @@ var fmtBytes = function(b) {
|
|||||||
return parseFloat((b / Math.pow(k, i)).toFixed(3)) + sizes[i];
|
return parseFloat((b / Math.pow(k, i)).toFixed(3)) + sizes[i];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
var urldesc = function(s) {
|
* Decode a string that was previously encoded in raw-url format.
|
||||||
|
*
|
||||||
|
* @param s
|
||||||
|
*/
|
||||||
|
var urldesc = function(s: string):string {
|
||||||
return decodeURIComponent(s.replace(/\+/g, " "));
|
return decodeURIComponent(s.replace(/\+/g, " "));
|
||||||
}
|
}
|
||||||
|
|
||||||
var linkify = function(str) {
|
/**
|
||||||
|
* Enhance an HTML string by automatically making links clickable, etc.
|
||||||
|
*
|
||||||
|
* @param str An HTML-safe string
|
||||||
|
*/
|
||||||
|
var linkify = function(str : string):string {
|
||||||
// n.b. str is already hesced
|
// n.b. str is already hesced
|
||||||
return (str
|
return (str
|
||||||
.replace(
|
.replace(
|
||||||
@ -68,17 +95,39 @@ var linkify = function(str) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
var sanitise = function(s) {
|
/**
|
||||||
|
* Convert a plain-text string into an enhanced, HTML-safe string.
|
||||||
|
*
|
||||||
|
* @param s
|
||||||
|
*/
|
||||||
|
var sanitise = function(s:string): string {
|
||||||
return linkify(hesc(s));
|
return linkify(hesc(s));
|
||||||
};
|
};
|
||||||
|
|
||||||
var textContent = function($el) {
|
/**
|
||||||
if ($el.textContent) return $el.textContent;
|
* Retrieve the plain-text content from an HTML element in a browser-compatible way.
|
||||||
if ($el.innerText) return $el.innerText;
|
*
|
||||||
|
* @param $el
|
||||||
|
*/
|
||||||
|
var textContent = function($el : HTMLElement) : string {
|
||||||
|
if ($el.textContent) {
|
||||||
|
return $el.textContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($el.innerText) {
|
||||||
|
return $el.innerText;
|
||||||
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
var negmod = function(l, r) {
|
/**
|
||||||
|
* Calculate the positive modulo of (l % r).
|
||||||
|
*
|
||||||
|
* @param l
|
||||||
|
* @param r
|
||||||
|
*/
|
||||||
|
var negmod = function(l:number, r:number):number {
|
||||||
var ret = l % r;
|
var ret = l % r;
|
||||||
if (l < 0) {
|
if (l < 0) {
|
||||||
return ret + r;
|
return ret + r;
|
||||||
@ -87,16 +136,27 @@ var negmod = function(l, r) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ref https://developer.mozilla.org/en/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
|
/**
|
||||||
var b64 = function(str) {
|
* Encode a string to base64 in a UTF8-safe way.
|
||||||
|
*
|
||||||
|
* @ref https://developer.mozilla.org/en/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
|
||||||
|
* @param str
|
||||||
|
*/
|
||||||
|
var b64 = function(str : string):string {
|
||||||
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
|
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
|
||||||
return String.fromCharCode('0x' + p1);
|
return String.fromCharCode(parseInt('0x' + p1));
|
||||||
})).replace(/=/g, '');
|
})).replace(/=/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ref https://gist.github.com/eligrey/1276030
|
/**
|
||||||
var appendInnerHTML = function($el, html) {
|
* Append content to an HTML element in a browser-compatible way.
|
||||||
var child = document.createElement("span");
|
*
|
||||||
|
* @ref https://gist.github.com/eligrey/1276030
|
||||||
|
* @param $el
|
||||||
|
* @param html
|
||||||
|
*/
|
||||||
|
var appendInnerHTML = function($el: HTMLElement, html:string) {
|
||||||
|
var child = document_createElement("span");
|
||||||
child.innerHTML = html;
|
child.innerHTML = html;
|
||||||
|
|
||||||
var node;
|
var node;
|
||||||
@ -105,30 +165,48 @@ var appendInnerHTML = function($el, html) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ref http://stackoverflow.com/a/5598797
|
/**
|
||||||
function getOffsetLeft( elem ) {
|
* Retrieve the left offset of a DOM element relative to the document.
|
||||||
|
*
|
||||||
|
* @ref http://stackoverflow.com/a/5598797
|
||||||
|
* @param elem
|
||||||
|
*/
|
||||||
|
function getOffsetLeft( elem: HTMLElement ):number {
|
||||||
var offsetLeft = 0;
|
var offsetLeft = 0;
|
||||||
do {
|
do {
|
||||||
if (!isNaN(elem.offsetLeft)) {
|
if (!isNaN(elem.offsetLeft)) {
|
||||||
offsetLeft += elem.offsetLeft;
|
offsetLeft += elem.offsetLeft;
|
||||||
}
|
}
|
||||||
} while (elem = elem.offsetParent);
|
} while (elem = <HTMLElement>elem.offsetParent);
|
||||||
return offsetLeft;
|
return offsetLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOffsetTop( elem ) {
|
/**
|
||||||
|
* Retrieve the top offset for a DOM element relative to the document.
|
||||||
|
*
|
||||||
|
* @ref http://stackoverflow.com/a/5598797
|
||||||
|
* @param elem
|
||||||
|
*/
|
||||||
|
function getOffsetTop( elem: HTMLElement ):number {
|
||||||
var offsetTop = 0;
|
var offsetTop = 0;
|
||||||
do {
|
do {
|
||||||
if (!isNaN(elem.offsetTop)) {
|
if (!isNaN(elem.offsetTop)) {
|
||||||
offsetTop += elem.offsetTop;
|
offsetTop += elem.offsetTop;
|
||||||
}
|
}
|
||||||
} while (elem = elem.offsetParent);
|
} while (elem = <HTMLElement>elem.offsetParent);
|
||||||
return offsetTop;
|
return offsetTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
var date_format = function(d, format) {
|
/**
|
||||||
|
* Format a string in a date format, analgous to strftime().
|
||||||
|
*
|
||||||
|
* @param d
|
||||||
|
* @param format Formatting string, supporting HisYmd character specifiers
|
||||||
|
* @return Plain text string
|
||||||
|
*/
|
||||||
|
var date_format = function(d:Date, format:string):string {
|
||||||
var pad = function(s) {
|
var pad = function(s) {
|
||||||
return (s < 10) ? '0'+s : ''+s ;
|
return (s < 10) ? '0'+s : ''+s ;
|
||||||
};
|
};
|
||||||
@ -137,20 +215,25 @@ var date_format = function(d, format) {
|
|||||||
ret = ret.replace(/H/g, pad(d.getHours()));
|
ret = ret.replace(/H/g, pad(d.getHours()));
|
||||||
ret = ret.replace(/i/g, pad(d.getMinutes()));
|
ret = ret.replace(/i/g, pad(d.getMinutes()));
|
||||||
ret = ret.replace(/s/g, pad(d.getSeconds()));
|
ret = ret.replace(/s/g, pad(d.getSeconds()));
|
||||||
ret = ret.replace(/Y/g, d.getFullYear());
|
ret = ret.replace(/Y/g, "" + d.getFullYear());
|
||||||
ret = ret.replace(/m/g, pad(d.getMonth() + 1));
|
ret = ret.replace(/m/g, pad(d.getMonth() + 1));
|
||||||
ret = ret.replace(/d/g, pad(d.getDate()));
|
ret = ret.replace(/d/g, pad(d.getDate()));
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* */
|
/**
|
||||||
|
* Emit an HTML5 notification.
|
||||||
var notify = function(title, body, tab) {
|
*
|
||||||
|
* @param title
|
||||||
|
* @param body
|
||||||
|
* @param tab
|
||||||
|
*/
|
||||||
|
var notify = function(title:string, body:string, tab:string) {
|
||||||
if (!("Notification" in window)) {
|
if (!("Notification" in window)) {
|
||||||
return; // not supported by browser
|
return; // not supported by browser
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (window.Notification.permission) {
|
switch ( (window as any).Notification.permission) {
|
||||||
case "granted": {
|
case "granted": {
|
||||||
var n = new Notification(title, {
|
var n = new Notification(title, {
|
||||||
body: body,
|
body: body,
|
||||||
@ -169,7 +252,7 @@ var notify = function(title, body, tab) {
|
|||||||
default: {
|
default: {
|
||||||
// Clarify permission and retry
|
// Clarify permission and retry
|
||||||
Notification.requestPermission(function(permission) {
|
Notification.requestPermission(function(permission) {
|
||||||
notify(title, body);
|
notify(title, body, "tab-main");
|
||||||
});
|
});
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
@ -178,7 +261,7 @@ var notify = function(title, body, tab) {
|
|||||||
/* Tab writers */
|
/* Tab writers */
|
||||||
|
|
||||||
var write = function(tab) {
|
var write = function(tab) {
|
||||||
var $tab = el('#inner-'+tab);
|
var $tab = document_getElementById("inner-"+tab);
|
||||||
return {
|
return {
|
||||||
'cls': function() {
|
'cls': function() {
|
||||||
$tab.innerHTML = '';
|
$tab.innerHTML = '';
|
||||||
@ -254,11 +337,11 @@ var userlist = {
|
|||||||
'add': function(u) {
|
'add': function(u) {
|
||||||
if (this.has(u)) return;
|
if (this.has(u)) return;
|
||||||
|
|
||||||
var userlists = el(".userlist");
|
var userlists = document_getElementsByClassName("userlist");
|
||||||
for (var l = 0, e = userlists.length; l !== e; ++l) {
|
for (var l = 0, e = userlists.length; l !== e; ++l) {
|
||||||
var userlist = userlists[l];
|
var userlist = userlists[l];
|
||||||
|
|
||||||
var to_add = document.createElement('li');
|
var to_add = document_createElement('li');
|
||||||
to_add.className = "user-" + b64(u);
|
to_add.className = "user-" + b64(u);
|
||||||
to_add.innerHTML = hesc(u);
|
to_add.innerHTML = hesc(u);
|
||||||
|
|
||||||
@ -283,7 +366,7 @@ var userlist = {
|
|||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
'del': function(u) {
|
'del': function(u) {
|
||||||
var userlists = el(".userlist");
|
var userlists = document_getElementsByClassName("userlist");
|
||||||
for (var l = 0, e = userlists.length; l !== e; ++l) {
|
for (var l = 0, e = userlists.length; l !== e; ++l) {
|
||||||
if (! userlists[l].children) continue;
|
if (! userlists[l].children) continue;
|
||||||
var userlist = userlists[l];
|
var userlist = userlists[l];
|
||||||
@ -300,7 +383,7 @@ var userlist = {
|
|||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
'clear': function() {
|
'clear': function() {
|
||||||
var userlists = el(".userlist");
|
var userlists = document_getElementsByClassName("userlist");
|
||||||
for (var i in userlists) {
|
for (var i in userlists) {
|
||||||
if (! userlists[i].children) continue;
|
if (! userlists[i].children) continue;
|
||||||
var userlist = userlists[i];
|
var userlist = userlists[i];
|
||||||
@ -313,22 +396,22 @@ var userlist = {
|
|||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
'names': function() {
|
'names': function() {
|
||||||
var userlist = el(".userlist")[0].children;
|
var userlist = document_getElementsByClassName("userlist")[0].children;
|
||||||
var ret = [];
|
var ret = [];
|
||||||
for (var i = 0, e = userlist.length; i < e; ++i) {
|
for (var i = 0, e = userlist.length; i < e; ++i) {
|
||||||
ret.push( textContent(userlist[i]) );
|
ret.push( textContent((<HTMLElement>userlist[i])) );
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
'has': function(u) {
|
'has': function(u) {
|
||||||
return el(".user-" + b64(u)).length !== 0; /* there are two - large and non-large */
|
return document_getElementsByClassName("user-" + b64(u)).length !== 0; /* there are two - large and non-large */
|
||||||
},
|
},
|
||||||
'count': function() {
|
'count': function() {
|
||||||
return el(".userlist")[0].children.length;
|
return document_getElementsByClassName("userlist")[0].children.length;
|
||||||
},
|
},
|
||||||
'setInfo': function(nick, props) {
|
'setInfo': function(nick, props) {
|
||||||
var baseClass = "user-" + b64(nick);
|
var baseClass = "user-" + b64(nick);
|
||||||
var $el = el("." + baseClass);
|
var $el = document_getElementsByClassName("" + baseClass);
|
||||||
var prop_str = [];
|
var prop_str = [];
|
||||||
if (props.Description.length > 0) {
|
if (props.Description.length > 0) {
|
||||||
prop_str.push(props.Description);
|
prop_str.push(props.Description);
|
||||||
@ -345,7 +428,7 @@ var userlist = {
|
|||||||
prop_str.push("Sharing " + fmtBytes(props.ShareSize));
|
prop_str.push("Sharing " + fmtBytes(props.ShareSize));
|
||||||
|
|
||||||
for (var i = 0; i < $el.length; ++i) {
|
for (var i = 0; i < $el.length; ++i) {
|
||||||
$el[i].title = prop_str.join("\n");
|
(<HTMLElement> $el[i]).title = prop_str.join("\n");
|
||||||
|
|
||||||
if (props.IsOperator) {
|
if (props.IsOperator) {
|
||||||
$el[i].className = baseClass + " user-is-operator";
|
$el[i].className = baseClass + " user-is-operator";
|
||||||
@ -357,7 +440,7 @@ var userlist = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var submit = function() {
|
var submit = function() {
|
||||||
var str = el("#chatbox").value;
|
var str = (<HTMLInputElement>document_getElementById("chatbox")).value;
|
||||||
if (! str.length) return;
|
if (! str.length) return;
|
||||||
|
|
||||||
if (hub_state === STATE_READY_FOR_LOGIN) {
|
if (hub_state === STATE_READY_FOR_LOGIN) {
|
||||||
@ -383,7 +466,7 @@ var submit = function() {
|
|||||||
write("tab-main").system("Connecting...");
|
write("tab-main").system("Connecting...");
|
||||||
|
|
||||||
} else if (hub_state === STATE_ACTIVE) {
|
} else if (hub_state === STATE_ACTIVE) {
|
||||||
if (pm_target !== false) {
|
if (pm_target !== PM_TARGET_NONE) {
|
||||||
sock.emit('priv', {'user': pm_target, 'message': str});
|
sock.emit('priv', {'user': pm_target, 'message': str});
|
||||||
writerFor(pm_target).pub(hub_last_nick, str );
|
writerFor(pm_target).pub(hub_last_nick, str );
|
||||||
} else {
|
} else {
|
||||||
@ -400,7 +483,7 @@ var submit = function() {
|
|||||||
write("tab-main").system("Invalid internal state.");
|
write("tab-main").system("Invalid internal state.");
|
||||||
}
|
}
|
||||||
|
|
||||||
el("#chatbox").value = '';
|
(<HTMLInputElement>document_getElementById("chatbox")).value = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
/* page visibility */
|
/* page visibility */
|
||||||
@ -413,10 +496,10 @@ var pagevis_setup = function(fnActive, fnInactive) {
|
|||||||
if (typeof document.hidden !== "undefined") {
|
if (typeof document.hidden !== "undefined") {
|
||||||
h = "hidden";
|
h = "hidden";
|
||||||
vc = "visibilitychange";
|
vc = "visibilitychange";
|
||||||
} else if (typeof document.msHidden !== "undefined") {
|
} else if (typeof (document as any).msHidden !== "undefined") {
|
||||||
h = "msHidden";
|
h = "msHidden";
|
||||||
vc = "msvisibilitychange";
|
vc = "msvisibilitychange";
|
||||||
} else if (typeof document.webkitHidden !== "undefined") {
|
} else if (typeof (document as any).webkitHidden !== "undefined") {
|
||||||
h = "webkitHidden";
|
h = "webkitHidden";
|
||||||
vc = "webkitvisibilitychange";
|
vc = "webkitvisibilitychange";
|
||||||
}
|
}
|
||||||
@ -447,13 +530,13 @@ var pagevis_setup = function(fnActive, fnInactive) {
|
|||||||
*/
|
*/
|
||||||
var tab_set = function(tab) {
|
var tab_set = function(tab) {
|
||||||
|
|
||||||
var tabs = el(".tabpane");
|
var tabs = document_getElementsByClassName("tabpane");
|
||||||
for (var i in tabs) {
|
for (var i in tabs) {
|
||||||
try {
|
try {
|
||||||
tabs[i].style.display = (tabs[i].id === tab ? 'block' : 'none');
|
(<HTMLElement> tabs[i]).style.display = (tabs[i].id === tab ? 'block' : 'none');
|
||||||
} catch (e) {};
|
} catch (e) {};
|
||||||
}
|
}
|
||||||
var tabitems = el(".tabitem");
|
var tabitems = document_getElementsByClassName("tabitem");
|
||||||
for (var i in tabitems) {
|
for (var i in tabitems) {
|
||||||
try {
|
try {
|
||||||
// Update UNREAD/SELECTED flags for the target
|
// Update UNREAD/SELECTED flags for the target
|
||||||
@ -466,7 +549,7 @@ var tab_set = function(tab) {
|
|||||||
} catch (e) {};
|
} catch (e) {};
|
||||||
}
|
}
|
||||||
|
|
||||||
pm_target = false;
|
pm_target = PM_TARGET_NONE;
|
||||||
for (var i in pm_tabs) {
|
for (var i in pm_tabs) {
|
||||||
if (pm_tabs[i] === tab) {
|
if (pm_tabs[i] === tab) {
|
||||||
pm_target = i;
|
pm_target = i;
|
||||||
@ -481,13 +564,13 @@ var tab_set = function(tab) {
|
|||||||
updateTitle();
|
updateTitle();
|
||||||
|
|
||||||
write(tab).scroll();
|
write(tab).scroll();
|
||||||
el("#chatbox").focus();
|
document_getElementById("chatbox").focus();
|
||||||
last_tab = tab;
|
last_tab = tab;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var tab_new = function(id, name) {
|
var tab_new = function(id, name) {
|
||||||
appendInnerHTML(el("#bar"),
|
appendInnerHTML(document_getElementById("bar"),
|
||||||
' <div class="tabitem" data-tab="tab-ext-'+id+'" id="tabitem-tab-ext-'+id+'">'+
|
' <div class="tabitem" data-tab="tab-ext-'+id+'" id="tabitem-tab-ext-'+id+'">'+
|
||||||
'<span class="tab-label">'+
|
'<span class="tab-label">'+
|
||||||
hesc(name)+
|
hesc(name)+
|
||||||
@ -495,7 +578,7 @@ var tab_new = function(id, name) {
|
|||||||
'<a class="tab-closer" data-tab="tab-ext-'+id+'">×</a>'+
|
'<a class="tab-closer" data-tab="tab-ext-'+id+'">×</a>'+
|
||||||
'</div> '
|
'</div> '
|
||||||
);
|
);
|
||||||
appendInnerHTML(el("#extratabs"),
|
appendInnerHTML(document_getElementById("extratabs"),
|
||||||
' <div class="tabpane content placement-mid" id="tab-ext-'+id+'" style="display:none;">'+
|
' <div class="tabpane content placement-mid" id="tab-ext-'+id+'" style="display:none;">'+
|
||||||
'<div class="content-inner" id="inner-tab-ext-'+id+'"></div>'+
|
'<div class="content-inner" id="inner-tab-ext-'+id+'"></div>'+
|
||||||
'</div>'
|
'</div>'
|
||||||
@ -508,10 +591,10 @@ var tab_free = function(id) {
|
|||||||
if (id === "tab-main") return;
|
if (id === "tab-main") return;
|
||||||
|
|
||||||
// remove tab item and body
|
// remove tab item and body
|
||||||
var $el = el("#tabitem-"+id);
|
var $el = document_getElementById("tabitem-"+id);
|
||||||
$el.parentNode.removeChild($el);
|
$el.parentNode.removeChild($el);
|
||||||
|
|
||||||
$el = el("#"+id);
|
$el = document_getElementById(""+id);
|
||||||
$el.parentNode.removeChild($el);
|
$el.parentNode.removeChild($el);
|
||||||
|
|
||||||
// clear from PM tabs
|
// clear from PM tabs
|
||||||
@ -546,22 +629,26 @@ var noprop = function(ev) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tab_addHandlers = function() {
|
var tab_addHandlers = function() {
|
||||||
var tabitems = el(".tabitem");
|
var tabitems = document_getElementsByClassName("tabitem");
|
||||||
for (var i = 0; i < tabitems.length; i++) {
|
for (var i = 0; i < tabitems.length; i++) {
|
||||||
if (! tabitems[i]) continue;
|
if (! tabitems[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
tabitems[i].onclick = function(ev) {
|
(<HTMLElement> tabitems[i]).onclick = function(ev) {
|
||||||
tab_set( this.getAttribute('data-tab') );
|
tab_set( this.getAttribute('data-tab') );
|
||||||
|
|
||||||
return noprop(ev);
|
return noprop(ev);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var tabclosers = el(".tab-closer");
|
var tabclosers = document_getElementsByClassName("tab-closer");
|
||||||
for (var i = 0; i < tabclosers.length; i++) {
|
for (var i = 0; i < tabclosers.length; i++) {
|
||||||
if (! tabclosers[i]) continue;
|
if (! tabclosers[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
tabclosers[i].onclick = function(ev) {
|
(<HTMLElement> tabclosers[i]).onclick = function(ev) {
|
||||||
tab_free( this.getAttribute('data-tab') );
|
tab_free( this.getAttribute('data-tab') );
|
||||||
|
|
||||||
return noprop(ev);
|
return noprop(ev);
|
||||||
@ -571,8 +658,8 @@ var tab_addHandlers = function() {
|
|||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
var maybeWriterFor = function(username) {
|
var maybeWriterFor = function(username: string) {
|
||||||
if (! username in pm_tabs || ! pm_tabs[username]) {
|
if (! (username in pm_tabs) || ! pm_tabs[username]) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -597,7 +684,7 @@ var tabcomplete_state = '';
|
|||||||
|
|
||||||
var tabcompletion_start = function(direction) {
|
var tabcompletion_start = function(direction) {
|
||||||
|
|
||||||
var cursor = el("#chatbox").value.replace(/^.*\s([^\s]+)$/, '$1');
|
var cursor = (<HTMLInputElement>document_getElementById("chatbox")).value.replace(/^.*\s([^\s]+)$/, '$1');
|
||||||
|
|
||||||
if (tabcomplete_state === '') {
|
if (tabcomplete_state === '') {
|
||||||
// new tab completion
|
// new tab completion
|
||||||
@ -633,10 +720,11 @@ var tabcompletion_start = function(direction) {
|
|||||||
|
|
||||||
// Replace in textbox
|
// Replace in textbox
|
||||||
|
|
||||||
var chatprefix = el("#chatbox").value.substr(0, el("#chatbox").value.length - cursor.length);
|
var $chatbox = (<HTMLInputElement>document_getElementById("chatbox"));
|
||||||
|
var chatprefix = $chatbox.value.substr(0, $chatbox.value.length - cursor.length);
|
||||||
|
|
||||||
el("#chatbox").value = chatprefix + targetName;
|
$chatbox.value = chatprefix + targetName;
|
||||||
el("#chatbox").focus();
|
$chatbox.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
var tabcompletion_inactive = function() {
|
var tabcompletion_inactive = function() {
|
||||||
@ -648,11 +736,11 @@ var tabcompletion_inactive = function() {
|
|||||||
var MenuList = function(el) {
|
var MenuList = function(el) {
|
||||||
this.el = el;
|
this.el = el;
|
||||||
|
|
||||||
this.div = document.createElement("div");
|
this.div = document_createElement("div");
|
||||||
this.div.classList.add("menu");
|
this.div.classList.add("menu");
|
||||||
this.div.style.position = "absolute";
|
this.div.style.position = "absolute";
|
||||||
|
|
||||||
this.ul = document.createElement("ul");
|
this.ul = document_createElement("ul");
|
||||||
this.div.appendChild(this.ul);
|
this.div.appendChild(this.ul);
|
||||||
|
|
||||||
document.body.appendChild(this.div);
|
document.body.appendChild(this.div);
|
||||||
@ -664,7 +752,7 @@ MenuList.prototype.clear = function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
MenuList.prototype.add = function(txt, cb) {
|
MenuList.prototype.add = function(txt, cb) {
|
||||||
var li = document.createElement("li");
|
var li = document_createElement("li");
|
||||||
li.innerHTML = txt;
|
li.innerHTML = txt;
|
||||||
li.onclick = cb;
|
li.onclick = cb;
|
||||||
this.ul.appendChild(li);
|
this.ul.appendChild(li);
|
||||||
@ -685,7 +773,7 @@ MenuList.prototype.toggle = function() {
|
|||||||
|
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
var menu = new MenuList(el("#menubutton"));
|
var menu = new MenuList(document_getElementById("menubutton"));
|
||||||
|
|
||||||
menu.reset = function() {
|
menu.reset = function() {
|
||||||
this.clear();
|
this.clear();
|
||||||
@ -788,7 +876,7 @@ var toggle_joinparts = function(ev) {
|
|||||||
|
|
||||||
var updateTitle = function() {
|
var updateTitle = function() {
|
||||||
var prefix = "";
|
var prefix = "";
|
||||||
var unrTabs = el(".unread");
|
var unrTabs = document_getElementsByClassName("unread");
|
||||||
if (unrTabs.length === 1 && unrTabs[0].getAttribute('data-tab') == "tab-main") {
|
if (unrTabs.length === 1 && unrTabs[0].getAttribute('data-tab') == "tab-main") {
|
||||||
prefix = "[" + mainchat_unread_count + " NEW] "
|
prefix = "[" + mainchat_unread_count + " NEW] "
|
||||||
} else if (unrTabs.length > 0) {
|
} else if (unrTabs.length > 0) {
|
||||||
@ -802,14 +890,15 @@ var updateTitle = function() {
|
|||||||
document.title = prefix + hub_hubname + suffix;
|
document.title = prefix + hub_hubname + suffix;
|
||||||
};
|
};
|
||||||
|
|
||||||
var sock = {};
|
var sock:SocketIOClient.Socket = null;
|
||||||
var hub_state = 0; // [disconnected, sent-nick, connected]
|
var hub_state = 0; // [disconnected, sent-nick, connected]
|
||||||
var hub_last_nick = '';
|
var hub_last_nick = '';
|
||||||
var hub_hubname = "Loading...";
|
var hub_hubname = "Loading...";
|
||||||
|
|
||||||
var pm_tabs = {}; // nick => tabid
|
var pm_tabs = {}; // nick => tabid
|
||||||
var next_tabid = 1;
|
var next_tabid = 1;
|
||||||
var pm_target = false;
|
const PM_TARGET_NONE = "";
|
||||||
|
var pm_target = PM_TARGET_NONE;
|
||||||
|
|
||||||
var last_tab = "tab-main";
|
var last_tab = "tab-main";
|
||||||
|
|
||||||
@ -893,7 +982,7 @@ var scrollback_move = function(delta) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
el("#chatbox").value = chat_scrollback[chat_scrollback_index];
|
(<HTMLInputElement>document_getElementById("chatbox")).value = chat_scrollback[chat_scrollback_index];
|
||||||
};
|
};
|
||||||
|
|
||||||
/* */
|
/* */
|
||||||
@ -914,29 +1003,30 @@ var persistence_get = function(key, fallback) {
|
|||||||
|
|
||||||
var transition = function(new_state) {
|
var transition = function(new_state) {
|
||||||
hub_state = new_state;
|
hub_state = new_state;
|
||||||
|
var $chatbox = (<HTMLInputElement>document_getElementById("chatbox"));
|
||||||
|
|
||||||
switch(new_state) {
|
switch(new_state) {
|
||||||
case STATE_DISCONNECTED: {
|
case STATE_DISCONNECTED: {
|
||||||
userlist.clear();
|
userlist.clear();
|
||||||
el("#chatbox").disabled = true;
|
$chatbox.disabled = true;
|
||||||
el("#chatbox").value = ''; // clear
|
$chatbox.value = ''; // clear
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case STATE_READY_FOR_LOGIN: {
|
case STATE_READY_FOR_LOGIN: {
|
||||||
userlist.clear();
|
userlist.clear();
|
||||||
el("#chatbox").spellcheck = false;
|
$chatbox.spellcheck = false;
|
||||||
el("#chatbox").disabled = false;
|
$chatbox.disabled = false;
|
||||||
el("#chatbox").value = ''; // clear
|
$chatbox.value = ''; // clear
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case STATE_CONNECTING: {
|
case STATE_CONNECTING: {
|
||||||
el("#chatbox").disabled = true;
|
$chatbox.disabled = true;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case STATE_ACTIVE: {
|
case STATE_ACTIVE: {
|
||||||
write("tab-main").system("Now talking on "+hub_hubname);
|
write("tab-main").system("Now talking on "+hub_hubname);
|
||||||
el("#chatbox").disabled = false;
|
$chatbox.disabled = false;
|
||||||
el("#chatbox").spellcheck = true;
|
$chatbox.spellcheck = true;
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -946,14 +1036,15 @@ var tab_is_visible = function(tabref) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tab_mark_unread = function(tabref) {
|
var tab_mark_unread = function(tabref) {
|
||||||
if (el("#tabitem-"+tabref).className.indexOf('unread') === -1) {
|
if (document_getElementById("tabitem-"+tabref).className.indexOf('unread') === -1) {
|
||||||
el("#tabitem-"+tabref).className += " unread";
|
document_getElementById("tabitem-"+tabref).className += " unread";
|
||||||
updateTitle();
|
updateTitle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
declare var contented: any;
|
||||||
var contented_url = "";
|
var contented_url = "";
|
||||||
var contented_loaded_sdk = false;
|
var contented_loaded_sdk = false;
|
||||||
var contented_load = function() {
|
var contented_load = function() {
|
||||||
@ -963,21 +1054,21 @@ var contented_load = function() {
|
|||||||
|
|
||||||
var onceSDKLoaded = function() {
|
var onceSDKLoaded = function() {
|
||||||
contented.init("#inner-tab-main", function(items) {
|
contented.init("#inner-tab-main", function(items) {
|
||||||
var val = el("#chatbox").value;
|
var val = (<HTMLInputElement> document_getElementById("chatbox")).value;
|
||||||
for (var i = 0; i < items.length; ++i) {
|
for (var i = 0; i < items.length; ++i) {
|
||||||
if (val.length > 0) {
|
if (val.length > 0) {
|
||||||
val += " ";
|
val += " ";
|
||||||
}
|
}
|
||||||
val += contented.getPreviewURL(items[i]);
|
val += contented.getPreviewURL(items[i]);
|
||||||
}
|
}
|
||||||
el("#chatbox").value = val;
|
(<HTMLInputElement> document_getElementById("chatbox")).value = val;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (contented_loaded_sdk) {
|
if (contented_loaded_sdk) {
|
||||||
onceSDKLoaded();
|
onceSDKLoaded();
|
||||||
} else {
|
} else {
|
||||||
var scriptElement = document.createElement('script');
|
var scriptElement = document_createElement('script');
|
||||||
scriptElement.onload = function() {
|
scriptElement.onload = function() {
|
||||||
contented_loaded_sdk = true;
|
contented_loaded_sdk = true;
|
||||||
onceSDKLoaded();
|
onceSDKLoaded();
|
||||||
@ -998,13 +1089,13 @@ window.onload = function() {
|
|||||||
|
|
||||||
// HTML event handlers
|
// HTML event handlers
|
||||||
|
|
||||||
el("#form-none").onsubmit = function(ev) {
|
document_getElementById("form-none").onsubmit = function(ev) {
|
||||||
submit();
|
submit();
|
||||||
|
|
||||||
return noprop(ev); // don't submit form
|
return noprop(ev); // don't submit form
|
||||||
};
|
};
|
||||||
|
|
||||||
el("#chatbox").onkeydown = function(ev) {
|
document_getElementById("chatbox").onkeydown = function(ev) {
|
||||||
if (ev.keyCode === 9 /* Tab */) {
|
if (ev.keyCode === 9 /* Tab */) {
|
||||||
tabcompletion_start( ev.shiftKey ? -1 : 1 );
|
tabcompletion_start( ev.shiftKey ? -1 : 1 );
|
||||||
return noprop(ev);
|
return noprop(ev);
|
||||||
@ -1032,7 +1123,7 @@ window.onload = function() {
|
|||||||
usermenu.hide();
|
usermenu.hide();
|
||||||
};
|
};
|
||||||
|
|
||||||
el("#menubutton").onclick = function(ev) {
|
document_getElementById("menubutton").onclick = function(ev) {
|
||||||
menu.toggle();
|
menu.toggle();
|
||||||
return noprop(ev);
|
return noprop(ev);
|
||||||
};
|
};
|
||||||
@ -1090,7 +1181,7 @@ window.onload = function() {
|
|||||||
if (pre_login.indexOf(":") !== -1) {
|
if (pre_login.indexOf(":") !== -1) {
|
||||||
pre_login = pre_login.substr(0, pre_login.indexOf(":")) + ":" + SENTINEL_PASSWORD;
|
pre_login = pre_login.substr(0, pre_login.indexOf(":")) + ":" + SENTINEL_PASSWORD;
|
||||||
}
|
}
|
||||||
el("#chatbox").value = pre_login;
|
(<HTMLInputElement>document_getElementById("chatbox")).value = pre_login;
|
||||||
|
|
||||||
if (have_cleared_once) {
|
if (have_cleared_once) {
|
||||||
// re-log-in automatically
|
// re-log-in automatically
|
@ -5,8 +5,7 @@
|
|||||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
<link rel="apple-touch-icon" href="./apple-touch-icon.png">
|
||||||
<link rel="stylesheet" href="/dcwebui.css">
|
|
||||||
<title>Loading...</title>
|
<title>Loading...</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -56,7 +55,6 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript" src="/socket.io-1.7.2.js"></script>
|
<script type="text/javascript" src="/bundle.js"></script>
|
||||||
<script type="text/javascript" src="/dcwebui.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
File diff suppressed because one or more lines are too long
9
go.mod
9
go.mod
@ -1,9 +0,0 @@
|
|||||||
module code.ivysaur.me/nmdc-webfrontend
|
|
||||||
|
|
||||||
require (
|
|
||||||
code.ivysaur.me/libnmdc v0.16.0
|
|
||||||
github.com/cxmcc/tiger v0.0.0-20170524142333-bde35e2713d7 // indirect
|
|
||||||
github.com/googollee/go-engine.io v0.0.0-20170224222511-80ae0e43aca1 // indirect
|
|
||||||
github.com/googollee/go-socket.io v0.0.0-20170525141029-5447e71f36d3
|
|
||||||
github.com/gorilla/websocket v1.2.0 // indirect
|
|
||||||
)
|
|
10
go.sum
10
go.sum
@ -1,10 +0,0 @@
|
|||||||
code.ivysaur.me/libnmdc v0.16.0 h1:/y4Olm/DoZn83vE/QV1NyjQ2MF20opEJ+sxwDlDTIoE=
|
|
||||||
code.ivysaur.me/libnmdc v0.16.0/go.mod h1:ZHCjIX/zm29hd2H8YtzOBHiSyowZNOHvgoMRjKbBLLg=
|
|
||||||
github.com/cxmcc/tiger v0.0.0-20170524142333-bde35e2713d7 h1:jBEtq1t2gpn2kEzvRlCUxvvrxl5aSWkXNPwe/hwvSNQ=
|
|
||||||
github.com/cxmcc/tiger v0.0.0-20170524142333-bde35e2713d7/go.mod h1:ruCYvt9rtYymAr4rNmfYJrl1dz8HSXUFP7cufqKOsDI=
|
|
||||||
github.com/googollee/go-engine.io v0.0.0-20170224222511-80ae0e43aca1 h1:EYruZXoG1VD2n22p93st1miPfLNB7KiTuyA7qit4gnU=
|
|
||||||
github.com/googollee/go-engine.io v0.0.0-20170224222511-80ae0e43aca1/go.mod h1:MBpz1MS3P4HtRcBpQU4HcjvWXZ9q+JWacMEh2/BFYbg=
|
|
||||||
github.com/googollee/go-socket.io v0.0.0-20170525141029-5447e71f36d3 h1:J+S/ZG2A5qZPZM4+WwpOA3cc5tVL8OTKf2mWEO2dXN0=
|
|
||||||
github.com/googollee/go-socket.io v0.0.0-20170525141029-5447e71f36d3/go.mod h1:ftBGBMhSYToR5oV4ImIPKvAIsNaTkLC+tTvoNafqxlQ=
|
|
||||||
github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
|
|
||||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
|
15
main.go
15
main.go
@ -48,19 +48,14 @@ func (this *App) HubWorker(Nick, Pass string, so socketio.Socket, done chan stru
|
|||||||
selfUser.ClientTag = this.cfg.Hub.Tag
|
selfUser.ClientTag = this.cfg.Hub.Tag
|
||||||
selfUser.ClientVersion = libnmdc.DEFAULT_CLIENT_VERSION
|
selfUser.ClientVersion = libnmdc.DEFAULT_CLIENT_VERSION
|
||||||
|
|
||||||
url := this.cfg.Hub.Address
|
|
||||||
if this.cfg.Hub.Port == 0 {
|
|
||||||
url = fmt.Sprintf("%s:%d", this.cfg.Hub.Address, this.cfg.Hub.Port)
|
|
||||||
}
|
|
||||||
|
|
||||||
hco := libnmdc.HubConnectionOptions{
|
hco := libnmdc.HubConnectionOptions{
|
||||||
Address: libnmdc.HubAddress(url),
|
Address: libnmdc.HubAddress(fmt.Sprintf("%s:%d", this.cfg.Hub.Address, this.cfg.Hub.Port)),
|
||||||
Self: selfUser,
|
Self: *selfUser,
|
||||||
NickPassword: Pass,
|
NickPassword: Pass,
|
||||||
|
NumEventsToBuffer: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
hubEvents := make(chan libnmdc.HubEvent, 10)
|
hub := hco.Connect()
|
||||||
hub := libnmdc.ConnectAsync(&hco, hubEvents)
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
hub.Disconnect()
|
hub.Disconnect()
|
||||||
@ -110,7 +105,7 @@ func (this *App) HubWorker(Nick, Pass string, so socketio.Socket, done chan stru
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
||||||
case hev, ok := <-hubEvents:
|
case hev, ok := <-hub.OnEvent:
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Printf("[%s] hub chan closed\n", so.Id())
|
log.Printf("[%s] hub chan closed\n", so.Id())
|
||||||
return // abandon
|
return // abandon
|
||||||
|
@ -12,7 +12,8 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"hub": {
|
"hub": {
|
||||||
"address": "nmdc://127.0.0.1:411",
|
"address": "127.0.0.1",
|
||||||
|
"port": 411,
|
||||||
"tag": "nmdc-webfrontend"
|
"tag": "nmdc-webfrontend"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4802
package-lock.json
generated
Normal file
4802
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
package.json
Normal file
32
package.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "nmdc-webfrontend",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "A web interface to an NMDC hub",
|
||||||
|
"homepage": "https://code.ivysaur.me/nmdc-webfrontend/",
|
||||||
|
"main": "s",
|
||||||
|
"scripts": {
|
||||||
|
"webpack": "webpack"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.ivysaur.me/code.ivysaur.me/nmdc-webfrontend"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/socket.io-client": "^1.4.31",
|
||||||
|
"awesome-typescript-loader": "^3.3.0",
|
||||||
|
"css-loader": "^0.28.7",
|
||||||
|
"html-minifier": "^3.5.6",
|
||||||
|
"less": "^2.7.3",
|
||||||
|
"less-loader": "^4.0.5",
|
||||||
|
"less-plugin-clean-css": "^1.5.1",
|
||||||
|
"socket.io-client": "^2.0.4",
|
||||||
|
"style-loader": "^0.19.0",
|
||||||
|
"ts-loader": "^3.1.1",
|
||||||
|
"typescript": "^2.6.1",
|
||||||
|
"uglify-js": "^3.1.8",
|
||||||
|
"webpack": "^3.8.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {}
|
||||||
|
}
|
9
tsconfig.json
Normal file
9
tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es3",
|
||||||
|
"removeComments": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"client/*"
|
||||||
|
]
|
||||||
|
}
|
14
vendor/code.ivysaur.me/libnmdc/.hgignore
generated
vendored
14
vendor/code.ivysaur.me/libnmdc/.hgignore
generated
vendored
@ -1,14 +0,0 @@
|
|||||||
mode:regex
|
|
||||||
|
|
||||||
# Compilation output
|
|
||||||
\.(?:exe|a)$
|
|
||||||
^pkg/
|
|
||||||
|
|
||||||
# Dependencies
|
|
||||||
^src/(?:github.com|gopkg.in|golang.org)/
|
|
||||||
|
|
||||||
# Scratch space
|
|
||||||
^src/nmdc/
|
|
||||||
|
|
||||||
# Binary release artefacts
|
|
||||||
/?__dist/
|
|
20
vendor/code.ivysaur.me/libnmdc/.hgtags
generated
vendored
20
vendor/code.ivysaur.me/libnmdc/.hgtags
generated
vendored
@ -1,20 +0,0 @@
|
|||||||
945ab4b16d05aa084f71bf5da9a3f687e0ec8bbd v0.1.0
|
|
||||||
02a360e95480b97ddad83add5db48b2766339a99 nmdc-log-service-1.0.0
|
|
||||||
137c1b65039e03c80379826a6efdfd808f6fbc8f v0.2.0
|
|
||||||
d8b64d5527c2a5e4d76872e5bc3d69f7646135c6 v0.3.0
|
|
||||||
fca41372e400853775b02e951f9db91d87f41adb nmdc-log-service-1.0.1
|
|
||||||
050b424a7c5d5a27c9323c8810f3afbead1f5b96 v0.4.0
|
|
||||||
da9f123633f9c28be6435ed7898139665d4c39d9 nmdc-log-service-1.0.2
|
|
||||||
75a78f6a78f249a2cd8aa3d29f7e5e6319b4e03b v0.5.0
|
|
||||||
4116422bb10229d887f9296970a166fa1ef8c5fd nmdc-log-service-1.0.3
|
|
||||||
cb86f3a40115cc46f450c0c83fd9b9d3b740e820 nmdc-log-service-1.0.4
|
|
||||||
cb86f3a40115cc46f450c0c83fd9b9d3b740e820 v0.6.0
|
|
||||||
71343a2c641a438206d30ea7e75dc89a11dbef00 v0.7.0
|
|
||||||
b0e57a5fcffdf4102d669db51a3648ddf66a0792 v0.8.0
|
|
||||||
e7c2c71ef24b386add728fad35fff4a996fccbac v0.9.0
|
|
||||||
3ecc037cf2d7080572fe87c2e39ecd153fb0e947 v0.10.0
|
|
||||||
5149ffe70ea8475e480b682345b31aa45a3352db v0.11.0
|
|
||||||
22b156a6fc2f6161765317f4ec9ab3731a26e0e2 v0.12.0
|
|
||||||
3ee0f4ea5142d66079a9500bdcd48a53bdcf362f v0.13.0
|
|
||||||
6422ed687cd308c339b6dc188bbe1034ed93f893 v0.14.0
|
|
||||||
84fb191007017862ffc37af68dcdace5d8c06eee v0.15.0
|
|
711
vendor/code.ivysaur.me/libnmdc/AdcProtocol.go
generated
vendored
711
vendor/code.ivysaur.me/libnmdc/AdcProtocol.go
generated
vendored
@ -1,711 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base32"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type adcState int
|
|
||||||
|
|
||||||
const (
|
|
||||||
adcStateProtocol adcState = 0
|
|
||||||
adcStateIdentify adcState = 1
|
|
||||||
adcStateVerify adcState = 2
|
|
||||||
adcStateNormal adcState = 3
|
|
||||||
adcStateData adcState = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
type AdcProtocol struct {
|
|
||||||
hc *HubConnection
|
|
||||||
state adcState
|
|
||||||
sid, pid, cid string // all in base32 encoding
|
|
||||||
supports map[string]struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// extra extensions that aren't flagged in SUPPORTS
|
|
||||||
adcSeparateApVe string = "SEPARATE_AP_VE" // we invented this string
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewAdcProtocol(hc *HubConnection) Protocol {
|
|
||||||
proto := AdcProtocol{
|
|
||||||
hc: hc,
|
|
||||||
state: adcStateProtocol,
|
|
||||||
supports: make(map[string]struct{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
rxPid := regexp.MustCompile("^[A-Z2-7]{39}$")
|
|
||||||
if !rxPid.MatchString(hc.Hco.AdcPID) {
|
|
||||||
hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Invalid custom PID, regenerating"})
|
|
||||||
hc.Hco.AdcPID = NewPID()
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_base32 := hc.Hco.AdcPID
|
|
||||||
cid_base32, err := proto.pid2cid(pid_base32)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
proto.cid = cid_base32
|
|
||||||
proto.pid = pid_base32
|
|
||||||
|
|
||||||
// Start logging in
|
|
||||||
hc.SayRaw("HSUP ADBASE ADTIGR ADUCMD\n")
|
|
||||||
|
|
||||||
return &proto
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) pid2cid(pid_base32 string) (string, error) {
|
|
||||||
|
|
||||||
pid_raw, err := base32.StdEncoding.DecodeString(pid_base32 + "=")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
cid_raw := Tiger(string(pid_raw))
|
|
||||||
cid_base32 := Base32(cid_raw)
|
|
||||||
|
|
||||||
return cid_base32, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) SID2Nick(sid string) (string, bool) {
|
|
||||||
this.hc.usersMut.Lock()
|
|
||||||
defer this.hc.usersMut.Unlock()
|
|
||||||
|
|
||||||
nick, ok := this.hc.userSIDs[sid]
|
|
||||||
return nick, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) Nick2SID(targetNick string) (string, bool) {
|
|
||||||
this.hc.usersMut.Lock()
|
|
||||||
defer this.hc.usersMut.Unlock()
|
|
||||||
|
|
||||||
for sid, nick := range this.hc.userSIDs {
|
|
||||||
if nick == targetNick {
|
|
||||||
return sid, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) ProcessCommand(msg string) {
|
|
||||||
|
|
||||||
if len(msg) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: msg})
|
|
||||||
|
|
||||||
parts := strings.Split(msg, " ")
|
|
||||||
switch parts[0] {
|
|
||||||
|
|
||||||
case "ISUP":
|
|
||||||
if !(this.state == adcStateProtocol || this.state == adcStateNormal) {
|
|
||||||
this.malformed(parts)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, supportflag := range parts[1:] {
|
|
||||||
if len(supportflag) < 2 {
|
|
||||||
this.malformed(parts)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if supportflag[0:2] == "AD" {
|
|
||||||
this.supports[supportflag[2:]] = struct{}{}
|
|
||||||
} else if supportflag[0:2] == "RM" {
|
|
||||||
delete(this.supports, supportflag[2:])
|
|
||||||
} else {
|
|
||||||
this.malformed(parts)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if this.state == adcStateProtocol {
|
|
||||||
this.state = adcStateIdentify
|
|
||||||
}
|
|
||||||
|
|
||||||
case "ISID":
|
|
||||||
if this.state != adcStateIdentify {
|
|
||||||
this.malformed(parts)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.sid = parts[1]
|
|
||||||
|
|
||||||
// State transition IDENTIFY --> VERIFY and send our own info
|
|
||||||
this.hc.SayRaw("BINF " + this.escape(this.sid) + " " + this.ourINFO(true) + "\n")
|
|
||||||
this.state = adcStateVerify
|
|
||||||
|
|
||||||
case "IINF":
|
|
||||||
// Hub telling information about itself
|
|
||||||
// ADCH++ sends this once we are successfully logged in
|
|
||||||
|
|
||||||
flags, err := this.parts2flags(parts[1:])
|
|
||||||
if err != nil {
|
|
||||||
this.logError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if flags["CT"] != "32" {
|
|
||||||
this.malformed(parts)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = this.handleHubInfo(flags)
|
|
||||||
if err != nil {
|
|
||||||
this.logError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.state != adcStateNormal {
|
|
||||||
this.enterNormalState() // successful login
|
|
||||||
}
|
|
||||||
|
|
||||||
case "BINF":
|
|
||||||
if this.state != adcStateNormal {
|
|
||||||
this.enterNormalState() // successful login
|
|
||||||
}
|
|
||||||
|
|
||||||
sid := parts[1]
|
|
||||||
flags, err := this.parts2flags(parts[2:])
|
|
||||||
if err != nil {
|
|
||||||
this.logError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log this user in, and associate this SID with this user
|
|
||||||
this.hc.usersMut.Lock()
|
|
||||||
defer this.hc.usersMut.Unlock()
|
|
||||||
|
|
||||||
oldNick, sidExists := this.hc.userSIDs[sid]
|
|
||||||
|
|
||||||
uinfo := UserInfo{}
|
|
||||||
if sidExists {
|
|
||||||
uinfo_lookup, ok := this.hc.users[oldNick]
|
|
||||||
if !ok {
|
|
||||||
// Shouldn't happen
|
|
||||||
this.hc.processEvent(HubEvent{
|
|
||||||
EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN,
|
|
||||||
Message: fmt.Sprintf("Hub connection corrupted (missing info for SID='%s' nick='%s'), disconnecting", sid, oldNick),
|
|
||||||
})
|
|
||||||
this.hc.Disconnect()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
uinfo = uinfo_lookup
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateUserInfo(&uinfo, flags)
|
|
||||||
newNick := uinfo.Nick
|
|
||||||
if len(newNick) == 0 {
|
|
||||||
this.logError(fmt.Errorf("Zero-length nick for user (SID='%s')", sid))
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldHandleNewUser := false
|
|
||||||
if sidExists && oldNick != newNick {
|
|
||||||
// Nick change = delete all trace of this user first, treat as new
|
|
||||||
|
|
||||||
delete(this.hc.users, oldNick)
|
|
||||||
delete(this.hc.userSIDs, sid)
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_USER_PART, Nick: oldNick})
|
|
||||||
shouldHandleNewUser = true
|
|
||||||
|
|
||||||
} else if sidExists && oldNick == newNick {
|
|
||||||
// Updating existing user
|
|
||||||
this.hc.users[newNick] = uinfo
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_USER_UPDATED_INFO, Nick: newNick})
|
|
||||||
|
|
||||||
} else if !sidExists {
|
|
||||||
// User joined
|
|
||||||
shouldHandleNewUser = true
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
if shouldHandleNewUser {
|
|
||||||
// Install this SID as pointing to this nick
|
|
||||||
this.hc.userSIDs[sid] = uinfo.Nick
|
|
||||||
|
|
||||||
// Check if this nick was in use by any other SID already
|
|
||||||
for otherSid, otherSidNick := range this.hc.userSIDs {
|
|
||||||
if otherSidNick == newNick && otherSid != sid {
|
|
||||||
this.hc.processEvent(HubEvent{
|
|
||||||
EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN,
|
|
||||||
Message: fmt.Sprintf("Hub connection corrupted (duplicate SIDs '%s' and '%s' for nick '%s'), disconnecting", sid, otherSid, newNick),
|
|
||||||
})
|
|
||||||
this.hc.Disconnect()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notifications
|
|
||||||
this.hc.users[newNick] = uinfo
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_USER_JOINED, Nick: newNick})
|
|
||||||
}
|
|
||||||
|
|
||||||
case "IMSG":
|
|
||||||
// General message from the hub
|
|
||||||
if len(parts) < 2 {
|
|
||||||
this.malformed(parts)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_HUB, Message: this.unescape(parts[1])})
|
|
||||||
|
|
||||||
case "ISTA":
|
|
||||||
// Error message from the hub
|
|
||||||
if len(parts) < 3 {
|
|
||||||
this.malformed(parts)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
code, _ := strconv.Atoi(parts[1])
|
|
||||||
msg := this.unescape(parts[2])
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_HUB, Message: this.ErrorMessage(code, msg)})
|
|
||||||
|
|
||||||
case "IQUI":
|
|
||||||
// Error message from the hub
|
|
||||||
// IQUI V3M6 DI1 MSNick\staken,\splease\spick\sanother\sone TL-1
|
|
||||||
if len(parts) < 2 {
|
|
||||||
this.malformed(parts)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sid := parts[1]
|
|
||||||
|
|
||||||
flags, err := this.parts2flags(parts[2:])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if sid == this.sid {
|
|
||||||
if msg, ok := flags["MS"]; ok {
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_HUB, Message: "The hub is closing our connection because: " + this.unescape(msg)})
|
|
||||||
} else {
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_HUB, Message: "The hub is closing our connection"})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.hc.usersMut.Lock()
|
|
||||||
defer this.hc.usersMut.Unlock()
|
|
||||||
otherSidNick, ok := this.hc.userSIDs[sid]
|
|
||||||
if ok {
|
|
||||||
delete(this.hc.userSIDs, sid)
|
|
||||||
delete(this.hc.users, otherSidNick)
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_USER_PART, Nick: otherSidNick})
|
|
||||||
} else {
|
|
||||||
// ??
|
|
||||||
this.logError(fmt.Errorf("An unknown user quit the hub (SID=%s)", sid))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case "BMSG":
|
|
||||||
// Message from a user
|
|
||||||
// BMSG ZVF4 hi
|
|
||||||
if len(parts) < 3 {
|
|
||||||
this.malformed(parts)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sid := this.unescape(parts[1])
|
|
||||||
msg := this.unescape(parts[2])
|
|
||||||
|
|
||||||
this.hc.usersMut.Lock()
|
|
||||||
defer this.hc.usersMut.Unlock()
|
|
||||||
nick, ok := this.hc.userSIDs[sid]
|
|
||||||
if !ok {
|
|
||||||
this.logError(fmt.Errorf("Recieved message from unknown SID '%s'", sid))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_PUBLIC, Nick: nick, Message: msg})
|
|
||||||
|
|
||||||
case "IGPA":
|
|
||||||
// Password is needed
|
|
||||||
// IGPA 7EIAAAECLMAAAPJQAAADQQYAAAWAYAAAKVFQAAF6EAAAAAYFAAAA
|
|
||||||
// HPAS LZDIJOTZDPWHINHGPT5RHT6WLU7DRME7DQO2O3Q
|
|
||||||
if len(parts) < 2 {
|
|
||||||
this.malformed(parts)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
For GPA/PAS, assuming that '12345' is the random data supplied in GPA, then;
|
|
||||||
PAS = Base32( Hash( password + '12345' ) )
|
|
||||||
|
|
||||||
GPA: The data parameter is at least 24 random bytes (base32 encoded).
|
|
||||||
*/
|
|
||||||
|
|
||||||
data_base32 := parts[1]
|
|
||||||
if len(data_base32)%8 != 0 {
|
|
||||||
data_base32 += strings.Repeat("=", 8-(len(data_base32)%8))
|
|
||||||
}
|
|
||||||
|
|
||||||
data_raw, err := base32.StdEncoding.DecodeString(data_base32)
|
|
||||||
if err != nil {
|
|
||||||
this.logError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resp := Base32(Tiger(this.hc.Hco.NickPassword + string(data_raw)))
|
|
||||||
this.hc.SayRaw("HPAS " + resp + "\n")
|
|
||||||
|
|
||||||
case "EMSG":
|
|
||||||
// Private message from other user
|
|
||||||
// EMSG I5RO FMWH test\spm PMI5RO
|
|
||||||
// EMSG sender recip==us message [flags...]
|
|
||||||
if len(parts) < 4 {
|
|
||||||
this.malformed(parts)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if parts[2] != this.sid {
|
|
||||||
this.logError(fmt.Errorf("Recieved a PM intended for someone else (got SID=%s expected SID=%s)", parts[2], this.sid))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
senderSid := parts[1]
|
|
||||||
senderNick, ok := this.SID2Nick(parts[1])
|
|
||||||
if !ok {
|
|
||||||
this.logError(fmt.Errorf("Recieved a PM from an unknown user (SID=%s)", senderSid))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := this.unescape(parts[3])
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_PRIVATE, Nick: senderNick, Message: msg})
|
|
||||||
|
|
||||||
case "ICMD":
|
|
||||||
// Usercommand
|
|
||||||
// ICMD ADCH++/About\sthis\shub TTHMSG\s+about\n CT3
|
|
||||||
|
|
||||||
if len(parts) < 2 {
|
|
||||||
this.malformed(parts)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
uc := UserCommand{
|
|
||||||
Message: this.unescape(parts[1]),
|
|
||||||
Type: USERCOMMAND_TYPE_RAW, // default
|
|
||||||
}
|
|
||||||
|
|
||||||
flags, err := this.parts2flags(parts[2:])
|
|
||||||
if err != nil {
|
|
||||||
this.malformed(parts)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if ct, ok := flags["CT"]; ok {
|
|
||||||
ct64, _ := strconv.ParseUint(ct, 10, 64)
|
|
||||||
uc.Context = UserCommandContext(ct64)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tt, ok := flags["TT"]; ok {
|
|
||||||
uc.Command = tt
|
|
||||||
}
|
|
||||||
|
|
||||||
if sp, ok := flags["SP"]; ok && sp == "1" {
|
|
||||||
uc.Type = USERCOMMAND_TYPE_SEPARATOR
|
|
||||||
}
|
|
||||||
|
|
||||||
if co, ok := flags["CO"]; ok && co == "1" {
|
|
||||||
uc.Type = USERCOMMAND_TYPE_NICKLIMITED // "Constrained" in ADC parlance
|
|
||||||
}
|
|
||||||
|
|
||||||
if rm, ok := flags["RM"]; ok && rm == "1" {
|
|
||||||
uc.RemoveThis = true
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_USERCOMMAND, UserCommand: &uc})
|
|
||||||
|
|
||||||
// Ignored messages
|
|
||||||
// ````````````````
|
|
||||||
|
|
||||||
case "DCTM": // Client-client ConnectToMe
|
|
||||||
case "BSCH": // Search
|
|
||||||
|
|
||||||
default:
|
|
||||||
this.malformed(parts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) infoFlagsFor(u *UserInfo) map[string]string {
|
|
||||||
parts := map[string]string{
|
|
||||||
"NI": u.Nick,
|
|
||||||
"SS": fmt.Sprintf("%d", u.ShareSize),
|
|
||||||
"SF": fmt.Sprintf("%d", u.SharedFiles),
|
|
||||||
"US": fmt.Sprintf("%d", u.UploadSpeedBps),
|
|
||||||
"DS": fmt.Sprintf("%d", u.DownloadSpeedBps),
|
|
||||||
"SL": fmt.Sprintf("%d", u.Slots),
|
|
||||||
"HN": fmt.Sprintf("%d", u.HubsUnregistered),
|
|
||||||
"HR": fmt.Sprintf("%d", u.HubsRegistered),
|
|
||||||
"HO": fmt.Sprintf("%d", u.HubsOperator),
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := this.supports[adcSeparateApVe]; ok {
|
|
||||||
parts["AP"] = u.ClientTag
|
|
||||||
parts["VE"] = u.ClientVersion
|
|
||||||
} else {
|
|
||||||
parts["VE"] = fmt.Sprintf("%s %s", u.ClientTag, u.ClientVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not send the hub a CT (it decides what type we are)
|
|
||||||
|
|
||||||
return parts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) ourINFO(includePid bool) string {
|
|
||||||
parts := this.infoFlagsFor(this.hc.Hco.Self)
|
|
||||||
parts["ID"] = this.cid
|
|
||||||
if includePid {
|
|
||||||
parts["PD"] = this.pid
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := ""
|
|
||||||
for k, v := range parts {
|
|
||||||
ret += " " + k + this.escape(v)
|
|
||||||
}
|
|
||||||
return ret[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) parts2flags(parts []string) (map[string]string, error) {
|
|
||||||
flags := make(map[string]string, len(parts))
|
|
||||||
for _, flag := range parts {
|
|
||||||
if len(flag) < 2 {
|
|
||||||
return nil, fmt.Errorf("Malformed flag '%s'", flag)
|
|
||||||
}
|
|
||||||
flags[flag[0:2]] = this.unescape(flag[2:])
|
|
||||||
}
|
|
||||||
|
|
||||||
return flags, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) handleHubInfo(flags map[string]string) error {
|
|
||||||
if flags["CT"] != "32" {
|
|
||||||
return fmt.Errorf("Expected CT==32")
|
|
||||||
}
|
|
||||||
|
|
||||||
// IINF DEADCH++\sTest\shub VE2.12.1\s(r"[unknown]")\sRelease HI1 NIADCH++ APADCH++ CT32
|
|
||||||
// AP: extension 3.24 "Application and version separation in INF"
|
|
||||||
// HI:
|
|
||||||
|
|
||||||
// Hub properties updated
|
|
||||||
|
|
||||||
// Special SUPPORT that is only indicated in IINF
|
|
||||||
if _, ok := flags["AP"]; ok {
|
|
||||||
this.supports[adcSeparateApVe] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hub's name is in "NI", hub description in "DE"
|
|
||||||
hubName, ok := flags["NI"]
|
|
||||||
if ok {
|
|
||||||
if hubDesc, ok := flags["DE"]; ok && len(hubDesc) > 0 {
|
|
||||||
hubName += " - " + hubDesc
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hc.HubName = hubName
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_HUBNAME_CHANGED, Nick: this.hc.HubName})
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) updateUserInfo(u *UserInfo, flags map[string]string) {
|
|
||||||
|
|
||||||
// User MyINFO
|
|
||||||
// BINF GUPR IDFEARIFD33NTGC4YBEZ3UFQS5R4ZXXTFL2QN2GRY PDZMIFLG5EKZG3BDRRMIJPG7ARNA6KW3JVIH3DF7Q NIivysaur5 SL3 FS3 SS0 SF0 HN1 HR0 HO0 VEEiskaltDC++\s2.2.9 US2621440 KPSHA256/3UPRORG4BLJ4CG6TO6R3G75A67LXOGD437NALQALRWJF6XBOECTA I40.0.0.0 U418301
|
|
||||||
// BINF GUPR I4172.17.0.1 U418301 IDFEARIFD33NTGC4YBEZ3UFQS5R4ZXXTFL2QN2GRY VEEiskaltDC++\s2.2.9 SF0 NIivysaur5 SL3 HN1 HO0 KPSHA256/3UPRORG4BLJ4CG6TO6R3G75A67LXOGD437NALQALRWJF6XBOECTA HR0 FS3 SS0 US2621440 SUSEGA,ADC0,TCP4,UDP4
|
|
||||||
// Or maybe only incremental:
|
|
||||||
// BINF Z3BA HO1
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
for prop, val := range flags {
|
|
||||||
switch prop {
|
|
||||||
case "ID":
|
|
||||||
u.CID = val
|
|
||||||
case "PD":
|
|
||||||
// ignore PID - it will only appear if we're talking about our own user
|
|
||||||
case "NI":
|
|
||||||
u.Nick = val
|
|
||||||
case "SL":
|
|
||||||
u.Slots, _ = strconv.ParseUint(val, 10, 64)
|
|
||||||
case "SS":
|
|
||||||
u.ShareSize, _ = strconv.ParseUint(val, 10, 64)
|
|
||||||
case "SF":
|
|
||||||
u.SharedFiles, _ = strconv.ParseUint(val, 10, 64)
|
|
||||||
case "HN":
|
|
||||||
u.HubsUnregistered, _ = strconv.ParseUint(val, 10, 64)
|
|
||||||
case "HR":
|
|
||||||
u.HubsRegistered, _ = strconv.ParseUint(val, 10, 64)
|
|
||||||
case "HO":
|
|
||||||
u.HubsOperator, _ = strconv.ParseUint(val, 10, 64)
|
|
||||||
case "US":
|
|
||||||
u.UploadSpeedBps, _ = strconv.ParseUint(val, 10, 64)
|
|
||||||
case "DS":
|
|
||||||
u.DownloadSpeedBps, _ = strconv.ParseUint(val, 10, 64)
|
|
||||||
case "KP":
|
|
||||||
u.Keyprint = val
|
|
||||||
case "I4":
|
|
||||||
u.IPv4Address = val
|
|
||||||
case "I6":
|
|
||||||
u.IPv6Address = val
|
|
||||||
case "U4":
|
|
||||||
u.IPv4UDPPort, _ = strconv.ParseUint(val, 10, 64)
|
|
||||||
case "U6":
|
|
||||||
u.IPv6UDPPort, _ = strconv.ParseUint(val, 10, 64)
|
|
||||||
case "SU":
|
|
||||||
u.SupportFlags = make(map[string]struct{})
|
|
||||||
for _, supportFlag := range strings.Split(val, ",") {
|
|
||||||
u.SupportFlags[supportFlag] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VE / AP
|
|
||||||
AP, hasAP := flags["AP"]
|
|
||||||
VE, hasVE := flags["VE"]
|
|
||||||
if hasAP && hasVE {
|
|
||||||
u.ClientTag = AP
|
|
||||||
u.ClientVersion = VE
|
|
||||||
} else if hasAP && !hasVE {
|
|
||||||
u.ClientTag, u.ClientVersion = this.getAPVEFromSingle(AP)
|
|
||||||
} else if !hasAP && hasVE {
|
|
||||||
u.ClientTag, u.ClientVersion = this.getAPVEFromSingle(VE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) getAPVEFromSingle(term string) (string, string) {
|
|
||||||
words := strings.Split(term, " ")
|
|
||||||
if len(words) > 1 {
|
|
||||||
return strings.Join(words[0:len(words)-1], " "), words[len(words)-1]
|
|
||||||
} else {
|
|
||||||
return term, "0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) enterNormalState() {
|
|
||||||
this.state = adcStateNormal
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTED})
|
|
||||||
this.hc.State = CONNECTIONSTATE_CONNECTED
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) malformed(parts []string) {
|
|
||||||
this.logError(fmt.Errorf("Ignoring malformed, unhandled, or out-of-state protocol command %v", parts))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) logError(e error) {
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Protocol error: " + e.Error()})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) escape(plaintext string) string {
|
|
||||||
// The string "\s" escapes space, "\n" newline and "\\" backslash. This version of the protocol reserves all other escapes for future use; any message containing unknown escapes must be discarded.
|
|
||||||
v1 := strings.Replace(plaintext, `\`, `\\`, -1)
|
|
||||||
v2 := strings.Replace(v1, "\n", `\n`, -1)
|
|
||||||
return strings.Replace(v2, " ", `\s`, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) unescape(encoded string) string {
|
|
||||||
v1 := strings.Replace(encoded, `\s`, " ", -1)
|
|
||||||
v2 := strings.Replace(v1, `\n`, "\n", -1)
|
|
||||||
return strings.Replace(v2, `\\`, `\`, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) SayPublic(msg string) {
|
|
||||||
this.hc.SayRaw("BMSG " + this.sid + " " + this.escape(msg) + "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) SayPrivate(user, message string) {
|
|
||||||
if sid, ok := this.Nick2SID(user); ok {
|
|
||||||
this.hc.SayRaw("DMSG " + this.sid + " " + sid + " " + this.escape(message) + "\n")
|
|
||||||
} else {
|
|
||||||
this.logError(fmt.Errorf("Unknown user '%s'", user))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) ProtoMessageSeparator() string {
|
|
||||||
return "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AdcProtocol) ErrorMessage(code int, msg string) string {
|
|
||||||
severity := code / 100
|
|
||||||
category := (code % 100) / 10
|
|
||||||
cat_sub := (code % 100)
|
|
||||||
|
|
||||||
formatSeverity := func(severity int) string {
|
|
||||||
switch severity {
|
|
||||||
case 0:
|
|
||||||
return "OK"
|
|
||||||
case 1:
|
|
||||||
return "Warning"
|
|
||||||
case 2:
|
|
||||||
return "Error"
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
formatCategory := func(category int) string {
|
|
||||||
switch category {
|
|
||||||
case 0:
|
|
||||||
return ""
|
|
||||||
case 1:
|
|
||||||
return "Hub not accepting users"
|
|
||||||
case 2:
|
|
||||||
return "Login failed"
|
|
||||||
case 3:
|
|
||||||
return "Access denied"
|
|
||||||
case 4:
|
|
||||||
return "Protocol error"
|
|
||||||
case 5:
|
|
||||||
return "Transfer error"
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
formatCatSub := func(cat_sub int) string {
|
|
||||||
switch cat_sub {
|
|
||||||
case 11:
|
|
||||||
return "Hub is full"
|
|
||||||
case 12:
|
|
||||||
return "Hub is disabled"
|
|
||||||
case 21:
|
|
||||||
return "Invalid nick"
|
|
||||||
case 22:
|
|
||||||
return "Nick is already in use"
|
|
||||||
case 23:
|
|
||||||
return "Invalid password"
|
|
||||||
case 24:
|
|
||||||
return "CID already connected"
|
|
||||||
case 25:
|
|
||||||
return "Access denied"
|
|
||||||
case 26:
|
|
||||||
return "Registered users only"
|
|
||||||
case 27:
|
|
||||||
return "Invalid PID"
|
|
||||||
case 31:
|
|
||||||
return "Permanently banned"
|
|
||||||
case 32:
|
|
||||||
return "Temporarily banned"
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parts := make([]string, 0, 4)
|
|
||||||
if fs := formatSeverity(severity); len(fs) > 0 {
|
|
||||||
parts = append(parts, fs)
|
|
||||||
}
|
|
||||||
if fc := formatCategory(category); len(fc) > 0 {
|
|
||||||
parts = append(parts, fc)
|
|
||||||
}
|
|
||||||
if fcs := formatCatSub(cat_sub); len(fcs) > 0 {
|
|
||||||
parts = append(parts, fcs)
|
|
||||||
}
|
|
||||||
if len(msg) > 0 {
|
|
||||||
parts = append(parts, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(parts, ": ") + fmt.Sprintf(" (code %d)", code)
|
|
||||||
|
|
||||||
}
|
|
80
vendor/code.ivysaur.me/libnmdc/AutodetectProtocol.go
generated
vendored
80
vendor/code.ivysaur.me/libnmdc/AutodetectProtocol.go
generated
vendored
@ -1,80 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AutodetectProtocol struct {
|
|
||||||
hc *HubConnection
|
|
||||||
|
|
||||||
realProtoMut sync.Mutex
|
|
||||||
realProto Protocol
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAutodetectProtocol(hc *HubConnection) Protocol {
|
|
||||||
proto := AutodetectProtocol{
|
|
||||||
hc: hc,
|
|
||||||
realProto: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
go proto.timeout()
|
|
||||||
|
|
||||||
return &proto
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AutodetectProtocol) timeout() {
|
|
||||||
time.Sleep(AUTODETECT_ADC_NMDC_TIMEOUT)
|
|
||||||
|
|
||||||
this.realProtoMut.Lock()
|
|
||||||
defer this.realProtoMut.Unlock()
|
|
||||||
|
|
||||||
if this.realProto == nil {
|
|
||||||
this.realProto = NewAdcProtocol(this.hc)
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Detected ADC protocol"})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AutodetectProtocol) ProcessCommand(msg string) {
|
|
||||||
this.realProtoMut.Lock()
|
|
||||||
defer this.realProtoMut.Unlock()
|
|
||||||
|
|
||||||
if this.realProto == nil {
|
|
||||||
// We actually got some data using $ as the separator?
|
|
||||||
// Upgrade to a full NMDC protocol
|
|
||||||
this.realProto = NewNmdcProtocol(this.hc)
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Detected NMDC protocol"})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.realProto.ProcessCommand(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AutodetectProtocol) SayPublic(msg string) {
|
|
||||||
this.realProtoMut.Lock()
|
|
||||||
defer this.realProtoMut.Unlock()
|
|
||||||
|
|
||||||
if this.realProto == nil {
|
|
||||||
this.realProto = NewNmdcProtocol(this.hc)
|
|
||||||
}
|
|
||||||
this.realProto.SayPublic(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AutodetectProtocol) SayPrivate(user, message string) {
|
|
||||||
this.realProtoMut.Lock()
|
|
||||||
defer this.realProtoMut.Unlock()
|
|
||||||
|
|
||||||
if this.realProto == nil {
|
|
||||||
this.realProto = NewNmdcProtocol(this.hc)
|
|
||||||
}
|
|
||||||
this.realProto.SayPrivate(user, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *AutodetectProtocol) ProtoMessageSeparator() string {
|
|
||||||
this.realProtoMut.Lock()
|
|
||||||
defer this.realProtoMut.Unlock()
|
|
||||||
|
|
||||||
if this.realProto == nil {
|
|
||||||
return "|"
|
|
||||||
}
|
|
||||||
return this.realProto.ProtoMessageSeparator()
|
|
||||||
}
|
|
29
vendor/code.ivysaur.me/libnmdc/ConnectionMode.go
generated
vendored
29
vendor/code.ivysaur.me/libnmdc/ConnectionMode.go
generated
vendored
@ -1,29 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConnectionMode rune
|
|
||||||
|
|
||||||
const (
|
|
||||||
CONNECTIONMODE_ACTIVE ConnectionMode = 'A' // 65
|
|
||||||
CONNECTIONMODE_PASSIVE ConnectionMode = 'P' // 49
|
|
||||||
CONNECTIONMODE_SOCKS5 ConnectionMode = '5' // 53
|
|
||||||
)
|
|
||||||
|
|
||||||
func (this ConnectionMode) String() string {
|
|
||||||
switch this {
|
|
||||||
case CONNECTIONMODE_ACTIVE:
|
|
||||||
return "Active"
|
|
||||||
|
|
||||||
case CONNECTIONMODE_PASSIVE:
|
|
||||||
return "Passive"
|
|
||||||
|
|
||||||
case CONNECTIONMODE_SOCKS5:
|
|
||||||
return "SOCKS5"
|
|
||||||
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("ConnectionMode(\"%s\")", string(this))
|
|
||||||
}
|
|
||||||
}
|
|
40
vendor/code.ivysaur.me/libnmdc/ConnectionState.go
generated
vendored
40
vendor/code.ivysaur.me/libnmdc/ConnectionState.go
generated
vendored
@ -1,40 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConnectionState int
|
|
||||||
|
|
||||||
const (
|
|
||||||
CONNECTIONSTATE_DISCONNECTED = 1
|
|
||||||
CONNECTIONSTATE_CONNECTING = 2 // Handshake in progress
|
|
||||||
CONNECTIONSTATE_CONNECTED = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
func (cs ConnectionState) String() string {
|
|
||||||
switch cs {
|
|
||||||
case CONNECTIONSTATE_DISCONNECTED:
|
|
||||||
return "Disconnected"
|
|
||||||
case CONNECTIONSTATE_CONNECTING:
|
|
||||||
return "Connecting"
|
|
||||||
case CONNECTIONSTATE_CONNECTED:
|
|
||||||
return "Connected"
|
|
||||||
default:
|
|
||||||
return "?"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkIsNetTimeout(err error) bool {
|
|
||||||
if err == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch err.(type) {
|
|
||||||
case net.Error:
|
|
||||||
return err.(net.Error).Timeout()
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
15
vendor/code.ivysaur.me/libnmdc/Gopkg.lock
generated
vendored
15
vendor/code.ivysaur.me/libnmdc/Gopkg.lock
generated
vendored
@ -1,15 +0,0 @@
|
|||||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
|
||||||
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/cxmcc/tiger"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "bde35e2713d7f674987c2ecb21a6b0fc33749516"
|
|
||||||
|
|
||||||
[solve-meta]
|
|
||||||
analyzer-name = "dep"
|
|
||||||
analyzer-version = 1
|
|
||||||
inputs-digest = "c88ee670a5600b482019325b6d6633bb6b5fe789596dc29ef809aa7bb013927b"
|
|
||||||
solver-name = "gps-cdcl"
|
|
||||||
solver-version = 1
|
|
48
vendor/code.ivysaur.me/libnmdc/HubAddress.go
generated
vendored
48
vendor/code.ivysaur.me/libnmdc/HubAddress.go
generated
vendored
@ -1,48 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HubAddress string
|
|
||||||
|
|
||||||
func (this *HubAddress) parse() url.URL {
|
|
||||||
parsed, err := url.Parse(strings.ToLower(string(*this)))
|
|
||||||
if err != nil || len(parsed.Host) == 0 {
|
|
||||||
parsed = &url.URL{
|
|
||||||
Scheme: "",
|
|
||||||
Host: string(*this),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add default port if not specified
|
|
||||||
if !strings.ContainsRune(parsed.Host, ':') {
|
|
||||||
parsed.Host = parsed.Host + ":411"
|
|
||||||
}
|
|
||||||
|
|
||||||
return *parsed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HubAddress) IsSecure() bool {
|
|
||||||
parsed := this.parse()
|
|
||||||
|
|
||||||
return parsed.Scheme == "nmdcs" || parsed.Scheme == "dchubs" || parsed.Scheme == "adcs"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HubAddress) GetHostOnly() string {
|
|
||||||
return this.parse().Host
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HubAddress) GetProtocol() HubProtocol {
|
|
||||||
parsed := this.parse()
|
|
||||||
|
|
||||||
switch parsed.Scheme {
|
|
||||||
case "nmdc", "dchub", "nmdcs", "dchubs":
|
|
||||||
return HubProtocolNmdc
|
|
||||||
case "adc", "adcs":
|
|
||||||
return HubProtocolAdc
|
|
||||||
default:
|
|
||||||
return HubProtocolAutodetect
|
|
||||||
}
|
|
||||||
}
|
|
229
vendor/code.ivysaur.me/libnmdc/HubConnection.go
generated
vendored
229
vendor/code.ivysaur.me/libnmdc/HubConnection.go
generated
vendored
@ -1,229 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"regexp"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HubConnection struct {
|
|
||||||
// Supplied parameters
|
|
||||||
Hco *HubConnectionOptions
|
|
||||||
|
|
||||||
// Current remote status
|
|
||||||
HubName string
|
|
||||||
State ConnectionState
|
|
||||||
|
|
||||||
usersMut sync.RWMutex
|
|
||||||
users map[string]UserInfo
|
|
||||||
userSIDs map[string]string
|
|
||||||
|
|
||||||
proto Protocol
|
|
||||||
|
|
||||||
// Event callback
|
|
||||||
processEvent func(HubEvent)
|
|
||||||
|
|
||||||
// Private state
|
|
||||||
conn net.Conn // this is an interface
|
|
||||||
connValid bool
|
|
||||||
autoReconnect bool
|
|
||||||
lastDataRecieved time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thread-safe user accessor.
|
|
||||||
func (this *HubConnection) Users(cb func(*map[string]UserInfo) error) error {
|
|
||||||
this.usersMut.Lock()
|
|
||||||
defer this.usersMut.Unlock()
|
|
||||||
|
|
||||||
return cb(&this.users)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HubConnection) SayPublic(message string) {
|
|
||||||
this.proto.SayPublic(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HubConnection) SayPrivate(recipient string, message string) {
|
|
||||||
this.proto.SayPrivate(recipient, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HubConnection) UserExists(nick string) bool {
|
|
||||||
this.usersMut.RLock()
|
|
||||||
defer this.usersMut.RUnlock()
|
|
||||||
|
|
||||||
_, already_existed := this.users[nick]
|
|
||||||
return already_existed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HubConnection) UserCount() int {
|
|
||||||
this.usersMut.RLock()
|
|
||||||
defer this.usersMut.RUnlock()
|
|
||||||
|
|
||||||
return len(this.users)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HubConnection) userJoined_NameOnly(nick string) {
|
|
||||||
if !this.UserExists(nick) {
|
|
||||||
|
|
||||||
this.usersMut.Lock()
|
|
||||||
this.users[nick] = *NewUserInfo(nick)
|
|
||||||
this.usersMut.Unlock() // Don't lock over a processEvent boundary
|
|
||||||
|
|
||||||
this.processEvent(HubEvent{EventType: EVENT_USER_JOINED, Nick: nick})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HubConnection) userJoined_Full(uinf *UserInfo) {
|
|
||||||
// n.b. also called when we get a replacement MyINFO for someone
|
|
||||||
this.usersMut.Lock()
|
|
||||||
_, userExisted := this.users[uinf.Nick] // don't use UserExists as it would deadlock the mutex
|
|
||||||
this.users[uinf.Nick] = *uinf
|
|
||||||
this.usersMut.Unlock() // Don't lock over a processEvent boundary
|
|
||||||
|
|
||||||
if !userExisted {
|
|
||||||
this.processEvent(HubEvent{EventType: EVENT_USER_JOINED, Nick: uinf.Nick})
|
|
||||||
} else {
|
|
||||||
this.processEvent(HubEvent{EventType: EVENT_USER_UPDATED_INFO, Nick: uinf.Nick})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SayRaw sends raw bytes over the TCP socket. Callers should add the protocol
|
|
||||||
// terminating character themselves (e.g. `|` for NMDC).
|
|
||||||
// Note that protocol messages are transmitted on the caller thread, not from
|
|
||||||
// any internal libnmdc thread.
|
|
||||||
func (this *HubConnection) SayRaw(protocolCommand string) error {
|
|
||||||
if !this.connValid {
|
|
||||||
return ErrNotConnected
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := this.conn.Write([]byte(protocolCommand))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HubConnection) SayKeepalive() error {
|
|
||||||
if !this.connValid {
|
|
||||||
return ErrNotConnected
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.SayRaw(this.proto.ProtoMessageSeparator())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HubConnection) Disconnect() {
|
|
||||||
this.autoReconnect = false
|
|
||||||
if this.conn != nil {
|
|
||||||
this.conn.Close()
|
|
||||||
}
|
|
||||||
// A CONNECTIONSTATE_DISCONNECTED message will be emitted by the worker.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HubConnection) worker() {
|
|
||||||
var fullBuffer string
|
|
||||||
var err error = nil
|
|
||||||
var nbytes int = 0
|
|
||||||
|
|
||||||
for {
|
|
||||||
|
|
||||||
// If we're not connected, attempt reconnect
|
|
||||||
if this.conn == nil {
|
|
||||||
|
|
||||||
fullBuffer = "" // clear
|
|
||||||
|
|
||||||
if this.Hco.Address.IsSecure() {
|
|
||||||
this.conn, err = tls.Dial("tcp", this.Hco.Address.GetHostOnly(), &tls.Config{
|
|
||||||
InsecureSkipVerify: this.Hco.SkipVerifyTLS,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.conn, err = net.Dial("tcp", this.Hco.Address.GetHostOnly())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
this.State = CONNECTIONSTATE_DISCONNECTED
|
|
||||||
this.connValid = false
|
|
||||||
this.proto = nil
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.State = CONNECTIONSTATE_CONNECTING
|
|
||||||
this.connValid = true
|
|
||||||
this.processEvent(HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTING})
|
|
||||||
this.proto = this.Hco.Address.GetProtocol().Create(this)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read from socket into our local buffer (blocking)
|
|
||||||
if this.connValid {
|
|
||||||
|
|
||||||
readBuff := make([]byte, 1024)
|
|
||||||
this.conn.SetReadDeadline(time.Now().Add(SEND_KEEPALIVE_EVERY))
|
|
||||||
|
|
||||||
nbytes, err = this.conn.Read(readBuff)
|
|
||||||
|
|
||||||
if checkIsNetTimeout(err) {
|
|
||||||
// No data before read deadline
|
|
||||||
err = nil
|
|
||||||
|
|
||||||
if this.proto == nil {
|
|
||||||
// Autodetect: switch to ADC
|
|
||||||
this.proto = NewAdcProtocol(this)
|
|
||||||
} else {
|
|
||||||
// Normal
|
|
||||||
// Send KA packet
|
|
||||||
err = this.SayKeepalive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if nbytes > 0 {
|
|
||||||
this.lastDataRecieved = time.Now()
|
|
||||||
fullBuffer += string(readBuff[0:nbytes])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.proto != nil {
|
|
||||||
rxSeparator := regexp.QuoteMeta(this.proto.ProtoMessageSeparator())
|
|
||||||
rxProtocolMessage := regexp.MustCompile(`(?ms)\A[^` + rxSeparator + `]*` + rxSeparator)
|
|
||||||
|
|
||||||
// Attempt to parse a message block
|
|
||||||
for len(fullBuffer) > 0 {
|
|
||||||
|
|
||||||
// FIXME nmdc
|
|
||||||
for len(fullBuffer) > 0 && fullBuffer[0] == '|' {
|
|
||||||
fullBuffer = fullBuffer[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
protocolMessage := rxProtocolMessage.FindString(fullBuffer)
|
|
||||||
if len(protocolMessage) > 0 {
|
|
||||||
this.proto.ProcessCommand(protocolMessage[:len(protocolMessage)-1])
|
|
||||||
fullBuffer = fullBuffer[len(protocolMessage):]
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil && time.Now().Sub(this.lastDataRecieved) > RECONNECT_IF_NO_DATA_RECIEVED_IN {
|
|
||||||
err = fmt.Errorf("No packets recieved since %s, connection presumed lost", this.lastDataRecieved.Format(time.RFC3339))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maybe we disconnected
|
|
||||||
// Perform this check *last*, to ensure we've had a final shot at
|
|
||||||
// clearing out any queued messages
|
|
||||||
if err != nil {
|
|
||||||
this.State = CONNECTIONSTATE_DISCONNECTED
|
|
||||||
this.conn = nil
|
|
||||||
this.connValid = false
|
|
||||||
this.proto = nil
|
|
||||||
this.processEvent(HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_DISCONNECTED, Message: err.Error()})
|
|
||||||
|
|
||||||
if this.autoReconnect {
|
|
||||||
time.Sleep(AUTO_RECONNECT_AFTER) // Wait before reconnect
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
return // leave the worker for good
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
79
vendor/code.ivysaur.me/libnmdc/HubConnectionOptions.go
generated
vendored
79
vendor/code.ivysaur.me/libnmdc/HubConnectionOptions.go
generated
vendored
@ -1,79 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HubConnectionOptions struct {
|
|
||||||
Address HubAddress
|
|
||||||
SkipVerifyTLS bool // using a negative verb, because bools default to false
|
|
||||||
SkipAutoReconnect bool // as above
|
|
||||||
Self *UserInfo
|
|
||||||
NickPassword string
|
|
||||||
AdcPID string // blank: autogenerate
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPID() string {
|
|
||||||
pidBytes := make([]byte, 24)
|
|
||||||
n, err := rand.Read(pidBytes)
|
|
||||||
if err != nil {
|
|
||||||
panic(err) // Insufficient cryptographic randomness
|
|
||||||
}
|
|
||||||
|
|
||||||
if n != 24 {
|
|
||||||
panic("Insufficient cryptographic randomness")
|
|
||||||
}
|
|
||||||
|
|
||||||
return Base32(pidBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HubConnectionOptions) prepareConnection() *HubConnection {
|
|
||||||
if this.Self.ClientTag == "" {
|
|
||||||
this.Self.ClientTag = DEFAULT_CLIENT_TAG
|
|
||||||
this.Self.ClientVersion = DEFAULT_CLIENT_VERSION
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shouldn't be blank either
|
|
||||||
if this.Self.ClientVersion == "" {
|
|
||||||
this.Self.ClientVersion = "0"
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.AdcPID == "" {
|
|
||||||
this.AdcPID = NewPID()
|
|
||||||
}
|
|
||||||
|
|
||||||
hc := HubConnection{
|
|
||||||
Hco: this,
|
|
||||||
HubName: DEFAULT_HUB_NAME,
|
|
||||||
State: CONNECTIONSTATE_DISCONNECTED,
|
|
||||||
users: make(map[string]UserInfo),
|
|
||||||
userSIDs: make(map[string]string),
|
|
||||||
|
|
||||||
autoReconnect: !this.SkipAutoReconnect,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &hc
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectAsync connects to a hub server, and spawns a background goroutine to handle
|
|
||||||
// protocol messages. Events will be sent by channel to the supplied onEvent channel,
|
|
||||||
// the client is responsible for selecting off this.
|
|
||||||
func ConnectAsync(opts *HubConnectionOptions, onEvent chan HubEvent) *HubConnection {
|
|
||||||
hc := opts.prepareConnection()
|
|
||||||
hc.processEvent = func(ev HubEvent) {
|
|
||||||
onEvent <- ev
|
|
||||||
}
|
|
||||||
|
|
||||||
go hc.worker()
|
|
||||||
return hc
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectSync connects to a hub server, and blocks forever to handle protocol messages.
|
|
||||||
// Client code should supply an event handling function as hco.OnEventSync.
|
|
||||||
func ConnectSync(opts *HubConnectionOptions, onEvent func(hub *HubConnection, ev HubEvent)) {
|
|
||||||
hc := opts.prepareConnection()
|
|
||||||
hc.processEvent = func(ev HubEvent) {
|
|
||||||
onEvent(hc, ev)
|
|
||||||
}
|
|
||||||
hc.worker()
|
|
||||||
}
|
|
25
vendor/code.ivysaur.me/libnmdc/HubEvent.go
generated
vendored
25
vendor/code.ivysaur.me/libnmdc/HubEvent.go
generated
vendored
@ -1,25 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
type HubEventType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
EVENT_PUBLIC HubEventType = 1
|
|
||||||
EVENT_PRIVATE HubEventType = 2
|
|
||||||
EVENT_SYSTEM_MESSAGE_FROM_HUB HubEventType = 3
|
|
||||||
EVENT_SYSTEM_MESSAGE_FROM_CONN HubEventType = 4
|
|
||||||
EVENT_USER_JOINED HubEventType = 5
|
|
||||||
EVENT_USER_PART HubEventType = 6
|
|
||||||
EVENT_USER_UPDATED_INFO HubEventType = 7
|
|
||||||
EVENT_CONNECTION_STATE_CHANGED HubEventType = 8
|
|
||||||
EVENT_HUBNAME_CHANGED HubEventType = 9
|
|
||||||
EVENT_DEBUG_MESSAGE HubEventType = 10
|
|
||||||
EVENT_USERCOMMAND HubEventType = 11
|
|
||||||
)
|
|
||||||
|
|
||||||
type HubEvent struct {
|
|
||||||
EventType HubEventType
|
|
||||||
Nick string
|
|
||||||
Message string
|
|
||||||
StateChange ConnectionState
|
|
||||||
UserCommand *UserCommand
|
|
||||||
}
|
|
407
vendor/code.ivysaur.me/libnmdc/NmdcProtocol.go
generated
vendored
407
vendor/code.ivysaur.me/libnmdc/NmdcProtocol.go
generated
vendored
@ -1,407 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type NmdcProtocol struct {
|
|
||||||
hc *HubConnection
|
|
||||||
sentOurHello bool
|
|
||||||
supports map[string]struct{}
|
|
||||||
|
|
||||||
rxPublicChat *regexp.Regexp
|
|
||||||
rxIncomingTo *regexp.Regexp
|
|
||||||
rxUserCommand *regexp.Regexp
|
|
||||||
rxMyInfo *regexp.Regexp
|
|
||||||
rxMyInfoNoTag *regexp.Regexp
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewNmdcProtocol(hc *HubConnection) Protocol {
|
|
||||||
proto := NmdcProtocol{}
|
|
||||||
proto.hc = hc
|
|
||||||
|
|
||||||
// With the `m` flag, use \A instead of ^ to anchor to start
|
|
||||||
// This fixes accidentally finding a better match in the middle of a multi-line message
|
|
||||||
|
|
||||||
proto.rxPublicChat = regexp.MustCompile(`(?ms)\A<([^>]*)> (.*)$`)
|
|
||||||
proto.rxIncomingTo = regexp.MustCompile(`(?ms)\A([^ ]+) From: ([^ ]+) \$<([^>]*)> (.*)`)
|
|
||||||
proto.rxUserCommand = regexp.MustCompile(`(?ms)\A(\d+) (\d+)\s?([^\$]*)\$?(.*)`)
|
|
||||||
|
|
||||||
// Format: $ALL <nick> <description>$ $<connection><flag>$<e-mail>$<sharesize>$
|
|
||||||
|
|
||||||
HEAD := `(?ms)^\$ALL ([^ ]+) `
|
|
||||||
FOOT := `\$.\$([^$]+)\$([^$]*)\$([0-9]*)\$$`
|
|
||||||
|
|
||||||
proto.rxMyInfo = regexp.MustCompile(HEAD + `([^<]*)<(.+?) V:([^,]+),M:(.),H:([0-9]+)/([0-9]+)/([0-9]+),S:([0-9]+)>` + FOOT)
|
|
||||||
proto.rxMyInfoNoTag = regexp.MustCompile(HEAD + `([^$]*)` + FOOT) // Fallback for no tag
|
|
||||||
|
|
||||||
// Done
|
|
||||||
return &proto
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *NmdcProtocol) ProcessCommand(message string) {
|
|
||||||
|
|
||||||
// Zero-length protocol message
|
|
||||||
// ````````````````````````````
|
|
||||||
if len(message) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public chat
|
|
||||||
// ```````````
|
|
||||||
if this.rxPublicChat.MatchString(message) {
|
|
||||||
pubchat_parts := this.rxPublicChat.FindStringSubmatch(message)
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_PUBLIC, Nick: pubchat_parts[1], Message: this.unescape(pubchat_parts[2])})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// System messages
|
|
||||||
// ```````````````
|
|
||||||
if message[0] != '$' {
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_HUB, Nick: this.hc.HubName, Message: this.unescape(message)})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Protocol messages
|
|
||||||
// `````````````````
|
|
||||||
|
|
||||||
commandParts := strings.SplitN(message, " ", 2)
|
|
||||||
switch commandParts[0] {
|
|
||||||
|
|
||||||
case "$Lock":
|
|
||||||
this.hc.SayRaw("$Supports NoHello NoGetINFO UserCommand UserIP2 QuickList ChatOnly|" +
|
|
||||||
"$Key " + this.unlock([]byte(commandParts[1])) + "|")
|
|
||||||
this.sentOurHello = false
|
|
||||||
|
|
||||||
case "$Hello":
|
|
||||||
if commandParts[1] == this.hc.Hco.Self.Nick && !this.sentOurHello {
|
|
||||||
this.hc.SayRaw("$Version 1,0091|")
|
|
||||||
this.hc.SayRaw("$GetNickList|")
|
|
||||||
this.sayInfo()
|
|
||||||
this.sentOurHello = true
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.hc.userJoined_NameOnly(commandParts[1])
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$HubName":
|
|
||||||
this.hc.HubName = commandParts[1]
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_HUBNAME_CHANGED, Nick: commandParts[1]})
|
|
||||||
|
|
||||||
case "$ValidateDenide": // sic
|
|
||||||
if len(this.hc.Hco.NickPassword) > 0 {
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Incorrect password."})
|
|
||||||
} else {
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Nick already in use."})
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$HubIsFull":
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Hub is full."})
|
|
||||||
|
|
||||||
case "$BadPass":
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "Incorrect password."})
|
|
||||||
|
|
||||||
case "$GetPass":
|
|
||||||
if len(this.hc.Hco.NickPassword) == 0 {
|
|
||||||
// We've got a problem. MyPass with no arguments is a syntax error with no message = instant close
|
|
||||||
// Just drop the connection
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_SYSTEM_MESSAGE_FROM_CONN, Message: "This account is passworded."})
|
|
||||||
this.hc.Disconnect()
|
|
||||||
} else {
|
|
||||||
this.hc.SayRaw("$MyPass " + this.escape(this.hc.Hco.NickPassword) + "|")
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$Quit":
|
|
||||||
this.hc.usersMut.Lock()
|
|
||||||
delete(this.hc.users, commandParts[1])
|
|
||||||
this.hc.usersMut.Unlock() // Don't lock over a processEvent boundary
|
|
||||||
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_USER_PART, Nick: commandParts[1]})
|
|
||||||
|
|
||||||
case "$MyINFO":
|
|
||||||
u, err := this.parseMyINFO(commandParts[1])
|
|
||||||
if err != nil {
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hc.userJoined_Full(u)
|
|
||||||
|
|
||||||
case "$NickList":
|
|
||||||
nicklist := strings.Split(commandParts[1], "$$")
|
|
||||||
for _, nick := range nicklist {
|
|
||||||
if len(nick) > 0 {
|
|
||||||
this.hc.userJoined_NameOnly(nick)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$OpList":
|
|
||||||
oplist := strings.Split(commandParts[1], "$$")
|
|
||||||
opmap := map[string]struct{}{}
|
|
||||||
|
|
||||||
// Organise/sort the list, and ensure we're not meeting an operator for
|
|
||||||
// the first time
|
|
||||||
for _, nick := range oplist {
|
|
||||||
if len(nick) > 0 {
|
|
||||||
opmap[nick] = struct{}{}
|
|
||||||
this.hc.userJoined_NameOnly(nick) // assert existence; noop otherwise
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark all mentioned nicks as being operators, and all unmentioned nicks
|
|
||||||
// as being /not/ an operator. (second pass minimises RW mutex use)
|
|
||||||
func() {
|
|
||||||
this.hc.usersMut.Lock()
|
|
||||||
defer this.hc.usersMut.Unlock()
|
|
||||||
|
|
||||||
for nick, userinfo := range this.hc.users {
|
|
||||||
_, isop := opmap[nick]
|
|
||||||
|
|
||||||
userinfo.IsOperator = isop
|
|
||||||
this.hc.users[nick] = userinfo
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
case "$To:":
|
|
||||||
valid := false
|
|
||||||
if this.rxIncomingTo.MatchString(commandParts[1]) {
|
|
||||||
txparts := this.rxIncomingTo.FindStringSubmatch(commandParts[1])
|
|
||||||
if txparts[1] == this.hc.Hco.Self.Nick && txparts[2] == txparts[3] {
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_PRIVATE, Nick: txparts[2], Message: this.unescape(txparts[4])})
|
|
||||||
valid = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !valid {
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Malformed private message '" + commandParts[1] + "'"})
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$UserIP":
|
|
||||||
this.hc.usersMut.Lock()
|
|
||||||
|
|
||||||
pairs := strings.Split(commandParts[1], "$$")
|
|
||||||
notifyOfUpdate := make([]string, 0, len(pairs))
|
|
||||||
|
|
||||||
nextIPPair:
|
|
||||||
for _, pair := range pairs {
|
|
||||||
parts := strings.SplitN(pair, " ", 2)
|
|
||||||
if len(parts) != 2 {
|
|
||||||
// ????
|
|
||||||
continue nextIPPair
|
|
||||||
}
|
|
||||||
|
|
||||||
ip2nick := parts[0]
|
|
||||||
ip2addr := parts[1]
|
|
||||||
|
|
||||||
uinfo, ok := this.hc.users[ip2nick]
|
|
||||||
if !ok {
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Recieved IP '" + ip2addr + "' for unknown user '" + ip2nick + "'"})
|
|
||||||
continue nextIPPair
|
|
||||||
}
|
|
||||||
|
|
||||||
if uinfo.IPAddress != ip2addr {
|
|
||||||
uinfo.IPAddress = ip2addr
|
|
||||||
notifyOfUpdate = append(notifyOfUpdate, ip2nick)
|
|
||||||
this.hc.users[ip2nick] = uinfo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hc.usersMut.Unlock()
|
|
||||||
|
|
||||||
for _, nick := range notifyOfUpdate {
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_USER_UPDATED_INFO, Nick: nick})
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$ForceMove":
|
|
||||||
this.hc.Hco.Address = HubAddress(commandParts[1])
|
|
||||||
this.hc.conn.Close() // we'll reconnect onto the new address
|
|
||||||
|
|
||||||
case "$UserCommand":
|
|
||||||
// $UserCommand 1 1 Group chat\New group chat$<%[mynick]> !groupchat_new||
|
|
||||||
if this.rxUserCommand.MatchString(commandParts[1]) {
|
|
||||||
usc := this.rxUserCommand.FindStringSubmatch(commandParts[1])
|
|
||||||
|
|
||||||
typeInt, _ := strconv.Atoi(usc[1])
|
|
||||||
contextInt, _ := strconv.Atoi(usc[2])
|
|
||||||
|
|
||||||
uscStruct := UserCommand{
|
|
||||||
Type: UserCommandType(typeInt),
|
|
||||||
Context: UserCommandContext(contextInt),
|
|
||||||
Message: usc[3],
|
|
||||||
Command: this.unescape(usc[4]),
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_USERCOMMAND, UserCommand: &uscStruct})
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Malformed usercommand '" + commandParts[1] + "'"})
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$Supports":
|
|
||||||
this.supports = make(map[string]struct{})
|
|
||||||
for _, s := range strings.Split(commandParts[1], " ") {
|
|
||||||
this.supports[s] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !this.sentOurHello {
|
|
||||||
|
|
||||||
// Need to log in.
|
|
||||||
// If the hub supports QuickList, we can skip one network roundtrip
|
|
||||||
if _, ok := this.supports["QuickList"]; ok {
|
|
||||||
this.sayInfo()
|
|
||||||
this.hc.SayRaw("$GetNickList|")
|
|
||||||
} else {
|
|
||||||
this.hc.SayRaw("$ValidateNick " + this.escape(this.hc.Hco.Self.Nick) + "|")
|
|
||||||
}
|
|
||||||
|
|
||||||
// This also counts as the end of the handshake from our POV. Consider
|
|
||||||
// ourselves logged in
|
|
||||||
this.sentOurHello = true
|
|
||||||
if this.hc.State != CONNECTIONSTATE_CONNECTED {
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_CONNECTION_STATE_CHANGED, StateChange: CONNECTIONSTATE_CONNECTED})
|
|
||||||
this.hc.State = CONNECTIONSTATE_CONNECTED
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// IGNORABLE COMMANDS
|
|
||||||
case "$HubTopic":
|
|
||||||
case "$Search":
|
|
||||||
case "$ConnectToMe":
|
|
||||||
|
|
||||||
default:
|
|
||||||
this.hc.processEvent(HubEvent{EventType: EVENT_DEBUG_MESSAGE, Message: "Unhandled protocol command '" + commandParts[0] + "'"})
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *NmdcProtocol) escape(plaintext string) string {
|
|
||||||
v1 := strings.Replace(plaintext, "&", "&", -1)
|
|
||||||
v2 := strings.Replace(v1, "|", "|", -1)
|
|
||||||
return strings.Replace(v2, "$", "$", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *NmdcProtocol) unescape(encoded string) string {
|
|
||||||
v1 := strings.Replace(encoded, "$", "$", -1)
|
|
||||||
v2 := strings.Replace(v1, "|", "|", -1)
|
|
||||||
return strings.Replace(v2, "&", "&", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *NmdcProtocol) SayPublic(message string) {
|
|
||||||
this.hc.SayRaw("<" + this.hc.Hco.Self.Nick + "> " + this.escape(message) + "|")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *NmdcProtocol) SayPrivate(recipient, message string) {
|
|
||||||
this.hc.SayRaw("$To: " + recipient + " From: " + this.hc.Hco.Self.Nick + " $<" + this.hc.Hco.Self.Nick + "> " + this.escape(message) + "|")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *NmdcProtocol) sayInfo() {
|
|
||||||
this.hc.SayRaw(this.getUserMyINFO(this.hc.Hco.Self) + "|")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *NmdcProtocol) parseMyINFO(protomsg string) (*UserInfo, error) {
|
|
||||||
ret := UserInfo{}
|
|
||||||
|
|
||||||
// Normal format (with tag in exact V/M/H/S order)
|
|
||||||
matches := this.rxMyInfo.FindStringSubmatch(protomsg)
|
|
||||||
if matches != nil {
|
|
||||||
ret.Nick = matches[1]
|
|
||||||
ret.Description = this.unescape(matches[2])
|
|
||||||
ret.ClientTag = this.unescape(matches[3])
|
|
||||||
ret.ClientVersion = matches[4]
|
|
||||||
ret.ConnectionMode = ConnectionMode(matches[5][0])
|
|
||||||
maybeParse(matches[6], &ret.HubsUnregistered, 0)
|
|
||||||
maybeParse(matches[7], &ret.HubsRegistered, 0)
|
|
||||||
maybeParse(matches[8], &ret.HubsOperator, 0)
|
|
||||||
maybeParse(matches[9], &ret.Slots, 0)
|
|
||||||
if len(matches[10]) > 1 {
|
|
||||||
ret.Speed = matches[10][:len(matches[10])-2]
|
|
||||||
} else {
|
|
||||||
ret.Speed = ""
|
|
||||||
}
|
|
||||||
ret.Flag = UserFlag(matches[10][len(matches[10])-1])
|
|
||||||
ret.Email = this.unescape(matches[11])
|
|
||||||
maybeParse(matches[12], &ret.ShareSize, 0)
|
|
||||||
|
|
||||||
return &ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// No-tag format, used in early connection
|
|
||||||
matches = this.rxMyInfoNoTag.FindStringSubmatch(protomsg)
|
|
||||||
if matches != nil {
|
|
||||||
ret.Nick = matches[1]
|
|
||||||
ret.Description = this.unescape(matches[2])
|
|
||||||
ret.ClientTag = ""
|
|
||||||
ret.ClientVersion = "0"
|
|
||||||
ret.ConnectionMode = CONNECTIONMODE_PASSIVE
|
|
||||||
ret.HubsUnregistered = 0
|
|
||||||
ret.HubsRegistered = 0
|
|
||||||
ret.HubsOperator = 0
|
|
||||||
ret.Slots = 0
|
|
||||||
|
|
||||||
if len(matches[3]) > 1 {
|
|
||||||
ret.Speed = matches[3][:len(matches[3])-2]
|
|
||||||
} else {
|
|
||||||
ret.Speed = ""
|
|
||||||
}
|
|
||||||
ret.Flag = UserFlag(matches[3][len(matches[3])-1])
|
|
||||||
ret.Email = this.unescape(matches[4])
|
|
||||||
maybeParse(matches[5], &ret.ShareSize, 0)
|
|
||||||
|
|
||||||
return &ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Couldn't get anything out of it...
|
|
||||||
return nil, errors.New("Malformed MyINFO")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the MyINFO command, WITH leading $MyINFO, and WITHOUT trailing pipe
|
|
||||||
func (this *NmdcProtocol) getUserMyINFO(u *UserInfo) string {
|
|
||||||
return fmt.Sprintf(
|
|
||||||
"$MyINFO $ALL %s %s<%s V:%s,M:%c,H:%d/%d/%d,S:%d>$ $%s%c$%s$%d$",
|
|
||||||
u.Nick,
|
|
||||||
u.Description,
|
|
||||||
u.ClientTag,
|
|
||||||
strings.Replace(u.ClientVersion, ",", "-", -1), // just in case
|
|
||||||
u.ConnectionMode,
|
|
||||||
u.HubsUnregistered,
|
|
||||||
u.HubsRegistered,
|
|
||||||
u.HubsOperator,
|
|
||||||
u.Slots,
|
|
||||||
u.Speed,
|
|
||||||
u.Flag,
|
|
||||||
u.Email,
|
|
||||||
u.ShareSize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *NmdcProtocol) ProtoMessageSeparator() string {
|
|
||||||
return "|"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *NmdcProtocol) unlock(lock []byte) string {
|
|
||||||
|
|
||||||
nibble_swap := func(b byte) byte {
|
|
||||||
return ((b << 4) & 0xF0) | ((b >> 4) & 0x0F)
|
|
||||||
}
|
|
||||||
|
|
||||||
chr := func(b byte) string {
|
|
||||||
if b == 0 || b == 5 || b == 36 || b == 96 || b == 124 || b == 126 {
|
|
||||||
return fmt.Sprintf("/%%DCN%04d%%/", b)
|
|
||||||
} else {
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
key := chr(nibble_swap(lock[0] ^ lock[len(lock)-2] ^ lock[len(lock)-3] ^ 5))
|
|
||||||
for i := 1; i < len(lock); i += 1 {
|
|
||||||
key += chr(nibble_swap(lock[i] ^ lock[i-1]))
|
|
||||||
}
|
|
||||||
|
|
||||||
return key
|
|
||||||
}
|
|
31
vendor/code.ivysaur.me/libnmdc/Protocol.go
generated
vendored
31
vendor/code.ivysaur.me/libnmdc/Protocol.go
generated
vendored
@ -1,31 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
type Protocol interface {
|
|
||||||
ProcessCommand(msg string)
|
|
||||||
|
|
||||||
SayPublic(string)
|
|
||||||
|
|
||||||
SayPrivate(user, message string)
|
|
||||||
|
|
||||||
ProtoMessageSeparator() string
|
|
||||||
}
|
|
||||||
|
|
||||||
type HubProtocol int
|
|
||||||
|
|
||||||
const (
|
|
||||||
HubProtocolAutodetect HubProtocol = 0
|
|
||||||
HubProtocolNmdc HubProtocol = 1
|
|
||||||
HubProtocolAdc HubProtocol = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
func (hp HubProtocol) Create(hc *HubConnection) Protocol {
|
|
||||||
if hp == HubProtocolNmdc {
|
|
||||||
return NewNmdcProtocol(hc)
|
|
||||||
|
|
||||||
} else if hp == HubProtocolAdc {
|
|
||||||
return NewAdcProtocol(hc)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return NewAutodetectProtocol(hc)
|
|
||||||
}
|
|
||||||
}
|
|
6
vendor/code.ivysaur.me/libnmdc/TODO.txt
generated
vendored
6
vendor/code.ivysaur.me/libnmdc/TODO.txt
generated
vendored
@ -1,6 +0,0 @@
|
|||||||
NMDC:
|
|
||||||
- Implement ZPipe ($ZOn)
|
|
||||||
|
|
||||||
ADC:
|
|
||||||
- Usercommands
|
|
||||||
- ???
|
|
27
vendor/code.ivysaur.me/libnmdc/UserCommand.go
generated
vendored
27
vendor/code.ivysaur.me/libnmdc/UserCommand.go
generated
vendored
@ -1,27 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
type UserCommandType uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
USERCOMMAND_TYPE_SEPARATOR UserCommandType = 0
|
|
||||||
USERCOMMAND_TYPE_RAW UserCommandType = 1
|
|
||||||
USERCOMMAND_TYPE_NICKLIMITED UserCommandType = 2
|
|
||||||
USERCOMMAND_TYPE_CLEARALL UserCommandType = 255
|
|
||||||
)
|
|
||||||
|
|
||||||
type UserCommandContext uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
USERCOMMAND_CONTEXT_HUB UserCommandContext = 1
|
|
||||||
USERCOMMAND_CONTEXT_USER UserCommandContext = 2
|
|
||||||
USERCOMMAND_CONTEXT_SEARCH UserCommandContext = 4
|
|
||||||
USERCOMMAND_CONTEXT_FILELIST UserCommandContext = 8
|
|
||||||
)
|
|
||||||
|
|
||||||
type UserCommand struct {
|
|
||||||
Type UserCommandType
|
|
||||||
Context UserCommandContext
|
|
||||||
Message string
|
|
||||||
Command string
|
|
||||||
RemoveThis bool // Currently only set by ADC hubs
|
|
||||||
}
|
|
17
vendor/code.ivysaur.me/libnmdc/UserFlag.go
generated
vendored
17
vendor/code.ivysaur.me/libnmdc/UserFlag.go
generated
vendored
@ -1,17 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
type UserFlag byte
|
|
||||||
|
|
||||||
const (
|
|
||||||
FLAG_NORMAL UserFlag = 1
|
|
||||||
FLAG_AWAY_1 UserFlag = 2
|
|
||||||
FLAG_AWAY_2 UserFlag = 3
|
|
||||||
FLAG_SERVER_1 UserFlag = 4
|
|
||||||
FLAG_SERVER_2 UserFlag = 5
|
|
||||||
FLAG_SERVER_AWAY_1 UserFlag = 6
|
|
||||||
FLAG_SERVER_AWAY_2 UserFlag = 7
|
|
||||||
FLAG_FIREBALL_1 UserFlag = 8
|
|
||||||
FLAG_FIREBALL_2 UserFlag = 9
|
|
||||||
FLAG_FIREBALL_AWAY_1 UserFlag = 10
|
|
||||||
FLAG_FIREBALL_AWAY_2 UserFlag = 11
|
|
||||||
)
|
|
53
vendor/code.ivysaur.me/libnmdc/UserInfo.go
generated
vendored
53
vendor/code.ivysaur.me/libnmdc/UserInfo.go
generated
vendored
@ -1,53 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
// This structure represents a user connected to a hub.
|
|
||||||
type UserInfo struct {
|
|
||||||
Nick string
|
|
||||||
Description string
|
|
||||||
ClientTag string
|
|
||||||
ClientVersion string
|
|
||||||
Email string
|
|
||||||
ShareSize uint64
|
|
||||||
Flag UserFlag
|
|
||||||
Slots uint64
|
|
||||||
HubsUnregistered uint64
|
|
||||||
HubsRegistered uint64
|
|
||||||
HubsOperator uint64
|
|
||||||
IsOperator bool
|
|
||||||
|
|
||||||
UserInfo_NMDCOnly
|
|
||||||
UserInfo_ADCOnly
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserInfo_NMDCOnly struct {
|
|
||||||
Speed string
|
|
||||||
IPAddress string
|
|
||||||
ConnectionMode ConnectionMode
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserInfo_ADCOnly struct {
|
|
||||||
SharedFiles uint64
|
|
||||||
UploadSpeedBps uint64
|
|
||||||
DownloadSpeedBps uint64
|
|
||||||
IsBot bool
|
|
||||||
IsRegistered bool
|
|
||||||
IsSuperUser bool
|
|
||||||
IsHubOwner bool
|
|
||||||
IPv4Address string // Passive <==> these fields are not set
|
|
||||||
IPv6Address string
|
|
||||||
IPv4UDPPort uint64
|
|
||||||
IPv6UDPPort uint64
|
|
||||||
Keyprint string
|
|
||||||
CID string
|
|
||||||
SupportFlags map[string]struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewUserInfo(username string) *UserInfo {
|
|
||||||
return &UserInfo{
|
|
||||||
Nick: username,
|
|
||||||
HubsUnregistered: 1,
|
|
||||||
UserInfo_NMDCOnly: UserInfo_NMDCOnly{
|
|
||||||
ConnectionMode: CONNECTIONMODE_PASSIVE,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
28
vendor/code.ivysaur.me/libnmdc/libnmdc.go
generated
vendored
28
vendor/code.ivysaur.me/libnmdc/libnmdc.go
generated
vendored
@ -1,28 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
DEFAULT_CLIENT_TAG string = "libnmdc.go"
|
|
||||||
DEFAULT_CLIENT_VERSION string = "0.16"
|
|
||||||
DEFAULT_HUB_NAME string = "(unknown)"
|
|
||||||
SEND_KEEPALIVE_EVERY time.Duration = 29 * time.Second
|
|
||||||
AUTO_RECONNECT_AFTER time.Duration = 30 * time.Second
|
|
||||||
RECONNECT_IF_NO_DATA_RECIEVED_IN time.Duration = 24 * time.Hour // we expect keepalives wayyyy more frequently than this
|
|
||||||
AUTODETECT_ADC_NMDC_TIMEOUT time.Duration = 3 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrNotConnected error = errors.New("Not connected")
|
|
||||||
|
|
||||||
func maybeParse(str string, dest *uint64, default_val uint64) {
|
|
||||||
sz, err := strconv.ParseUint(str, 10, 64)
|
|
||||||
if err == nil {
|
|
||||||
*dest = sz
|
|
||||||
} else {
|
|
||||||
*dest = default_val
|
|
||||||
}
|
|
||||||
}
|
|
41
vendor/code.ivysaur.me/libnmdc/tth.go
generated
vendored
41
vendor/code.ivysaur.me/libnmdc/tth.go
generated
vendored
@ -1,41 +0,0 @@
|
|||||||
package libnmdc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base32"
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/cxmcc/tiger"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Base32 encodes the input slice in BASE32 string format without any trailing
|
|
||||||
// padding equals characters.
|
|
||||||
func Base32(input []byte) string {
|
|
||||||
return strings.TrimRight(base32.StdEncoding.EncodeToString(input), "=")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TTH returns the TTH hash of a string in raw byte format. Use the Base32()
|
|
||||||
// function to convert it to normal string format.
|
|
||||||
// This is a basic implementation that only supports content up to 1024 bytes in length.
|
|
||||||
func TTH(input string) ([]byte, error) {
|
|
||||||
|
|
||||||
// Short segments do not need to be padded.
|
|
||||||
// Content above 1024 bytes needs tree handling (0x00 prefix for leaf nodes,
|
|
||||||
// 0x01 prefix for hash nodes) but for content less than 1024 bytes, just
|
|
||||||
// return the leaf hash
|
|
||||||
// @ref http://adc.sourceforge.net/draft-jchapweske-thex-02.html
|
|
||||||
if len(input) > 1024 {
|
|
||||||
return nil, errors.New("TTH content exceeded 1024 bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Single leaf hash only
|
|
||||||
leafHash := tiger.New()
|
|
||||||
leafHash.Write([]byte("\x00" + input))
|
|
||||||
return leafHash.Sum(nil), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Tiger(input string) []byte {
|
|
||||||
leafHash := tiger.New()
|
|
||||||
leafHash.Write([]byte(input))
|
|
||||||
return leafHash.Sum(nil)
|
|
||||||
}
|
|
22
vendor/github.com/cxmcc/tiger/.gitignore
generated
vendored
22
vendor/github.com/cxmcc/tiger/.gitignore
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
7
vendor/github.com/cxmcc/tiger/.travis.yml
generated
vendored
7
vendor/github.com/cxmcc/tiger/.travis.yml
generated
vendored
@ -1,7 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.x
|
|
||||||
- 1.6
|
|
||||||
- 1.7.x
|
|
||||||
- master
|
|
20
vendor/github.com/cxmcc/tiger/LICENSE
generated
vendored
20
vendor/github.com/cxmcc/tiger/LICENSE
generated
vendored
@ -1,20 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 Xiuming Chen
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
48
vendor/github.com/cxmcc/tiger/README.md
generated
vendored
48
vendor/github.com/cxmcc/tiger/README.md
generated
vendored
@ -1,48 +0,0 @@
|
|||||||
Tiger cryptographic hash function for Go
|
|
||||||
|
|
||||||
-----
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/cxmcc/tiger.svg?branch=master)](https://travis-ci.org/cxmcc/tiger)
|
|
||||||
[![GoDoc](http://godoc.org/github.com/cxmcc/tiger?status.png)](http://godoc.org/github.com/cxmcc/tiger)
|
|
||||||
|
|
||||||
|
|
||||||
### About Tiger
|
|
||||||
|
|
||||||
* Tiger cryptographic hash function is designed by Ross Anderson and Eli Biham in 1995.
|
|
||||||
* The size of a Tiger hash value is 192 bits. Truncated versions (Tiger/128, Tiger/160) are simply prefixes of Tiger/192.
|
|
||||||
* Tiger2 is a variant where the message is padded by first appending a byte 0x80, rather than 0x01 as in the case of Tiger.
|
|
||||||
* Links: [paper](http://www.cs.technion.ac.il/~biham/Reports/Tiger/), [wikipedia](http://en.wikipedia.org/wiki/Tiger_\(cryptography\))
|
|
||||||
|
|
||||||
### API Documentation
|
|
||||||
|
|
||||||
Implementing [hash.Hash](http://golang.org/pkg/hash/#Hash). Usage is pretty much the same as other stanard hashing libraries.
|
|
||||||
Documentation currently available at Godoc: [http://godoc.org/github.com/cxmcc/tiger](http://godoc.org/github.com/cxmcc/tiger)
|
|
||||||
|
|
||||||
|
|
||||||
### Installing
|
|
||||||
~~~
|
|
||||||
go get github.com/cxmcc/tiger
|
|
||||||
~~~
|
|
||||||
|
|
||||||
### Example
|
|
||||||
~~~ go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"github.com/cxmcc/tiger"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
h := tiger.New()
|
|
||||||
io.WriteString(h, "Example for tiger")
|
|
||||||
fmt.Printf("Output: %x\n", h.Sum(nil))
|
|
||||||
// Output: 82bd060e19f945014f0063e8f0e6d7decfa9edfd97e76743
|
|
||||||
}
|
|
||||||
~~~
|
|
||||||
|
|
||||||
|
|
||||||
### License
|
|
||||||
|
|
||||||
It's MIT License
|
|
96
vendor/github.com/cxmcc/tiger/compress.go
generated
vendored
96
vendor/github.com/cxmcc/tiger/compress.go
generated
vendored
@ -1,96 +0,0 @@
|
|||||||
package tiger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
var littleEndian bool
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
x := uint32(0x04030201)
|
|
||||||
y := [4]byte{0x1, 0x2, 0x3, 0x4}
|
|
||||||
littleEndian = *(*[4]byte)(unsafe.Pointer(&x)) == y
|
|
||||||
}
|
|
||||||
|
|
||||||
func pass(a, b, c uint64, x []uint64, mul uint64) (uint64, uint64, uint64) {
|
|
||||||
a, b, c = round(a, b, c, x[0], mul)
|
|
||||||
b, c, a = round(b, c, a, x[1], mul)
|
|
||||||
c, a, b = round(c, a, b, x[2], mul)
|
|
||||||
a, b, c = round(a, b, c, x[3], mul)
|
|
||||||
b, c, a = round(b, c, a, x[4], mul)
|
|
||||||
c, a, b = round(c, a, b, x[5], mul)
|
|
||||||
a, b, c = round(a, b, c, x[6], mul)
|
|
||||||
b, c, a = round(b, c, a, x[7], mul)
|
|
||||||
return a, b, c
|
|
||||||
}
|
|
||||||
|
|
||||||
func round(a, b, c, x, mul uint64) (uint64, uint64, uint64) {
|
|
||||||
c ^= x
|
|
||||||
a -= t1[c&0xff] ^ t2[(c>>16)&0xff] ^ t3[(c>>32)&0xff] ^ t4[(c>>48)&0xff]
|
|
||||||
b += t4[(c>>8)&0xff] ^ t3[(c>>24)&0xff] ^ t2[(c>>40)&0xff] ^ t1[(c>>56)&0xff]
|
|
||||||
b *= mul
|
|
||||||
return a, b, c
|
|
||||||
}
|
|
||||||
|
|
||||||
func keySchedule(x []uint64) {
|
|
||||||
x[0] -= x[7] ^ 0xa5a5a5a5a5a5a5a5
|
|
||||||
x[1] ^= x[0]
|
|
||||||
x[2] += x[1]
|
|
||||||
x[3] -= x[2] ^ ((^x[1]) << 19)
|
|
||||||
x[4] ^= x[3]
|
|
||||||
x[5] += x[4]
|
|
||||||
x[6] -= x[5] ^ ((^x[4]) >> 23)
|
|
||||||
x[7] ^= x[6]
|
|
||||||
x[0] += x[7]
|
|
||||||
x[1] -= x[0] ^ ((^x[7]) << 19)
|
|
||||||
x[2] ^= x[1]
|
|
||||||
x[3] += x[2]
|
|
||||||
x[4] -= x[3] ^ ((^x[2]) >> 23)
|
|
||||||
x[5] ^= x[4]
|
|
||||||
x[6] += x[5]
|
|
||||||
x[7] -= x[6] ^ 0x0123456789abcdef
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *digest) compress(data []byte) {
|
|
||||||
// save_abc
|
|
||||||
a := d.a
|
|
||||||
b := d.b
|
|
||||||
c := d.c
|
|
||||||
|
|
||||||
var x []uint64
|
|
||||||
if littleEndian {
|
|
||||||
x = []uint64{
|
|
||||||
binary.LittleEndian.Uint64(data[0:8]),
|
|
||||||
binary.LittleEndian.Uint64(data[8:16]),
|
|
||||||
binary.LittleEndian.Uint64(data[16:24]),
|
|
||||||
binary.LittleEndian.Uint64(data[24:32]),
|
|
||||||
binary.LittleEndian.Uint64(data[32:40]),
|
|
||||||
binary.LittleEndian.Uint64(data[40:48]),
|
|
||||||
binary.LittleEndian.Uint64(data[48:56]),
|
|
||||||
binary.LittleEndian.Uint64(data[56:64]),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
x = []uint64{
|
|
||||||
binary.BigEndian.Uint64(data[0:8]),
|
|
||||||
binary.BigEndian.Uint64(data[8:16]),
|
|
||||||
binary.BigEndian.Uint64(data[16:24]),
|
|
||||||
binary.BigEndian.Uint64(data[24:32]),
|
|
||||||
binary.BigEndian.Uint64(data[32:40]),
|
|
||||||
binary.BigEndian.Uint64(data[40:48]),
|
|
||||||
binary.BigEndian.Uint64(data[48:56]),
|
|
||||||
binary.BigEndian.Uint64(data[56:64]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
d.a, d.b, d.c = pass(d.a, d.b, d.c, x, 5)
|
|
||||||
keySchedule(x)
|
|
||||||
d.c, d.a, d.b = pass(d.c, d.a, d.b, x, 7)
|
|
||||||
keySchedule(x)
|
|
||||||
d.b, d.c, d.a = pass(d.b, d.c, d.a, x, 9)
|
|
||||||
|
|
||||||
// feedforward
|
|
||||||
d.a ^= a
|
|
||||||
d.b -= b
|
|
||||||
d.c += c
|
|
||||||
}
|
|
269
vendor/github.com/cxmcc/tiger/sboxes.go
generated
vendored
269
vendor/github.com/cxmcc/tiger/sboxes.go
generated
vendored
@ -1,269 +0,0 @@
|
|||||||
package tiger
|
|
||||||
|
|
||||||
var t1 = [...]uint64{
|
|
||||||
0x02aab17cf7e90c5e, 0xac424b03e243a8ec, 0x72cd5be30dd5fcd3, 0x6d019b93f6f97f3a,
|
|
||||||
0xcd9978ffd21f9193, 0x7573a1c9708029e2, 0xb164326b922a83c3, 0x46883eee04915870,
|
|
||||||
0xeaace3057103ece6, 0xc54169b808a3535c, 0x4ce754918ddec47c, 0x0aa2f4dfdc0df40c,
|
|
||||||
0x10b76f18a74dbefa, 0xc6ccb6235ad1ab6a, 0x13726121572fe2ff, 0x1a488c6f199d921e,
|
|
||||||
0x4bc9f9f4da0007ca, 0x26f5e6f6e85241c7, 0x859079dbea5947b6, 0x4f1885c5c99e8c92,
|
|
||||||
0xd78e761ea96f864b, 0x8e36428c52b5c17d, 0x69cf6827373063c1, 0xb607c93d9bb4c56e,
|
|
||||||
0x7d820e760e76b5ea, 0x645c9cc6f07fdc42, 0xbf38a078243342e0, 0x5f6b343c9d2e7d04,
|
|
||||||
0xf2c28aeb600b0ec6, 0x6c0ed85f7254bcac, 0x71592281a4db4fe5, 0x1967fa69ce0fed9f,
|
|
||||||
0xfd5293f8b96545db, 0xc879e9d7f2a7600b, 0x860248920193194e, 0xa4f9533b2d9cc0b3,
|
|
||||||
0x9053836c15957613, 0xdb6dcf8afc357bf1, 0x18beea7a7a370f57, 0x037117ca50b99066,
|
|
||||||
0x6ab30a9774424a35, 0xf4e92f02e325249b, 0x7739db07061ccae1, 0xd8f3b49ceca42a05,
|
|
||||||
0xbd56be3f51382f73, 0x45faed5843b0bb28, 0x1c813d5c11bf1f83, 0x8af0e4b6d75fa169,
|
|
||||||
0x33ee18a487ad9999, 0x3c26e8eab1c94410, 0xb510102bc0a822f9, 0x141eef310ce6123b,
|
|
||||||
0xfc65b90059ddb154, 0xe0158640c5e0e607, 0x884e079826c3a3cf, 0x930d0d9523c535fd,
|
|
||||||
0x35638d754e9a2b00, 0x4085fccf40469dd5, 0xc4b17ad28be23a4c, 0xcab2f0fc6a3e6a2e,
|
|
||||||
0x2860971a6b943fcd, 0x3dde6ee212e30446, 0x6222f32ae01765ae, 0x5d550bb5478308fe,
|
|
||||||
0xa9efa98da0eda22a, 0xc351a71686c40da7, 0x1105586d9c867c84, 0xdcffee85fda22853,
|
|
||||||
0xccfbd0262c5eef76, 0xbaf294cb8990d201, 0xe69464f52afad975, 0x94b013afdf133e14,
|
|
||||||
0x06a7d1a32823c958, 0x6f95fe5130f61119, 0xd92ab34e462c06c0, 0xed7bde33887c71d2,
|
|
||||||
0x79746d6e6518393e, 0x5ba419385d713329, 0x7c1ba6b948a97564, 0x31987c197bfdac67,
|
|
||||||
0xde6c23c44b053d02, 0x581c49fed002d64d, 0xdd474d6338261571, 0xaa4546c3e473d062,
|
|
||||||
0x928fce349455f860, 0x48161bbacaab94d9, 0x63912430770e6f68, 0x6ec8a5e602c6641c,
|
|
||||||
0x87282515337ddd2b, 0x2cda6b42034b701b, 0xb03d37c181cb096d, 0xe108438266c71c6f,
|
|
||||||
0x2b3180c7eb51b255, 0xdf92b82f96c08bbc, 0x5c68c8c0a632f3ba, 0x5504cc861c3d0556,
|
|
||||||
0xabbfa4e55fb26b8f, 0x41848b0ab3baceb4, 0xb334a273aa445d32, 0xbca696f0a85ad881,
|
|
||||||
0x24f6ec65b528d56c, 0x0ce1512e90f4524a, 0x4e9dd79d5506d35a, 0x258905fac6ce9779,
|
|
||||||
0x2019295b3e109b33, 0xf8a9478b73a054cc, 0x2924f2f934417eb0, 0x3993357d536d1bc4,
|
|
||||||
0x38a81ac21db6ff8b, 0x47c4fbf17d6016bf, 0x1e0faadd7667e3f5, 0x7abcff62938beb96,
|
|
||||||
0xa78dad948fc179c9, 0x8f1f98b72911e50d, 0x61e48eae27121a91, 0x4d62f7ad31859808,
|
|
||||||
0xeceba345ef5ceaeb, 0xf5ceb25ebc9684ce, 0xf633e20cb7f76221, 0xa32cdf06ab8293e4,
|
|
||||||
0x985a202ca5ee2ca4, 0xcf0b8447cc8a8fb1, 0x9f765244979859a3, 0xa8d516b1a1240017,
|
|
||||||
0x0bd7ba3ebb5dc726, 0xe54bca55b86adb39, 0x1d7a3afd6c478063, 0x519ec608e7669edd,
|
|
||||||
0x0e5715a2d149aa23, 0x177d4571848ff194, 0xeeb55f3241014c22, 0x0f5e5ca13a6e2ec2,
|
|
||||||
0x8029927b75f5c361, 0xad139fabc3d6e436, 0x0d5df1a94ccf402f, 0x3e8bd948bea5dfc8,
|
|
||||||
0xa5a0d357bd3ff77e, 0xa2d12e251f74f645, 0x66fd9e525e81a082, 0x2e0c90ce7f687a49,
|
|
||||||
0xc2e8bcbeba973bc5, 0x000001bce509745f, 0x423777bbe6dab3d6, 0xd1661c7eaef06eb5,
|
|
||||||
0xa1781f354daacfd8, 0x2d11284a2b16affc, 0xf1fc4f67fa891d1f, 0x73ecc25dcb920ada,
|
|
||||||
0xae610c22c2a12651, 0x96e0a810d356b78a, 0x5a9a381f2fe7870f, 0xd5ad62ede94e5530,
|
|
||||||
0xd225e5e8368d1427, 0x65977b70c7af4631, 0x99f889b2de39d74f, 0x233f30bf54e1d143,
|
|
||||||
0x9a9675d3d9a63c97, 0x5470554ff334f9a8, 0x166acb744a4f5688, 0x70c74caab2e4aead,
|
|
||||||
0xf0d091646f294d12, 0x57b82a89684031d1, 0xefd95a5a61be0b6b, 0x2fbd12e969f2f29a,
|
|
||||||
0x9bd37013feff9fe8, 0x3f9b0404d6085a06, 0x4940c1f3166cfe15, 0x09542c4dcdf3defb,
|
|
||||||
0xb4c5218385cd5ce3, 0xc935b7dc4462a641, 0x3417f8a68ed3b63f, 0xb80959295b215b40,
|
|
||||||
0xf99cdaef3b8c8572, 0x018c0614f8fcb95d, 0x1b14accd1a3acdf3, 0x84d471f200bb732d,
|
|
||||||
0xc1a3110e95e8da16, 0x430a7220bf1a82b8, 0xb77e090d39df210e, 0x5ef4bd9f3cd05e9d,
|
|
||||||
0x9d4ff6da7e57a444, 0xda1d60e183d4a5f8, 0xb287c38417998e47, 0xfe3edc121bb31886,
|
|
||||||
0xc7fe3ccc980ccbef, 0xe46fb590189bfd03, 0x3732fd469a4c57dc, 0x7ef700a07cf1ad65,
|
|
||||||
0x59c64468a31d8859, 0x762fb0b4d45b61f6, 0x155baed099047718, 0x68755e4c3d50baa6,
|
|
||||||
0xe9214e7f22d8b4df, 0x2addbf532eac95f4, 0x32ae3909b4bd0109, 0x834df537b08e3450,
|
|
||||||
0xfa209da84220728d, 0x9e691d9b9efe23f7, 0x0446d288c4ae8d7f, 0x7b4cc524e169785b,
|
|
||||||
0x21d87f0135ca1385, 0xcebb400f137b8aa5, 0x272e2b66580796be, 0x3612264125c2b0de,
|
|
||||||
0x057702bdad1efbb2, 0xd4babb8eacf84be9, 0x91583139641bc67b, 0x8bdc2de08036e024,
|
|
||||||
0x603c8156f49f68ed, 0xf7d236f7dbef5111, 0x9727c4598ad21e80, 0xa08a0896670a5fd7,
|
|
||||||
0xcb4a8f4309eba9cb, 0x81af564b0f7036a1, 0xc0b99aa778199abd, 0x959f1ec83fc8e952,
|
|
||||||
0x8c505077794a81b9, 0x3acaaf8f056338f0, 0x07b43f50627a6778, 0x4a44ab49f5eccc77,
|
|
||||||
0x3bc3d6e4b679ee98, 0x9cc0d4d1cf14108c, 0x4406c00b206bc8a0, 0x82a18854c8d72d89,
|
|
||||||
0x67e366b35c3c432c, 0xb923dd61102b37f2, 0x56ab2779d884271d, 0xbe83e1b0ff1525af,
|
|
||||||
0xfb7c65d4217e49a9, 0x6bdbe0e76d48e7d4, 0x08df828745d9179e, 0x22ea6a9add53bd34,
|
|
||||||
0xe36e141c5622200a, 0x7f805d1b8cb750ee, 0xafe5c7a59f58e837, 0xe27f996a4fb1c23c,
|
|
||||||
0xd3867dfb0775f0d0, 0xd0e673de6e88891a, 0x123aeb9eafb86c25, 0x30f1d5d5c145b895,
|
|
||||||
0xbb434a2dee7269e7, 0x78cb67ecf931fa38, 0xf33b0372323bbf9c, 0x52d66336fb279c74,
|
|
||||||
0x505f33ac0afb4eaa, 0xe8a5cd99a2cce187, 0x534974801e2d30bb, 0x8d2d5711d5876d90,
|
|
||||||
0x1f1a412891bc038e, 0xd6e2e71d82e56648, 0x74036c3a497732b7, 0x89b67ed96361f5ab,
|
|
||||||
0xffed95d8f1ea02a2, 0xe72b3bd61464d43d, 0xa6300f170bdc4820, 0xebc18760ed78a77a,
|
|
||||||
}
|
|
||||||
|
|
||||||
var t2 = [...]uint64{
|
|
||||||
0xe6a6be5a05a12138, 0xb5a122a5b4f87c98, 0x563c6089140b6990, 0x4c46cb2e391f5dd5,
|
|
||||||
0xd932addbc9b79434, 0x08ea70e42015aff5, 0xd765a6673e478cf1, 0xc4fb757eab278d99,
|
|
||||||
0xdf11c6862d6e0692, 0xddeb84f10d7f3b16, 0x6f2ef604a665ea04, 0x4a8e0f0ff0e0dfb3,
|
|
||||||
0xa5edeef83dbcba51, 0xfc4f0a2a0ea4371e, 0xe83e1da85cb38429, 0xdc8ff882ba1b1ce2,
|
|
||||||
0xcd45505e8353e80d, 0x18d19a00d4db0717, 0x34a0cfeda5f38101, 0x0be77e518887caf2,
|
|
||||||
0x1e341438b3c45136, 0xe05797f49089ccf9, 0xffd23f9df2591d14, 0x543dda228595c5cd,
|
|
||||||
0x661f81fd99052a33, 0x8736e641db0f7b76, 0x15227725418e5307, 0xe25f7f46162eb2fa,
|
|
||||||
0x48a8b2126c13d9fe, 0xafdc541792e76eea, 0x03d912bfc6d1898f, 0x31b1aafa1b83f51b,
|
|
||||||
0xf1ac2796e42ab7d9, 0x40a3a7d7fcd2ebac, 0x1056136d0afbbcc5, 0x7889e1dd9a6d0c85,
|
|
||||||
0xd33525782a7974aa, 0xa7e25d09078ac09b, 0xbd4138b3eac6edd0, 0x920abfbe71eb9e70,
|
|
||||||
0xa2a5d0f54fc2625c, 0xc054e36b0b1290a3, 0xf6dd59ff62fe932b, 0x3537354511a8ac7d,
|
|
||||||
0xca845e9172fadcd4, 0x84f82b60329d20dc, 0x79c62ce1cd672f18, 0x8b09a2add124642c,
|
|
||||||
0xd0c1e96a19d9e726, 0x5a786a9b4ba9500c, 0x0e020336634c43f3, 0xc17b474aeb66d822,
|
|
||||||
0x6a731ae3ec9baac2, 0x8226667ae0840258, 0x67d4567691caeca5, 0x1d94155c4875adb5,
|
|
||||||
0x6d00fd985b813fdf, 0x51286efcb774cd06, 0x5e8834471fa744af, 0xf72ca0aee761ae2e,
|
|
||||||
0xbe40e4cdaee8e09a, 0xe9970bbb5118f665, 0x726e4beb33df1964, 0x703b000729199762,
|
|
||||||
0x4631d816f5ef30a7, 0xb880b5b51504a6be, 0x641793c37ed84b6c, 0x7b21ed77f6e97d96,
|
|
||||||
0x776306312ef96b73, 0xae528948e86ff3f4, 0x53dbd7f286a3f8f8, 0x16cadce74cfc1063,
|
|
||||||
0x005c19bdfa52c6dd, 0x68868f5d64d46ad3, 0x3a9d512ccf1e186a, 0x367e62c2385660ae,
|
|
||||||
0xe359e7ea77dcb1d7, 0x526c0773749abe6e, 0x735ae5f9d09f734b, 0x493fc7cc8a558ba8,
|
|
||||||
0xb0b9c1533041ab45, 0x321958ba470a59bd, 0x852db00b5f46c393, 0x91209b2bd336b0e5,
|
|
||||||
0x6e604f7d659ef19f, 0xb99a8ae2782ccb24, 0xccf52ab6c814c4c7, 0x4727d9afbe11727b,
|
|
||||||
0x7e950d0c0121b34d, 0x756f435670ad471f, 0xf5add442615a6849, 0x4e87e09980b9957a,
|
|
||||||
0x2acfa1df50aee355, 0xd898263afd2fd556, 0xc8f4924dd80c8fd6, 0xcf99ca3d754a173a,
|
|
||||||
0xfe477bacaf91bf3c, 0xed5371f6d690c12d, 0x831a5c285e687094, 0xc5d3c90a3708a0a4,
|
|
||||||
0x0f7f903717d06580, 0x19f9bb13b8fdf27f, 0xb1bd6f1b4d502843, 0x1c761ba38fff4012,
|
|
||||||
0x0d1530c4e2e21f3b, 0x8943ce69a7372c8a, 0xe5184e11feb5ce66, 0x618bdb80bd736621,
|
|
||||||
0x7d29bad68b574d0b, 0x81bb613e25e6fe5b, 0x071c9c10bc07913f, 0xc7beeb7909ac2d97,
|
|
||||||
0xc3e58d353bc5d757, 0xeb017892f38f61e8, 0xd4effb9c9b1cc21a, 0x99727d26f494f7ab,
|
|
||||||
0xa3e063a2956b3e03, 0x9d4a8b9a4aa09c30, 0x3f6ab7d500090fb4, 0x9cc0f2a057268ac0,
|
|
||||||
0x3dee9d2dedbf42d1, 0x330f49c87960a972, 0xc6b2720287421b41, 0x0ac59ec07c00369c,
|
|
||||||
0xef4eac49cb353425, 0xf450244eef0129d8, 0x8acc46e5caf4deb6, 0x2ffeab63989263f7,
|
|
||||||
0x8f7cb9fe5d7a4578, 0x5bd8f7644e634635, 0x427a7315bf2dc900, 0x17d0c4aa2125261c,
|
|
||||||
0x3992486c93518e50, 0xb4cbfee0a2d7d4c3, 0x7c75d6202c5ddd8d, 0xdbc295d8e35b6c61,
|
|
||||||
0x60b369d302032b19, 0xce42685fdce44132, 0x06f3ddb9ddf65610, 0x8ea4d21db5e148f0,
|
|
||||||
0x20b0fce62fcd496f, 0x2c1b912358b0ee31, 0xb28317b818f5a308, 0xa89c1e189ca6d2cf,
|
|
||||||
0x0c6b18576aaadbc8, 0xb65deaa91299fae3, 0xfb2b794b7f1027e7, 0x04e4317f443b5beb,
|
|
||||||
0x4b852d325939d0a6, 0xd5ae6beefb207ffc, 0x309682b281c7d374, 0xbae309a194c3b475,
|
|
||||||
0x8cc3f97b13b49f05, 0x98a9422ff8293967, 0x244b16b01076ff7c, 0xf8bf571c663d67ee,
|
|
||||||
0x1f0d6758eee30da1, 0xc9b611d97adeb9b7, 0xb7afd5887b6c57a2, 0x6290ae846b984fe1,
|
|
||||||
0x94df4cdeacc1a5fd, 0x058a5bd1c5483aff, 0x63166cc142ba3c37, 0x8db8526eb2f76f40,
|
|
||||||
0xe10880036f0d6d4e, 0x9e0523c9971d311d, 0x45ec2824cc7cd691, 0x575b8359e62382c9,
|
|
||||||
0xfa9e400dc4889995, 0xd1823ecb45721568, 0xdafd983b8206082f, 0xaa7d29082386a8cb,
|
|
||||||
0x269fcd4403b87588, 0x1b91f5f728bdd1e0, 0xe4669f39040201f6, 0x7a1d7c218cf04ade,
|
|
||||||
0x65623c29d79ce5ce, 0x2368449096c00bb1, 0xab9bf1879da503ba, 0xbc23ecb1a458058e,
|
|
||||||
0x9a58df01bb401ecc, 0xa070e868a85f143d, 0x4ff188307df2239e, 0x14d565b41a641183,
|
|
||||||
0xee13337452701602, 0x950e3dcf3f285e09, 0x59930254b9c80953, 0x3bf299408930da6d,
|
|
||||||
0xa955943f53691387, 0xa15edecaa9cb8784, 0x29142127352be9a0, 0x76f0371fff4e7afb,
|
|
||||||
0x0239f450274f2228, 0xbb073af01d5e868b, 0xbfc80571c10e96c1, 0xd267088568222e23,
|
|
||||||
0x9671a3d48e80b5b0, 0x55b5d38ae193bb81, 0x693ae2d0a18b04b8, 0x5c48b4ecadd5335f,
|
|
||||||
0xfd743b194916a1ca, 0x2577018134be98c4, 0xe77987e83c54a4ad, 0x28e11014da33e1b9,
|
|
||||||
0x270cc59e226aa213, 0x71495f756d1a5f60, 0x9be853fb60afef77, 0xadc786a7f7443dbf,
|
|
||||||
0x0904456173b29a82, 0x58bc7a66c232bd5e, 0xf306558c673ac8b2, 0x41f639c6b6c9772a,
|
|
||||||
0x216defe99fda35da, 0x11640cc71c7be615, 0x93c43694565c5527, 0xea038e6246777839,
|
|
||||||
0xf9abf3ce5a3e2469, 0x741e768d0fd312d2, 0x0144b883ced652c6, 0xc20b5a5ba33f8552,
|
|
||||||
0x1ae69633c3435a9d, 0x97a28ca4088cfdec, 0x8824a43c1e96f420, 0x37612fa66eeea746,
|
|
||||||
0x6b4cb165f9cf0e5a, 0x43aa1c06a0abfb4a, 0x7f4dc26ff162796b, 0x6cbacc8e54ed9b0f,
|
|
||||||
0xa6b7ffefd2bb253e, 0x2e25bc95b0a29d4f, 0x86d6a58bdef1388c, 0xded74ac576b6f054,
|
|
||||||
0x8030bdbc2b45805d, 0x3c81af70e94d9289, 0x3eff6dda9e3100db, 0xb38dc39fdfcc8847,
|
|
||||||
0x123885528d17b87e, 0xf2da0ed240b1b642, 0x44cefadcd54bf9a9, 0x1312200e433c7ee6,
|
|
||||||
0x9ffcc84f3a78c748, 0xf0cd1f72248576bb, 0xec6974053638cfe4, 0x2ba7b67c0cec4e4c,
|
|
||||||
0xac2f4df3e5ce32ed, 0xcb33d14326ea4c11, 0xa4e9044cc77e58bc, 0x5f513293d934fcef,
|
|
||||||
0x5dc9645506e55444, 0x50de418f317de40a, 0x388cb31a69dde259, 0x2db4a83455820a86,
|
|
||||||
0x9010a91e84711ae9, 0x4df7f0b7b1498371, 0xd62a2eabc0977179, 0x22fac097aa8d5c0e,
|
|
||||||
}
|
|
||||||
|
|
||||||
var t3 = [...]uint64{
|
|
||||||
0xf49fcc2ff1daf39b, 0x487fd5c66ff29281, 0xe8a30667fcdca83f, 0x2c9b4be3d2fcce63,
|
|
||||||
0xda3ff74b93fbbbc2, 0x2fa165d2fe70ba66, 0xa103e279970e93d4, 0xbecdec77b0e45e71,
|
|
||||||
0xcfb41e723985e497, 0xb70aaa025ef75017, 0xd42309f03840b8e0, 0x8efc1ad035898579,
|
|
||||||
0x96c6920be2b2abc5, 0x66af4163375a9172, 0x2174abdcca7127fb, 0xb33ccea64a72ff41,
|
|
||||||
0xf04a4933083066a5, 0x8d970acdd7289af5, 0x8f96e8e031c8c25e, 0xf3fec02276875d47,
|
|
||||||
0xec7bf310056190dd, 0xf5adb0aebb0f1491, 0x9b50f8850fd58892, 0x4975488358b74de8,
|
|
||||||
0xa3354ff691531c61, 0x0702bbe481d2c6ee, 0x89fb24057deded98, 0xac3075138596e902,
|
|
||||||
0x1d2d3580172772ed, 0xeb738fc28e6bc30d, 0x5854ef8f63044326, 0x9e5c52325add3bbe,
|
|
||||||
0x90aa53cf325c4623, 0xc1d24d51349dd067, 0x2051cfeea69ea624, 0x13220f0a862e7e4f,
|
|
||||||
0xce39399404e04864, 0xd9c42ca47086fcb7, 0x685ad2238a03e7cc, 0x066484b2ab2ff1db,
|
|
||||||
0xfe9d5d70efbf79ec, 0x5b13b9dd9c481854, 0x15f0d475ed1509ad, 0x0bebcd060ec79851,
|
|
||||||
0xd58c6791183ab7f8, 0xd1187c5052f3eee4, 0xc95d1192e54e82ff, 0x86eea14cb9ac6ca2,
|
|
||||||
0x3485beb153677d5d, 0xdd191d781f8c492a, 0xf60866baa784ebf9, 0x518f643ba2d08c74,
|
|
||||||
0x8852e956e1087c22, 0xa768cb8dc410ae8d, 0x38047726bfec8e1a, 0xa67738b4cd3b45aa,
|
|
||||||
0xad16691cec0dde19, 0xc6d4319380462e07, 0xc5a5876d0ba61938, 0x16b9fa1fa58fd840,
|
|
||||||
0x188ab1173ca74f18, 0xabda2f98c99c021f, 0x3e0580ab134ae816, 0x5f3b05b773645abb,
|
|
||||||
0x2501a2be5575f2f6, 0x1b2f74004e7e8ba9, 0x1cd7580371e8d953, 0x7f6ed89562764e30,
|
|
||||||
0xb15926ff596f003d, 0x9f65293da8c5d6b9, 0x6ecef04dd690f84c, 0x4782275fff33af88,
|
|
||||||
0xe41433083f820801, 0xfd0dfe409a1af9b5, 0x4325a3342cdb396b, 0x8ae77e62b301b252,
|
|
||||||
0xc36f9e9f6655615a, 0x85455a2d92d32c09, 0xf2c7dea949477485, 0x63cfb4c133a39eba,
|
|
||||||
0x83b040cc6ebc5462, 0x3b9454c8fdb326b0, 0x56f56a9e87ffd78c, 0x2dc2940d99f42bc6,
|
|
||||||
0x98f7df096b096e2d, 0x19a6e01e3ad852bf, 0x42a99ccbdbd4b40b, 0xa59998af45e9c559,
|
|
||||||
0x366295e807d93186, 0x6b48181bfaa1f773, 0x1fec57e2157a0a1d, 0x4667446af6201ad5,
|
|
||||||
0xe615ebcacfb0f075, 0xb8f31f4f68290778, 0x22713ed6ce22d11e, 0x3057c1a72ec3c93b,
|
|
||||||
0xcb46acc37c3f1f2f, 0xdbb893fd02aaf50e, 0x331fd92e600b9fcf, 0xa498f96148ea3ad6,
|
|
||||||
0xa8d8426e8b6a83ea, 0xa089b274b7735cdc, 0x87f6b3731e524a11, 0x118808e5cbc96749,
|
|
||||||
0x9906e4c7b19bd394, 0xafed7f7e9b24a20c, 0x6509eadeeb3644a7, 0x6c1ef1d3e8ef0ede,
|
|
||||||
0xb9c97d43e9798fb4, 0xa2f2d784740c28a3, 0x7b8496476197566f, 0x7a5be3e6b65f069d,
|
|
||||||
0xf96330ed78be6f10, 0xeee60de77a076a15, 0x2b4bee4aa08b9bd0, 0x6a56a63ec7b8894e,
|
|
||||||
0x02121359ba34fef4, 0x4cbf99f8283703fc, 0x398071350caf30c8, 0xd0a77a89f017687a,
|
|
||||||
0xf1c1a9eb9e423569, 0x8c7976282dee8199, 0x5d1737a5dd1f7abd, 0x4f53433c09a9fa80,
|
|
||||||
0xfa8b0c53df7ca1d9, 0x3fd9dcbc886ccb77, 0xc040917ca91b4720, 0x7dd00142f9d1dcdf,
|
|
||||||
0x8476fc1d4f387b58, 0x23f8e7c5f3316503, 0x032a2244e7e37339, 0x5c87a5d750f5a74b,
|
|
||||||
0x082b4cc43698992e, 0xdf917becb858f63c, 0x3270b8fc5bf86dda, 0x10ae72bb29b5dd76,
|
|
||||||
0x576ac94e7700362b, 0x1ad112dac61efb8f, 0x691bc30ec5faa427, 0xff246311cc327143,
|
|
||||||
0x3142368e30e53206, 0x71380e31e02ca396, 0x958d5c960aad76f1, 0xf8d6f430c16da536,
|
|
||||||
0xc8ffd13f1be7e1d2, 0x7578ae66004ddbe1, 0x05833f01067be646, 0xbb34b5ad3bfe586d,
|
|
||||||
0x095f34c9a12b97f0, 0x247ab64525d60ca8, 0xdcdbc6f3017477d1, 0x4a2e14d4decad24d,
|
|
||||||
0xbdb5e6d9be0a1eeb, 0x2a7e70f7794301ab, 0xdef42d8a270540fd, 0x01078ec0a34c22c1,
|
|
||||||
0xe5de511af4c16387, 0x7ebb3a52bd9a330a, 0x77697857aa7d6435, 0x004e831603ae4c32,
|
|
||||||
0xe7a21020ad78e312, 0x9d41a70c6ab420f2, 0x28e06c18ea1141e6, 0xd2b28cbd984f6b28,
|
|
||||||
0x26b75f6c446e9d83, 0xba47568c4d418d7f, 0xd80badbfe6183d8e, 0x0e206d7f5f166044,
|
|
||||||
0xe258a43911cbca3e, 0x723a1746b21dc0bc, 0xc7caa854f5d7cdd3, 0x7cac32883d261d9c,
|
|
||||||
0x7690c26423ba942c, 0x17e55524478042b8, 0xe0be477656a2389f, 0x4d289b5e67ab2da0,
|
|
||||||
0x44862b9c8fbbfd31, 0xb47cc8049d141365, 0x822c1b362b91c793, 0x4eb14655fb13dfd8,
|
|
||||||
0x1ecbba0714e2a97b, 0x6143459d5cde5f14, 0x53a8fbf1d5f0ac89, 0x97ea04d81c5e5b00,
|
|
||||||
0x622181a8d4fdb3f3, 0xe9bcd341572a1208, 0x1411258643cce58a, 0x9144c5fea4c6e0a4,
|
|
||||||
0x0d33d06565cf620f, 0x54a48d489f219ca1, 0xc43e5eac6d63c821, 0xa9728b3a72770daf,
|
|
||||||
0xd7934e7b20df87ef, 0xe35503b61a3e86e5, 0xcae321fbc819d504, 0x129a50b3ac60bfa6,
|
|
||||||
0xcd5e68ea7e9fb6c3, 0xb01c90199483b1c7, 0x3de93cd5c295376c, 0xaed52edf2ab9ad13,
|
|
||||||
0x2e60f512c0a07884, 0xbc3d86a3e36210c9, 0x35269d9b163951ce, 0x0c7d6e2ad0cdb5fa,
|
|
||||||
0x59e86297d87f5733, 0x298ef221898db0e7, 0x55000029d1a5aa7e, 0x8bc08ae1b5061b45,
|
|
||||||
0xc2c31c2b6c92703a, 0x94cc596baf25ef42, 0x0a1d73db22540456, 0x04b6a0f9d9c4179a,
|
|
||||||
0xeffdafa2ae3d3c60, 0xf7c8075bb49496c4, 0x9cc5c7141d1cd4e3, 0x78bd1638218e5534,
|
|
||||||
0xb2f11568f850246a, 0xedfabcfa9502bc29, 0x796ce5f2da23051b, 0xaae128b0dc93537c,
|
|
||||||
0x3a493da0ee4b29ae, 0xb5df6b2c416895d7, 0xfcabbd25122d7f37, 0x70810b58105dc4b1,
|
|
||||||
0xe10fdd37f7882a90, 0x524dcab5518a3f5c, 0x3c9e85878451255b, 0x4029828119bd34e2,
|
|
||||||
0x74a05b6f5d3ceccb, 0xb610021542e13eca, 0x0ff979d12f59e2ac, 0x6037da27e4f9cc50,
|
|
||||||
0x5e92975a0df1847d, 0xd66de190d3e623fe, 0x5032d6b87b568048, 0x9a36b7ce8235216e,
|
|
||||||
0x80272a7a24f64b4a, 0x93efed8b8c6916f7, 0x37ddbff44cce1555, 0x4b95db5d4b99bd25,
|
|
||||||
0x92d3fda169812fc0, 0xfb1a4a9a90660bb6, 0x730c196946a4b9b2, 0x81e289aa7f49da68,
|
|
||||||
0x64669a0f83b1a05f, 0x27b3ff7d9644f48b, 0xcc6b615c8db675b3, 0x674f20b9bcebbe95,
|
|
||||||
0x6f31238275655982, 0x5ae488713e45cf05, 0xbf619f9954c21157, 0xeabac46040a8eae9,
|
|
||||||
0x454c6fe9f2c0c1cd, 0x419cf6496412691c, 0xd3dc3bef265b0f70, 0x6d0e60f5c3578a9e,
|
|
||||||
}
|
|
||||||
|
|
||||||
var t4 = [...]uint64{
|
|
||||||
0x5b0e608526323c55, 0x1a46c1a9fa1b59f5, 0xa9e245a17c4c8ffa, 0x65ca5159db2955d7,
|
|
||||||
0x05db0a76ce35afc2, 0x81eac77ea9113d45, 0x528ef88ab6ac0a0d, 0xa09ea253597be3ff,
|
|
||||||
0x430ddfb3ac48cd56, 0xc4b3a67af45ce46f, 0x4ececfd8fbe2d05e, 0x3ef56f10b39935f0,
|
|
||||||
0x0b22d6829cd619c6, 0x17fd460a74df2069, 0x6cf8cc8e8510ed40, 0xd6c824bf3a6ecaa7,
|
|
||||||
0x61243d581a817049, 0x048bacb6bbc163a2, 0xd9a38ac27d44cc32, 0x7fddff5baaf410ab,
|
|
||||||
0xad6d495aa804824b, 0xe1a6a74f2d8c9f94, 0xd4f7851235dee8e3, 0xfd4b7f886540d893,
|
|
||||||
0x247c20042aa4bfda, 0x096ea1c517d1327c, 0xd56966b4361a6685, 0x277da5c31221057d,
|
|
||||||
0x94d59893a43acff7, 0x64f0c51ccdc02281, 0x3d33bcc4ff6189db, 0xe005cb184ce66af1,
|
|
||||||
0xff5ccd1d1db99bea, 0xb0b854a7fe42980f, 0x7bd46a6a718d4b9f, 0xd10fa8cc22a5fd8c,
|
|
||||||
0xd31484952be4bd31, 0xc7fa975fcb243847, 0x4886ed1e5846c407, 0x28cddb791eb70b04,
|
|
||||||
0xc2b00be2f573417f, 0x5c9590452180f877, 0x7a6bddfff370eb00, 0xce509e38d6d9d6a4,
|
|
||||||
0xebeb0f00647fa702, 0x1dcc06cf76606f06, 0xe4d9f28ba286ff0a, 0xd85a305dc918c262,
|
|
||||||
0x475b1d8732225f54, 0x2d4fb51668ccb5fe, 0xa679b9d9d72bba20, 0x53841c0d912d43a5,
|
|
||||||
0x3b7eaa48bf12a4e8, 0x781e0e47f22f1ddf, 0xeff20ce60ab50973, 0x20d261d19dffb742,
|
|
||||||
0x16a12b03062a2e39, 0x1960eb2239650495, 0x251c16fed50eb8b8, 0x9ac0c330f826016e,
|
|
||||||
0xed152665953e7671, 0x02d63194a6369570, 0x5074f08394b1c987, 0x70ba598c90b25ce1,
|
|
||||||
0x794a15810b9742f6, 0x0d5925e9fcaf8c6c, 0x3067716cd868744e, 0x910ab077e8d7731b,
|
|
||||||
0x6a61bbdb5ac42f61, 0x93513efbf0851567, 0xf494724b9e83e9d5, 0xe887e1985c09648d,
|
|
||||||
0x34b1d3c675370cfd, 0xdc35e433bc0d255d, 0xd0aab84234131be0, 0x08042a50b48b7eaf,
|
|
||||||
0x9997c4ee44a3ab35, 0x829a7b49201799d0, 0x263b8307b7c54441, 0x752f95f4fd6a6ca6,
|
|
||||||
0x927217402c08c6e5, 0x2a8ab754a795d9ee, 0xa442f7552f72943d, 0x2c31334e19781208,
|
|
||||||
0x4fa98d7ceaee6291, 0x55c3862f665db309, 0xbd0610175d53b1f3, 0x46fe6cb840413f27,
|
|
||||||
0x3fe03792df0cfa59, 0xcfe700372eb85e8f, 0xa7be29e7adbce118, 0xe544ee5cde8431dd,
|
|
||||||
0x8a781b1b41f1873e, 0xa5c94c78a0d2f0e7, 0x39412e2877b60728, 0xa1265ef3afc9a62c,
|
|
||||||
0xbcc2770c6a2506c5, 0x3ab66dd5dce1ce12, 0xe65499d04a675b37, 0x7d8f523481bfd216,
|
|
||||||
0x0f6f64fcec15f389, 0x74efbe618b5b13c8, 0xacdc82b714273e1d, 0xdd40bfe003199d17,
|
|
||||||
0x37e99257e7e061f8, 0xfa52626904775aaa, 0x8bbbf63a463d56f9, 0xf0013f1543a26e64,
|
|
||||||
0xa8307e9f879ec898, 0xcc4c27a4150177cc, 0x1b432f2cca1d3348, 0xde1d1f8f9f6fa013,
|
|
||||||
0x606602a047a7ddd6, 0xd237ab64cc1cb2c7, 0x9b938e7225fcd1d3, 0xec4e03708e0ff476,
|
|
||||||
0xfeb2fbda3d03c12d, 0xae0bced2ee43889a, 0x22cb8923ebfb4f43, 0x69360d013cf7396d,
|
|
||||||
0x855e3602d2d4e022, 0x073805bad01f784c, 0x33e17a133852f546, 0xdf4874058ac7b638,
|
|
||||||
0xba92b29c678aa14a, 0x0ce89fc76cfaadcd, 0x5f9d4e0908339e34, 0xf1afe9291f5923b9,
|
|
||||||
0x6e3480f60f4a265f, 0xeebf3a2ab29b841c, 0xe21938a88f91b4ad, 0x57dfeff845c6d3c3,
|
|
||||||
0x2f006b0bf62caaf2, 0x62f479ef6f75ee78, 0x11a55ad41c8916a9, 0xf229d29084fed453,
|
|
||||||
0x42f1c27b16b000e6, 0x2b1f76749823c074, 0x4b76eca3c2745360, 0x8c98f463b91691bd,
|
|
||||||
0x14bcc93cf1ade66a, 0x8885213e6d458397, 0x8e177df0274d4711, 0xb49b73b5503f2951,
|
|
||||||
0x10168168c3f96b6b, 0x0e3d963b63cab0ae, 0x8dfc4b5655a1db14, 0xf789f1356e14de5c,
|
|
||||||
0x683e68af4e51dac1, 0xc9a84f9d8d4b0fd9, 0x3691e03f52a0f9d1, 0x5ed86e46e1878e80,
|
|
||||||
0x3c711a0e99d07150, 0x5a0865b20c4e9310, 0x56fbfc1fe4f0682e, 0xea8d5de3105edf9b,
|
|
||||||
0x71abfdb12379187a, 0x2eb99de1bee77b9c, 0x21ecc0ea33cf4523, 0x59a4d7521805c7a1,
|
|
||||||
0x3896f5eb56ae7c72, 0xaa638f3db18f75dc, 0x9f39358dabe9808e, 0xb7defa91c00b72ac,
|
|
||||||
0x6b5541fd62492d92, 0x6dc6dee8f92e4d5b, 0x353f57abc4beea7e, 0x735769d6da5690ce,
|
|
||||||
0x0a234aa642391484, 0xf6f9508028f80d9d, 0xb8e319a27ab3f215, 0x31ad9c1151341a4d,
|
|
||||||
0x773c22a57bef5805, 0x45c7561a07968633, 0xf913da9e249dbe36, 0xda652d9b78a64c68,
|
|
||||||
0x4c27a97f3bc334ef, 0x76621220e66b17f4, 0x967743899acd7d0b, 0xf3ee5bcae0ed6782,
|
|
||||||
0x409f753600c879fc, 0x06d09a39b5926db6, 0x6f83aeb0317ac588, 0x01e6ca4a86381f21,
|
|
||||||
0x66ff3462d19f3025, 0x72207c24ddfd3bfb, 0x4af6b6d3e2ece2eb, 0x9c994dbec7ea08de,
|
|
||||||
0x49ace597b09a8bc4, 0xb38c4766cf0797ba, 0x131b9373c57c2a75, 0xb1822cce61931e58,
|
|
||||||
0x9d7555b909ba1c0c, 0x127fafdd937d11d2, 0x29da3badc66d92e4, 0xa2c1d57154c2ecbc,
|
|
||||||
0x58c5134d82f6fe24, 0x1c3ae3515b62274f, 0xe907c82e01cb8126, 0xf8ed091913e37fcb,
|
|
||||||
0x3249d8f9c80046c9, 0x80cf9bede388fb63, 0x1881539a116cf19e, 0x5103f3f76bd52457,
|
|
||||||
0x15b7e6f5ae47f7a8, 0xdbd7c6ded47e9ccf, 0x44e55c410228bb1a, 0xb647d4255edb4e99,
|
|
||||||
0x5d11882bb8aafc30, 0xf5098bbb29d3212a, 0x8fb5ea14e90296b3, 0x677b942157dd025a,
|
|
||||||
0xfb58e7c0a390acb5, 0x89d3674c83bd4a01, 0x9e2da4df4bf3b93b, 0xfcc41e328cab4829,
|
|
||||||
0x03f38c96ba582c52, 0xcad1bdbd7fd85db2, 0xbbb442c16082ae83, 0xb95fe86ba5da9ab0,
|
|
||||||
0xb22e04673771a93f, 0x845358c9493152d8, 0xbe2a488697b4541e, 0x95a2dc2dd38e6966,
|
|
||||||
0xc02c11ac923c852b, 0x2388b1990df2a87b, 0x7c8008fa1b4f37be, 0x1f70d0c84d54e503,
|
|
||||||
0x5490adec7ece57d4, 0x002b3c27d9063a3a, 0x7eaea3848030a2bf, 0xc602326ded2003c0,
|
|
||||||
0x83a7287d69a94086, 0xc57a5fcb30f57a8a, 0xb56844e479ebe779, 0xa373b40f05dcbce9,
|
|
||||||
0xd71a786e88570ee2, 0x879cbacdbde8f6a0, 0x976ad1bcc164a32f, 0xab21e25e9666d78b,
|
|
||||||
0x901063aae5e5c33c, 0x9818b34448698d90, 0xe36487ae3e1e8abb, 0xafbdf931893bdcb4,
|
|
||||||
0x6345a0dc5fbbd519, 0x8628fe269b9465ca, 0x1e5d01603f9c51ec, 0x4de44006a15049b7,
|
|
||||||
0xbf6c70e5f776cbb1, 0x411218f2ef552bed, 0xcb0c0708705a36a3, 0xe74d14754f986044,
|
|
||||||
0xcd56d9430ea8280e, 0xc12591d7535f5065, 0xc83223f1720aef96, 0xc3a0396f7363a51f,
|
|
||||||
}
|
|
117
vendor/github.com/cxmcc/tiger/tiger.go
generated
vendored
117
vendor/github.com/cxmcc/tiger/tiger.go
generated
vendored
@ -1,117 +0,0 @@
|
|||||||
// Package tiger implements the Tiger hash algorithm
|
|
||||||
// https://github.com/cxmcc/tiger
|
|
||||||
package tiger
|
|
||||||
|
|
||||||
import "hash"
|
|
||||||
|
|
||||||
// The size of a Tiger hash value in bytes
|
|
||||||
const Size = 24
|
|
||||||
|
|
||||||
// The blocksize of Tiger hash function in bytes
|
|
||||||
const BlockSize = 64
|
|
||||||
|
|
||||||
const (
|
|
||||||
chunk = 64
|
|
||||||
initA = 0x0123456789abcdef
|
|
||||||
initB = 0xfedcba9876543210
|
|
||||||
initC = 0xf096a5b4c3b2e187
|
|
||||||
)
|
|
||||||
|
|
||||||
type digest struct {
|
|
||||||
a uint64
|
|
||||||
b uint64
|
|
||||||
c uint64
|
|
||||||
x [chunk]byte
|
|
||||||
nx int
|
|
||||||
length uint64
|
|
||||||
ver int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *digest) Reset() {
|
|
||||||
d.a = initA
|
|
||||||
d.b = initB
|
|
||||||
d.c = initC
|
|
||||||
d.nx = 0
|
|
||||||
d.length = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a new hash.Hash computing the Tiger hash value
|
|
||||||
func New() hash.Hash {
|
|
||||||
d := new(digest)
|
|
||||||
d.Reset()
|
|
||||||
d.ver = 1
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a new hash.Hash computing the Tiger2 hash value
|
|
||||||
func New2() hash.Hash {
|
|
||||||
d := new(digest)
|
|
||||||
d.Reset()
|
|
||||||
d.ver = 2
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *digest) BlockSize() int {
|
|
||||||
return BlockSize
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *digest) Size() int {
|
|
||||||
return Size
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *digest) Write(p []byte) (length int, err error) {
|
|
||||||
length = len(p)
|
|
||||||
d.length += uint64(length)
|
|
||||||
if d.nx > 0 {
|
|
||||||
n := len(p)
|
|
||||||
if n > chunk-d.nx {
|
|
||||||
n = chunk - d.nx
|
|
||||||
}
|
|
||||||
copy(d.x[d.nx:d.nx+n], p[:n])
|
|
||||||
d.nx += n
|
|
||||||
if d.nx == chunk {
|
|
||||||
d.compress(d.x[:chunk])
|
|
||||||
d.nx = 0
|
|
||||||
}
|
|
||||||
p = p[n:]
|
|
||||||
}
|
|
||||||
for len(p) >= chunk {
|
|
||||||
d.compress(p[:chunk])
|
|
||||||
p = p[chunk:]
|
|
||||||
}
|
|
||||||
if len(p) > 0 {
|
|
||||||
d.nx = copy(d.x[:], p)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d digest) Sum(in []byte) []byte {
|
|
||||||
length := d.length
|
|
||||||
var tmp [64]byte
|
|
||||||
if d.ver == 1 {
|
|
||||||
tmp[0] = 0x01
|
|
||||||
} else {
|
|
||||||
tmp[0] = 0x80
|
|
||||||
}
|
|
||||||
|
|
||||||
size := length & 0x3f
|
|
||||||
if size < 56 {
|
|
||||||
d.Write(tmp[:56-size])
|
|
||||||
} else {
|
|
||||||
d.Write(tmp[:64+56-size])
|
|
||||||
}
|
|
||||||
|
|
||||||
length <<= 3
|
|
||||||
for i := uint(0); i < 8; i++ {
|
|
||||||
tmp[i] = byte(length >> (8 * i))
|
|
||||||
}
|
|
||||||
d.Write(tmp[:8])
|
|
||||||
|
|
||||||
for i := uint(0); i < 8; i++ {
|
|
||||||
tmp[i] = byte(d.a >> (8 * i))
|
|
||||||
tmp[i+8] = byte(d.b >> (8 * i))
|
|
||||||
tmp[i+16] = byte(d.c >> (8 * i))
|
|
||||||
}
|
|
||||||
|
|
||||||
return append(in, tmp[:24]...)
|
|
||||||
}
|
|
7
vendor/github.com/googollee/go-engine.io/.travis.yml
generated
vendored
7
vendor/github.com/googollee/go-engine.io/.travis.yml
generated
vendored
@ -1,7 +0,0 @@
|
|||||||
language: go
|
|
||||||
go: 1.5
|
|
||||||
install:
|
|
||||||
- go get "github.com/smartystreets/goconvey/convey"
|
|
||||||
- go get -v .
|
|
||||||
script:
|
|
||||||
- go test -race -v ./...
|
|
23
vendor/github.com/googollee/go-engine.io/LICENSE
generated
vendored
23
vendor/github.com/googollee/go-engine.io/LICENSE
generated
vendored
@ -1,23 +0,0 @@
|
|||||||
Copyright (c) 2014-2014 Googol Lee <i@googol.im>
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
1. Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
||||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
||||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
||||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
||||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
78
vendor/github.com/googollee/go-engine.io/README.md
generated
vendored
78
vendor/github.com/googollee/go-engine.io/README.md
generated
vendored
@ -1,78 +0,0 @@
|
|||||||
# go-engine.io
|
|
||||||
|
|
||||||
[![GoDoc](http://godoc.org/github.com/googollee/go-engine.io?status.svg)](http://godoc.org/github.com/googollee/go-engine.io) [![Build Status](https://travis-ci.org/googollee/go-engine.io.svg)](https://travis-ci.org/googollee/go-engine.io)
|
|
||||||
|
|
||||||
go-engine.io is the implement of engine.io in golang, which is transport-based cross-browser/cross-device bi-directional communication layer for [go-socket.io](https://github.com/googollee/go-socket.io).
|
|
||||||
|
|
||||||
It is compatible with node.js implement, and supported long-polling and websocket transport.
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
Install the package with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/googollee/go-engine.io
|
|
||||||
```
|
|
||||||
|
|
||||||
Import it with:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/googollee/go-engine.io"
|
|
||||||
```
|
|
||||||
|
|
||||||
and use `engineio` as the package name inside the code.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
Please check example folder for details.
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/googollee/go-engine.io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
server, err := engineio.NewServer(nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
conn, _ := server.Accept()
|
|
||||||
go func() {
|
|
||||||
defer conn.Close()
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
t, r, _ := conn.NextReader()
|
|
||||||
b, _ := ioutil.ReadAll(r)
|
|
||||||
r.Close()
|
|
||||||
if t == engineio.MessageText {
|
|
||||||
log.Println(t, string(b))
|
|
||||||
} else {
|
|
||||||
log.Println(t, hex.EncodeToString(b))
|
|
||||||
}
|
|
||||||
w, _ := conn.NextWriter(t)
|
|
||||||
w.Write([]byte("pong"))
|
|
||||||
w.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
http.Handle("/engine.io/", server)
|
|
||||||
http.Handle("/", http.FileServer(http.Dir("./asset")))
|
|
||||||
log.Println("Serving at localhost:5000...")
|
|
||||||
log.Fatal(http.ListenAndServe(":5000", nil))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The 3-clause BSD License - see LICENSE for more details
|
|
50
vendor/github.com/googollee/go-engine.io/ioutil.go
generated
vendored
50
vendor/github.com/googollee/go-engine.io/ioutil.go
generated
vendored
@ -1,50 +0,0 @@
|
|||||||
package engineio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/googollee/go-engine.io/parser"
|
|
||||||
"io"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type connReader struct {
|
|
||||||
*parser.PacketDecoder
|
|
||||||
closeChan chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newConnReader(d *parser.PacketDecoder, closeChan chan struct{}) *connReader {
|
|
||||||
return &connReader{
|
|
||||||
PacketDecoder: d,
|
|
||||||
closeChan: closeChan,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *connReader) Close() error {
|
|
||||||
if r.closeChan == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
r.closeChan <- struct{}{}
|
|
||||||
r.closeChan = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type connWriter struct {
|
|
||||||
io.WriteCloser
|
|
||||||
locker *sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func newConnWriter(w io.WriteCloser, locker *sync.Mutex) *connWriter {
|
|
||||||
return &connWriter{
|
|
||||||
WriteCloser: w,
|
|
||||||
locker: locker,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *connWriter) Close() error {
|
|
||||||
defer func() {
|
|
||||||
if w.locker != nil {
|
|
||||||
w.locker.Unlock()
|
|
||||||
w.locker = nil
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return w.WriteCloser.Close()
|
|
||||||
}
|
|
8
vendor/github.com/googollee/go-engine.io/message/message.go
generated
vendored
8
vendor/github.com/googollee/go-engine.io/message/message.go
generated
vendored
@ -1,8 +0,0 @@
|
|||||||
package message
|
|
||||||
|
|
||||||
type MessageType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
MessageText MessageType = iota
|
|
||||||
MessageBinary
|
|
||||||
)
|
|
45
vendor/github.com/googollee/go-engine.io/parser/limit_reader.go
generated
vendored
45
vendor/github.com/googollee/go-engine.io/parser/limit_reader.go
generated
vendored
@ -1,45 +0,0 @@
|
|||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
type limitReader struct {
|
|
||||||
io.Reader
|
|
||||||
remain int
|
|
||||||
}
|
|
||||||
|
|
||||||
func newLimitReader(r io.Reader, limit int) *limitReader {
|
|
||||||
return &limitReader{
|
|
||||||
Reader: r,
|
|
||||||
remain: limit,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *limitReader) Read(b []byte) (int, error) {
|
|
||||||
if r.remain == 0 {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
if len(b) > r.remain {
|
|
||||||
b = b[:r.remain]
|
|
||||||
}
|
|
||||||
n, err := r.Reader.Read(b)
|
|
||||||
r.remain -= n
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *limitReader) Close() error {
|
|
||||||
if r.remain > 0 {
|
|
||||||
b := make([]byte, 10240)
|
|
||||||
for {
|
|
||||||
_, err := r.Read(b)
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
191
vendor/github.com/googollee/go-engine.io/parser/packet.go
generated
vendored
191
vendor/github.com/googollee/go-engine.io/parser/packet.go
generated
vendored
@ -1,191 +0,0 @@
|
|||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/googollee/go-engine.io/message"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PacketType is the type of packet
|
|
||||||
type PacketType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
OPEN PacketType = "open"
|
|
||||||
CLOSE PacketType = "close"
|
|
||||||
PING PacketType = "ping"
|
|
||||||
PONG PacketType = "pong"
|
|
||||||
MESSAGE PacketType = "message"
|
|
||||||
UPGRADE PacketType = "upgrade"
|
|
||||||
NOOP PacketType = "noop"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ByteToType(b byte) (PacketType, error) {
|
|
||||||
switch b {
|
|
||||||
case 0:
|
|
||||||
return OPEN, nil
|
|
||||||
case 1:
|
|
||||||
return CLOSE, nil
|
|
||||||
case 2:
|
|
||||||
return PING, nil
|
|
||||||
case 3:
|
|
||||||
return PONG, nil
|
|
||||||
case 4:
|
|
||||||
return MESSAGE, nil
|
|
||||||
case 5:
|
|
||||||
return UPGRADE, nil
|
|
||||||
case 6:
|
|
||||||
return NOOP, nil
|
|
||||||
}
|
|
||||||
return NOOP, fmt.Errorf("invalid byte 0x%x", b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Byte return the byte of type
|
|
||||||
func (t PacketType) Byte() byte {
|
|
||||||
switch t {
|
|
||||||
case OPEN:
|
|
||||||
return 0
|
|
||||||
case CLOSE:
|
|
||||||
return 1
|
|
||||||
case PING:
|
|
||||||
return 2
|
|
||||||
case PONG:
|
|
||||||
return 3
|
|
||||||
case MESSAGE:
|
|
||||||
return 4
|
|
||||||
case UPGRADE:
|
|
||||||
return 5
|
|
||||||
}
|
|
||||||
return 6
|
|
||||||
}
|
|
||||||
|
|
||||||
// packetEncoder is the encoder which encode the packet.
|
|
||||||
type PacketEncoder struct {
|
|
||||||
closer io.Closer
|
|
||||||
w io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStringEncoder return the encoder which encode type t to writer w, as string.
|
|
||||||
func NewStringEncoder(w io.Writer, t PacketType) (*PacketEncoder, error) {
|
|
||||||
return newEncoder(w, t.Byte()+'0')
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBinaryEncoder return the encoder which encode type t to writer w, as binary.
|
|
||||||
func NewBinaryEncoder(w io.Writer, t PacketType) (*PacketEncoder, error) {
|
|
||||||
return newEncoder(w, t.Byte())
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEncoder(w io.Writer, t byte) (*PacketEncoder, error) {
|
|
||||||
if _, err := w.Write([]byte{t}); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
closer, ok := w.(io.Closer)
|
|
||||||
if !ok {
|
|
||||||
closer = nil
|
|
||||||
}
|
|
||||||
return &PacketEncoder{
|
|
||||||
closer: closer,
|
|
||||||
w: w,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewB64Encoder return the encoder which encode type t to writer w, as string. When write binary, it uses base64.
|
|
||||||
func NewB64Encoder(w io.Writer, t PacketType) (*PacketEncoder, error) {
|
|
||||||
_, err := w.Write([]byte{'b', t.Byte() + '0'})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
base := base64.NewEncoder(base64.StdEncoding, w)
|
|
||||||
return &PacketEncoder{
|
|
||||||
closer: base,
|
|
||||||
w: base,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes bytes p.
|
|
||||||
func (e *PacketEncoder) Write(p []byte) (int, error) {
|
|
||||||
return e.w.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the encoder.
|
|
||||||
func (e *PacketEncoder) Close() error {
|
|
||||||
if e.closer != nil {
|
|
||||||
return e.closer.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// packetDecoder is the decoder which decode data to packet.
|
|
||||||
type PacketDecoder struct {
|
|
||||||
closer io.Closer
|
|
||||||
r io.Reader
|
|
||||||
t PacketType
|
|
||||||
msgType message.MessageType
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDecoder return the decoder which decode from reader r.
|
|
||||||
func NewDecoder(r io.Reader) (*PacketDecoder, error) {
|
|
||||||
var closer io.Closer
|
|
||||||
if limit, ok := r.(*limitReader); ok {
|
|
||||||
closer = limit
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if closer != nil {
|
|
||||||
closer.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
b := []byte{0xff}
|
|
||||||
if _, err := r.Read(b); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
msgType := message.MessageText
|
|
||||||
if b[0] == 'b' {
|
|
||||||
if _, err := r.Read(b); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r = base64.NewDecoder(base64.StdEncoding, r)
|
|
||||||
msgType = message.MessageBinary
|
|
||||||
}
|
|
||||||
if b[0] >= '0' {
|
|
||||||
b[0] = b[0] - '0'
|
|
||||||
} else {
|
|
||||||
msgType = message.MessageBinary
|
|
||||||
}
|
|
||||||
t, err := ByteToType(b[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret := &PacketDecoder{
|
|
||||||
closer: closer,
|
|
||||||
r: r,
|
|
||||||
t: t,
|
|
||||||
msgType: msgType,
|
|
||||||
}
|
|
||||||
closer = nil
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read reads packet data to bytes p.
|
|
||||||
func (d *PacketDecoder) Read(p []byte) (int, error) {
|
|
||||||
return d.r.Read(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns the type of packet.
|
|
||||||
func (d *PacketDecoder) Type() PacketType {
|
|
||||||
return d.t
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageType returns the type of message, binary or string.
|
|
||||||
func (d *PacketDecoder) MessageType() message.MessageType {
|
|
||||||
return d.msgType
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the decoder.
|
|
||||||
func (d *PacketDecoder) Close() error {
|
|
||||||
if d.closer != nil {
|
|
||||||
return d.closer.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
3
vendor/github.com/googollee/go-engine.io/parser/parser.go
generated
vendored
3
vendor/github.com/googollee/go-engine.io/parser/parser.go
generated
vendored
@ -1,3 +0,0 @@
|
|||||||
package parser
|
|
||||||
|
|
||||||
const Protocol = 3
|
|
170
vendor/github.com/googollee/go-engine.io/parser/payload.go
generated
vendored
170
vendor/github.com/googollee/go-engine.io/parser/payload.go
generated
vendored
@ -1,170 +0,0 @@
|
|||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// payloadEncoder is the encoder to encode packets as payload. It can be used in multi-thread.
|
|
||||||
type PayloadEncoder struct {
|
|
||||||
buffers [][]byte
|
|
||||||
locker sync.Mutex
|
|
||||||
isString bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStringPayloadEncoder returns the encoder which encode as string.
|
|
||||||
func NewStringPayloadEncoder() *PayloadEncoder {
|
|
||||||
return &PayloadEncoder{
|
|
||||||
isString: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStringPayloadEncoder returns the encoder which encode as binary.
|
|
||||||
func NewBinaryPayloadEncoder() *PayloadEncoder {
|
|
||||||
return &PayloadEncoder{
|
|
||||||
isString: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type encoder struct {
|
|
||||||
*PacketEncoder
|
|
||||||
buf *bytes.Buffer
|
|
||||||
binaryPrefix string
|
|
||||||
payload *PayloadEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e encoder) Close() error {
|
|
||||||
if err := e.PacketEncoder.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var buffer []byte
|
|
||||||
if e.payload.isString {
|
|
||||||
buffer = []byte(fmt.Sprintf("%d:%s", e.buf.Len(), e.buf.String()))
|
|
||||||
} else {
|
|
||||||
buffer = []byte(fmt.Sprintf("%s%d", e.binaryPrefix, e.buf.Len()))
|
|
||||||
for i, n := 0, len(buffer); i < n; i++ {
|
|
||||||
buffer[i] = buffer[i] - '0'
|
|
||||||
}
|
|
||||||
buffer = append(buffer, 0xff)
|
|
||||||
buffer = append(buffer, e.buf.Bytes()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
e.payload.locker.Lock()
|
|
||||||
e.payload.buffers = append(e.payload.buffers, buffer)
|
|
||||||
e.payload.locker.Unlock()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextString returns the encoder with packet type t and encode as string.
|
|
||||||
func (e *PayloadEncoder) NextString(t PacketType) (io.WriteCloser, error) {
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
pEncoder, err := NewStringEncoder(buf, t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return encoder{
|
|
||||||
PacketEncoder: pEncoder,
|
|
||||||
buf: buf,
|
|
||||||
binaryPrefix: "0",
|
|
||||||
payload: e,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextBinary returns the encoder with packet type t and encode as binary.
|
|
||||||
func (e *PayloadEncoder) NextBinary(t PacketType) (io.WriteCloser, error) {
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
var pEncoder *PacketEncoder
|
|
||||||
var err error
|
|
||||||
if e.isString {
|
|
||||||
pEncoder, err = NewB64Encoder(buf, t)
|
|
||||||
} else {
|
|
||||||
pEncoder, err = NewBinaryEncoder(buf, t)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return encoder{
|
|
||||||
PacketEncoder: pEncoder,
|
|
||||||
buf: buf,
|
|
||||||
binaryPrefix: "1",
|
|
||||||
payload: e,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeTo writes encoded payload to writer w. It will clear the buffer of encoder.
|
|
||||||
func (e *PayloadEncoder) EncodeTo(w io.Writer) error {
|
|
||||||
e.locker.Lock()
|
|
||||||
buffers := e.buffers
|
|
||||||
e.buffers = nil
|
|
||||||
e.locker.Unlock()
|
|
||||||
|
|
||||||
for _, b := range buffers {
|
|
||||||
for len(b) > 0 {
|
|
||||||
n, err := w.Write(b)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
b = b[n:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//IsString returns true if payload encode to string, otherwise returns false.
|
|
||||||
func (e *PayloadEncoder) IsString() bool {
|
|
||||||
return e.isString
|
|
||||||
}
|
|
||||||
|
|
||||||
// payloadDecoder is the decoder to decode payload.
|
|
||||||
type PayloadDecoder struct {
|
|
||||||
r *bufio.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPaylaodDecoder returns the payload decoder which read from reader r.
|
|
||||||
func NewPayloadDecoder(r io.Reader) *PayloadDecoder {
|
|
||||||
br, ok := r.(*bufio.Reader)
|
|
||||||
if !ok {
|
|
||||||
br = bufio.NewReader(r)
|
|
||||||
}
|
|
||||||
return &PayloadDecoder{
|
|
||||||
r: br,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next returns the packet decoder. Make sure it will be closed after used.
|
|
||||||
func (d *PayloadDecoder) Next() (*PacketDecoder, error) {
|
|
||||||
firstByte, err := d.r.Peek(1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
isBinary := firstByte[0] < '0'
|
|
||||||
delim := byte(':')
|
|
||||||
if isBinary {
|
|
||||||
d.r.ReadByte()
|
|
||||||
delim = 0xff
|
|
||||||
}
|
|
||||||
line, err := d.r.ReadBytes(delim)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
l := len(line)
|
|
||||||
if l < 1 {
|
|
||||||
return nil, fmt.Errorf("invalid input")
|
|
||||||
}
|
|
||||||
lenByte := line[:l-1]
|
|
||||||
if isBinary {
|
|
||||||
for i, n := 0, l; i < n; i++ {
|
|
||||||
line[i] = line[i] + '0'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
packetLen, err := strconv.ParseInt(string(lenByte), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid input")
|
|
||||||
}
|
|
||||||
return NewDecoder(newLimitReader(d.r, int(packetLen)))
|
|
||||||
}
|
|
149
vendor/github.com/googollee/go-engine.io/polling/client.go
generated
vendored
149
vendor/github.com/googollee/go-engine.io/polling/client.go
generated
vendored
@ -1,149 +0,0 @@
|
|||||||
package polling
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"github.com/googollee/go-engine.io/message"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/googollee/go-engine.io/parser"
|
|
||||||
"github.com/googollee/go-engine.io/transport"
|
|
||||||
)
|
|
||||||
|
|
||||||
type client struct {
|
|
||||||
req http.Request
|
|
||||||
url url.URL
|
|
||||||
seq uint
|
|
||||||
getResp *http.Response
|
|
||||||
postResp *http.Response
|
|
||||||
resp *http.Response
|
|
||||||
payloadDecoder *parser.PayloadDecoder
|
|
||||||
payloadEncoder *parser.PayloadEncoder
|
|
||||||
client *http.Client
|
|
||||||
state state
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClient(r *http.Request) (transport.Client, error) {
|
|
||||||
newEncoder := parser.NewBinaryPayloadEncoder
|
|
||||||
if _, ok := r.URL.Query()["b64"]; ok {
|
|
||||||
newEncoder = parser.NewStringPayloadEncoder
|
|
||||||
}
|
|
||||||
ret := &client{
|
|
||||||
req: *r,
|
|
||||||
url: *r.URL,
|
|
||||||
seq: 0,
|
|
||||||
payloadEncoder: newEncoder(),
|
|
||||||
client: http.DefaultClient,
|
|
||||||
state: stateNormal,
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) Response() *http.Response {
|
|
||||||
return c.resp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) NextReader() (*parser.PacketDecoder, error) {
|
|
||||||
if c.state != stateNormal {
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
if c.payloadDecoder != nil {
|
|
||||||
ret, err := c.payloadDecoder.Next()
|
|
||||||
if err != io.EOF {
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
c.getResp.Body.Close()
|
|
||||||
c.payloadDecoder = nil
|
|
||||||
}
|
|
||||||
req := c.getReq()
|
|
||||||
req.Method = "GET"
|
|
||||||
var err error
|
|
||||||
c.getResp, err = c.client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if c.resp == nil {
|
|
||||||
c.resp = c.getResp
|
|
||||||
}
|
|
||||||
c.payloadDecoder = parser.NewPayloadDecoder(c.getResp.Body)
|
|
||||||
return c.payloadDecoder.Next()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) NextWriter(messageType message.MessageType, packetType parser.PacketType) (io.WriteCloser, error) {
|
|
||||||
if c.state != stateNormal {
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
next := c.payloadEncoder.NextBinary
|
|
||||||
if messageType == message.MessageText {
|
|
||||||
next = c.payloadEncoder.NextString
|
|
||||||
}
|
|
||||||
w, err := next(packetType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return newClientWriter(c, w), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) Close() error {
|
|
||||||
if c.state != stateNormal {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
c.state = stateClosed
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) getReq() *http.Request {
|
|
||||||
req := c.req
|
|
||||||
url := c.url
|
|
||||||
req.URL = &url
|
|
||||||
query := req.URL.Query()
|
|
||||||
query.Set("t", fmt.Sprintf("%d-%d", time.Now().Unix()*1000, c.seq))
|
|
||||||
c.seq++
|
|
||||||
req.URL.RawQuery = query.Encode()
|
|
||||||
return &req
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) doPost() error {
|
|
||||||
if c.state != stateNormal {
|
|
||||||
return io.EOF
|
|
||||||
}
|
|
||||||
req := c.getReq()
|
|
||||||
req.Method = "POST"
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
if err := c.payloadEncoder.EncodeTo(buf); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.Body = ioutil.NopCloser(buf)
|
|
||||||
var err error
|
|
||||||
c.postResp, err = c.client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if c.resp == nil {
|
|
||||||
c.resp = c.postResp
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type clientWriter struct {
|
|
||||||
io.WriteCloser
|
|
||||||
client *client
|
|
||||||
}
|
|
||||||
|
|
||||||
func newClientWriter(c *client, w io.WriteCloser) io.WriteCloser {
|
|
||||||
return &clientWriter{
|
|
||||||
WriteCloser: w,
|
|
||||||
client: c,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *clientWriter) Close() error {
|
|
||||||
if err := w.WriteCloser.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return w.client.doPost()
|
|
||||||
}
|
|
197
vendor/github.com/googollee/go-engine.io/polling/server.go
generated
vendored
197
vendor/github.com/googollee/go-engine.io/polling/server.go
generated
vendored
@ -1,197 +0,0 @@
|
|||||||
package polling
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"html/template"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/googollee/go-engine.io/message"
|
|
||||||
"github.com/googollee/go-engine.io/parser"
|
|
||||||
"github.com/googollee/go-engine.io/transport"
|
|
||||||
)
|
|
||||||
|
|
||||||
type state int
|
|
||||||
|
|
||||||
const (
|
|
||||||
stateUnknow state = iota
|
|
||||||
stateNormal
|
|
||||||
stateClosing
|
|
||||||
stateClosed
|
|
||||||
)
|
|
||||||
|
|
||||||
type Polling struct {
|
|
||||||
sendChan chan bool
|
|
||||||
encoder *parser.PayloadEncoder
|
|
||||||
callback transport.Callback
|
|
||||||
getLocker *Locker
|
|
||||||
postLocker *Locker
|
|
||||||
state state
|
|
||||||
stateLocker sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewServer(w http.ResponseWriter, r *http.Request, callback transport.Callback) (transport.Server, error) {
|
|
||||||
newEncoder := parser.NewBinaryPayloadEncoder
|
|
||||||
if r.URL.Query()["b64"] != nil {
|
|
||||||
newEncoder = parser.NewStringPayloadEncoder
|
|
||||||
}
|
|
||||||
ret := &Polling{
|
|
||||||
sendChan: MakeSendChan(),
|
|
||||||
encoder: newEncoder(),
|
|
||||||
callback: callback,
|
|
||||||
getLocker: NewLocker(),
|
|
||||||
postLocker: NewLocker(),
|
|
||||||
state: stateNormal,
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Polling) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
switch r.Method {
|
|
||||||
case "GET":
|
|
||||||
p.get(w, r)
|
|
||||||
case "POST":
|
|
||||||
p.post(w, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Polling) Close() error {
|
|
||||||
if p.getState() != stateNormal {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
close(p.sendChan)
|
|
||||||
p.setState(stateClosing)
|
|
||||||
if p.getLocker.TryLock() {
|
|
||||||
if p.postLocker.TryLock() {
|
|
||||||
p.callback.OnClose(p)
|
|
||||||
p.setState(stateClosed)
|
|
||||||
p.postLocker.Unlock()
|
|
||||||
}
|
|
||||||
p.getLocker.Unlock()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Polling) NextWriter(msgType message.MessageType, packetType parser.PacketType) (io.WriteCloser, error) {
|
|
||||||
if p.getState() != stateNormal {
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret io.WriteCloser
|
|
||||||
var err error
|
|
||||||
switch msgType {
|
|
||||||
case message.MessageText:
|
|
||||||
ret, err = p.encoder.NextString(packetType)
|
|
||||||
case message.MessageBinary:
|
|
||||||
ret, err = p.encoder.NextBinary(packetType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return NewWriter(ret, p), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Polling) get(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if !p.getLocker.TryLock() {
|
|
||||||
http.Error(w, "overlay get", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if p.getState() != stateNormal {
|
|
||||||
http.Error(w, "closed", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if p.getState() == stateClosing {
|
|
||||||
if p.postLocker.TryLock() {
|
|
||||||
p.setState(stateClosed)
|
|
||||||
p.callback.OnClose(p)
|
|
||||||
p.postLocker.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.getLocker.Unlock()
|
|
||||||
}()
|
|
||||||
|
|
||||||
<-p.sendChan
|
|
||||||
|
|
||||||
if j := r.URL.Query().Get("j"); j != "" {
|
|
||||||
// JSONP Polling
|
|
||||||
w.Header().Set("Content-Type", "text/javascript; charset=UTF-8")
|
|
||||||
tmp := bytes.Buffer{}
|
|
||||||
p.encoder.EncodeTo(&tmp)
|
|
||||||
pl := template.JSEscapeString(tmp.String())
|
|
||||||
w.Write([]byte("___eio[" + j + "](\""))
|
|
||||||
w.Write([]byte(pl))
|
|
||||||
w.Write([]byte("\");"))
|
|
||||||
} else {
|
|
||||||
// XHR Polling
|
|
||||||
if p.encoder.IsString() {
|
|
||||||
w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
|
|
||||||
} else {
|
|
||||||
w.Header().Set("Content-Type", "application/octet-stream")
|
|
||||||
}
|
|
||||||
p.encoder.EncodeTo(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Polling) post(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "text/html")
|
|
||||||
if !p.postLocker.TryLock() {
|
|
||||||
http.Error(w, "overlay post", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if p.getState() != stateNormal {
|
|
||||||
http.Error(w, "closed", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if p.getState() == stateClosing {
|
|
||||||
if p.getLocker.TryLock() {
|
|
||||||
p.setState(stateClosed)
|
|
||||||
p.callback.OnClose(p)
|
|
||||||
p.getLocker.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.postLocker.Unlock()
|
|
||||||
}()
|
|
||||||
|
|
||||||
var decoder *parser.PayloadDecoder
|
|
||||||
if j := r.URL.Query().Get("j"); j != "" {
|
|
||||||
// JSONP Polling
|
|
||||||
d := r.FormValue("d")
|
|
||||||
decoder = parser.NewPayloadDecoder(bytes.NewBufferString(d))
|
|
||||||
} else {
|
|
||||||
// XHR Polling
|
|
||||||
decoder = parser.NewPayloadDecoder(r.Body)
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
d, err := decoder.Next()
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
p.callback.OnPacket(d)
|
|
||||||
d.Close()
|
|
||||||
}
|
|
||||||
w.Write([]byte("ok"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Polling) setState(s state) {
|
|
||||||
p.stateLocker.Lock()
|
|
||||||
defer p.stateLocker.Unlock()
|
|
||||||
p.state = s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Polling) getState() state {
|
|
||||||
p.stateLocker.Lock()
|
|
||||||
defer p.stateLocker.Unlock()
|
|
||||||
return p.state
|
|
||||||
}
|
|
28
vendor/github.com/googollee/go-engine.io/polling/try_locker.go
generated
vendored
28
vendor/github.com/googollee/go-engine.io/polling/try_locker.go
generated
vendored
@ -1,28 +0,0 @@
|
|||||||
package polling
|
|
||||||
|
|
||||||
type Locker struct {
|
|
||||||
locker chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLocker() *Locker {
|
|
||||||
return &Locker{
|
|
||||||
locker: make(chan struct{}, 1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Locker) Lock() {
|
|
||||||
l.locker <- struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Locker) TryLock() bool {
|
|
||||||
select {
|
|
||||||
case l.locker <- struct{}{}:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Locker) Unlock() {
|
|
||||||
<-l.locker
|
|
||||||
}
|
|
33
vendor/github.com/googollee/go-engine.io/polling/writer.go
generated
vendored
33
vendor/github.com/googollee/go-engine.io/polling/writer.go
generated
vendored
@ -1,33 +0,0 @@
|
|||||||
package polling
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func MakeSendChan() chan bool {
|
|
||||||
return make(chan bool, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Writer struct {
|
|
||||||
io.WriteCloser
|
|
||||||
server *Polling
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWriter(w io.WriteCloser, server *Polling) *Writer {
|
|
||||||
return &Writer{
|
|
||||||
WriteCloser: w,
|
|
||||||
server: server,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Writer) Close() error {
|
|
||||||
if w.server.getState() != stateNormal {
|
|
||||||
return errors.New("use of closed network connection")
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case w.server.sendChan <- true:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
return w.WriteCloser.Close()
|
|
||||||
}
|
|
12
vendor/github.com/googollee/go-engine.io/polling/xhr.go
generated
vendored
12
vendor/github.com/googollee/go-engine.io/polling/xhr.go
generated
vendored
@ -1,12 +0,0 @@
|
|||||||
package polling
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/googollee/go-engine.io/transport"
|
|
||||||
)
|
|
||||||
|
|
||||||
var Creater = transport.Creater{
|
|
||||||
Name: "polling",
|
|
||||||
Upgrading: false,
|
|
||||||
Server: NewServer,
|
|
||||||
Client: NewClient,
|
|
||||||
}
|
|
188
vendor/github.com/googollee/go-engine.io/server.go
generated
vendored
188
vendor/github.com/googollee/go-engine.io/server.go
generated
vendored
@ -1,188 +0,0 @@
|
|||||||
package engineio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/md5"
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/googollee/go-engine.io/polling"
|
|
||||||
"github.com/googollee/go-engine.io/websocket"
|
|
||||||
)
|
|
||||||
|
|
||||||
type config struct {
|
|
||||||
PingTimeout time.Duration
|
|
||||||
PingInterval time.Duration
|
|
||||||
MaxConnection int
|
|
||||||
AllowRequest func(*http.Request) error
|
|
||||||
AllowUpgrades bool
|
|
||||||
Cookie string
|
|
||||||
NewId func(r *http.Request) string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server is the server of engine.io.
|
|
||||||
type Server struct {
|
|
||||||
config config
|
|
||||||
socketChan chan Conn
|
|
||||||
serverSessions Sessions
|
|
||||||
creaters transportCreaters
|
|
||||||
currentConnection int32
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServer returns the server suppported given transports. If transports is nil, server will use ["polling", "websocket"] as default.
|
|
||||||
func NewServer(transports []string) (*Server, error) {
|
|
||||||
if transports == nil {
|
|
||||||
transports = []string{"polling", "websocket"}
|
|
||||||
}
|
|
||||||
creaters := make(transportCreaters)
|
|
||||||
for _, t := range transports {
|
|
||||||
switch t {
|
|
||||||
case "polling":
|
|
||||||
creaters[t] = polling.Creater
|
|
||||||
case "websocket":
|
|
||||||
creaters[t] = websocket.Creater
|
|
||||||
default:
|
|
||||||
return nil, InvalidError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &Server{
|
|
||||||
config: config{
|
|
||||||
PingTimeout: 60000 * time.Millisecond,
|
|
||||||
PingInterval: 25000 * time.Millisecond,
|
|
||||||
MaxConnection: 1000,
|
|
||||||
AllowRequest: func(*http.Request) error { return nil },
|
|
||||||
AllowUpgrades: true,
|
|
||||||
Cookie: "io",
|
|
||||||
NewId: newId,
|
|
||||||
},
|
|
||||||
socketChan: make(chan Conn),
|
|
||||||
serverSessions: newServerSessions(),
|
|
||||||
creaters: creaters,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPingTimeout sets the timeout of ping. When time out, server will close connection. Default is 60s.
|
|
||||||
func (s *Server) SetPingTimeout(t time.Duration) {
|
|
||||||
s.config.PingTimeout = t
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPingInterval sets the interval of ping. Default is 25s.
|
|
||||||
func (s *Server) SetPingInterval(t time.Duration) {
|
|
||||||
s.config.PingInterval = t
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMaxConnection sets the max connetion. Default is 1000.
|
|
||||||
func (s *Server) SetMaxConnection(n int) {
|
|
||||||
s.config.MaxConnection = n
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMaxConnection returns the current max connection
|
|
||||||
func (s *Server) GetMaxConnection() int {
|
|
||||||
return s.config.MaxConnection
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count returns a count of current number of active connections in session
|
|
||||||
func (s *Server) Count() int {
|
|
||||||
return int(atomic.LoadInt32(&s.currentConnection))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAllowRequest sets the middleware function when establish connection. If it return non-nil, connection won't be established. Default will allow all request.
|
|
||||||
func (s *Server) SetAllowRequest(f func(*http.Request) error) {
|
|
||||||
s.config.AllowRequest = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAllowUpgrades sets whether server allows transport upgrade. Default is true.
|
|
||||||
func (s *Server) SetAllowUpgrades(allow bool) {
|
|
||||||
s.config.AllowUpgrades = allow
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCookie sets the name of cookie which used by engine.io. Default is "io".
|
|
||||||
func (s *Server) SetCookie(prefix string) {
|
|
||||||
s.config.Cookie = prefix
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNewId sets the callback func to generate new connection id. By default, id is generated from remote addr + current time stamp
|
|
||||||
func (s *Server) SetNewId(f func(*http.Request) string) {
|
|
||||||
s.config.NewId = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSessionManager sets the sessions as server's session manager. Default sessions is single process manager. You can custom it as load balance.
|
|
||||||
func (s *Server) SetSessionManager(sessions Sessions) {
|
|
||||||
s.serverSessions = sessions
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeHTTP handles http request.
|
|
||||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
defer r.Body.Close()
|
|
||||||
|
|
||||||
sid := r.URL.Query().Get("sid")
|
|
||||||
conn := s.serverSessions.Get(sid)
|
|
||||||
if conn == nil {
|
|
||||||
if sid != "" {
|
|
||||||
http.Error(w, "invalid sid", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.config.AllowRequest(r); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n := atomic.AddInt32(&s.currentConnection, 1)
|
|
||||||
if int(n) > s.config.MaxConnection {
|
|
||||||
atomic.AddInt32(&s.currentConnection, -1)
|
|
||||||
http.Error(w, "too many connections", http.StatusServiceUnavailable)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sid = s.config.NewId(r)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
conn, err = newServerConn(sid, w, r, s)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s.serverSessions.Set(sid, conn)
|
|
||||||
|
|
||||||
s.socketChan <- conn
|
|
||||||
}
|
|
||||||
http.SetCookie(w, &http.Cookie{
|
|
||||||
Name: s.config.Cookie,
|
|
||||||
Value: sid,
|
|
||||||
})
|
|
||||||
|
|
||||||
conn.(*serverConn).ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept returns Conn when client connect to server.
|
|
||||||
func (s *Server) Accept() (Conn, error) {
|
|
||||||
return <-s.socketChan, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) configure() config {
|
|
||||||
return s.config
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) transports() transportCreaters {
|
|
||||||
return s.creaters
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) onClose(id string) {
|
|
||||||
s.serverSessions.Remove(id)
|
|
||||||
atomic.AddInt32(&s.currentConnection, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newId(r *http.Request) string {
|
|
||||||
hash := fmt.Sprintf("%s %s", r.RemoteAddr, time.Now())
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
sum := md5.Sum([]byte(hash))
|
|
||||||
encoder := base64.NewEncoder(base64.URLEncoding, buf)
|
|
||||||
encoder.Write(sum[:])
|
|
||||||
encoder.Close()
|
|
||||||
return buf.String()[:20]
|
|
||||||
}
|
|
388
vendor/github.com/googollee/go-engine.io/server_conn.go
generated
vendored
388
vendor/github.com/googollee/go-engine.io/server_conn.go
generated
vendored
@ -1,388 +0,0 @@
|
|||||||
package engineio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/googollee/go-engine.io/message"
|
|
||||||
"github.com/googollee/go-engine.io/parser"
|
|
||||||
"github.com/googollee/go-engine.io/transport"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MessageType message.MessageType
|
|
||||||
|
|
||||||
const (
|
|
||||||
MessageBinary MessageType = MessageType(message.MessageBinary)
|
|
||||||
MessageText MessageType = MessageType(message.MessageText)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Conn is the connection object of engine.io.
|
|
||||||
type Conn interface {
|
|
||||||
|
|
||||||
// Id returns the session id of connection.
|
|
||||||
Id() string
|
|
||||||
|
|
||||||
// Request returns the first http request when established connection.
|
|
||||||
Request() *http.Request
|
|
||||||
|
|
||||||
// Close closes the connection.
|
|
||||||
Close() error
|
|
||||||
|
|
||||||
// NextReader returns the next message type, reader. If no message received, it will block.
|
|
||||||
NextReader() (MessageType, io.ReadCloser, error)
|
|
||||||
|
|
||||||
// NextWriter returns the next message writer with given message type.
|
|
||||||
NextWriter(messageType MessageType) (io.WriteCloser, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type transportCreaters map[string]transport.Creater
|
|
||||||
|
|
||||||
func (c transportCreaters) Get(name string) transport.Creater {
|
|
||||||
return c[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
type serverCallback interface {
|
|
||||||
configure() config
|
|
||||||
transports() transportCreaters
|
|
||||||
onClose(sid string)
|
|
||||||
}
|
|
||||||
|
|
||||||
type state int
|
|
||||||
|
|
||||||
const (
|
|
||||||
stateUnknow state = iota
|
|
||||||
stateNormal
|
|
||||||
stateUpgrading
|
|
||||||
stateClosing
|
|
||||||
stateClosed
|
|
||||||
)
|
|
||||||
|
|
||||||
type serverConn struct {
|
|
||||||
id string
|
|
||||||
request *http.Request
|
|
||||||
callback serverCallback
|
|
||||||
writerLocker sync.Mutex
|
|
||||||
transportLocker sync.RWMutex
|
|
||||||
currentName string
|
|
||||||
current transport.Server
|
|
||||||
upgradingName string
|
|
||||||
upgrading transport.Server
|
|
||||||
state state
|
|
||||||
stateLocker sync.RWMutex
|
|
||||||
readerChan chan *connReader
|
|
||||||
pingTimeout time.Duration
|
|
||||||
pingInterval time.Duration
|
|
||||||
pingChan chan bool
|
|
||||||
pingLocker sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
var InvalidError = errors.New("invalid transport")
|
|
||||||
|
|
||||||
func newServerConn(id string, w http.ResponseWriter, r *http.Request, callback serverCallback) (*serverConn, error) {
|
|
||||||
transportName := r.URL.Query().Get("transport")
|
|
||||||
creater := callback.transports().Get(transportName)
|
|
||||||
if creater.Name == "" {
|
|
||||||
return nil, InvalidError
|
|
||||||
}
|
|
||||||
ret := &serverConn{
|
|
||||||
id: id,
|
|
||||||
request: r,
|
|
||||||
callback: callback,
|
|
||||||
state: stateNormal,
|
|
||||||
readerChan: make(chan *connReader),
|
|
||||||
pingTimeout: callback.configure().PingTimeout,
|
|
||||||
pingInterval: callback.configure().PingInterval,
|
|
||||||
pingChan: make(chan bool),
|
|
||||||
}
|
|
||||||
transport, err := creater.Server(w, r, ret)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret.setCurrent(transportName, transport)
|
|
||||||
if err := ret.onOpen(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
go ret.pingLoop()
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) Id() string {
|
|
||||||
return c.id
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) Request() *http.Request {
|
|
||||||
return c.request
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) NextReader() (MessageType, io.ReadCloser, error) {
|
|
||||||
if c.getState() == stateClosed {
|
|
||||||
return MessageBinary, nil, io.EOF
|
|
||||||
}
|
|
||||||
ret := <-c.readerChan
|
|
||||||
if ret == nil {
|
|
||||||
return MessageBinary, nil, io.EOF
|
|
||||||
}
|
|
||||||
return MessageType(ret.MessageType()), ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) NextWriter(t MessageType) (io.WriteCloser, error) {
|
|
||||||
switch c.getState() {
|
|
||||||
case stateUpgrading:
|
|
||||||
for i := 0; i < 30; i++ {
|
|
||||||
time.Sleep(50 * time.Millisecond)
|
|
||||||
if c.getState() != stateUpgrading {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.getState() == stateUpgrading {
|
|
||||||
return nil, fmt.Errorf("upgrading")
|
|
||||||
}
|
|
||||||
case stateNormal:
|
|
||||||
default:
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
c.writerLocker.Lock()
|
|
||||||
ret, err := c.getCurrent().NextWriter(message.MessageType(t), parser.MESSAGE)
|
|
||||||
if err != nil {
|
|
||||||
c.writerLocker.Unlock()
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
writer := newConnWriter(ret, &c.writerLocker)
|
|
||||||
return writer, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) Close() error {
|
|
||||||
if c.getState() != stateNormal && c.getState() != stateUpgrading {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if c.upgrading != nil {
|
|
||||||
c.upgrading.Close()
|
|
||||||
}
|
|
||||||
c.writerLocker.Lock()
|
|
||||||
if w, err := c.getCurrent().NextWriter(message.MessageText, parser.CLOSE); err == nil {
|
|
||||||
writer := newConnWriter(w, &c.writerLocker)
|
|
||||||
writer.Close()
|
|
||||||
} else {
|
|
||||||
c.writerLocker.Unlock()
|
|
||||||
}
|
|
||||||
if err := c.getCurrent().Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.setState(stateClosing)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
transportName := r.URL.Query().Get("transport")
|
|
||||||
if c.currentName != transportName {
|
|
||||||
creater := c.callback.transports().Get(transportName)
|
|
||||||
if creater.Name == "" {
|
|
||||||
http.Error(w, fmt.Sprintf("invalid transport %s", transportName), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
u, err := creater.Server(w, r, c)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.setUpgrading(creater.Name, u)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.current.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) OnPacket(r *parser.PacketDecoder) {
|
|
||||||
if s := c.getState(); s != stateNormal && s != stateUpgrading {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch r.Type() {
|
|
||||||
case parser.OPEN:
|
|
||||||
case parser.CLOSE:
|
|
||||||
c.getCurrent().Close()
|
|
||||||
case parser.PING:
|
|
||||||
c.writerLocker.Lock()
|
|
||||||
t := c.getCurrent()
|
|
||||||
u := c.getUpgrade()
|
|
||||||
newWriter := t.NextWriter
|
|
||||||
if u != nil {
|
|
||||||
if w, _ := t.NextWriter(message.MessageText, parser.NOOP); w != nil {
|
|
||||||
w.Close()
|
|
||||||
}
|
|
||||||
newWriter = u.NextWriter
|
|
||||||
}
|
|
||||||
if w, _ := newWriter(message.MessageText, parser.PONG); w != nil {
|
|
||||||
io.Copy(w, r)
|
|
||||||
w.Close()
|
|
||||||
}
|
|
||||||
c.writerLocker.Unlock()
|
|
||||||
fallthrough
|
|
||||||
case parser.PONG:
|
|
||||||
c.pingLocker.Lock()
|
|
||||||
defer c.pingLocker.Unlock()
|
|
||||||
if s := c.getState(); s != stateNormal && s != stateUpgrading {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.pingChan <- true
|
|
||||||
case parser.MESSAGE:
|
|
||||||
closeChan := make(chan struct{})
|
|
||||||
c.readerChan <- newConnReader(r, closeChan)
|
|
||||||
<-closeChan
|
|
||||||
close(closeChan)
|
|
||||||
r.Close()
|
|
||||||
case parser.UPGRADE:
|
|
||||||
c.upgraded()
|
|
||||||
case parser.NOOP:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) OnClose(server transport.Server) {
|
|
||||||
if t := c.getUpgrade(); server == t {
|
|
||||||
c.setUpgrading("", nil)
|
|
||||||
t.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t := c.getCurrent()
|
|
||||||
if server != t {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.Close()
|
|
||||||
if t := c.getUpgrade(); t != nil {
|
|
||||||
t.Close()
|
|
||||||
c.setUpgrading("", nil)
|
|
||||||
}
|
|
||||||
c.setState(stateClosed)
|
|
||||||
close(c.readerChan)
|
|
||||||
c.pingLocker.Lock()
|
|
||||||
close(c.pingChan)
|
|
||||||
c.pingLocker.Unlock()
|
|
||||||
c.callback.onClose(c.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *serverConn) onOpen() error {
|
|
||||||
upgrades := []string{}
|
|
||||||
for name := range s.callback.transports() {
|
|
||||||
if name == s.currentName {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
upgrades = append(upgrades, name)
|
|
||||||
}
|
|
||||||
type connectionInfo struct {
|
|
||||||
Sid string `json:"sid"`
|
|
||||||
Upgrades []string `json:"upgrades"`
|
|
||||||
PingInterval time.Duration `json:"pingInterval"`
|
|
||||||
PingTimeout time.Duration `json:"pingTimeout"`
|
|
||||||
}
|
|
||||||
resp := connectionInfo{
|
|
||||||
Sid: s.Id(),
|
|
||||||
Upgrades: upgrades,
|
|
||||||
PingInterval: s.callback.configure().PingInterval / time.Millisecond,
|
|
||||||
PingTimeout: s.callback.configure().PingTimeout / time.Millisecond,
|
|
||||||
}
|
|
||||||
w, err := s.getCurrent().NextWriter(message.MessageText, parser.OPEN)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
encoder := json.NewEncoder(w)
|
|
||||||
if err := encoder.Encode(resp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) getCurrent() transport.Server {
|
|
||||||
c.transportLocker.RLock()
|
|
||||||
defer c.transportLocker.RUnlock()
|
|
||||||
|
|
||||||
return c.current
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) getUpgrade() transport.Server {
|
|
||||||
c.transportLocker.RLock()
|
|
||||||
defer c.transportLocker.RUnlock()
|
|
||||||
|
|
||||||
return c.upgrading
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) setCurrent(name string, s transport.Server) {
|
|
||||||
c.transportLocker.Lock()
|
|
||||||
defer c.transportLocker.Unlock()
|
|
||||||
|
|
||||||
c.currentName = name
|
|
||||||
c.current = s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) setUpgrading(name string, s transport.Server) {
|
|
||||||
c.transportLocker.Lock()
|
|
||||||
defer c.transportLocker.Unlock()
|
|
||||||
|
|
||||||
c.upgradingName = name
|
|
||||||
c.upgrading = s
|
|
||||||
c.setState(stateUpgrading)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) upgraded() {
|
|
||||||
c.transportLocker.Lock()
|
|
||||||
|
|
||||||
current := c.current
|
|
||||||
c.current = c.upgrading
|
|
||||||
c.currentName = c.upgradingName
|
|
||||||
c.upgrading = nil
|
|
||||||
c.upgradingName = ""
|
|
||||||
|
|
||||||
c.transportLocker.Unlock()
|
|
||||||
|
|
||||||
current.Close()
|
|
||||||
c.setState(stateNormal)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) getState() state {
|
|
||||||
c.stateLocker.RLock()
|
|
||||||
defer c.stateLocker.RUnlock()
|
|
||||||
return c.state
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) setState(state state) {
|
|
||||||
c.stateLocker.Lock()
|
|
||||||
defer c.stateLocker.Unlock()
|
|
||||||
c.state = state
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *serverConn) pingLoop() {
|
|
||||||
lastPing := time.Now()
|
|
||||||
lastTry := lastPing
|
|
||||||
for {
|
|
||||||
now := time.Now()
|
|
||||||
pingDiff := now.Sub(lastPing)
|
|
||||||
tryDiff := now.Sub(lastTry)
|
|
||||||
select {
|
|
||||||
case ok := <-c.pingChan:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
lastPing = time.Now()
|
|
||||||
lastTry = lastPing
|
|
||||||
case <-time.After(c.pingInterval - tryDiff):
|
|
||||||
c.writerLocker.Lock()
|
|
||||||
if w, _ := c.getCurrent().NextWriter(message.MessageText, parser.PING); w != nil {
|
|
||||||
writer := newConnWriter(w, &c.writerLocker)
|
|
||||||
writer.Close()
|
|
||||||
} else {
|
|
||||||
c.writerLocker.Unlock()
|
|
||||||
}
|
|
||||||
lastTry = time.Now()
|
|
||||||
case <-time.After(c.pingTimeout - pingDiff):
|
|
||||||
c.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
47
vendor/github.com/googollee/go-engine.io/sessions.go
generated
vendored
47
vendor/github.com/googollee/go-engine.io/sessions.go
generated
vendored
@ -1,47 +0,0 @@
|
|||||||
package engineio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Sessions interface {
|
|
||||||
Get(id string) Conn
|
|
||||||
Set(id string, conn Conn)
|
|
||||||
Remove(id string)
|
|
||||||
}
|
|
||||||
|
|
||||||
type serverSessions struct {
|
|
||||||
sessions map[string]Conn
|
|
||||||
locker sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func newServerSessions() *serverSessions {
|
|
||||||
return &serverSessions{
|
|
||||||
sessions: make(map[string]Conn),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *serverSessions) Get(id string) Conn {
|
|
||||||
s.locker.RLock()
|
|
||||||
defer s.locker.RUnlock()
|
|
||||||
|
|
||||||
ret, ok := s.sessions[id]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *serverSessions) Set(id string, conn Conn) {
|
|
||||||
s.locker.Lock()
|
|
||||||
defer s.locker.Unlock()
|
|
||||||
|
|
||||||
s.sessions[id] = conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *serverSessions) Remove(id string) {
|
|
||||||
s.locker.Lock()
|
|
||||||
defer s.locker.Unlock()
|
|
||||||
|
|
||||||
delete(s.sessions, id)
|
|
||||||
}
|
|
50
vendor/github.com/googollee/go-engine.io/transport/transport.go
generated
vendored
50
vendor/github.com/googollee/go-engine.io/transport/transport.go
generated
vendored
@ -1,50 +0,0 @@
|
|||||||
package transport
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/googollee/go-engine.io/message"
|
|
||||||
"github.com/googollee/go-engine.io/parser"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Callback interface {
|
|
||||||
OnPacket(r *parser.PacketDecoder)
|
|
||||||
OnClose(server Server)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Creater struct {
|
|
||||||
Name string
|
|
||||||
Upgrading bool
|
|
||||||
Server func(w http.ResponseWriter, r *http.Request, callback Callback) (Server, error)
|
|
||||||
Client func(r *http.Request) (Client, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server is a transport layer in server to connect client.
|
|
||||||
type Server interface {
|
|
||||||
|
|
||||||
// ServeHTTP handles the http request. It will call conn.onPacket when receive packet.
|
|
||||||
ServeHTTP(http.ResponseWriter, *http.Request)
|
|
||||||
|
|
||||||
// Close closes the transport.
|
|
||||||
Close() error
|
|
||||||
|
|
||||||
// NextWriter returns packet writer. This function call should be synced.
|
|
||||||
NextWriter(messageType message.MessageType, packetType parser.PacketType) (io.WriteCloser, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client is a transport layer in client to connect server.
|
|
||||||
type Client interface {
|
|
||||||
|
|
||||||
// Response returns the response of last http request.
|
|
||||||
Response() *http.Response
|
|
||||||
|
|
||||||
// NextReader returns packet decoder. This function call should be synced.
|
|
||||||
NextReader() (*parser.PacketDecoder, error)
|
|
||||||
|
|
||||||
// NextWriter returns packet writer. This function call should be synced.
|
|
||||||
NextWriter(messageType message.MessageType, packetType parser.PacketType) (io.WriteCloser, error)
|
|
||||||
|
|
||||||
// Close closes the transport.
|
|
||||||
Close() error
|
|
||||||
}
|
|
72
vendor/github.com/googollee/go-engine.io/websocket/client.go
generated
vendored
72
vendor/github.com/googollee/go-engine.io/websocket/client.go
generated
vendored
@ -1,72 +0,0 @@
|
|||||||
package websocket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/googollee/go-engine.io/message"
|
|
||||||
"github.com/googollee/go-engine.io/parser"
|
|
||||||
"github.com/googollee/go-engine.io/transport"
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
)
|
|
||||||
|
|
||||||
type client struct {
|
|
||||||
conn *websocket.Conn
|
|
||||||
resp *http.Response
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClient(r *http.Request) (transport.Client, error) {
|
|
||||||
dialer := websocket.DefaultDialer
|
|
||||||
|
|
||||||
conn, resp, err := dialer.Dial(r.URL.String(), r.Header)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &client{
|
|
||||||
conn: conn,
|
|
||||||
resp: resp,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) Response() *http.Response {
|
|
||||||
return c.resp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) NextReader() (*parser.PacketDecoder, error) {
|
|
||||||
var reader io.Reader
|
|
||||||
for {
|
|
||||||
t, r, err := c.conn.NextReader()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch t {
|
|
||||||
case websocket.TextMessage:
|
|
||||||
fallthrough
|
|
||||||
case websocket.BinaryMessage:
|
|
||||||
reader = r
|
|
||||||
return parser.NewDecoder(reader)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) NextWriter(msgType message.MessageType, packetType parser.PacketType) (io.WriteCloser, error) {
|
|
||||||
wsType, newEncoder := websocket.TextMessage, parser.NewStringEncoder
|
|
||||||
if msgType == message.MessageBinary {
|
|
||||||
wsType, newEncoder = websocket.BinaryMessage, parser.NewBinaryEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
w, err := c.conn.NextWriter(wsType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret, err := newEncoder(w, packetType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) Close() error {
|
|
||||||
return c.conn.Close()
|
|
||||||
}
|
|
81
vendor/github.com/googollee/go-engine.io/websocket/server.go
generated
vendored
81
vendor/github.com/googollee/go-engine.io/websocket/server.go
generated
vendored
@ -1,81 +0,0 @@
|
|||||||
package websocket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/googollee/go-engine.io/message"
|
|
||||||
"github.com/googollee/go-engine.io/parser"
|
|
||||||
"github.com/googollee/go-engine.io/transport"
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Server struct {
|
|
||||||
callback transport.Callback
|
|
||||||
conn *websocket.Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewServer(w http.ResponseWriter, r *http.Request, callback transport.Callback) (transport.Server, error) {
|
|
||||||
conn, err := websocket.Upgrade(w, r, nil, 10240, 10240)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := &Server{
|
|
||||||
callback: callback,
|
|
||||||
conn: conn,
|
|
||||||
}
|
|
||||||
|
|
||||||
go ret.serveHTTP(w, r)
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) NextWriter(msgType message.MessageType, packetType parser.PacketType) (io.WriteCloser, error) {
|
|
||||||
wsType, newEncoder := websocket.TextMessage, parser.NewStringEncoder
|
|
||||||
if msgType == message.MessageBinary {
|
|
||||||
wsType, newEncoder = websocket.BinaryMessage, parser.NewBinaryEncoder
|
|
||||||
}
|
|
||||||
|
|
||||||
w, err := s.conn.NextWriter(wsType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret, err := newEncoder(w, packetType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) Close() error {
|
|
||||||
return s.conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
defer s.callback.OnClose(s)
|
|
||||||
|
|
||||||
for {
|
|
||||||
t, r, err := s.conn.NextReader()
|
|
||||||
if err != nil {
|
|
||||||
s.conn.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t {
|
|
||||||
case websocket.TextMessage:
|
|
||||||
fallthrough
|
|
||||||
case websocket.BinaryMessage:
|
|
||||||
decoder, err := parser.NewDecoder(r)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.callback.OnPacket(decoder)
|
|
||||||
decoder.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
12
vendor/github.com/googollee/go-engine.io/websocket/websocket.go
generated
vendored
12
vendor/github.com/googollee/go-engine.io/websocket/websocket.go
generated
vendored
@ -1,12 +0,0 @@
|
|||||||
package websocket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/googollee/go-engine.io/transport"
|
|
||||||
)
|
|
||||||
|
|
||||||
var Creater = transport.Creater{
|
|
||||||
Name: "websocket",
|
|
||||||
Upgrading: true,
|
|
||||||
Server: NewServer,
|
|
||||||
Client: NewClient,
|
|
||||||
}
|
|
5
vendor/github.com/googollee/go-socket.io/.travis.yml
generated
vendored
5
vendor/github.com/googollee/go-socket.io/.travis.yml
generated
vendored
@ -1,5 +0,0 @@
|
|||||||
language: go
|
|
||||||
go: 1.5
|
|
||||||
install:
|
|
||||||
- go get "github.com/smartystreets/goconvey/convey"
|
|
||||||
- go get -v .
|
|
23
vendor/github.com/googollee/go-socket.io/LICENSE
generated
vendored
23
vendor/github.com/googollee/go-socket.io/LICENSE
generated
vendored
@ -1,23 +0,0 @@
|
|||||||
Copyright (c) 2014-2014 Googol Lee <i@googol.im>
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
1. Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
3. The name of the author may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
||||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
||||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
||||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
||||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
127
vendor/github.com/googollee/go-socket.io/README.md
generated
vendored
127
vendor/github.com/googollee/go-socket.io/README.md
generated
vendored
@ -1,127 +0,0 @@
|
|||||||
# socket.io
|
|
||||||
|
|
||||||
[![GoDoc](http://godoc.org/github.com/googollee/go-socket.io?status.svg)](http://godoc.org/github.com/googollee/go-socket.io) [![Build Status](https://travis-ci.org/googollee/go-socket.io.svg)](https://travis-ci.org/googollee/go-socket.io)
|
|
||||||
|
|
||||||
**Please use v1.4 branch, or import "gopkg.in/googollee/go-socket.io.v1". I have no time to maintain master branch now**
|
|
||||||
|
|
||||||
go-socket.io is an implementation of [socket.io](http://socket.io) in golang, which is a realtime application framework.
|
|
||||||
|
|
||||||
It is compatible with latest implementation of socket.io in node.js, and supports room and namespace.
|
|
||||||
|
|
||||||
* for compatability with socket.io 0.9.x, please use branch 0.9.x *
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
Install the package with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/googollee/go-socket.io
|
|
||||||
```
|
|
||||||
|
|
||||||
Import it with:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/googollee/go-socket.io"
|
|
||||||
```
|
|
||||||
|
|
||||||
and use `socketio` as the package name inside the code.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
Please check the example folder for details.
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/googollee/go-socket.io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
server, err := socketio.NewServer(nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
server.On("connection", func(so socketio.Socket) {
|
|
||||||
log.Println("on connection")
|
|
||||||
so.Join("chat")
|
|
||||||
so.On("chat message", func(msg string) {
|
|
||||||
log.Println("emit:", so.Emit("chat message", msg))
|
|
||||||
so.BroadcastTo("chat", "chat message", msg)
|
|
||||||
})
|
|
||||||
so.On("disconnection", func() {
|
|
||||||
log.Println("on disconnect")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
server.On("error", func(so socketio.Socket, err error) {
|
|
||||||
log.Println("error:", err)
|
|
||||||
})
|
|
||||||
|
|
||||||
http.Handle("/socket.io/", server)
|
|
||||||
http.Handle("/", http.FileServer(http.Dir("./asset")))
|
|
||||||
log.Println("Serving at localhost:5000...")
|
|
||||||
log.Fatal(http.ListenAndServe(":5000", nil))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Acknowledgements in go-socket.io 1.X.X
|
|
||||||
|
|
||||||
[See documentation about acknowledgements](http://socket.io/docs/#sending-and-getting-data-(acknowledgements))
|
|
||||||
|
|
||||||
##### Sending ACK with data from SERVER to CLIENT
|
|
||||||
|
|
||||||
* Client-side
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
//using client-side socket.io-1.X.X.js
|
|
||||||
socket.emit('some:event', JSON.stringify(someData), function(data){
|
|
||||||
console.log('ACK from server wtih data: ', data));
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
* Server-side
|
|
||||||
|
|
||||||
```go
|
|
||||||
// The return type may vary depending on whether you will return
|
|
||||||
// In golang implementation of socket.io don't used callbacks for acknowledgement,
|
|
||||||
// but used return value, which wrapped into ack package and returned to the client's callback in JavaScript
|
|
||||||
so.On("some:event", func(msg string) string {
|
|
||||||
return msg //Sending ack with data in msg back to client, using "return statement"
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Sending ACK with data from CLIENT to SERVER
|
|
||||||
|
|
||||||
* Client-side
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
//using client-side socket.io-1.X.X.js
|
|
||||||
//last parameter of "on" handler is callback for sending ack to server with data or without data
|
|
||||||
socket.on('some:event', function (msg, sendAckCb) {
|
|
||||||
//Sending ACK with data to server after receiving some:event from server
|
|
||||||
sendAckCb(JSON.stringify(data)); // for example used serializing to JSON
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* Server-side
|
|
||||||
|
|
||||||
```go
|
|
||||||
//You can use Emit or BroadcastTo with last parameter as callback for handling ack from client
|
|
||||||
//Sending packet to room "room_name" and event "some:event"
|
|
||||||
so.BroadcastTo("room_name", "some:event", dataForClient, func (so socketio.Socket, data string) {
|
|
||||||
log.Println("Client ACK with data: ", data)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Or
|
|
||||||
|
|
||||||
so.Emit("some:event", dataForClient, func (so socketio.Socket, data string) {
|
|
||||||
log.Println("Client ACK with data: ", data)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The 3-clause BSD License - see LICENSE for more details
|
|
70
vendor/github.com/googollee/go-socket.io/adapter.go
generated
vendored
70
vendor/github.com/googollee/go-socket.io/adapter.go
generated
vendored
@ -1,70 +0,0 @@
|
|||||||
package socketio
|
|
||||||
|
|
||||||
import "sync"
|
|
||||||
|
|
||||||
// BroadcastAdaptor is the adaptor to handle broadcasts.
|
|
||||||
type BroadcastAdaptor interface {
|
|
||||||
|
|
||||||
// Join causes the socket to join a room.
|
|
||||||
Join(room string, socket Socket) error
|
|
||||||
|
|
||||||
// Leave causes the socket to leave a room.
|
|
||||||
Leave(room string, socket Socket) error
|
|
||||||
|
|
||||||
// Send will send an event with args to the room. If "ignore" is not nil, the event will be excluded from being sent to "ignore".
|
|
||||||
Send(ignore Socket, room, event string, args ...interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
var newBroadcast = newBroadcastDefault
|
|
||||||
|
|
||||||
type broadcast struct {
|
|
||||||
m map[string]map[string]Socket
|
|
||||||
sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBroadcastDefault() BroadcastAdaptor {
|
|
||||||
return &broadcast{
|
|
||||||
m: make(map[string]map[string]Socket),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *broadcast) Join(room string, socket Socket) error {
|
|
||||||
b.Lock()
|
|
||||||
sockets, ok := b.m[room]
|
|
||||||
if !ok {
|
|
||||||
sockets = make(map[string]Socket)
|
|
||||||
}
|
|
||||||
sockets[socket.Id()] = socket
|
|
||||||
b.m[room] = sockets
|
|
||||||
b.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *broadcast) Leave(room string, socket Socket) error {
|
|
||||||
b.Lock()
|
|
||||||
defer b.Unlock()
|
|
||||||
sockets, ok := b.m[room]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
delete(sockets, socket.Id())
|
|
||||||
if len(sockets) == 0 {
|
|
||||||
delete(b.m, room)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
b.m[room] = sockets
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *broadcast) Send(ignore Socket, room, event string, args ...interface{}) error {
|
|
||||||
b.RLock()
|
|
||||||
sockets := b.m[room]
|
|
||||||
for id, s := range sockets {
|
|
||||||
if ignore != nil && ignore.Id() == id {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
s.Emit(event, args...)
|
|
||||||
}
|
|
||||||
b.RUnlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
168
vendor/github.com/googollee/go-socket.io/attachment.go
generated
vendored
168
vendor/github.com/googollee/go-socket.io/attachment.go
generated
vendored
@ -1,168 +0,0 @@
|
|||||||
package socketio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Attachment is an attachment handler used in emit args. All attachments will be sent as binary data in the transport layer. When using an attachment, make sure it is a pointer.
|
|
||||||
//
|
|
||||||
// For example:
|
|
||||||
//
|
|
||||||
// type Arg struct {
|
|
||||||
// Title string `json:"title"`
|
|
||||||
// File *Attachment `json:"file"`
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// f, _ := os.Open("./some_file")
|
|
||||||
// arg := Arg{
|
|
||||||
// Title: "some_file",
|
|
||||||
// File: &Attachment{
|
|
||||||
// Data: f,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// socket.Emit("send file", arg)
|
|
||||||
// socket.On("get file", func(so Socket, arg Arg) {
|
|
||||||
// b, _ := ioutil.ReadAll(arg.File.Data)
|
|
||||||
// })
|
|
||||||
type Attachment struct {
|
|
||||||
Data io.ReadWriter
|
|
||||||
num int
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeAttachments(v interface{}) []io.Reader {
|
|
||||||
index := 0
|
|
||||||
return encodeAttachmentValue(reflect.ValueOf(v), &index)
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeAttachmentValue(v reflect.Value, index *int) []io.Reader {
|
|
||||||
v = reflect.Indirect(v)
|
|
||||||
ret := []io.Reader{}
|
|
||||||
if !v.IsValid() {
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
if v.Type().Name() == "Attachment" {
|
|
||||||
a, ok := v.Addr().Interface().(*Attachment)
|
|
||||||
if !ok {
|
|
||||||
panic("can't convert")
|
|
||||||
}
|
|
||||||
a.num = *index
|
|
||||||
ret = append(ret, a.Data)
|
|
||||||
(*index)++
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
for i, n := 0, v.NumField(); i < n; i++ {
|
|
||||||
var r []io.Reader
|
|
||||||
r = encodeAttachmentValue(v.Field(i), index)
|
|
||||||
ret = append(ret, r...)
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
if v.IsNil() {
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
for _, key := range v.MapKeys() {
|
|
||||||
var r []io.Reader
|
|
||||||
r = encodeAttachmentValue(v.MapIndex(key), index)
|
|
||||||
ret = append(ret, r...)
|
|
||||||
}
|
|
||||||
case reflect.Slice:
|
|
||||||
if v.IsNil() {
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case reflect.Array:
|
|
||||||
for i, n := 0, v.Len(); i < n; i++ {
|
|
||||||
var r []io.Reader
|
|
||||||
r = encodeAttachmentValue(v.Index(i), index)
|
|
||||||
ret = append(ret, r...)
|
|
||||||
}
|
|
||||||
case reflect.Interface:
|
|
||||||
ret = encodeAttachmentValue(reflect.ValueOf(v.Interface()), index)
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeAttachments(v interface{}, binary [][]byte) error {
|
|
||||||
return decodeAttachmentValue(reflect.ValueOf(v), binary)
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeAttachmentValue(v reflect.Value, binary [][]byte) error {
|
|
||||||
v = reflect.Indirect(v)
|
|
||||||
if !v.IsValid() {
|
|
||||||
return fmt.Errorf("invalid value")
|
|
||||||
}
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
if v.Type().Name() == "Attachment" {
|
|
||||||
a, ok := v.Addr().Interface().(*Attachment)
|
|
||||||
if !ok {
|
|
||||||
panic("can't convert")
|
|
||||||
}
|
|
||||||
if a.num >= len(binary) || a.num < 0 {
|
|
||||||
return fmt.Errorf("out of range")
|
|
||||||
}
|
|
||||||
if a.Data == nil {
|
|
||||||
a.Data = bytes.NewBuffer(nil)
|
|
||||||
}
|
|
||||||
for b := binary[a.num]; len(b) > 0; {
|
|
||||||
n, err := a.Data.Write(b)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
b = b[n:]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for i, n := 0, v.NumField(); i < n; i++ {
|
|
||||||
if err := decodeAttachmentValue(v.Field(i), binary); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
if v.IsNil() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for _, key := range v.MapKeys() {
|
|
||||||
if err := decodeAttachmentValue(v.MapIndex(key), binary); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Slice:
|
|
||||||
if v.IsNil() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case reflect.Array:
|
|
||||||
for i, n := 0, v.Len(); i < n; i++ {
|
|
||||||
if err := decodeAttachmentValue(v.Index(i), binary); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Interface:
|
|
||||||
if err := decodeAttachmentValue(reflect.ValueOf(v.Interface()), binary); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Attachment) MarshalJSON() ([]byte, error) {
|
|
||||||
return []byte(fmt.Sprintf("{\"_placeholder\":true,\"num\":%d}", a.num)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Attachment) UnmarshalJSON(b []byte) error {
|
|
||||||
var v struct {
|
|
||||||
Num int `json:"num"`
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(b, &v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.num = v.Num
|
|
||||||
return nil
|
|
||||||
}
|
|
82
vendor/github.com/googollee/go-socket.io/caller.go
generated
vendored
82
vendor/github.com/googollee/go-socket.io/caller.go
generated
vendored
@ -1,82 +0,0 @@
|
|||||||
package socketio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
type caller struct {
|
|
||||||
Func reflect.Value
|
|
||||||
Args []reflect.Type
|
|
||||||
NeedSocket bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCaller(f interface{}) (*caller, error) {
|
|
||||||
fv := reflect.ValueOf(f)
|
|
||||||
if fv.Kind() != reflect.Func {
|
|
||||||
return nil, fmt.Errorf("f is not func")
|
|
||||||
}
|
|
||||||
ft := fv.Type()
|
|
||||||
if ft.NumIn() == 0 {
|
|
||||||
return &caller{
|
|
||||||
Func: fv,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
args := make([]reflect.Type, ft.NumIn())
|
|
||||||
for i, n := 0, ft.NumIn(); i < n; i++ {
|
|
||||||
args[i] = ft.In(i)
|
|
||||||
}
|
|
||||||
needSocket := false
|
|
||||||
if args[0].Name() == "Socket" {
|
|
||||||
args = args[1:]
|
|
||||||
needSocket = true
|
|
||||||
}
|
|
||||||
return &caller{
|
|
||||||
Func: fv,
|
|
||||||
Args: args,
|
|
||||||
NeedSocket: needSocket,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *caller) GetArgs() []interface{} {
|
|
||||||
ret := make([]interface{}, len(c.Args))
|
|
||||||
for i, argT := range c.Args {
|
|
||||||
if argT.Kind() == reflect.Ptr {
|
|
||||||
argT = argT.Elem()
|
|
||||||
}
|
|
||||||
v := reflect.New(argT)
|
|
||||||
ret[i] = v.Interface()
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *caller) Call(so Socket, args []interface{}) []reflect.Value {
|
|
||||||
var a []reflect.Value
|
|
||||||
diff := 0
|
|
||||||
if c.NeedSocket {
|
|
||||||
diff = 1
|
|
||||||
a = make([]reflect.Value, len(args)+1)
|
|
||||||
a[0] = reflect.ValueOf(so)
|
|
||||||
} else {
|
|
||||||
a = make([]reflect.Value, len(args))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) != len(c.Args) {
|
|
||||||
return []reflect.Value{reflect.ValueOf([]interface{}{}), reflect.ValueOf(errors.New("Arguments do not match"))}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, arg := range args {
|
|
||||||
v := reflect.ValueOf(arg)
|
|
||||||
if c.Args[i].Kind() != reflect.Ptr {
|
|
||||||
if v.IsValid() {
|
|
||||||
v = v.Elem()
|
|
||||||
} else {
|
|
||||||
v = reflect.Zero(c.Args[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
a[i+diff] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Func.Call(a)
|
|
||||||
}
|
|
213
vendor/github.com/googollee/go-socket.io/handler.go
generated
vendored
213
vendor/github.com/googollee/go-socket.io/handler.go
generated
vendored
@ -1,213 +0,0 @@
|
|||||||
package socketio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type baseHandler struct {
|
|
||||||
events map[string]*caller
|
|
||||||
name string
|
|
||||||
broadcast BroadcastAdaptor
|
|
||||||
evMu sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBaseHandler(name string, broadcast BroadcastAdaptor) *baseHandler {
|
|
||||||
return &baseHandler{
|
|
||||||
events: make(map[string]*caller),
|
|
||||||
name: name,
|
|
||||||
broadcast: broadcast,
|
|
||||||
evMu: sync.Mutex{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// On registers the function f to handle an event.
|
|
||||||
func (h *baseHandler) On(event string, f interface{}) error {
|
|
||||||
c, err := newCaller(f)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
h.evMu.Lock()
|
|
||||||
h.events[event] = c
|
|
||||||
h.evMu.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type socketHandler struct {
|
|
||||||
*baseHandler
|
|
||||||
acksmu sync.Mutex
|
|
||||||
acks map[int]*caller
|
|
||||||
socket *socket
|
|
||||||
rooms map[string]struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSocketHandler(s *socket, base *baseHandler) *socketHandler {
|
|
||||||
events := make(map[string]*caller)
|
|
||||||
base.evMu.Lock()
|
|
||||||
for k, v := range base.events {
|
|
||||||
events[k] = v
|
|
||||||
}
|
|
||||||
base.evMu.Unlock()
|
|
||||||
return &socketHandler{
|
|
||||||
baseHandler: &baseHandler{
|
|
||||||
events: events,
|
|
||||||
broadcast: base.broadcast,
|
|
||||||
evMu: base.evMu,
|
|
||||||
},
|
|
||||||
acks: make(map[int]*caller),
|
|
||||||
socket: s,
|
|
||||||
rooms: make(map[string]struct{}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *socketHandler) Emit(event string, args ...interface{}) error {
|
|
||||||
var c *caller
|
|
||||||
if l := len(args); l > 0 {
|
|
||||||
fv := reflect.ValueOf(args[l-1])
|
|
||||||
if fv.Kind() == reflect.Func {
|
|
||||||
var err error
|
|
||||||
c, err = newCaller(args[l-1])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
args = args[:l-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
args = append([]interface{}{event}, args...)
|
|
||||||
if c != nil {
|
|
||||||
id, err := h.socket.sendId(args)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
h.acksmu.Lock()
|
|
||||||
h.acks[id] = c
|
|
||||||
h.acksmu.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return h.socket.send(args)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *socketHandler) Rooms() []string {
|
|
||||||
ret := make([]string, len(h.rooms))
|
|
||||||
i := 0
|
|
||||||
for room := range h.rooms {
|
|
||||||
ret[i] = room
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *socketHandler) Join(room string) error {
|
|
||||||
if err := h.baseHandler.broadcast.Join(h.broadcastName(room), h.socket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
h.rooms[room] = struct{}{}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *socketHandler) Leave(room string) error {
|
|
||||||
if err := h.baseHandler.broadcast.Leave(h.broadcastName(room), h.socket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
delete(h.rooms, room)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *socketHandler) LeaveAll() error {
|
|
||||||
for room := range h.rooms {
|
|
||||||
if err := h.baseHandler.broadcast.Leave(h.broadcastName(room), h.socket); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *baseHandler) BroadcastTo(room, event string, args ...interface{}) error {
|
|
||||||
return h.broadcast.Send(nil, h.broadcastName(room), event, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *socketHandler) BroadcastTo(room, event string, args ...interface{}) error {
|
|
||||||
return h.baseHandler.broadcast.Send(h.socket, h.broadcastName(room), event, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *baseHandler) broadcastName(room string) string {
|
|
||||||
return fmt.Sprintf("%s:%s", h.name, room)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *socketHandler) onPacket(decoder *decoder, packet *packet) ([]interface{}, error) {
|
|
||||||
var message string
|
|
||||||
switch packet.Type {
|
|
||||||
case _CONNECT:
|
|
||||||
message = "connection"
|
|
||||||
case _DISCONNECT:
|
|
||||||
message = "disconnection"
|
|
||||||
case _ERROR:
|
|
||||||
message = "error"
|
|
||||||
case _ACK:
|
|
||||||
fallthrough
|
|
||||||
case _BINARY_ACK:
|
|
||||||
return nil, h.onAck(packet.Id, decoder, packet)
|
|
||||||
default:
|
|
||||||
if decoder != nil {
|
|
||||||
message = decoder.Message()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h.evMu.Lock()
|
|
||||||
c, ok := h.events[message]
|
|
||||||
h.evMu.Unlock()
|
|
||||||
if !ok {
|
|
||||||
// If the message is not recognized by the server, the decoder.currentCloser
|
|
||||||
// needs to be closed otherwise the server will be stuck until the e
|
|
||||||
if decoder != nil {
|
|
||||||
decoder.Close()
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
args := c.GetArgs()
|
|
||||||
olen := len(args)
|
|
||||||
if olen > 0 && decoder != nil {
|
|
||||||
packet.Data = &args
|
|
||||||
if err := decoder.DecodeData(packet); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := len(args); i < olen; i++ {
|
|
||||||
args = append(args, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
retV := c.Call(h.socket, args)
|
|
||||||
if len(retV) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
if last, ok := retV[len(retV)-1].Interface().(error); ok {
|
|
||||||
err = last
|
|
||||||
retV = retV[0 : len(retV)-1]
|
|
||||||
}
|
|
||||||
ret := make([]interface{}, len(retV))
|
|
||||||
for i, v := range retV {
|
|
||||||
ret[i] = v.Interface()
|
|
||||||
}
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *socketHandler) onAck(id int, decoder *decoder, packet *packet) error {
|
|
||||||
h.acksmu.Lock()
|
|
||||||
c, ok := h.acks[id]
|
|
||||||
if !ok {
|
|
||||||
h.acksmu.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
delete(h.acks, id)
|
|
||||||
h.acksmu.Unlock()
|
|
||||||
|
|
||||||
args := c.GetArgs()
|
|
||||||
packet.Data = &args
|
|
||||||
if err := decoder.DecodeData(packet); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.Call(h.socket, args)
|
|
||||||
return nil
|
|
||||||
}
|
|
34
vendor/github.com/googollee/go-socket.io/ioutil.go
generated
vendored
34
vendor/github.com/googollee/go-socket.io/ioutil.go
generated
vendored
@ -1,34 +0,0 @@
|
|||||||
package socketio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
type writerHelper struct {
|
|
||||||
writer io.Writer
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWriterHelper(w io.Writer) *writerHelper {
|
|
||||||
return &writerHelper{
|
|
||||||
writer: w,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *writerHelper) Write(p []byte) {
|
|
||||||
if h.err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for len(p) > 0 {
|
|
||||||
n, err := h.writer.Write(p)
|
|
||||||
if err != nil {
|
|
||||||
h.err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p = p[n:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *writerHelper) Error() error {
|
|
||||||
return h.err
|
|
||||||
}
|
|
6
vendor/github.com/googollee/go-socket.io/main.go
generated
vendored
6
vendor/github.com/googollee/go-socket.io/main.go
generated
vendored
@ -1,6 +0,0 @@
|
|||||||
/*
|
|
||||||
go-socket.io is a server implementation of socket.io in golang.
|
|
||||||
|
|
||||||
It is compatible with the official Node.js implementation.
|
|
||||||
*/
|
|
||||||
package socketio
|
|
60
vendor/github.com/googollee/go-socket.io/message_reader.go
generated
vendored
60
vendor/github.com/googollee/go-socket.io/message_reader.go
generated
vendored
@ -1,60 +0,0 @@
|
|||||||
package socketio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
)
|
|
||||||
|
|
||||||
type messageReader struct {
|
|
||||||
reader *bufio.Reader
|
|
||||||
message string
|
|
||||||
firstRead bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMessageReader(bufr *bufio.Reader) (*messageReader, error) {
|
|
||||||
if _, err := bufr.ReadBytes('"'); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
msg, err := bufr.ReadBytes('"')
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
b, err := bufr.Peek(1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if b[0] == ',' {
|
|
||||||
bufr.ReadByte()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if b[0] != ' ' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
bufr.ReadByte()
|
|
||||||
}
|
|
||||||
return &messageReader{
|
|
||||||
reader: bufr,
|
|
||||||
message: string(msg[:len(msg)-1]),
|
|
||||||
firstRead: true,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *messageReader) Message() string {
|
|
||||||
return r.message
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *messageReader) Read(b []byte) (int, error) {
|
|
||||||
if len(b) == 0 {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
if r.firstRead {
|
|
||||||
r.firstRead = false
|
|
||||||
b[0] = '['
|
|
||||||
n, err := r.reader.Read(b[1:])
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
return n + 1, err
|
|
||||||
}
|
|
||||||
return r.reader.Read(b)
|
|
||||||
}
|
|
47
vendor/github.com/googollee/go-socket.io/namespace.go
generated
vendored
47
vendor/github.com/googollee/go-socket.io/namespace.go
generated
vendored
@ -1,47 +0,0 @@
|
|||||||
package socketio
|
|
||||||
|
|
||||||
// Namespace is the name space of a socket.io handler.
|
|
||||||
type Namespace interface {
|
|
||||||
|
|
||||||
// Name returns the name of the namespace.
|
|
||||||
Name() string
|
|
||||||
|
|
||||||
// Of returns the namespace with given name.
|
|
||||||
Of(name string) Namespace
|
|
||||||
|
|
||||||
// On registers the function f to handle an event.
|
|
||||||
On(event string, f interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type namespace struct {
|
|
||||||
*baseHandler
|
|
||||||
root map[string]Namespace
|
|
||||||
}
|
|
||||||
|
|
||||||
func newNamespace(broadcast BroadcastAdaptor) *namespace {
|
|
||||||
ret := &namespace{
|
|
||||||
baseHandler: newBaseHandler("", broadcast),
|
|
||||||
root: make(map[string]Namespace),
|
|
||||||
}
|
|
||||||
ret.root[ret.Name()] = ret
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *namespace) Name() string {
|
|
||||||
return n.baseHandler.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *namespace) Of(name string) Namespace {
|
|
||||||
if name == "/" {
|
|
||||||
name = ""
|
|
||||||
}
|
|
||||||
if ret, ok := n.root[name]; ok {
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
ret := &namespace{
|
|
||||||
baseHandler: newBaseHandler(name, n.baseHandler.broadcast),
|
|
||||||
root: n.root,
|
|
||||||
}
|
|
||||||
n.root[name] = ret
|
|
||||||
return ret
|
|
||||||
}
|
|
338
vendor/github.com/googollee/go-socket.io/parser.go
generated
vendored
338
vendor/github.com/googollee/go-socket.io/parser.go
generated
vendored
@ -1,338 +0,0 @@
|
|||||||
package socketio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/googollee/go-engine.io"
|
|
||||||
)
|
|
||||||
|
|
||||||
const Protocol = 4
|
|
||||||
|
|
||||||
type packetType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
_CONNECT packetType = iota
|
|
||||||
_DISCONNECT
|
|
||||||
_EVENT
|
|
||||||
_ACK
|
|
||||||
_ERROR
|
|
||||||
_BINARY_EVENT
|
|
||||||
_BINARY_ACK
|
|
||||||
)
|
|
||||||
|
|
||||||
func (t packetType) String() string {
|
|
||||||
switch t {
|
|
||||||
case _CONNECT:
|
|
||||||
return "connect"
|
|
||||||
case _DISCONNECT:
|
|
||||||
return "disconnect"
|
|
||||||
case _EVENT:
|
|
||||||
return "event"
|
|
||||||
case _ACK:
|
|
||||||
return "ack"
|
|
||||||
case _ERROR:
|
|
||||||
return "error"
|
|
||||||
case _BINARY_EVENT:
|
|
||||||
return "binary_event"
|
|
||||||
case _BINARY_ACK:
|
|
||||||
return "binary_ack"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("unknown(%d)", t)
|
|
||||||
}
|
|
||||||
|
|
||||||
type frameReader interface {
|
|
||||||
NextReader() (engineio.MessageType, io.ReadCloser, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type frameWriter interface {
|
|
||||||
NextWriter(engineio.MessageType) (io.WriteCloser, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type packet struct {
|
|
||||||
Type packetType
|
|
||||||
NSP string
|
|
||||||
Id int
|
|
||||||
Data interface{}
|
|
||||||
attachNumber int
|
|
||||||
}
|
|
||||||
|
|
||||||
type encoder struct {
|
|
||||||
w frameWriter
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEncoder(w frameWriter) *encoder {
|
|
||||||
return &encoder{
|
|
||||||
w: w,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encoder) Encode(v packet) error {
|
|
||||||
attachments := encodeAttachments(v.Data)
|
|
||||||
v.attachNumber = len(attachments)
|
|
||||||
if v.attachNumber > 0 {
|
|
||||||
v.Type += _BINARY_EVENT - _EVENT
|
|
||||||
}
|
|
||||||
if err := e.encodePacket(v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, a := range attachments {
|
|
||||||
if err := e.writeBinary(a); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encoder) encodePacket(v packet) error {
|
|
||||||
writer, err := e.w.NextWriter(engineio.MessageText)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer writer.Close()
|
|
||||||
|
|
||||||
w := newTrimWriter(writer, "\n")
|
|
||||||
wh := newWriterHelper(w)
|
|
||||||
wh.Write([]byte{byte(v.Type) + '0'})
|
|
||||||
if v.Type == _BINARY_EVENT || v.Type == _BINARY_ACK {
|
|
||||||
wh.Write([]byte(fmt.Sprintf("%d-", v.attachNumber)))
|
|
||||||
}
|
|
||||||
needEnd := false
|
|
||||||
if v.NSP != "" {
|
|
||||||
wh.Write([]byte(v.NSP))
|
|
||||||
needEnd = true
|
|
||||||
}
|
|
||||||
if v.Id >= 0 {
|
|
||||||
f := "%d"
|
|
||||||
if needEnd {
|
|
||||||
f = ",%d"
|
|
||||||
needEnd = false
|
|
||||||
}
|
|
||||||
wh.Write([]byte(fmt.Sprintf(f, v.Id)))
|
|
||||||
}
|
|
||||||
if v.Data != nil {
|
|
||||||
if needEnd {
|
|
||||||
wh.Write([]byte{','})
|
|
||||||
needEnd = false
|
|
||||||
}
|
|
||||||
if wh.Error() != nil {
|
|
||||||
return wh.Error()
|
|
||||||
}
|
|
||||||
encoder := json.NewEncoder(w)
|
|
||||||
return encoder.Encode(v.Data)
|
|
||||||
}
|
|
||||||
return wh.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encoder) writeBinary(r io.Reader) error {
|
|
||||||
writer, err := e.w.NextWriter(engineio.MessageBinary)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer writer.Close()
|
|
||||||
|
|
||||||
if _, err := io.Copy(writer, r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
type decoder struct {
|
|
||||||
reader frameReader
|
|
||||||
message string
|
|
||||||
current io.Reader
|
|
||||||
currentCloser io.Closer
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDecoder(r frameReader) *decoder {
|
|
||||||
return &decoder{
|
|
||||||
reader: r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) Close() {
|
|
||||||
if d != nil && d.currentCloser != nil {
|
|
||||||
d.currentCloser.Close()
|
|
||||||
d.current = nil
|
|
||||||
d.currentCloser = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) Decode(v *packet) error {
|
|
||||||
ty, r, err := d.reader.NextReader()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if d.current != nil {
|
|
||||||
d.Close()
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if d.current == nil {
|
|
||||||
r.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if ty != engineio.MessageText {
|
|
||||||
return fmt.Errorf("need text package")
|
|
||||||
}
|
|
||||||
reader := bufio.NewReader(r)
|
|
||||||
|
|
||||||
v.Id = -1
|
|
||||||
|
|
||||||
t, err := reader.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.Type = packetType(t - '0')
|
|
||||||
|
|
||||||
if v.Type == _BINARY_EVENT || v.Type == _BINARY_ACK {
|
|
||||||
num, err := reader.ReadBytes('-')
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
numLen := len(num)
|
|
||||||
if numLen == 0 {
|
|
||||||
return fmt.Errorf("invalid packet")
|
|
||||||
}
|
|
||||||
n, err := strconv.ParseInt(string(num[:numLen-1]), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid packet")
|
|
||||||
}
|
|
||||||
v.attachNumber = int(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
next, err := reader.Peek(1)
|
|
||||||
if err == io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(next) == 0 {
|
|
||||||
return fmt.Errorf("invalid packet")
|
|
||||||
}
|
|
||||||
|
|
||||||
if next[0] == '/' {
|
|
||||||
path, err := reader.ReadBytes(',')
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pathLen := len(path)
|
|
||||||
if pathLen == 0 {
|
|
||||||
return fmt.Errorf("invalid packet")
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
path = path[:pathLen-1]
|
|
||||||
}
|
|
||||||
v.NSP = string(path)
|
|
||||||
if err == io.EOF {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
id := bytes.NewBuffer(nil)
|
|
||||||
finish := false
|
|
||||||
for {
|
|
||||||
next, err := reader.Peek(1)
|
|
||||||
if err == io.EOF {
|
|
||||||
finish = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if '0' <= next[0] && next[0] <= '9' {
|
|
||||||
if err := id.WriteByte(next[0]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
reader.ReadByte()
|
|
||||||
}
|
|
||||||
if id.Len() > 0 {
|
|
||||||
id, err := strconv.ParseInt(id.String(), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.Id = int(id)
|
|
||||||
}
|
|
||||||
if finish {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v.Type {
|
|
||||||
case _EVENT:
|
|
||||||
fallthrough
|
|
||||||
case _BINARY_EVENT:
|
|
||||||
msgReader, err := newMessageReader(reader)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.message = msgReader.Message()
|
|
||||||
d.current = msgReader
|
|
||||||
d.currentCloser = r
|
|
||||||
case _ACK:
|
|
||||||
fallthrough
|
|
||||||
case _BINARY_ACK:
|
|
||||||
d.current = reader
|
|
||||||
d.currentCloser = r
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) Message() string {
|
|
||||||
return d.message
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) DecodeData(v *packet) error {
|
|
||||||
if d.current == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
d.Close()
|
|
||||||
}()
|
|
||||||
decoder := json.NewDecoder(d.current)
|
|
||||||
if err := decoder.Decode(v.Data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if v.Type == _BINARY_EVENT || v.Type == _BINARY_ACK {
|
|
||||||
binary, err := d.decodeBinary(v.attachNumber)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := decodeAttachments(v.Data, binary); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.Type -= _BINARY_EVENT - _EVENT
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decoder) decodeBinary(num int) ([][]byte, error) {
|
|
||||||
ret := make([][]byte, num)
|
|
||||||
for i := 0; i < num; i++ {
|
|
||||||
d.currentCloser.Close()
|
|
||||||
t, r, err := d.reader.NextReader()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
d.currentCloser = r
|
|
||||||
if t == engineio.MessageText {
|
|
||||||
return nil, fmt.Errorf("need binary")
|
|
||||||
}
|
|
||||||
b, err := ioutil.ReadAll(r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret[i] = b
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
106
vendor/github.com/googollee/go-socket.io/server.go
generated
vendored
106
vendor/github.com/googollee/go-socket.io/server.go
generated
vendored
@ -1,106 +0,0 @@
|
|||||||
package socketio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/googollee/go-engine.io"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Server is the server of socket.io.
|
|
||||||
type Server struct {
|
|
||||||
*namespace
|
|
||||||
broadcast BroadcastAdaptor
|
|
||||||
eio *engineio.Server
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServer returns the server supported given transports. If transports is nil, the server will use ["polling", "websocket"] as default.
|
|
||||||
func NewServer(transportNames []string) (*Server, error) {
|
|
||||||
eio, err := engineio.NewServer(transportNames)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret := &Server{
|
|
||||||
namespace: newNamespace(newBroadcastDefault()),
|
|
||||||
eio: eio,
|
|
||||||
}
|
|
||||||
go ret.loop()
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPingTimeout sets the timeout of a connection ping. When it times out, the server will close the connection with the client. Default is 60s.
|
|
||||||
func (s *Server) SetPingTimeout(t time.Duration) {
|
|
||||||
s.eio.SetPingTimeout(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPingInterval sets the interval of pings. Default is 25s.
|
|
||||||
func (s *Server) SetPingInterval(t time.Duration) {
|
|
||||||
s.eio.SetPingInterval(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMaxConnection sets the maximum number of connections with clients. Default is 1000.
|
|
||||||
func (s *Server) SetMaxConnection(n int) {
|
|
||||||
s.eio.SetMaxConnection(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMaxConnection returns the current max connection
|
|
||||||
func (s *Server) GetMaxConnection() int {
|
|
||||||
return s.eio.GetMaxConnection()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count returns the current number of connected clients in session
|
|
||||||
func (s *Server) Count() int {
|
|
||||||
return s.eio.Count()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAllowRequest sets the middleware function when a connection is established. If a non-nil value is returned, the connection won't be established. Default will allow all connections.
|
|
||||||
func (s *Server) SetAllowRequest(f func(*http.Request) error) {
|
|
||||||
s.eio.SetAllowRequest(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAllowUpgrades sets whether server allows transport upgrades. Default is true.
|
|
||||||
func (s *Server) SetAllowUpgrades(allow bool) {
|
|
||||||
s.eio.SetAllowUpgrades(allow)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCookie sets the name of the cookie used by engine.io. Default is "io".
|
|
||||||
func (s *Server) SetCookie(prefix string) {
|
|
||||||
s.eio.SetCookie(prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNewId sets the callback func to generate new connection id. By default, id is generated from remote address + current time stamp
|
|
||||||
func (s *Server) SetNewId(f func(*http.Request) string) {
|
|
||||||
s.eio.SetNewId(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSessionsManager sets the sessions as server's session manager. Default sessions is a single process manager. You can customize it as a load balancer.
|
|
||||||
func (s *Server) SetSessionManager(sessions engineio.Sessions) {
|
|
||||||
s.eio.SetSessionManager(sessions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAdaptor sets the adaptor of broadcast. Default is an in-process broadcast implementation.
|
|
||||||
func (s *Server) SetAdaptor(adaptor BroadcastAdaptor) {
|
|
||||||
s.namespace = newNamespace(adaptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeHTTP handles http requests.
|
|
||||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
s.eio.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BroadcastTo is a server level broadcast function.
|
|
||||||
func (s *Server) BroadcastTo(room, message string, args ...interface{}) {
|
|
||||||
s.namespace.BroadcastTo(room, message, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) loop() {
|
|
||||||
for {
|
|
||||||
conn, err := s.eio.Accept()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s := newSocket(conn, s.baseHandler)
|
|
||||||
go func(s *socket) {
|
|
||||||
s.loop()
|
|
||||||
}(s)
|
|
||||||
}
|
|
||||||
}
|
|
174
vendor/github.com/googollee/go-socket.io/socket.go
generated
vendored
174
vendor/github.com/googollee/go-socket.io/socket.go
generated
vendored
@ -1,174 +0,0 @@
|
|||||||
package socketio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/googollee/go-engine.io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Socket is the socket object of socket.io.
|
|
||||||
type Socket interface {
|
|
||||||
|
|
||||||
// Id returns the session id of socket.
|
|
||||||
Id() string
|
|
||||||
|
|
||||||
// Rooms returns the rooms name joined now.
|
|
||||||
Rooms() []string
|
|
||||||
|
|
||||||
// Request returns the first http request when established connection.
|
|
||||||
Request() *http.Request
|
|
||||||
|
|
||||||
// On registers the function f to handle an event.
|
|
||||||
On(event string, f interface{}) error
|
|
||||||
|
|
||||||
// Emit emits an event with given args.
|
|
||||||
Emit(event string, args ...interface{}) error
|
|
||||||
|
|
||||||
// Join joins the room.
|
|
||||||
Join(room string) error
|
|
||||||
|
|
||||||
// Leave leaves the room.
|
|
||||||
Leave(room string) error
|
|
||||||
|
|
||||||
// Disconnect disconnect the socket.
|
|
||||||
Disconnect()
|
|
||||||
|
|
||||||
// BroadcastTo broadcasts an event to the room with given args.
|
|
||||||
BroadcastTo(room, event string, args ...interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type socket struct {
|
|
||||||
*socketHandler
|
|
||||||
conn engineio.Conn
|
|
||||||
namespace string
|
|
||||||
id int
|
|
||||||
mu sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSocket(conn engineio.Conn, base *baseHandler) *socket {
|
|
||||||
ret := &socket{
|
|
||||||
conn: conn,
|
|
||||||
}
|
|
||||||
ret.socketHandler = newSocketHandler(ret, base)
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *socket) Id() string {
|
|
||||||
return s.conn.Id()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *socket) Request() *http.Request {
|
|
||||||
return s.conn.Request()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *socket) Emit(event string, args ...interface{}) error {
|
|
||||||
if err := s.socketHandler.Emit(event, args...); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if event == "disconnect" {
|
|
||||||
s.conn.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *socket) Disconnect() {
|
|
||||||
s.conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *socket) send(args []interface{}) error {
|
|
||||||
packet := packet{
|
|
||||||
Type: _EVENT,
|
|
||||||
Id: -1,
|
|
||||||
NSP: s.namespace,
|
|
||||||
Data: args,
|
|
||||||
}
|
|
||||||
encoder := newEncoder(s.conn)
|
|
||||||
return encoder.Encode(packet)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *socket) sendConnect() error {
|
|
||||||
packet := packet{
|
|
||||||
Type: _CONNECT,
|
|
||||||
Id: -1,
|
|
||||||
NSP: s.namespace,
|
|
||||||
}
|
|
||||||
encoder := newEncoder(s.conn)
|
|
||||||
return encoder.Encode(packet)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *socket) sendId(args []interface{}) (int, error) {
|
|
||||||
s.mu.Lock()
|
|
||||||
packet := packet{
|
|
||||||
Type: _EVENT,
|
|
||||||
Id: s.id,
|
|
||||||
NSP: s.namespace,
|
|
||||||
Data: args,
|
|
||||||
}
|
|
||||||
s.id++
|
|
||||||
if s.id < 0 {
|
|
||||||
s.id = 0
|
|
||||||
}
|
|
||||||
s.mu.Unlock()
|
|
||||||
|
|
||||||
encoder := newEncoder(s.conn)
|
|
||||||
err := encoder.Encode(packet)
|
|
||||||
if err != nil {
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
return packet.Id, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *socket) loop() error {
|
|
||||||
defer func() {
|
|
||||||
s.LeaveAll()
|
|
||||||
p := packet{
|
|
||||||
Type: _DISCONNECT,
|
|
||||||
Id: -1,
|
|
||||||
}
|
|
||||||
s.socketHandler.onPacket(nil, &p)
|
|
||||||
}()
|
|
||||||
|
|
||||||
p := packet{
|
|
||||||
Type: _CONNECT,
|
|
||||||
Id: -1,
|
|
||||||
}
|
|
||||||
encoder := newEncoder(s.conn)
|
|
||||||
if err := encoder.Encode(p); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.socketHandler.onPacket(nil, &p)
|
|
||||||
for {
|
|
||||||
decoder := newDecoder(s.conn)
|
|
||||||
var p packet
|
|
||||||
if err := decoder.Decode(&p); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ret, err := s.socketHandler.onPacket(decoder, &p)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch p.Type {
|
|
||||||
case _CONNECT:
|
|
||||||
s.namespace = p.NSP
|
|
||||||
s.sendConnect()
|
|
||||||
case _BINARY_EVENT:
|
|
||||||
fallthrough
|
|
||||||
case _EVENT:
|
|
||||||
if p.Id >= 0 {
|
|
||||||
p := packet{
|
|
||||||
Type: _ACK,
|
|
||||||
Id: p.Id,
|
|
||||||
NSP: s.namespace,
|
|
||||||
Data: ret,
|
|
||||||
}
|
|
||||||
encoder := newEncoder(s.conn)
|
|
||||||
if err := encoder.Encode(p); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case _DISCONNECT:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
45
vendor/github.com/googollee/go-socket.io/trim_writer.go
generated
vendored
45
vendor/github.com/googollee/go-socket.io/trim_writer.go
generated
vendored
@ -1,45 +0,0 @@
|
|||||||
package socketio
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
type trimWriter struct {
|
|
||||||
trimChars string
|
|
||||||
trimBuf []byte
|
|
||||||
output io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTrimWriter(w io.Writer, trimChars string) *trimWriter {
|
|
||||||
return &trimWriter{
|
|
||||||
trimChars: trimChars,
|
|
||||||
output: w,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *trimWriter) Write(p []byte) (int, error) {
|
|
||||||
out := bytes.TrimRight(p, w.trimChars)
|
|
||||||
buf := p[len(out):]
|
|
||||||
var written int
|
|
||||||
if (len(out) > 0) && (w.trimBuf != nil) {
|
|
||||||
var err error
|
|
||||||
if written, err = w.output.Write(w.trimBuf); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
w.trimBuf = nil
|
|
||||||
}
|
|
||||||
if w.trimBuf != nil {
|
|
||||||
w.trimBuf = append(w.trimBuf, buf...)
|
|
||||||
} else {
|
|
||||||
w.trimBuf = buf
|
|
||||||
}
|
|
||||||
if len(p) == 0 {
|
|
||||||
return written, nil
|
|
||||||
}
|
|
||||||
ret, err := w.output.Write(out)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return written + ret, nil
|
|
||||||
}
|
|
25
vendor/github.com/gorilla/websocket/.gitignore
generated
vendored
25
vendor/github.com/gorilla/websocket/.gitignore
generated
vendored
@ -1,25 +0,0 @@
|
|||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
||||||
|
|
||||||
.idea/
|
|
||||||
*.iml
|
|
19
vendor/github.com/gorilla/websocket/.travis.yml
generated
vendored
19
vendor/github.com/gorilla/websocket/.travis.yml
generated
vendored
@ -1,19 +0,0 @@
|
|||||||
language: go
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- go: 1.4
|
|
||||||
- go: 1.5
|
|
||||||
- go: 1.6
|
|
||||||
- go: 1.7
|
|
||||||
- go: 1.8
|
|
||||||
- go: tip
|
|
||||||
allow_failures:
|
|
||||||
- go: tip
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go get -t -v ./...
|
|
||||||
- diff -u <(echo -n) <(gofmt -d .)
|
|
||||||
- go vet $(go list ./... | grep -v /vendor/)
|
|
||||||
- go test -v -race ./...
|
|
8
vendor/github.com/gorilla/websocket/AUTHORS
generated
vendored
8
vendor/github.com/gorilla/websocket/AUTHORS
generated
vendored
@ -1,8 +0,0 @@
|
|||||||
# This is the official list of Gorilla WebSocket authors for copyright
|
|
||||||
# purposes.
|
|
||||||
#
|
|
||||||
# Please keep the list sorted.
|
|
||||||
|
|
||||||
Gary Burd <gary@beagledreams.com>
|
|
||||||
Joachim Bauch <mail@joachim-bauch.de>
|
|
||||||
|
|
22
vendor/github.com/gorilla/websocket/LICENSE
generated
vendored
22
vendor/github.com/gorilla/websocket/LICENSE
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
64
vendor/github.com/gorilla/websocket/README.md
generated
vendored
64
vendor/github.com/gorilla/websocket/README.md
generated
vendored
@ -1,64 +0,0 @@
|
|||||||
# Gorilla WebSocket
|
|
||||||
|
|
||||||
Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
|
|
||||||
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket)
|
|
||||||
[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket)
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
|
|
||||||
* [API Reference](http://godoc.org/github.com/gorilla/websocket)
|
|
||||||
* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)
|
|
||||||
* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
|
|
||||||
* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
|
|
||||||
* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)
|
|
||||||
|
|
||||||
### Status
|
|
||||||
|
|
||||||
The Gorilla WebSocket package provides a complete and tested implementation of
|
|
||||||
the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The
|
|
||||||
package API is stable.
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
go get github.com/gorilla/websocket
|
|
||||||
|
|
||||||
### Protocol Compliance
|
|
||||||
|
|
||||||
The Gorilla WebSocket package passes the server tests in the [Autobahn Test
|
|
||||||
Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn
|
|
||||||
subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).
|
|
||||||
|
|
||||||
### Gorilla WebSocket compared with other packages
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th></th>
|
|
||||||
<th><a href="http://godoc.org/github.com/gorilla/websocket">github.com/gorilla</a></th>
|
|
||||||
<th><a href="http://godoc.org/golang.org/x/net/websocket">golang.org/x/net</a></th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<tr><td colspan="3"><a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a> Features</td></tr>
|
|
||||||
<tr><td>Passes <a href="http://autobahn.ws/testsuite/">Autobahn Test Suite</a></td><td><a href="https://github.com/gorilla/websocket/tree/master/examples/autobahn">Yes</a></td><td>No</td></tr>
|
|
||||||
<tr><td>Receive <a href="https://tools.ietf.org/html/rfc6455#section-5.4">fragmented</a> message<td>Yes</td><td><a href="https://code.google.com/p/go/issues/detail?id=7632">No</a>, see note 1</td></tr>
|
|
||||||
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">close</a> message</td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td><a href="https://code.google.com/p/go/issues/detail?id=4588">No</a></td></tr>
|
|
||||||
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">pings</a> and receive <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pongs</a></td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td>No</td></tr>
|
|
||||||
<tr><td>Get the <a href="https://tools.ietf.org/html/rfc6455#section-5.6">type</a> of a received data message</td><td>Yes</td><td>Yes, see note 2</td></tr>
|
|
||||||
<tr><td colspan="3">Other Features</tr></td>
|
|
||||||
<tr><td><a href="https://tools.ietf.org/html/rfc7692">Compression Extensions</a></td><td>Experimental</td><td>No</td></tr>
|
|
||||||
<tr><td>Read message using io.Reader</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextReader">Yes</a></td><td>No, see note 3</td></tr>
|
|
||||||
<tr><td>Write message using io.WriteCloser</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextWriter">Yes</a></td><td>No, see note 3</td></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
|
|
||||||
1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html).
|
|
||||||
2. The application can get the type of a received data message by implementing
|
|
||||||
a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal)
|
|
||||||
function.
|
|
||||||
3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries.
|
|
||||||
Read returns when the input buffer is full or a frame boundary is
|
|
||||||
encountered. Each call to Write sends a single frame message. The Gorilla
|
|
||||||
io.Reader and io.WriteCloser operate on a single WebSocket message.
|
|
||||||
|
|
392
vendor/github.com/gorilla/websocket/client.go
generated
vendored
392
vendor/github.com/gorilla/websocket/client.go
generated
vendored
@ -1,392 +0,0 @@
|
|||||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrBadHandshake is returned when the server response to opening handshake is
|
|
||||||
// invalid.
|
|
||||||
var ErrBadHandshake = errors.New("websocket: bad handshake")
|
|
||||||
|
|
||||||
var errInvalidCompression = errors.New("websocket: invalid compression negotiation")
|
|
||||||
|
|
||||||
// NewClient creates a new client connection using the given net connection.
|
|
||||||
// The URL u specifies the host and request URI. Use requestHeader to specify
|
|
||||||
// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
|
|
||||||
// (Cookie). Use the response.Header to get the selected subprotocol
|
|
||||||
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
|
|
||||||
//
|
|
||||||
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
|
|
||||||
// non-nil *http.Response so that callers can handle redirects, authentication,
|
|
||||||
// etc.
|
|
||||||
//
|
|
||||||
// Deprecated: Use Dialer instead.
|
|
||||||
func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) {
|
|
||||||
d := Dialer{
|
|
||||||
ReadBufferSize: readBufSize,
|
|
||||||
WriteBufferSize: writeBufSize,
|
|
||||||
NetDial: func(net, addr string) (net.Conn, error) {
|
|
||||||
return netConn, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return d.Dial(u.String(), requestHeader)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Dialer contains options for connecting to WebSocket server.
|
|
||||||
type Dialer struct {
|
|
||||||
// NetDial specifies the dial function for creating TCP connections. If
|
|
||||||
// NetDial is nil, net.Dial is used.
|
|
||||||
NetDial func(network, addr string) (net.Conn, error)
|
|
||||||
|
|
||||||
// Proxy specifies a function to return a proxy for a given
|
|
||||||
// Request. If the function returns a non-nil error, the
|
|
||||||
// request is aborted with the provided error.
|
|
||||||
// If Proxy is nil or returns a nil *URL, no proxy is used.
|
|
||||||
Proxy func(*http.Request) (*url.URL, error)
|
|
||||||
|
|
||||||
// TLSClientConfig specifies the TLS configuration to use with tls.Client.
|
|
||||||
// If nil, the default configuration is used.
|
|
||||||
TLSClientConfig *tls.Config
|
|
||||||
|
|
||||||
// HandshakeTimeout specifies the duration for the handshake to complete.
|
|
||||||
HandshakeTimeout time.Duration
|
|
||||||
|
|
||||||
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
|
|
||||||
// size is zero, then a useful default size is used. The I/O buffer sizes
|
|
||||||
// do not limit the size of the messages that can be sent or received.
|
|
||||||
ReadBufferSize, WriteBufferSize int
|
|
||||||
|
|
||||||
// Subprotocols specifies the client's requested subprotocols.
|
|
||||||
Subprotocols []string
|
|
||||||
|
|
||||||
// EnableCompression specifies if the client should attempt to negotiate
|
|
||||||
// per message compression (RFC 7692). Setting this value to true does not
|
|
||||||
// guarantee that compression will be supported. Currently only "no context
|
|
||||||
// takeover" modes are supported.
|
|
||||||
EnableCompression bool
|
|
||||||
|
|
||||||
// Jar specifies the cookie jar.
|
|
||||||
// If Jar is nil, cookies are not sent in requests and ignored
|
|
||||||
// in responses.
|
|
||||||
Jar http.CookieJar
|
|
||||||
}
|
|
||||||
|
|
||||||
var errMalformedURL = errors.New("malformed ws or wss URL")
|
|
||||||
|
|
||||||
// parseURL parses the URL.
|
|
||||||
//
|
|
||||||
// This function is a replacement for the standard library url.Parse function.
|
|
||||||
// In Go 1.4 and earlier, url.Parse loses information from the path.
|
|
||||||
func parseURL(s string) (*url.URL, error) {
|
|
||||||
// From the RFC:
|
|
||||||
//
|
|
||||||
// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
|
|
||||||
// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
|
|
||||||
var u url.URL
|
|
||||||
switch {
|
|
||||||
case strings.HasPrefix(s, "ws://"):
|
|
||||||
u.Scheme = "ws"
|
|
||||||
s = s[len("ws://"):]
|
|
||||||
case strings.HasPrefix(s, "wss://"):
|
|
||||||
u.Scheme = "wss"
|
|
||||||
s = s[len("wss://"):]
|
|
||||||
default:
|
|
||||||
return nil, errMalformedURL
|
|
||||||
}
|
|
||||||
|
|
||||||
if i := strings.Index(s, "?"); i >= 0 {
|
|
||||||
u.RawQuery = s[i+1:]
|
|
||||||
s = s[:i]
|
|
||||||
}
|
|
||||||
|
|
||||||
if i := strings.Index(s, "/"); i >= 0 {
|
|
||||||
u.Opaque = s[i:]
|
|
||||||
s = s[:i]
|
|
||||||
} else {
|
|
||||||
u.Opaque = "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
u.Host = s
|
|
||||||
|
|
||||||
if strings.Contains(u.Host, "@") {
|
|
||||||
// Don't bother parsing user information because user information is
|
|
||||||
// not allowed in websocket URIs.
|
|
||||||
return nil, errMalformedURL
|
|
||||||
}
|
|
||||||
|
|
||||||
return &u, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
|
|
||||||
hostPort = u.Host
|
|
||||||
hostNoPort = u.Host
|
|
||||||
if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") {
|
|
||||||
hostNoPort = hostNoPort[:i]
|
|
||||||
} else {
|
|
||||||
switch u.Scheme {
|
|
||||||
case "wss":
|
|
||||||
hostPort += ":443"
|
|
||||||
case "https":
|
|
||||||
hostPort += ":443"
|
|
||||||
default:
|
|
||||||
hostPort += ":80"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hostPort, hostNoPort
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultDialer is a dialer with all fields set to the default zero values.
|
|
||||||
var DefaultDialer = &Dialer{
|
|
||||||
Proxy: http.ProxyFromEnvironment,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial creates a new client connection. Use requestHeader to specify the
|
|
||||||
// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie).
|
|
||||||
// Use the response.Header to get the selected subprotocol
|
|
||||||
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
|
|
||||||
//
|
|
||||||
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
|
|
||||||
// non-nil *http.Response so that callers can handle redirects, authentication,
|
|
||||||
// etcetera. The response body may not contain the entire response and does not
|
|
||||||
// need to be closed by the application.
|
|
||||||
func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
|
|
||||||
|
|
||||||
if d == nil {
|
|
||||||
d = &Dialer{
|
|
||||||
Proxy: http.ProxyFromEnvironment,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
challengeKey, err := generateChallengeKey()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := parseURL(urlStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch u.Scheme {
|
|
||||||
case "ws":
|
|
||||||
u.Scheme = "http"
|
|
||||||
case "wss":
|
|
||||||
u.Scheme = "https"
|
|
||||||
default:
|
|
||||||
return nil, nil, errMalformedURL
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.User != nil {
|
|
||||||
// User name and password are not allowed in websocket URIs.
|
|
||||||
return nil, nil, errMalformedURL
|
|
||||||
}
|
|
||||||
|
|
||||||
req := &http.Request{
|
|
||||||
Method: "GET",
|
|
||||||
URL: u,
|
|
||||||
Proto: "HTTP/1.1",
|
|
||||||
ProtoMajor: 1,
|
|
||||||
ProtoMinor: 1,
|
|
||||||
Header: make(http.Header),
|
|
||||||
Host: u.Host,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the cookies present in the cookie jar of the dialer
|
|
||||||
if d.Jar != nil {
|
|
||||||
for _, cookie := range d.Jar.Cookies(u) {
|
|
||||||
req.AddCookie(cookie)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the request headers using the capitalization for names and values in
|
|
||||||
// RFC examples. Although the capitalization shouldn't matter, there are
|
|
||||||
// servers that depend on it. The Header.Set method is not used because the
|
|
||||||
// method canonicalizes the header names.
|
|
||||||
req.Header["Upgrade"] = []string{"websocket"}
|
|
||||||
req.Header["Connection"] = []string{"Upgrade"}
|
|
||||||
req.Header["Sec-WebSocket-Key"] = []string{challengeKey}
|
|
||||||
req.Header["Sec-WebSocket-Version"] = []string{"13"}
|
|
||||||
if len(d.Subprotocols) > 0 {
|
|
||||||
req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")}
|
|
||||||
}
|
|
||||||
for k, vs := range requestHeader {
|
|
||||||
switch {
|
|
||||||
case k == "Host":
|
|
||||||
if len(vs) > 0 {
|
|
||||||
req.Host = vs[0]
|
|
||||||
}
|
|
||||||
case k == "Upgrade" ||
|
|
||||||
k == "Connection" ||
|
|
||||||
k == "Sec-Websocket-Key" ||
|
|
||||||
k == "Sec-Websocket-Version" ||
|
|
||||||
k == "Sec-Websocket-Extensions" ||
|
|
||||||
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
|
||||||
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
|
||||||
default:
|
|
||||||
req.Header[k] = vs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.EnableCompression {
|
|
||||||
req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover")
|
|
||||||
}
|
|
||||||
|
|
||||||
hostPort, hostNoPort := hostPortNoPort(u)
|
|
||||||
|
|
||||||
var proxyURL *url.URL
|
|
||||||
// Check wether the proxy method has been configured
|
|
||||||
if d.Proxy != nil {
|
|
||||||
proxyURL, err = d.Proxy(req)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var targetHostPort string
|
|
||||||
if proxyURL != nil {
|
|
||||||
targetHostPort, _ = hostPortNoPort(proxyURL)
|
|
||||||
} else {
|
|
||||||
targetHostPort = hostPort
|
|
||||||
}
|
|
||||||
|
|
||||||
var deadline time.Time
|
|
||||||
if d.HandshakeTimeout != 0 {
|
|
||||||
deadline = time.Now().Add(d.HandshakeTimeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
netDial := d.NetDial
|
|
||||||
if netDial == nil {
|
|
||||||
netDialer := &net.Dialer{Deadline: deadline}
|
|
||||||
netDial = netDialer.Dial
|
|
||||||
}
|
|
||||||
|
|
||||||
netConn, err := netDial("tcp", targetHostPort)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if netConn != nil {
|
|
||||||
netConn.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := netConn.SetDeadline(deadline); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if proxyURL != nil {
|
|
||||||
connectHeader := make(http.Header)
|
|
||||||
if user := proxyURL.User; user != nil {
|
|
||||||
proxyUser := user.Username()
|
|
||||||
if proxyPassword, passwordSet := user.Password(); passwordSet {
|
|
||||||
credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
|
|
||||||
connectHeader.Set("Proxy-Authorization", "Basic "+credential)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
connectReq := &http.Request{
|
|
||||||
Method: "CONNECT",
|
|
||||||
URL: &url.URL{Opaque: hostPort},
|
|
||||||
Host: hostPort,
|
|
||||||
Header: connectHeader,
|
|
||||||
}
|
|
||||||
|
|
||||||
connectReq.Write(netConn)
|
|
||||||
|
|
||||||
// Read response.
|
|
||||||
// Okay to use and discard buffered reader here, because
|
|
||||||
// TLS server will not speak until spoken to.
|
|
||||||
br := bufio.NewReader(netConn)
|
|
||||||
resp, err := http.ReadResponse(br, connectReq)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
f := strings.SplitN(resp.Status, " ", 2)
|
|
||||||
return nil, nil, errors.New(f[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Scheme == "https" {
|
|
||||||
cfg := cloneTLSConfig(d.TLSClientConfig)
|
|
||||||
if cfg.ServerName == "" {
|
|
||||||
cfg.ServerName = hostNoPort
|
|
||||||
}
|
|
||||||
tlsConn := tls.Client(netConn, cfg)
|
|
||||||
netConn = tlsConn
|
|
||||||
if err := tlsConn.Handshake(); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if !cfg.InsecureSkipVerify {
|
|
||||||
if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize)
|
|
||||||
|
|
||||||
if err := req.Write(netConn); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := http.ReadResponse(conn.br, req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.Jar != nil {
|
|
||||||
if rc := resp.Cookies(); len(rc) > 0 {
|
|
||||||
d.Jar.SetCookies(u, rc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode != 101 ||
|
|
||||||
!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
|
|
||||||
!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
|
|
||||||
resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) {
|
|
||||||
// Before closing the network connection on return from this
|
|
||||||
// function, slurp up some of the response to aid application
|
|
||||||
// debugging.
|
|
||||||
buf := make([]byte, 1024)
|
|
||||||
n, _ := io.ReadFull(resp.Body, buf)
|
|
||||||
resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n]))
|
|
||||||
return nil, resp, ErrBadHandshake
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ext := range parseExtensions(resp.Header) {
|
|
||||||
if ext[""] != "permessage-deflate" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, snct := ext["server_no_context_takeover"]
|
|
||||||
_, cnct := ext["client_no_context_takeover"]
|
|
||||||
if !snct || !cnct {
|
|
||||||
return nil, resp, errInvalidCompression
|
|
||||||
}
|
|
||||||
conn.newCompressionWriter = compressNoContextTakeover
|
|
||||||
conn.newDecompressionReader = decompressNoContextTakeover
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
|
||||||
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
|
||||||
|
|
||||||
netConn.SetDeadline(time.Time{})
|
|
||||||
netConn = nil // to avoid close in defer.
|
|
||||||
return conn, resp, nil
|
|
||||||
}
|
|
16
vendor/github.com/gorilla/websocket/client_clone.go
generated
vendored
16
vendor/github.com/gorilla/websocket/client_clone.go
generated
vendored
@ -1,16 +0,0 @@
|
|||||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build go1.8
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
import "crypto/tls"
|
|
||||||
|
|
||||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
|
||||||
if cfg == nil {
|
|
||||||
return &tls.Config{}
|
|
||||||
}
|
|
||||||
return cfg.Clone()
|
|
||||||
}
|
|
38
vendor/github.com/gorilla/websocket/client_clone_legacy.go
generated
vendored
38
vendor/github.com/gorilla/websocket/client_clone_legacy.go
generated
vendored
@ -1,38 +0,0 @@
|
|||||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build !go1.8
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
import "crypto/tls"
|
|
||||||
|
|
||||||
// cloneTLSConfig clones all public fields except the fields
|
|
||||||
// SessionTicketsDisabled and SessionTicketKey. This avoids copying the
|
|
||||||
// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a
|
|
||||||
// config in active use.
|
|
||||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
|
||||||
if cfg == nil {
|
|
||||||
return &tls.Config{}
|
|
||||||
}
|
|
||||||
return &tls.Config{
|
|
||||||
Rand: cfg.Rand,
|
|
||||||
Time: cfg.Time,
|
|
||||||
Certificates: cfg.Certificates,
|
|
||||||
NameToCertificate: cfg.NameToCertificate,
|
|
||||||
GetCertificate: cfg.GetCertificate,
|
|
||||||
RootCAs: cfg.RootCAs,
|
|
||||||
NextProtos: cfg.NextProtos,
|
|
||||||
ServerName: cfg.ServerName,
|
|
||||||
ClientAuth: cfg.ClientAuth,
|
|
||||||
ClientCAs: cfg.ClientCAs,
|
|
||||||
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
|
||||||
CipherSuites: cfg.CipherSuites,
|
|
||||||
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
|
|
||||||
ClientSessionCache: cfg.ClientSessionCache,
|
|
||||||
MinVersion: cfg.MinVersion,
|
|
||||||
MaxVersion: cfg.MaxVersion,
|
|
||||||
CurvePreferences: cfg.CurvePreferences,
|
|
||||||
}
|
|
||||||
}
|
|
148
vendor/github.com/gorilla/websocket/compression.go
generated
vendored
148
vendor/github.com/gorilla/websocket/compression.go
generated
vendored
@ -1,148 +0,0 @@
|
|||||||
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"compress/flate"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6
|
|
||||||
maxCompressionLevel = flate.BestCompression
|
|
||||||
defaultCompressionLevel = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool
|
|
||||||
flateReaderPool = sync.Pool{New: func() interface{} {
|
|
||||||
return flate.NewReader(nil)
|
|
||||||
}}
|
|
||||||
)
|
|
||||||
|
|
||||||
func decompressNoContextTakeover(r io.Reader) io.ReadCloser {
|
|
||||||
const tail =
|
|
||||||
// Add four bytes as specified in RFC
|
|
||||||
"\x00\x00\xff\xff" +
|
|
||||||
// Add final block to squelch unexpected EOF error from flate reader.
|
|
||||||
"\x01\x00\x00\xff\xff"
|
|
||||||
|
|
||||||
fr, _ := flateReaderPool.Get().(io.ReadCloser)
|
|
||||||
fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
|
|
||||||
return &flateReadWrapper{fr}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidCompressionLevel(level int) bool {
|
|
||||||
return minCompressionLevel <= level && level <= maxCompressionLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser {
|
|
||||||
p := &flateWriterPools[level-minCompressionLevel]
|
|
||||||
tw := &truncWriter{w: w}
|
|
||||||
fw, _ := p.Get().(*flate.Writer)
|
|
||||||
if fw == nil {
|
|
||||||
fw, _ = flate.NewWriter(tw, level)
|
|
||||||
} else {
|
|
||||||
fw.Reset(tw)
|
|
||||||
}
|
|
||||||
return &flateWriteWrapper{fw: fw, tw: tw, p: p}
|
|
||||||
}
|
|
||||||
|
|
||||||
// truncWriter is an io.Writer that writes all but the last four bytes of the
|
|
||||||
// stream to another io.Writer.
|
|
||||||
type truncWriter struct {
|
|
||||||
w io.WriteCloser
|
|
||||||
n int
|
|
||||||
p [4]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *truncWriter) Write(p []byte) (int, error) {
|
|
||||||
n := 0
|
|
||||||
|
|
||||||
// fill buffer first for simplicity.
|
|
||||||
if w.n < len(w.p) {
|
|
||||||
n = copy(w.p[w.n:], p)
|
|
||||||
p = p[n:]
|
|
||||||
w.n += n
|
|
||||||
if len(p) == 0 {
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m := len(p)
|
|
||||||
if m > len(w.p) {
|
|
||||||
m = len(w.p)
|
|
||||||
}
|
|
||||||
|
|
||||||
if nn, err := w.w.Write(w.p[:m]); err != nil {
|
|
||||||
return n + nn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(w.p[:], w.p[m:])
|
|
||||||
copy(w.p[len(w.p)-m:], p[len(p)-m:])
|
|
||||||
nn, err := w.w.Write(p[:len(p)-m])
|
|
||||||
return n + nn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type flateWriteWrapper struct {
|
|
||||||
fw *flate.Writer
|
|
||||||
tw *truncWriter
|
|
||||||
p *sync.Pool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *flateWriteWrapper) Write(p []byte) (int, error) {
|
|
||||||
if w.fw == nil {
|
|
||||||
return 0, errWriteClosed
|
|
||||||
}
|
|
||||||
return w.fw.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *flateWriteWrapper) Close() error {
|
|
||||||
if w.fw == nil {
|
|
||||||
return errWriteClosed
|
|
||||||
}
|
|
||||||
err1 := w.fw.Flush()
|
|
||||||
w.p.Put(w.fw)
|
|
||||||
w.fw = nil
|
|
||||||
if w.tw.p != [4]byte{0, 0, 0xff, 0xff} {
|
|
||||||
return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
|
|
||||||
}
|
|
||||||
err2 := w.tw.w.Close()
|
|
||||||
if err1 != nil {
|
|
||||||
return err1
|
|
||||||
}
|
|
||||||
return err2
|
|
||||||
}
|
|
||||||
|
|
||||||
type flateReadWrapper struct {
|
|
||||||
fr io.ReadCloser
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *flateReadWrapper) Read(p []byte) (int, error) {
|
|
||||||
if r.fr == nil {
|
|
||||||
return 0, io.ErrClosedPipe
|
|
||||||
}
|
|
||||||
n, err := r.fr.Read(p)
|
|
||||||
if err == io.EOF {
|
|
||||||
// Preemptively place the reader back in the pool. This helps with
|
|
||||||
// scenarios where the application does not call NextReader() soon after
|
|
||||||
// this final read.
|
|
||||||
r.Close()
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *flateReadWrapper) Close() error {
|
|
||||||
if r.fr == nil {
|
|
||||||
return io.ErrClosedPipe
|
|
||||||
}
|
|
||||||
err := r.fr.Close()
|
|
||||||
flateReaderPool.Put(r.fr)
|
|
||||||
r.fr = nil
|
|
||||||
return err
|
|
||||||
}
|
|
1149
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
1149
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
File diff suppressed because it is too large
Load Diff
18
vendor/github.com/gorilla/websocket/conn_read.go
generated
vendored
18
vendor/github.com/gorilla/websocket/conn_read.go
generated
vendored
@ -1,18 +0,0 @@
|
|||||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build go1.5
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
import "io"
|
|
||||||
|
|
||||||
func (c *Conn) read(n int) ([]byte, error) {
|
|
||||||
p, err := c.br.Peek(n)
|
|
||||||
if err == io.EOF {
|
|
||||||
err = errUnexpectedEOF
|
|
||||||
}
|
|
||||||
c.br.Discard(len(p))
|
|
||||||
return p, err
|
|
||||||
}
|
|
21
vendor/github.com/gorilla/websocket/conn_read_legacy.go
generated
vendored
21
vendor/github.com/gorilla/websocket/conn_read_legacy.go
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build !go1.5
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
import "io"
|
|
||||||
|
|
||||||
func (c *Conn) read(n int) ([]byte, error) {
|
|
||||||
p, err := c.br.Peek(n)
|
|
||||||
if err == io.EOF {
|
|
||||||
err = errUnexpectedEOF
|
|
||||||
}
|
|
||||||
if len(p) > 0 {
|
|
||||||
// advance over the bytes just read
|
|
||||||
io.ReadFull(c.br, p)
|
|
||||||
}
|
|
||||||
return p, err
|
|
||||||
}
|
|
180
vendor/github.com/gorilla/websocket/doc.go
generated
vendored
180
vendor/github.com/gorilla/websocket/doc.go
generated
vendored
@ -1,180 +0,0 @@
|
|||||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package websocket implements the WebSocket protocol defined in RFC 6455.
|
|
||||||
//
|
|
||||||
// Overview
|
|
||||||
//
|
|
||||||
// The Conn type represents a WebSocket connection. A server application uses
|
|
||||||
// the Upgrade function from an Upgrader object with a HTTP request handler
|
|
||||||
// to get a pointer to a Conn:
|
|
||||||
//
|
|
||||||
// var upgrader = websocket.Upgrader{
|
|
||||||
// ReadBufferSize: 1024,
|
|
||||||
// WriteBufferSize: 1024,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func handler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// conn, err := upgrader.Upgrade(w, r, nil)
|
|
||||||
// if err != nil {
|
|
||||||
// log.Println(err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// ... Use conn to send and receive messages.
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Call the connection's WriteMessage and ReadMessage methods to send and
|
|
||||||
// receive messages as a slice of bytes. This snippet of code shows how to echo
|
|
||||||
// messages using these methods:
|
|
||||||
//
|
|
||||||
// for {
|
|
||||||
// messageType, p, err := conn.ReadMessage()
|
|
||||||
// if err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// if err = conn.WriteMessage(messageType, p); err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// In above snippet of code, p is a []byte and messageType is an int with value
|
|
||||||
// websocket.BinaryMessage or websocket.TextMessage.
|
|
||||||
//
|
|
||||||
// An application can also send and receive messages using the io.WriteCloser
|
|
||||||
// and io.Reader interfaces. To send a message, call the connection NextWriter
|
|
||||||
// method to get an io.WriteCloser, write the message to the writer and close
|
|
||||||
// the writer when done. To receive a message, call the connection NextReader
|
|
||||||
// method to get an io.Reader and read until io.EOF is returned. This snippet
|
|
||||||
// shows how to echo messages using the NextWriter and NextReader methods:
|
|
||||||
//
|
|
||||||
// for {
|
|
||||||
// messageType, r, err := conn.NextReader()
|
|
||||||
// if err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// w, err := conn.NextWriter(messageType)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// if _, err := io.Copy(w, r); err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// if err := w.Close(); err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Data Messages
|
|
||||||
//
|
|
||||||
// The WebSocket protocol distinguishes between text and binary data messages.
|
|
||||||
// Text messages are interpreted as UTF-8 encoded text. The interpretation of
|
|
||||||
// binary messages is left to the application.
|
|
||||||
//
|
|
||||||
// This package uses the TextMessage and BinaryMessage integer constants to
|
|
||||||
// identify the two data message types. The ReadMessage and NextReader methods
|
|
||||||
// return the type of the received message. The messageType argument to the
|
|
||||||
// WriteMessage and NextWriter methods specifies the type of a sent message.
|
|
||||||
//
|
|
||||||
// It is the application's responsibility to ensure that text messages are
|
|
||||||
// valid UTF-8 encoded text.
|
|
||||||
//
|
|
||||||
// Control Messages
|
|
||||||
//
|
|
||||||
// The WebSocket protocol defines three types of control messages: close, ping
|
|
||||||
// and pong. Call the connection WriteControl, WriteMessage or NextWriter
|
|
||||||
// methods to send a control message to the peer.
|
|
||||||
//
|
|
||||||
// Connections handle received close messages by sending a close message to the
|
|
||||||
// peer and returning a *CloseError from the the NextReader, ReadMessage or the
|
|
||||||
// message Read method.
|
|
||||||
//
|
|
||||||
// Connections handle received ping and pong messages by invoking callback
|
|
||||||
// functions set with SetPingHandler and SetPongHandler methods. The callback
|
|
||||||
// functions are called from the NextReader, ReadMessage and the message Read
|
|
||||||
// methods.
|
|
||||||
//
|
|
||||||
// The default ping handler sends a pong to the peer. The application's reading
|
|
||||||
// goroutine can block for a short time while the handler writes the pong data
|
|
||||||
// to the connection.
|
|
||||||
//
|
|
||||||
// The application must read the connection to process ping, pong and close
|
|
||||||
// messages sent from the peer. If the application is not otherwise interested
|
|
||||||
// in messages from the peer, then the application should start a goroutine to
|
|
||||||
// read and discard messages from the peer. A simple example is:
|
|
||||||
//
|
|
||||||
// func readLoop(c *websocket.Conn) {
|
|
||||||
// for {
|
|
||||||
// if _, _, err := c.NextReader(); err != nil {
|
|
||||||
// c.Close()
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Concurrency
|
|
||||||
//
|
|
||||||
// Connections support one concurrent reader and one concurrent writer.
|
|
||||||
//
|
|
||||||
// Applications are responsible for ensuring that no more than one goroutine
|
|
||||||
// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
|
|
||||||
// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and
|
|
||||||
// that no more than one goroutine calls the read methods (NextReader,
|
|
||||||
// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler)
|
|
||||||
// concurrently.
|
|
||||||
//
|
|
||||||
// The Close and WriteControl methods can be called concurrently with all other
|
|
||||||
// methods.
|
|
||||||
//
|
|
||||||
// Origin Considerations
|
|
||||||
//
|
|
||||||
// Web browsers allow Javascript applications to open a WebSocket connection to
|
|
||||||
// any host. It's up to the server to enforce an origin policy using the Origin
|
|
||||||
// request header sent by the browser.
|
|
||||||
//
|
|
||||||
// The Upgrader calls the function specified in the CheckOrigin field to check
|
|
||||||
// the origin. If the CheckOrigin function returns false, then the Upgrade
|
|
||||||
// method fails the WebSocket handshake with HTTP status 403.
|
|
||||||
//
|
|
||||||
// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail
|
|
||||||
// the handshake if the Origin request header is present and not equal to the
|
|
||||||
// Host request header.
|
|
||||||
//
|
|
||||||
// An application can allow connections from any origin by specifying a
|
|
||||||
// function that always returns true:
|
|
||||||
//
|
|
||||||
// var upgrader = websocket.Upgrader{
|
|
||||||
// CheckOrigin: func(r *http.Request) bool { return true },
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// The deprecated Upgrade function does not enforce an origin policy. It's the
|
|
||||||
// application's responsibility to check the Origin header before calling
|
|
||||||
// Upgrade.
|
|
||||||
//
|
|
||||||
// Compression EXPERIMENTAL
|
|
||||||
//
|
|
||||||
// Per message compression extensions (RFC 7692) are experimentally supported
|
|
||||||
// by this package in a limited capacity. Setting the EnableCompression option
|
|
||||||
// to true in Dialer or Upgrader will attempt to negotiate per message deflate
|
|
||||||
// support.
|
|
||||||
//
|
|
||||||
// var upgrader = websocket.Upgrader{
|
|
||||||
// EnableCompression: true,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// If compression was successfully negotiated with the connection's peer, any
|
|
||||||
// message received in compressed form will be automatically decompressed.
|
|
||||||
// All Read methods will return uncompressed bytes.
|
|
||||||
//
|
|
||||||
// Per message compression of messages written to a connection can be enabled
|
|
||||||
// or disabled by calling the corresponding Conn method:
|
|
||||||
//
|
|
||||||
// conn.EnableWriteCompression(false)
|
|
||||||
//
|
|
||||||
// Currently this package does not support compression with "context takeover".
|
|
||||||
// This means that messages must be compressed and decompressed in isolation,
|
|
||||||
// without retaining sliding window or dictionary state across messages. For
|
|
||||||
// more details refer to RFC 7692.
|
|
||||||
//
|
|
||||||
// Use of compression is experimental and may result in decreased performance.
|
|
||||||
package websocket
|
|
55
vendor/github.com/gorilla/websocket/json.go
generated
vendored
55
vendor/github.com/gorilla/websocket/json.go
generated
vendored
@ -1,55 +0,0 @@
|
|||||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WriteJSON is deprecated, use c.WriteJSON instead.
|
|
||||||
func WriteJSON(c *Conn, v interface{}) error {
|
|
||||||
return c.WriteJSON(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteJSON writes the JSON encoding of v to the connection.
|
|
||||||
//
|
|
||||||
// See the documentation for encoding/json Marshal for details about the
|
|
||||||
// conversion of Go values to JSON.
|
|
||||||
func (c *Conn) WriteJSON(v interface{}) error {
|
|
||||||
w, err := c.NextWriter(TextMessage)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err1 := json.NewEncoder(w).Encode(v)
|
|
||||||
err2 := w.Close()
|
|
||||||
if err1 != nil {
|
|
||||||
return err1
|
|
||||||
}
|
|
||||||
return err2
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadJSON is deprecated, use c.ReadJSON instead.
|
|
||||||
func ReadJSON(c *Conn, v interface{}) error {
|
|
||||||
return c.ReadJSON(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadJSON reads the next JSON-encoded message from the connection and stores
|
|
||||||
// it in the value pointed to by v.
|
|
||||||
//
|
|
||||||
// See the documentation for the encoding/json Unmarshal function for details
|
|
||||||
// about the conversion of JSON to a Go value.
|
|
||||||
func (c *Conn) ReadJSON(v interface{}) error {
|
|
||||||
_, r, err := c.NextReader()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = json.NewDecoder(r).Decode(v)
|
|
||||||
if err == io.EOF {
|
|
||||||
// One value is expected in the message.
|
|
||||||
err = io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
55
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
55
vendor/github.com/gorilla/websocket/mask.go
generated
vendored
@ -1,55 +0,0 @@
|
|||||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
|
|
||||||
// this source code is governed by a BSD-style license that can be found in the
|
|
||||||
// LICENSE file.
|
|
||||||
|
|
||||||
// +build !appengine
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
import "unsafe"
|
|
||||||
|
|
||||||
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
|
||||||
|
|
||||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
|
||||||
|
|
||||||
// Mask one byte at a time for small buffers.
|
|
||||||
if len(b) < 2*wordSize {
|
|
||||||
for i := range b {
|
|
||||||
b[i] ^= key[pos&3]
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
return pos & 3
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mask one byte at a time to word boundary.
|
|
||||||
if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {
|
|
||||||
n = wordSize - n
|
|
||||||
for i := range b[:n] {
|
|
||||||
b[i] ^= key[pos&3]
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
b = b[n:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create aligned word size key.
|
|
||||||
var k [wordSize]byte
|
|
||||||
for i := range k {
|
|
||||||
k[i] = key[(pos+i)&3]
|
|
||||||
}
|
|
||||||
kw := *(*uintptr)(unsafe.Pointer(&k))
|
|
||||||
|
|
||||||
// Mask one word at a time.
|
|
||||||
n := (len(b) / wordSize) * wordSize
|
|
||||||
for i := 0; i < n; i += wordSize {
|
|
||||||
*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mask one byte at a time for remaining bytes.
|
|
||||||
b = b[n:]
|
|
||||||
for i := range b {
|
|
||||||
b[i] ^= key[pos&3]
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
|
|
||||||
return pos & 3
|
|
||||||
}
|
|
15
vendor/github.com/gorilla/websocket/mask_safe.go
generated
vendored
15
vendor/github.com/gorilla/websocket/mask_safe.go
generated
vendored
@ -1,15 +0,0 @@
|
|||||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
|
|
||||||
// this source code is governed by a BSD-style license that can be found in the
|
|
||||||
// LICENSE file.
|
|
||||||
|
|
||||||
// +build appengine
|
|
||||||
|
|
||||||
package websocket
|
|
||||||
|
|
||||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
|
||||||
for i := range b {
|
|
||||||
b[i] ^= key[pos&3]
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
return pos & 3
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user