├── CNAME ├── Safari └── .gitkeep ├── Unity └── .gitkeep ├── SPEC.md ├── Chrome ├── icon.png ├── content.js ├── manifest.json ├── background.js └── socketify.js ├── Firefox ├── icon.png ├── content.js ├── manifest.json ├── background.js └── socketify.js ├── Installer ├── Chrome.gif ├── Firefox.gif └── Messenger.gif ├── Messenger ├── install_linux.go ├── endian.go ├── install.go ├── udpPeer.go ├── tcpClient.go ├── main.go ├── tcpServer.go ├── install_darwin.go └── install_windows.go ├── LICENSE ├── Example ├── index.html ├── style.css └── script.js ├── README.md ├── INSTALL.md ├── LOGO.svg └── API.md /CNAME: -------------------------------------------------------------------------------- 1 | socketify.net -------------------------------------------------------------------------------- /Safari/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Unity/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SPEC.md: -------------------------------------------------------------------------------- 1 | # Protocol Specification 2 | -------------------------------------------------------------------------------- /Chrome/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetAsmCom/Socketify/HEAD/Chrome/icon.png -------------------------------------------------------------------------------- /Firefox/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetAsmCom/Socketify/HEAD/Firefox/icon.png -------------------------------------------------------------------------------- /Installer/Chrome.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetAsmCom/Socketify/HEAD/Installer/Chrome.gif -------------------------------------------------------------------------------- /Installer/Firefox.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetAsmCom/Socketify/HEAD/Installer/Firefox.gif -------------------------------------------------------------------------------- /Installer/Messenger.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetAsmCom/Socketify/HEAD/Installer/Messenger.gif -------------------------------------------------------------------------------- /Messenger/install_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package main 4 | 5 | import "os" 6 | 7 | func install(chromeExtID string, firefoxExtID string) bool { 8 | os.Stdout.Write([]byte("install: linux installation not implemented yet\n")) 9 | return false 10 | } 11 | 12 | func uninstall() { 13 | os.Stdout.Write([]byte("uninstall: linux uninstallation not implemented yet\n")) 14 | } 15 | -------------------------------------------------------------------------------- /Messenger/endian.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "unsafe" 6 | ) 7 | 8 | func isBigEndian() bool { 9 | i := 0x1 10 | m := (*[int(unsafe.Sizeof(0))]byte)(unsafe.Pointer(&i)) 11 | return m[0] == 0 12 | } 13 | 14 | func isLittleEndian() bool { 15 | return !isBigEndian() 16 | } 17 | 18 | var nativeEndian = map[bool]binary.ByteOrder{ 19 | true: binary.BigEndian, 20 | false: binary.LittleEndian, 21 | }[isBigEndian()] 22 | -------------------------------------------------------------------------------- /Messenger/install.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const ( 4 | chromeManifest = `{ 5 | "name": "net.socketify.messenger", 6 | "description": "Socketify - TCP and UDP Sockets API", 7 | "path": "BIN_PATH", 8 | "type": "stdio", 9 | "allowed_origins": [ 10 | "chrome-extension://EXT_ID/" 11 | ] 12 | }` 13 | 14 | firefoxManifest = `{ 15 | "name": "net.socketify.messenger", 16 | "description": "Socketify - TCP and UDP Sockets API", 17 | "path": "BIN_PATH", 18 | "type": "stdio", 19 | "allowed_extensions": [ 20 | "EXT_ID" 21 | ] 22 | }` 23 | ) 24 | -------------------------------------------------------------------------------- /Chrome/content.js: -------------------------------------------------------------------------------- 1 | var script = document.createElement("script"); 2 | script.src = chrome.extension.getURL("socketify.js"); 3 | document.documentElement.appendChild(script); 4 | 5 | window.addEventListener("message", function (event) { 6 | if (event.source !== window || event.data._tab.dir !== "socketify-outbound") { 7 | return; 8 | } 9 | 10 | chrome.runtime.sendMessage(event.data._tab._ext); 11 | }, false); 12 | 13 | chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) { 14 | window.postMessage({ 15 | _tab: { 16 | dir: "socketify-inbound", 17 | _ext: message 18 | } 19 | }, "*"); 20 | }); 21 | 22 | chrome.runtime.sendMessage({ init: true }); 23 | -------------------------------------------------------------------------------- /Firefox/content.js: -------------------------------------------------------------------------------- 1 | var script = document.createElement("script"); 2 | script.src = browser.extension.getURL("socketify.js"); 3 | document.documentElement.appendChild(script); 4 | 5 | window.addEventListener("message", function (event) { 6 | if (event.source !== window || event.data._tab.dir !== "socketify-outbound") { 7 | return; 8 | } 9 | 10 | browser.runtime.sendMessage(event.data._tab._ext); 11 | }, false); 12 | 13 | browser.runtime.onMessage.addListener(function (message, sender, sendResponse) { 14 | window.postMessage({ 15 | _tab: { 16 | dir: "socketify-inbound", 17 | _ext: message 18 | } 19 | }, "*"); 20 | }); 21 | 22 | browser.runtime.sendMessage({ init: true }); 23 | -------------------------------------------------------------------------------- /Chrome/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Socketify", 3 | "description": "TCP and UDP Sockets API", 4 | "version": "1.0", 5 | "icons": { 6 | "16": "icon.png", 7 | "48": "icon.png", 8 | "96": "icon.png", 9 | "128": "icon.png" 10 | }, 11 | "browser_action": { 12 | "default_icon": "icon.png" 13 | }, 14 | "background": { 15 | "scripts": [ 16 | "background.js" 17 | ], 18 | "persistent": true 19 | }, 20 | "permissions": [ 21 | "nativeMessaging", 22 | "tabs", 23 | "*://*/*", 24 | "" 25 | ], 26 | "content_scripts": [ 27 | { 28 | "matches": [ 29 | "*://*/*" 30 | ], 31 | "js": [ 32 | "content.js" 33 | ], 34 | "run_at": "document_start", 35 | "all_frames": true 36 | } 37 | ], 38 | "web_accessible_resources": [ 39 | "socketify.js" 40 | ], 41 | "manifest_version": 2 42 | } -------------------------------------------------------------------------------- /Firefox/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Socketify", 3 | "description": "TCP and UDP Sockets API", 4 | "version": "1.0", 5 | "applications": { 6 | "gecko": { 7 | "id": "extension@socketify.net" 8 | } 9 | }, 10 | "icons": { 11 | "16": "icon.png", 12 | "48": "icon.png", 13 | "96": "icon.png", 14 | "128": "icon.png" 15 | }, 16 | "browser_action": { 17 | "default_icon": "icon.png" 18 | }, 19 | "background": { 20 | "scripts": [ 21 | "background.js" 22 | ] 23 | }, 24 | "permissions": [ 25 | "nativeMessaging", 26 | "tabs", 27 | "*://*/*", 28 | "" 29 | ], 30 | "content_scripts": [ 31 | { 32 | "matches": [ 33 | "*://*/*" 34 | ], 35 | "js": [ 36 | "content.js" 37 | ], 38 | "run_at": "document_start", 39 | "all_frames": true 40 | } 41 | ], 42 | "web_accessible_resources": [ 43 | "socketify.js" 44 | ], 45 | "manifest_version": 2 46 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 MFatihMAR 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Socketify Example 7 | 8 | 9 | 10 | 11 | 12 |
Uh-oh! Socketify is unavailable :(
13 |
14 |
15 | UDP Peer 16 | TCP Client 17 | TCP Server 18 |
19 |
20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Socketify 4 | 5 | > TCP and UDP Sockets API on Chrome, Firefox and Safari desktop browsers with extensions via native messaging. 6 | 7 | **What?** A cross-platform, cross-browser extension for desktop browsers that injects simple & easy-to-use `UdpPeer`, `TcpServer` and `TcpClient` sockets API into page window, available in plain JavaScript. 8 | 9 | **Why?** I was prototyping a web-based multiplayer-online game then realized that WebSocket and WebRTC standard APIs are not flexible enough to achieve custom networking solutions when needed. After that I took the challenge and decided to provide raw UDP and TCP sockets with a simple API so that people can implement their own network transport layer on top. Especially for real-time games, you'd better use thin UDP transport layer to fight with network congestion! 10 | 11 | **How?** Using _Native Messaging_ APIs on [Chrome↗](https://developer.chrome.com/extensions/nativeMessaging) and [Firefox↗](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging), we are exchanging messages with native host app ([Messenger](Messenger)) so it does all socket operations for us. 12 | 13 | ## Getting Started 14 | 15 | - [Installation Guides](INSTALL.md) 16 | - [Example Test Page](Example/index.html) 17 | - [API Documentation](API.md) 18 | 19 | ## TODO 20 | 21 | - [x] Native Messaging Host 22 | - [x] Socketify API 23 | - [x] Chrome Extension 24 | - [x] Firefox Porting 25 | - [x] Installation Guides 26 | - [ ] API Documentation 27 | - [ ] Unity WebGL Support 28 | - [ ] Extension Popup Menu 29 | - [ ] Host App Installer 30 | - [ ] Safari Extension 31 | - [ ] Encryption Support 32 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Installation Guides 2 | 3 | You need to install both [`Extension`](#extension) and [`Messenger`](#messenger) to have [`Socketify` API](API.md) available. 4 | 5 | ## Contents 6 | 7 | - [Extension](#extension) 8 | - [Chrome](#chrome) 9 | - [Firefox](#firefox) 10 | - [Safari](#safari) 11 | - [Messenger](#messenger) 12 | 13 | ## Extension 14 | 15 | ### Chrome 16 | 17 | ![Load Chrome Extension](Installer/Chrome.gif) 18 | 19 | 1. Go to `chrome://extensions` 20 | 2. Turn `Developer mode` switch on 21 | 3. Click `Load unpacked` button 22 | 4. Navigate and select `Chrome` extension directory 23 | 5. Note extension's `ID` because you will need it while [installing `Messenger`](#messenger) 24 | 25 | ### Firefox 26 | 27 | ![Load Firefox Extension](Installer/Firefox.gif) 28 | 29 | 1. Go to `about:debugging` 30 | 2. Click `Load Temporary Add-on` button 31 | 3. Navigate and open `manifest.json` under `Firefox` directory 32 | 4. Note `Extension ID` because you will need it while [installing `Messenger`](#messenger) 33 | 34 | ### Safari 35 | 36 | > TODO: Planned 37 | 38 | ## Messenger 39 | 40 | ![Build and Install Messenger Host App](Installer/Messenger.gif) 41 | 42 | 1. [Download and install Go↗](https://golang.org) 43 | 2. Open terminal/console 44 | 3. Go to `Messenger` directory 45 | 4. _(only for Windows)_ Get [`registry`↗](https://godoc.org/golang.org/x/sys/windows/registry) package 46 | ```console 47 | go get -u golang.org/x/sys/windows/registry 48 | ``` 49 | 5. Build the app 50 | ```console 51 | go build 52 | ``` 53 | 6. Install by specifying extension `ID`s 54 | ```console 55 | ./Messenger -install -chromeExtID= -firefoxExtID= 56 | 57 | Messenger.exe -install -chromeExtID= -firefoxExtID= 58 | ``` 59 | -------------------------------------------------------------------------------- /LOGO.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Messenger/udpPeer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | "os" 6 | ) 7 | 8 | var udpPeerClosed = false 9 | var udpPeerSocket *net.UDPConn 10 | 11 | func udpPeer(addrStr string) { 12 | address, error := net.ResolveUDPAddr("udp", addrStr) 13 | if error != nil { 14 | write(message{ 15 | Event: "close", 16 | Error: "cannot resolve udp address", 17 | Debug: error.Error(), 18 | }) 19 | os.Exit(1) 20 | } 21 | 22 | udpPeerSocket, error = net.ListenUDP("udp", address) 23 | if error != nil { 24 | write(message{ 25 | Event: "close", 26 | Error: "cannot open udp socket", 27 | Debug: error.Error(), 28 | }) 29 | os.Exit(1) 30 | } 31 | 32 | defer udpPeerSocket.Close() 33 | go udpPeerReceive() 34 | write(message{ 35 | Event: "open", 36 | Address: udpPeerSocket.LocalAddr().String(), 37 | }) 38 | 39 | for { 40 | msg := read() 41 | switch msg.Event { 42 | case "error": 43 | if !udpPeerClosed { 44 | write(message{ 45 | Event: "close", 46 | Error: msg.Error, 47 | Debug: msg.Debug, 48 | }) 49 | udpPeerClosed = true 50 | } 51 | os.Exit(1) 52 | break 53 | case "send": 54 | udpPeerSend(msg) 55 | break 56 | case "close": 57 | if !udpPeerClosed { 58 | write(message{ 59 | Event: "close", 60 | }) 61 | udpPeerClosed = true 62 | } 63 | udpPeerSocket.Close() 64 | os.Exit(0) 65 | break 66 | } 67 | } 68 | } 69 | 70 | func udpPeerSend(msg message) { 71 | address, error := net.ResolveUDPAddr("udp", msg.Address) 72 | if error != nil { 73 | return 74 | } 75 | 76 | if address.IP == nil || address.Port == 0 { 77 | return 78 | } 79 | 80 | _, error = udpPeerSocket.WriteToUDP([]byte(msg.Payload), address) 81 | if error != nil { 82 | if !udpPeerClosed { 83 | write(message{ 84 | Event: "close", 85 | Error: "cannot write to udp socket", 86 | Debug: error.Error(), 87 | }) 88 | udpPeerClosed = true 89 | } 90 | os.Exit(1) 91 | } 92 | } 93 | 94 | func udpPeerReceive() { 95 | for { 96 | buffer := make([]byte, 1500) 97 | length, address, error := udpPeerSocket.ReadFromUDP(buffer) 98 | if error != nil { 99 | if !udpPeerClosed { 100 | write(message{ 101 | Event: "close", 102 | Error: "cannot read from udp socket", 103 | Debug: error.Error(), 104 | }) 105 | udpPeerClosed = true 106 | } 107 | os.Exit(1) 108 | } 109 | 110 | write(message{ 111 | Event: "receive", 112 | Address: address.String(), 113 | Payload: string(buffer[:length]), 114 | }) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Chrome/background.js: -------------------------------------------------------------------------------- 1 | var messengerPorts = {}; 2 | 3 | chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) { 4 | if (changeInfo.status !== "complete" || !tab.url.startsWith("file:///")) { 5 | return; 6 | } 7 | 8 | chrome.tabs.executeScript(tabId, { file: "content.js" }); 9 | }); 10 | 11 | function disconnectTabPorts(tabPorts) { 12 | for (var id in tabPorts) { 13 | var port = tabPorts[id]; 14 | if (!port) { 15 | continue; 16 | } 17 | 18 | port.disconnect(); 19 | delete tabPorts[id]; 20 | } 21 | } 22 | 23 | chrome.tabs.onRemoved.addListener(function (tabId, removeInfo) { 24 | var tabPorts = messengerPorts[tabId]; 25 | if (!tabPorts) { 26 | return; 27 | } 28 | 29 | disconnectTabPorts(tabPorts); 30 | delete messengerPorts[tabId]; 31 | }); 32 | 33 | chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) { 34 | if (!sender.tab) { 35 | return; 36 | } 37 | 38 | var tabId = sender.tab.id; 39 | 40 | var tabPorts = messengerPorts[tabId]; 41 | if (!tabPorts) { 42 | tabPorts = {}; 43 | messengerPorts[tabId] = tabPorts; 44 | } else if (message.init) { 45 | disconnectTabPorts(tabPorts); 46 | } 47 | 48 | var id = message.id; 49 | if (!id) { 50 | return; 51 | } 52 | 53 | var port = tabPorts[id]; 54 | if (!port) { 55 | if (message._msg.event === "open-udpPeer" || 56 | message._msg.event === "open-tcpServer" || 57 | message._msg.event === "open-tcpClient") { 58 | tabPorts[id] = port = chrome.runtime.connectNative("net.socketify.messenger"); 59 | port.onDisconnect.addListener(function () { 60 | delete tabPorts[id]; 61 | }); 62 | port.onMessage.addListener(function (message) { 63 | if (message.payload) { 64 | message.payload = JSON.parse(message.payload) 65 | } 66 | 67 | chrome.tabs.sendMessage(tabId, { 68 | id: id, 69 | _msg: message 70 | }); 71 | }); 72 | 73 | port.postMessage({ 74 | event: message._msg.event, 75 | address: message._msg.address 76 | }); 77 | } 78 | return; 79 | } 80 | 81 | port.postMessage({ 82 | event: message._msg.event, 83 | address: message._msg.address, 84 | payload: JSON.stringify(message._msg.payload) 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /Example/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | font-size: 0; 5 | box-sizing: border-box; 6 | border: 0; 7 | outline: 0; 8 | font-family: sans-serif; 9 | } 10 | 11 | html, 12 | body { 13 | height: 100%; 14 | } 15 | 16 | #unavailable { 17 | display: none; 18 | margin: 64px; 19 | font-size: 24px; 20 | color: red; 21 | text-align: center; 22 | } 23 | 24 | #page { 25 | display: none; 26 | margin: auto; 27 | padding: 32px; 28 | max-width: 960px; 29 | height: 100%; 30 | } 31 | 32 | #type { 33 | height: 48px; 34 | } 35 | 36 | #type a { 37 | display: inline-block; 38 | margin-right: 32px; 39 | font-size: 32px; 40 | color: gray; 41 | cursor: pointer; 42 | vertical-align: bottom; 43 | } 44 | 45 | #type .active { 46 | box-shadow: 0 2px 0 black; 47 | color: black; 48 | } 49 | 50 | #type a:hover { 51 | color: black; 52 | } 53 | 54 | #events { 55 | margin-top: 16px; 56 | } 57 | 58 | #events * { 59 | height: 48px; 60 | } 61 | 62 | #events input { 63 | padding: 0 16px; 64 | width: calc(100% - 416px); 65 | font-size: 16px; 66 | border: 2px solid black; 67 | } 68 | 69 | #events button { 70 | font-size: 16px; 71 | padding: 12px 16px; 72 | margin-left: 8px; 73 | width: 96px; 74 | color: white; 75 | background: black; 76 | cursor: pointer; 77 | } 78 | 79 | #events button:hover { 80 | background: gray; 81 | } 82 | 83 | #events button:active { 84 | color: black; 85 | } 86 | 87 | #logs { 88 | margin-top: 32px; 89 | padding: 32px; 90 | height: calc(100% - 144px); 91 | overflow-x: hidden; 92 | overflow-y: scroll; 93 | background: #eee; 94 | } 95 | 96 | #logs div { 97 | margin-bottom: 16px; 98 | } 99 | 100 | #logs div span { 101 | display: inline-block; 102 | margin-right: 8px; 103 | font-size: 16px; 104 | } 105 | 106 | #logs div span b { 107 | font-size: 16px; 108 | } 109 | 110 | #logs div .type { 111 | font-size: 14px; 112 | padding: 4px 8px; 113 | color: #eee; 114 | background: black; 115 | } 116 | 117 | #logs div .outEvent { 118 | font-weight: bold; 119 | color: blue; 120 | } 121 | 122 | #logs div .inEvent { 123 | font-weight: bold; 124 | color: green; 125 | } 126 | 127 | #logs div .payload { 128 | margin-top: 8px; 129 | margin-left: 24px; 130 | padding-left: 16px; 131 | border-left: 1px solid gray; 132 | } 133 | 134 | #logs div .payload div { 135 | font-size: 14px; 136 | } 137 | 138 | .error { 139 | color: red; 140 | } -------------------------------------------------------------------------------- /Firefox/background.js: -------------------------------------------------------------------------------- 1 | var messengerPorts = {}; 2 | 3 | browser.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) { 4 | if (changeInfo.status !== "complete" || !tab.url.startsWith("file:///")) { 5 | return; 6 | } 7 | 8 | browser.tabs.executeScript(tabId, { file: "content.js" }); 9 | }); 10 | 11 | function disconnectTabPorts(tabPorts) { 12 | for (var id in tabPorts) { 13 | var port = tabPorts[id]; 14 | if (!port) { 15 | continue; 16 | } 17 | 18 | port.disconnect(); 19 | delete tabPorts[id]; 20 | } 21 | } 22 | 23 | browser.tabs.onRemoved.addListener(function (tabId, removeInfo) { 24 | var tabPorts = messengerPorts[tabId]; 25 | if (!tabPorts) { 26 | return; 27 | } 28 | 29 | disconnectTabPorts(tabPorts); 30 | delete messengerPorts[tabId]; 31 | }); 32 | 33 | browser.runtime.onMessage.addListener(function (message, sender, sendResponse) { 34 | if (!sender.tab) { 35 | return; 36 | } 37 | 38 | var tabId = sender.tab.id; 39 | 40 | var tabPorts = messengerPorts[tabId]; 41 | if (!tabPorts) { 42 | tabPorts = {}; 43 | messengerPorts[tabId] = tabPorts; 44 | } else if (message.init) { 45 | disconnectTabPorts(tabPorts); 46 | } 47 | 48 | var id = message.id; 49 | if (!id) { 50 | return; 51 | } 52 | 53 | var port = tabPorts[id]; 54 | if (!port) { 55 | if (message._msg.event === "open-udpPeer" || 56 | message._msg.event === "open-tcpServer" || 57 | message._msg.event === "open-tcpClient") { 58 | tabPorts[id] = port = browser.runtime.connectNative("net.socketify.messenger"); 59 | port.onDisconnect.addListener(function () { 60 | delete tabPorts[id]; 61 | }); 62 | port.onMessage.addListener(function (message) { 63 | if (message.payload) { 64 | message.payload = JSON.parse(message.payload) 65 | } 66 | 67 | browser.tabs.sendMessage(tabId, { 68 | id: id, 69 | _msg: message 70 | }); 71 | }); 72 | 73 | port.postMessage({ 74 | event: message._msg.event, 75 | address: message._msg.address 76 | }); 77 | } 78 | return; 79 | } 80 | 81 | port.postMessage({ 82 | event: message._msg.event, 83 | address: message._msg.address, 84 | payload: JSON.stringify(message._msg.payload) 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /Messenger/tcpClient.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "os" 7 | ) 8 | 9 | var tcpClientClosed = false 10 | var tcpClientSocket *net.TCPConn 11 | 12 | func tcpClient(addrStr string) { 13 | address, error := net.ResolveTCPAddr("tcp", addrStr) 14 | if error != nil { 15 | write(message{ 16 | Event: "close", 17 | Error: "cannot resolve tcp address", 18 | Debug: error.Error(), 19 | }) 20 | os.Exit(1) 21 | } 22 | 23 | tcpClientSocket, error = net.DialTCP("tcp", nil, address) 24 | if error != nil { 25 | write(message{ 26 | Event: "close", 27 | Error: "cannot connect tcp socket", 28 | Debug: error.Error(), 29 | }) 30 | os.Exit(1) 31 | } 32 | 33 | defer tcpClientSocket.Close() 34 | go tcpClientReceive() 35 | write(message{ 36 | Event: "open", 37 | Address: tcpClientSocket.LocalAddr().String(), 38 | }) 39 | 40 | for { 41 | msg := read() 42 | switch msg.Event { 43 | case "error": 44 | if !tcpClientClosed { 45 | write(message{ 46 | Event: "close", 47 | Error: msg.Error, 48 | Debug: msg.Debug, 49 | }) 50 | tcpClientClosed = true 51 | } 52 | os.Exit(1) 53 | break 54 | case "send": 55 | tcpClientSend(msg) 56 | break 57 | case "close": 58 | if !tcpClientClosed { 59 | write(message{ 60 | Event: "close", 61 | }) 62 | tcpClientClosed = true 63 | } 64 | tcpClientSocket.Close() 65 | os.Exit(0) 66 | break 67 | } 68 | } 69 | } 70 | 71 | func tcpClientSend(msg message) { 72 | _, error := tcpClientSocket.Write([]byte(msg.Payload)) 73 | if error != nil { 74 | if !tcpClientClosed { 75 | write(message{ 76 | Event: "close", 77 | Error: "cannot write to tcp socket", 78 | Debug: error.Error(), 79 | }) 80 | tcpClientClosed = true 81 | } 82 | os.Exit(1) 83 | } 84 | } 85 | 86 | func tcpClientReceive() { 87 | for { 88 | buffer := make([]byte, 1500) 89 | length, error := tcpClientSocket.Read(buffer) 90 | if error != nil { 91 | if error == io.EOF { 92 | if !tcpClientClosed { 93 | write(message{ 94 | Event: "close", 95 | Error: "connection closed", 96 | Debug: error.Error(), 97 | }) 98 | tcpClientClosed = true 99 | } 100 | os.Exit(0) 101 | } else { 102 | if !tcpClientClosed { 103 | write(message{ 104 | Event: "close", 105 | Error: "cannot read from tcp socket", 106 | Debug: error.Error(), 107 | }) 108 | tcpClientClosed = true 109 | } 110 | os.Exit(1) 111 | } 112 | } 113 | 114 | write(message{ 115 | Event: "receive", 116 | Payload: string(buffer[:length]), 117 | }) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Messenger/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "fmt" 7 | "os" 8 | ) 9 | 10 | const ( 11 | version = "1.0" 12 | ) 13 | 14 | type message struct { 15 | Event string `json:"event"` 16 | Address string `json:"address,omitempty"` 17 | Payload string `json:"payload,omitempty"` 18 | Error string `json:"error,omitempty"` 19 | Debug string `json:"debug,omitempty"` 20 | } 21 | 22 | func write(msg message) { 23 | msgBytes, error := json.Marshal(msg) 24 | if error != nil { 25 | return 26 | } 27 | 28 | lenBytes := make([]byte, 4) 29 | nativeEndian.PutUint32(lenBytes, uint32(len(msgBytes))) 30 | os.Stdout.Write(lenBytes) 31 | 32 | os.Stdout.Write(msgBytes) 33 | } 34 | 35 | func read() message { 36 | buffer := make([]byte, 4) 37 | length, error := os.Stdin.Read(buffer) 38 | if error != nil || length != 4 { 39 | return message{ 40 | Event: "error", 41 | Error: "cannot read input size", 42 | Debug: error.Error() + ">" + string(buffer), 43 | } 44 | } 45 | 46 | size := nativeEndian.Uint32(buffer) 47 | buffer = make([]byte, size) 48 | length, error = os.Stdin.Read(buffer) 49 | if error != nil && length != int(size) { 50 | return message{ 51 | Event: "error", 52 | Error: "cannot read input message", 53 | Debug: error.Error() + ">" + string(buffer), 54 | } 55 | } 56 | 57 | msg := message{} 58 | error = json.Unmarshal(buffer, &msg) 59 | if error != nil { 60 | return message{ 61 | Event: "error", 62 | Error: "cannot deserialize input message", 63 | Debug: error.Error() + ">" + string(buffer), 64 | } 65 | } 66 | 67 | return msg 68 | } 69 | 70 | func main() { 71 | versionPtr := flag.Bool("version", false, "print version") 72 | installPtr := flag.Bool("install", false, "install host app for browsers") 73 | chromeExtIDPtr := flag.String("chromeExtID", "aaaaaaaaaaabbbbbbbbbbccccccccccc", "chrome extension id to allow") 74 | firefoxExtIDPtr := flag.String("firefoxExtID", "extension@socketify.net", "firefox extension id to allow") 75 | uninstallPtr := flag.Bool("uninstall", false, "uninstall host app from browsers") 76 | 77 | flag.Parse() 78 | 79 | if *versionPtr { 80 | os.Stdout.Write([]byte(fmt.Sprintf("version: %s\n", version))) 81 | os.Exit(0) 82 | } 83 | 84 | if *installPtr { 85 | if install(*chromeExtIDPtr, *firefoxExtIDPtr) { 86 | os.Stdout.Write([]byte("install: succeeded\n")) 87 | os.Exit(0) 88 | } else { 89 | os.Stdout.Write([]byte("install: failed\n")) 90 | os.Exit(1) 91 | } 92 | } 93 | 94 | if *uninstallPtr { 95 | uninstall() 96 | os.Stdout.Write([]byte("uninstall: completed\n")) 97 | os.Exit(0) 98 | } 99 | 100 | init := read() 101 | if init.Event == "error" { 102 | write(message{ 103 | Event: "close", 104 | Error: init.Error, 105 | Debug: init.Debug, 106 | }) 107 | os.Exit(1) 108 | } 109 | 110 | switch init.Event { 111 | case "open-udpPeer": 112 | udpPeer(init.Address) 113 | break 114 | case "open-tcpServer": 115 | tcpServer(init.Address) 116 | break 117 | case "open-tcpClient": 118 | tcpClient(init.Address) 119 | break 120 | default: 121 | write(message{ 122 | Event: "close", 123 | Error: "unknown socket type", 124 | }) 125 | os.Exit(1) 126 | break 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Messenger/tcpServer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "os" 7 | ) 8 | 9 | var tcpServerClosed = false 10 | var tcpServerSocket *net.TCPListener 11 | var tcpServerConnections = make(map[string]*net.TCPConn) 12 | 13 | func tcpServer(addrStr string) { 14 | address, error := net.ResolveTCPAddr("tcp", addrStr) 15 | if error != nil { 16 | write(message{ 17 | Event: "close", 18 | Error: "cannot resolve tcp address", 19 | Debug: error.Error(), 20 | }) 21 | os.Exit(1) 22 | } 23 | 24 | tcpServerSocket, error = net.ListenTCP("tcp", address) 25 | if error != nil { 26 | write(message{ 27 | Event: "close", 28 | Error: "cannot listen tcp socket", 29 | Debug: error.Error(), 30 | }) 31 | os.Exit(1) 32 | } 33 | 34 | defer tcpServerSocket.Close() 35 | go tcpServerAccept() 36 | write(message{ 37 | Event: "open", 38 | Address: tcpServerSocket.Addr().String(), 39 | }) 40 | 41 | for { 42 | msg := read() 43 | switch msg.Event { 44 | case "error": 45 | if !tcpServerClosed { 46 | write(message{ 47 | Event: "close", 48 | Error: msg.Error, 49 | Debug: msg.Debug, 50 | }) 51 | tcpServerClosed = true 52 | } 53 | os.Exit(1) 54 | break 55 | case "send": 56 | tcpServerSend(msg) 57 | break 58 | case "drop": 59 | tcpServerDrop(msg.Address, "", "") 60 | break 61 | case "close": 62 | if !tcpServerClosed { 63 | write(message{ 64 | Event: "close", 65 | }) 66 | tcpServerClosed = true 67 | } 68 | tcpServerSocket.Close() 69 | os.Exit(0) 70 | break 71 | } 72 | } 73 | } 74 | 75 | func tcpServerAccept() { 76 | for { 77 | connection, error := tcpServerSocket.AcceptTCP() 78 | if error != nil { 79 | if !tcpServerClosed { 80 | write(message{ 81 | Event: "close", 82 | Error: "cannot accept tcp connection", 83 | Debug: error.Error(), 84 | }) 85 | tcpServerClosed = true 86 | } 87 | os.Exit(1) 88 | } 89 | 90 | addrStr := connection.RemoteAddr().String() 91 | tcpServerConnections[addrStr] = connection 92 | go tcpServerReceive(connection) 93 | write(message{ 94 | Event: "connect", 95 | Address: addrStr, 96 | }) 97 | } 98 | } 99 | 100 | func tcpServerReceive(conn *net.TCPConn) { 101 | addrStr := conn.RemoteAddr().String() 102 | for { 103 | buffer := make([]byte, 1500) 104 | length, error := conn.Read(buffer) 105 | if error != nil { 106 | if error == io.EOF { 107 | tcpServerDrop(addrStr, "connection closed", error.Error()) 108 | } else { 109 | tcpServerDrop(addrStr, "cannot read from tcp socket", error.Error()) 110 | } 111 | return 112 | } 113 | 114 | write(message{ 115 | Event: "receive", 116 | Address: addrStr, 117 | Payload: string(buffer[:length]), 118 | }) 119 | } 120 | } 121 | 122 | func tcpServerSend(msg message) { 123 | conn, ok := tcpServerConnections[msg.Address] 124 | if !ok { 125 | return 126 | } 127 | 128 | _, error := conn.Write([]byte(msg.Payload)) 129 | if error != nil { 130 | tcpServerDrop(conn.RemoteAddr().String(), "cannot write to tcp socket", error.Error()) 131 | } 132 | } 133 | 134 | func tcpServerDrop(addrStr string, error string, debug string) { 135 | conn, ok := tcpServerConnections[addrStr] 136 | if !ok { 137 | return 138 | } 139 | 140 | write(message{ 141 | Event: "disconnect", 142 | Address: addrStr, 143 | Error: error, 144 | Debug: debug, 145 | }) 146 | 147 | delete(tcpServerConnections, addrStr) 148 | conn.Close() 149 | } 150 | -------------------------------------------------------------------------------- /Messenger/install_darwin.go: -------------------------------------------------------------------------------- 1 | // +build darwin 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "os/user" 10 | "path/filepath" 11 | "strings" 12 | ) 13 | 14 | func install(chromeExtID string, firefoxExtID string) bool { 15 | currentUser, error := user.Current() 16 | if error != nil { 17 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 18 | os.Stdout.Write([]byte("install: cannot get current user\n")) 19 | return false 20 | } 21 | _ = os.Mkdir(filepath.Join(currentUser.HomeDir, "Socketify"), os.ModePerm) 22 | 23 | binaryPath, error := os.Executable() 24 | if error != nil { 25 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 26 | os.Stdout.Write([]byte("install: cannot get binary path\n")) 27 | return false 28 | } 29 | 30 | binaryStat, error := os.Stat(binaryPath) 31 | if error != nil { 32 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 33 | os.Stdout.Write([]byte("install: cannot get binary stat\n")) 34 | return false 35 | } 36 | 37 | if binaryStat.Mode().IsRegular() != true { 38 | os.Stdout.Write([]byte("install: binary is not a regular file\n")) 39 | return false 40 | } 41 | 42 | binaryBytes, error := ioutil.ReadFile(binaryPath) 43 | if error != nil { 44 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 45 | os.Stdout.Write([]byte("install: cannot read binary\n")) 46 | return false 47 | } 48 | 49 | userBinaryPath := filepath.Join(currentUser.HomeDir, "Socketify", binaryStat.Name()) 50 | error = ioutil.WriteFile(userBinaryPath, binaryBytes, os.ModePerm) 51 | if error != nil { 52 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 53 | os.Stdout.Write([]byte("install: cannot write binary\n")) 54 | return false 55 | } 56 | 57 | chromeManifestString := chromeManifest 58 | chromeManifestString = strings.Replace(chromeManifestString, "BIN_PATH", filepath.ToSlash(userBinaryPath), 1) 59 | chromeManifestString = strings.Replace(chromeManifestString, "EXT_ID", chromeExtID, 1) 60 | 61 | firefoxManifestString := firefoxManifest 62 | firefoxManifestString = strings.Replace(firefoxManifestString, "BIN_PATH", filepath.ToSlash(userBinaryPath), 1) 63 | firefoxManifestString = strings.Replace(firefoxManifestString, "EXT_ID", firefoxExtID, 1) 64 | 65 | chromeManifestPath := filepath.Join(currentUser.HomeDir, 66 | "Library", "Application Support", 67 | "Google", "Chrome", "NativeMessagingHosts", 68 | "net.socketify.messenger.json") 69 | error = ioutil.WriteFile(chromeManifestPath, []byte(chromeManifestString), os.ModePerm) 70 | if error != nil { 71 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 72 | os.Stdout.Write([]byte("install: cannot write chrome manifest\n")) 73 | return false 74 | } 75 | 76 | firefoxManifestPath := filepath.Join(currentUser.HomeDir, 77 | "Library", "Application Support", 78 | "Mozilla", "NativeMessagingHosts", 79 | "net.socketify.messenger.json") 80 | error = ioutil.WriteFile(firefoxManifestPath, []byte(firefoxManifestString), os.ModePerm) 81 | if error != nil { 82 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 83 | os.Stdout.Write([]byte("install: cannot write firefox manifest\n")) 84 | return false 85 | } 86 | 87 | return true 88 | } 89 | 90 | func uninstall() { 91 | currentUser, error := user.Current() 92 | if error != nil { 93 | os.Stdout.Write([]byte(fmt.Sprintf("uninstall: %s\n", error.Error()))) 94 | os.Stdout.Write([]byte("uninstall: cannot get current user\n")) 95 | return 96 | } 97 | 98 | binaryPath, error := os.Executable() 99 | if error != nil { 100 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 101 | os.Stdout.Write([]byte("uninstall: cannot get binary path\n")) 102 | return 103 | } 104 | 105 | binaryStat, error := os.Stat(binaryPath) 106 | if error != nil { 107 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 108 | os.Stdout.Write([]byte("uninstall: cannot get binary stat\n")) 109 | return 110 | } 111 | 112 | if binaryStat.Mode().IsRegular() != true { 113 | os.Stdout.Write([]byte("uninstall: binary is not a regular file\n")) 114 | return 115 | } 116 | 117 | userBinaryPath := filepath.Join(currentUser.HomeDir, "Socketify", binaryStat.Name()) 118 | error = os.Remove(userBinaryPath) 119 | if error != nil { 120 | os.Stdout.Write([]byte(fmt.Sprintf("uninstall: %s\n", error.Error()))) 121 | os.Stdout.Write([]byte("uninstall: cannot remove binary\n")) 122 | } 123 | 124 | chromeManifestPath := filepath.Join(currentUser.HomeDir, 125 | "Library", "Application Support", 126 | "Google", "Chrome", "NativeMessagingHosts", 127 | "net.socketify.messenger.json") 128 | error = os.Remove(chromeManifestPath) 129 | if error != nil { 130 | os.Stdout.Write([]byte(fmt.Sprintf("uninstall: %s\n", error.Error()))) 131 | os.Stdout.Write([]byte("uninstall: cannot remove chrome manifest\n")) 132 | } 133 | 134 | firefoxManifestPath := filepath.Join(currentUser.HomeDir, 135 | "Library", "Application Support", 136 | "Mozilla", "NativeMessagingHosts", 137 | "net.socketify.messenger.json") 138 | error = os.Remove(firefoxManifestPath) 139 | if error != nil { 140 | os.Stdout.Write([]byte(fmt.Sprintf("uninstall: %s\n", error.Error()))) 141 | os.Stdout.Write([]byte("uninstall: cannot remove firefox manifest\n")) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /Messenger/install_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "os/user" 10 | "path/filepath" 11 | "strings" 12 | 13 | "golang.org/x/sys/windows/registry" 14 | ) 15 | 16 | func install(chromeExtID string, firefoxExtID string) bool { 17 | currentUser, error := user.Current() 18 | if error != nil { 19 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 20 | os.Stdout.Write([]byte("install: cannot get current user\n")) 21 | return false 22 | } 23 | _ = os.Mkdir(filepath.Join(currentUser.HomeDir, "Socketify"), os.ModePerm) 24 | 25 | binaryPath, error := os.Executable() 26 | if error != nil { 27 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 28 | os.Stdout.Write([]byte("install: cannot get binary path\n")) 29 | return false 30 | } 31 | 32 | binaryStat, error := os.Stat(binaryPath) 33 | if error != nil { 34 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 35 | os.Stdout.Write([]byte("install: cannot get binary stat\n")) 36 | return false 37 | } 38 | 39 | if binaryStat.Mode().IsRegular() != true { 40 | os.Stdout.Write([]byte("install: binary is not a regular file\n")) 41 | return false 42 | } 43 | 44 | binaryBytes, error := ioutil.ReadFile(binaryPath) 45 | if error != nil { 46 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 47 | os.Stdout.Write([]byte("install: cannot read binary\n")) 48 | return false 49 | } 50 | 51 | userBinaryPath := filepath.Join(currentUser.HomeDir, "Socketify", binaryStat.Name()) 52 | error = ioutil.WriteFile(userBinaryPath, binaryBytes, os.ModePerm) 53 | if error != nil { 54 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 55 | os.Stdout.Write([]byte("install: cannot write binary\n")) 56 | return false 57 | } 58 | 59 | chromeManifestString := chromeManifest 60 | chromeManifestString = strings.Replace(chromeManifestString, "BIN_PATH", filepath.ToSlash(userBinaryPath), 1) 61 | chromeManifestString = strings.Replace(chromeManifestString, "EXT_ID", chromeExtID, 1) 62 | 63 | firefoxManifestString := firefoxManifest 64 | firefoxManifestString = strings.Replace(firefoxManifestString, "BIN_PATH", filepath.ToSlash(userBinaryPath), 1) 65 | firefoxManifestString = strings.Replace(firefoxManifestString, "EXT_ID", firefoxExtID, 1) 66 | 67 | chromeManifestPath := filepath.Join(currentUser.HomeDir, "Socketify", "net.socketify.messenger_chrome.json") 68 | error = ioutil.WriteFile(chromeManifestPath, []byte(chromeManifestString), os.ModePerm) 69 | if error != nil { 70 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 71 | os.Stdout.Write([]byte("install: cannot write chrome manifest\n")) 72 | return false 73 | } 74 | 75 | firefoxManifestPath := filepath.Join(currentUser.HomeDir, "Socketify", "net.socketify.messenger_firefox.json") 76 | error = ioutil.WriteFile(firefoxManifestPath, []byte(firefoxManifestString), os.ModePerm) 77 | if error != nil { 78 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 79 | os.Stdout.Write([]byte([]byte("install: cannot write firefox manifest\n"))) 80 | return false 81 | } 82 | 83 | key, _, error := registry.CreateKey(registry.CURRENT_USER, `SOFTWARE\Google\Chrome\NativeMessagingHosts\net.socketify.messenger`, registry.WRITE) 84 | if error != nil { 85 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 86 | os.Stdout.Write([]byte("install: cannot create chrome registry key\n")) 87 | return false 88 | } 89 | error = key.SetStringValue("", chromeManifestPath) 90 | if error != nil { 91 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 92 | os.Stdout.Write([]byte("install: cannot set chrome registry key\n")) 93 | return false 94 | } 95 | 96 | key, _, error = registry.CreateKey(registry.CURRENT_USER, `SOFTWARE\Mozilla\NativeMessagingHosts\net.socketify.messenger`, registry.WRITE) 97 | if error != nil { 98 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 99 | os.Stdout.Write([]byte("install: cannot create firefox registry key\n")) 100 | return false 101 | } 102 | error = key.SetStringValue("", firefoxManifestPath) 103 | if error != nil { 104 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 105 | os.Stdout.Write([]byte("install: cannot set firefox registry key\n")) 106 | return false 107 | } 108 | 109 | return true 110 | } 111 | 112 | func uninstall() { 113 | currentUser, error := user.Current() 114 | if error != nil { 115 | os.Stdout.Write([]byte(fmt.Sprintf("uninstall: %s\n", error.Error()))) 116 | os.Stdout.Write([]byte("uninstall: cannot get current user\n")) 117 | return 118 | } 119 | 120 | binaryPath, error := os.Executable() 121 | if error != nil { 122 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 123 | os.Stdout.Write([]byte("uninstall: cannot get binary path\n")) 124 | return 125 | } 126 | 127 | binaryStat, error := os.Stat(binaryPath) 128 | if error != nil { 129 | os.Stdout.Write([]byte(fmt.Sprintf("install: %s\n", error.Error()))) 130 | os.Stdout.Write([]byte("uninstall: cannot get binary stat\n")) 131 | return 132 | } 133 | 134 | if binaryStat.Mode().IsRegular() != true { 135 | os.Stdout.Write([]byte("uninstall: binary is not a regular file\n")) 136 | return 137 | } 138 | 139 | userBinaryPath := filepath.Join(currentUser.HomeDir, "Socketify", binaryStat.Name()) 140 | error = os.Remove(userBinaryPath) 141 | if error != nil { 142 | os.Stdout.Write([]byte(fmt.Sprintf("uninstall: %s\n", error.Error()))) 143 | os.Stdout.Write([]byte("uninstall: cannot remove binary\n")) 144 | } 145 | 146 | chromeManifestPath := filepath.Join(currentUser.HomeDir, "Socketify", "net.socketify.messenger_chrome.json") 147 | error = os.Remove(chromeManifestPath) 148 | if error != nil { 149 | os.Stdout.Write([]byte(fmt.Sprintf("uninstall: %s\n", error.Error()))) 150 | os.Stdout.Write([]byte("uninstall: cannot remove chrome manifest\n")) 151 | } 152 | 153 | firefoxManifestPath := filepath.Join(currentUser.HomeDir, "Socketify", "net.socketify.messenger_firefox.json") 154 | error = os.Remove(firefoxManifestPath) 155 | if error != nil { 156 | os.Stdout.Write([]byte(fmt.Sprintf("uninstall: %s\n", error.Error()))) 157 | os.Stdout.Write([]byte("uninstall: cannot remove firefox manifest\n")) 158 | } 159 | 160 | error = registry.DeleteKey(registry.CURRENT_USER, `SOFTWARE\Google\Chrome\NativeMessagingHosts\net.socketify.messenger`) 161 | if error != nil { 162 | os.Stdout.Write([]byte(fmt.Sprintf("uninstall: %s\n", error.Error()))) 163 | os.Stdout.Write([]byte("uninstall: cannot delete chrome registry key\n")) 164 | } 165 | 166 | error = registry.DeleteKey(registry.CURRENT_USER, `SOFTWARE\Mozilla\NativeMessagingHosts\net.socketify.messenger`) 167 | if error != nil { 168 | os.Stdout.Write([]byte(fmt.Sprintf("uninstall: %s\n", error.Error()))) 169 | os.Stdout.Write([]byte("uninstall: cannot delete firefox registry key\n")) 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /Chrome/socketify.js: -------------------------------------------------------------------------------- 1 | window.uuidv4 = function () { 2 | return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { 3 | var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); 4 | return v.toString(16); 5 | }); 6 | }; 7 | 8 | window.socketify = { 9 | _sockets: {}, 10 | _post: function (message) { 11 | window.postMessage({ 12 | _tab: { 13 | dir: "socketify-outbound", 14 | _ext: message 15 | } 16 | }, "*"); 17 | }, 18 | _handle: function (message) { 19 | var id = message.id; 20 | if (!id) { 21 | return; 22 | } 23 | 24 | var socket = socketify._sockets[id]; 25 | if (!socket) { 26 | return; 27 | } 28 | 29 | var msg = message._msg; 30 | if (!msg) { 31 | return; 32 | } 33 | 34 | switch (msg.event) { 35 | case "open": { 36 | var openHandler = socket.onOpen; 37 | if (openHandler) { 38 | openHandler(msg.address); 39 | } 40 | } return; 41 | case "connect": { 42 | var connectHandler = socket.onConnect; 43 | if (connectHandler && socket.id[0] === 's') { 44 | connectHandler(msg.address); 45 | } 46 | } return; 47 | case "receive": { 48 | var receiveHandler = socket.onReceive; 49 | if (receiveHandler) { 50 | if (socket.id[0] === 'c') { 51 | receiveHandler(msg.payload); 52 | } else { 53 | receiveHandler(msg.address, msg.payload); 54 | } 55 | } 56 | } return; 57 | case "disconnect": { 58 | var disconnectHandler = socket.onDisconnect; 59 | if (disconnectHandler && socket.id[0] === 's') { 60 | disconnectHandler(msg.address, msg.error); 61 | } 62 | } return; 63 | case "close": { 64 | var closeHandler = socket.onClose; 65 | if (closeHandler) { 66 | closeHandler(msg.error); 67 | } 68 | } return; 69 | } 70 | }, 71 | tcpServer: function (address, handlers) { 72 | var id = `s-${uuidv4()}`; 73 | var socket = { 74 | id: id, 75 | onOpen: handlers.onOpen || function (address) { /* unhandled */ }, 76 | onConnect: handlers.onConnect || function (address) { /* unhandled */ }, 77 | onReceive: handlers.onReceive || function (address, message) { /* unhandled */ }, 78 | onDisconnect: handlers.onDisconnect || function (address, error) { /* unhandled */ }, 79 | onClose: handlers.onClose || function (error) { /* unhandled */ }, 80 | send: function (target, message) { 81 | socketify._post({ 82 | id: id, 83 | _msg: { 84 | event: "send", 85 | address: target, 86 | payload: message 87 | } 88 | }); 89 | }, 90 | drop: function (target) { 91 | socketify._post({ 92 | id: id, 93 | _msg: { 94 | event: "drop", 95 | address: target 96 | } 97 | }); 98 | }, 99 | close: function () { 100 | socketify._post({ 101 | id: id, 102 | _msg: { 103 | event: "close" 104 | } 105 | }); 106 | } 107 | }; 108 | 109 | socketify._sockets[id] = socket; 110 | socketify._post({ 111 | id: id, 112 | _msg: { 113 | event: "open-tcpServer", 114 | address: address 115 | } 116 | }); 117 | 118 | return socket; 119 | }, 120 | tcpClient: function (address, handlers) { 121 | var id = `c-${uuidv4()}`; 122 | var socket = { 123 | id: id, 124 | onOpen: handlers.onOpen || function (address) { /* unhandled */ }, 125 | onReceive: handlers.onReceive || function (message) { /* unhandled */ }, 126 | onClose: handlers.onClose || function (error) { /* unhandled */ }, 127 | send: function (message) { 128 | socketify._post({ 129 | id: id, 130 | _msg: { 131 | event: "send", 132 | payload: message 133 | } 134 | }); 135 | }, 136 | close: function () { 137 | socketify._post({ 138 | id: id, 139 | _msg: { 140 | event: "close" 141 | } 142 | }); 143 | } 144 | }; 145 | 146 | socketify._sockets[id] = socket; 147 | socketify._post({ 148 | id: id, 149 | _msg: { 150 | event: "open-tcpClient", 151 | address: address 152 | } 153 | }); 154 | 155 | return socket; 156 | }, 157 | udpPeer: function (address, handlers) { 158 | var id = `p-${uuidv4()}`; 159 | var socket = { 160 | id: id, 161 | onOpen: handlers.onOpen || function (address) { /* unhandled */ }, 162 | onReceive: handlers.onReceive || function (address, message) { /* unhandled */ }, 163 | onClose: handlers.onClose || function (error) { /* unhandled */ }, 164 | send: function (target, message) { 165 | socketify._post({ 166 | id: id, 167 | _msg: { 168 | event: "send", 169 | address: target, 170 | payload: message 171 | } 172 | }); 173 | }, 174 | close: function () { 175 | socketify._post({ 176 | id: id, 177 | _msg: { 178 | event: "close" 179 | } 180 | }); 181 | } 182 | }; 183 | 184 | socketify._sockets[id] = socket; 185 | socketify._post({ 186 | id: id, 187 | _msg: { 188 | event: "open-udpPeer", 189 | address: address 190 | } 191 | }); 192 | 193 | return socket; 194 | } 195 | }; 196 | 197 | window.addEventListener("message", function (event) { 198 | if (event.source !== window || event.data._tab.dir !== "socketify-inbound") { 199 | return; 200 | } 201 | 202 | socketify._handle(event.data._tab._ext); 203 | }, false); 204 | -------------------------------------------------------------------------------- /Firefox/socketify.js: -------------------------------------------------------------------------------- 1 | window.uuidv4 = function () { 2 | return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { 3 | var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); 4 | return v.toString(16); 5 | }); 6 | }; 7 | 8 | window.socketify = { 9 | _sockets: {}, 10 | _post: function (message) { 11 | window.postMessage({ 12 | _tab: { 13 | dir: "socketify-outbound", 14 | _ext: message 15 | } 16 | }, "*"); 17 | }, 18 | _handle: function (message) { 19 | var id = message.id; 20 | if (!id) { 21 | return; 22 | } 23 | 24 | var socket = socketify._sockets[id]; 25 | if (!socket) { 26 | return; 27 | } 28 | 29 | var msg = message._msg; 30 | if (!msg) { 31 | return; 32 | } 33 | 34 | switch (msg.event) { 35 | case "open": { 36 | var openHandler = socket.onOpen; 37 | if (openHandler) { 38 | openHandler(msg.address); 39 | } 40 | } return; 41 | case "connect": { 42 | var connectHandler = socket.onConnect; 43 | if (connectHandler && socket.id[0] === 's') { 44 | connectHandler(msg.address); 45 | } 46 | } return; 47 | case "receive": { 48 | var receiveHandler = socket.onReceive; 49 | if (receiveHandler) { 50 | if (socket.id[0] === 'c') { 51 | receiveHandler(msg.payload); 52 | } else { 53 | receiveHandler(msg.address, msg.payload); 54 | } 55 | } 56 | } return; 57 | case "disconnect": { 58 | var disconnectHandler = socket.onDisconnect; 59 | if (disconnectHandler && socket.id[0] === 's') { 60 | disconnectHandler(msg.address, msg.error); 61 | } 62 | } return; 63 | case "close": { 64 | var closeHandler = socket.onClose; 65 | if (closeHandler) { 66 | closeHandler(msg.error); 67 | } 68 | } return; 69 | } 70 | }, 71 | tcpServer: function (address, handlers) { 72 | var id = `s-${uuidv4()}`; 73 | var socket = { 74 | id: id, 75 | onOpen: handlers.onOpen || function (address) { /* unhandled */ }, 76 | onConnect: handlers.onConnect || function (address) { /* unhandled */ }, 77 | onReceive: handlers.onReceive || function (address, message) { /* unhandled */ }, 78 | onDisconnect: handlers.onDisconnect || function (address, error) { /* unhandled */ }, 79 | onClose: handlers.onClose || function (error) { /* unhandled */ }, 80 | send: function (target, message) { 81 | socketify._post({ 82 | id: id, 83 | _msg: { 84 | event: "send", 85 | address: target, 86 | payload: message 87 | } 88 | }); 89 | }, 90 | drop: function (target) { 91 | socketify._post({ 92 | id: id, 93 | _msg: { 94 | event: "drop", 95 | address: target 96 | } 97 | }); 98 | }, 99 | close: function () { 100 | socketify._post({ 101 | id: id, 102 | _msg: { 103 | event: "close" 104 | } 105 | }); 106 | } 107 | }; 108 | 109 | socketify._sockets[id] = socket; 110 | socketify._post({ 111 | id: id, 112 | _msg: { 113 | event: "open-tcpServer", 114 | address: address 115 | } 116 | }); 117 | 118 | return socket; 119 | }, 120 | tcpClient: function (address, handlers) { 121 | var id = `c-${uuidv4()}`; 122 | var socket = { 123 | id: id, 124 | onOpen: handlers.onOpen || function (address) { /* unhandled */ }, 125 | onReceive: handlers.onReceive || function (message) { /* unhandled */ }, 126 | onClose: handlers.onClose || function (error) { /* unhandled */ }, 127 | send: function (message) { 128 | socketify._post({ 129 | id: id, 130 | _msg: { 131 | event: "send", 132 | payload: message 133 | } 134 | }); 135 | }, 136 | close: function () { 137 | socketify._post({ 138 | id: id, 139 | _msg: { 140 | event: "close" 141 | } 142 | }); 143 | } 144 | }; 145 | 146 | socketify._sockets[id] = socket; 147 | socketify._post({ 148 | id: id, 149 | _msg: { 150 | event: "open-tcpClient", 151 | address: address 152 | } 153 | }); 154 | 155 | return socket; 156 | }, 157 | udpPeer: function (address, handlers) { 158 | var id = `p-${uuidv4()}`; 159 | var socket = { 160 | id: id, 161 | onOpen: handlers.onOpen || function (address) { /* unhandled */ }, 162 | onReceive: handlers.onReceive || function (address, message) { /* unhandled */ }, 163 | onClose: handlers.onClose || function (error) { /* unhandled */ }, 164 | send: function (target, message) { 165 | socketify._post({ 166 | id: id, 167 | _msg: { 168 | event: "send", 169 | address: target, 170 | payload: message 171 | } 172 | }); 173 | }, 174 | close: function () { 175 | socketify._post({ 176 | id: id, 177 | _msg: { 178 | event: "close" 179 | } 180 | }); 181 | } 182 | }; 183 | 184 | socketify._sockets[id] = socket; 185 | socketify._post({ 186 | id: id, 187 | _msg: { 188 | event: "open-udpPeer", 189 | address: address 190 | } 191 | }); 192 | 193 | return socket; 194 | } 195 | }; 196 | 197 | window.addEventListener("message", function (event) { 198 | if (event.source !== window || event.data._tab.dir !== "socketify-inbound") { 199 | return; 200 | } 201 | 202 | socketify._handle(event.data._tab._ext); 203 | }, false); 204 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | # API Documentation 2 | 3 | ## Contents 4 | 5 | - [uuidv4](#function-uuidv4) 6 | - [socketify](#object-socketify) 7 | - [udpPeer](#function-udppeerbindaddress-handlers) 8 | - [tcpClient](#function-tcpclientserveraddress-handlers) 9 | - [tcpServer](#function-tcpserverlistenaddress-handlers) 10 | - [udpPeer](#object-udppeer) 11 | - [id](#string-id) 12 | - [onOpen](#function-onopenbindaddress) 13 | - [onReceive](#function-onreceivepeeraddress-message) 14 | - [onClose](#function-oncloseerror) 15 | - [send](#function-sendpeeraddress-message) 16 | - [close](#function-close) 17 | - [tcpClient](#object-tcpclient) 18 | - [id](#string-id-1) 19 | - [onOpen](#function-onopenlocaladdress) 20 | - [onReceive](#function-onreceivemessage) 21 | - [onClose](#function-oncloseerror-1) 22 | - [send](#function-sendmessage) 23 | - [close](#function-close-1) 24 | - [tcpServer](#object-tcpserver) 25 | - [id](#string-id-2) 26 | - [onOpen](#function-onopenlistenaddress) 27 | - [onConnect](#function-onconnectclientaddress) 28 | - [onReceive](#function-onreceiveclientaddress-message) 29 | - [onDisconnect](#function-ondisconnectclientaddress-error) 30 | - [onClose](#function-oncloseerror-2) 31 | - [send](#function-sendclientaddress-message) 32 | - [drop](#function-dropclientaddress) 33 | - [close](#function-close-2) 34 | 35 | ## `function` uuidv4 36 | 37 | Takes no parameter, generates and returns [UUIDv4↗](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)) `string` back. 38 | 39 | ```js 40 | var coolID = uuidv4(); 41 | console.log(`my cool universally unique ID is ${coolID}`); 42 | ``` 43 | 44 | ## `object` socketify 45 | 46 | This object being injected by content script at document start event and exposes socket creation functions to window. 47 | 48 | ### `function` udpPeer(bindAddress, handlers) 49 | 50 | - `string` **bindAddress** 51 | 52 | local address to bind socket 53 | 54 | - `object` **handlers** 55 | 56 | contains [`onOpen`](#function-onopenbindaddress), [`onReceive`](#function-onreceivepeeraddress-message), [`onClose`](#function-oncloseerror) event handling functions 57 | 58 | **returns** [`object` udpPeer](#object-udppeer) 59 | 60 | Creates UDP socket, binds to specified address and fires `onOpen` or `onClose` event afterwards. 61 | 62 | ```js 63 | var myPeer = socketify.udpPeer(":9696", { 64 | onOpen: function (address) { 65 | console.log(`peer bound to <${address}>`); 66 | }, 67 | onReceive: function (address, message) { 68 | console.log(`peer received <${address}>: ${message}`); 69 | }, 70 | onClose: function (error) { 71 | if (error) { 72 | console.log(`peer closed with error: ${error}`); 73 | } else { 74 | console.log(`peer closed`); 75 | } 76 | } 77 | }); 78 | ``` 79 | 80 | ### `function` tcpClient(serverAddress, handlers) 81 | 82 | - `string` **serverAddress** 83 | 84 | server address to connect 85 | 86 | - `object` **handlers** 87 | 88 | contains [`onOpen`](#function-onopenlocaladdress), [`onReceive`](#function-onreceivemessage), [`onClose`](#function-oncloseerror-1) event functions 89 | 90 | **returns** [`object` tcpClient](#object-tcpclient) 91 | 92 | Creates TCP socket, opens connection to server with specified address and fires `onOpen` or `onClose` event afterwards. 93 | 94 | ```js 95 | var myClient = socketify.tcpClient("127.0.0.1:9696", { 96 | onOpen: function (address) { 97 | console.log(`client bound to <${address}> and connected`); 98 | }, 99 | onReceive: function (message) { 100 | console.log(`client received: ${message}`); 101 | }, 102 | onClose: function (error) { 103 | if (error) { 104 | console.log(`client closed with error: ${error}`); 105 | } else { 106 | console.log(`client closed`); 107 | } 108 | } 109 | }); 110 | ``` 111 | 112 | ### `function` tcpServer(listenAddress, handlers) 113 | 114 | - `string` **listenAddress** 115 | 116 | local address to listen from 117 | 118 | - `object` **handlers** 119 | 120 | contains [`onOpen`](#function-onopenlistenaddress), [`onConnect`](#function-onconnectclientaddress), [`onReceive`](#function-onreceiveclientaddress-message), [`onDisconnect`](#function-ondisconnectclientaddress-error), [`onClose`](#function-oncloseerror-2) event functions 121 | 122 | **returns** [`object` tcpServer](#object-tcpserver) 123 | 124 | Creates TCP socket, starts listening on specified address and fires `onOpen` or `onClose` afterwards. 125 | 126 | ```js 127 | var myServer = socketify.tcpServer(":9696", { 128 | onOpen: function (address) { 129 | console.log(`server bound to <${address}> and listening`); 130 | }, 131 | onConnect: function (address) { 132 | console.log(`server connected to <${address}>`); 133 | }, 134 | onReceive: function (address, message) { 135 | console.log(`server received <${address}>: ${message}`); 136 | }, 137 | onDisconnect: function (address, error) { 138 | if (error) { 139 | console.log(`server disconnected from <${address}> with error: ${error}`); 140 | } else { 141 | console.log(`server disconnected from <${address}>`); 142 | } 143 | }, 144 | onClose: function (error) { 145 | if (error) { 146 | console.log(`server closed with error: ${error}`); 147 | } else { 148 | console.log(`server closed`); 149 | } 150 | } 151 | }); 152 | ``` 153 | 154 | ## `object` udpPeer 155 | 156 | UDP socket instance on browser that bridges native calls. 157 | 158 | ### `string` id 159 | 160 | Unique socket id assigned by window to specify instance, primarly used to dispatch calls. 161 | 162 | ```js 163 | // Sample code 164 | ``` 165 | 166 | ### `function` onOpen(bindAddress) 167 | 168 | - `string` **bindAddress** 169 | 170 | local bound address 171 | 172 | > This function does things! 173 | 174 | ```js 175 | // Sample code 176 | ``` 177 | 178 | ### `function` onReceive(peerAddress, message) 179 | 180 | - `string` **peerAddress** 181 | 182 | sender peer address 183 | 184 | - `object` **message** 185 | 186 | message received from peer 187 | 188 | > This function does things! 189 | 190 | ```js 191 | // Sample code 192 | ``` 193 | 194 | ### `function` onClose(error) 195 | 196 | - `string` **error** _(optional)_ 197 | 198 | error message, will be `undefined` if socket closed with [`function` close()](#function-close) 199 | 200 | > This function does things! 201 | 202 | ```js 203 | // Sample code 204 | ``` 205 | 206 | ### `function` send(peerAddress, message) 207 | 208 | - `string` **peerAddress** 209 | 210 | target peer address 211 | 212 | - `object` **message** 213 | 214 | message to send to peer 215 | 216 | > This function does things! 217 | 218 | ```js 219 | // Sample code 220 | ``` 221 | 222 | ### `function` close() 223 | 224 | > This function does things! 225 | 226 | ```js 227 | // Sample code 228 | ``` 229 | 230 | ## `object` tcpClient 231 | 232 | TCP client socket instance on browser that bridges native calls. 233 | 234 | ### `string` id 235 | 236 | Unique socket id assigned by window to specify instance, primarly used to dispatch calls. 237 | 238 | ```js 239 | // Sample code 240 | ``` 241 | 242 | ### `function` onOpen(localAddress) 243 | 244 | - `string` **localAddress** 245 | 246 | local bound address 247 | 248 | > This function does things! 249 | 250 | ```js 251 | // Sample code 252 | ``` 253 | 254 | ### `function` onReceive(message) 255 | 256 | - `object` **message** 257 | 258 | message received from server 259 | 260 | > This function does things! 261 | 262 | ```js 263 | // Sample code 264 | ``` 265 | 266 | ### `function` onClose(error) 267 | 268 | - `string` **error** _(optional)_ 269 | 270 | error message, will be `undefined` if socket closed with [`function` close()](#function-close-1) 271 | 272 | > This function does things! 273 | 274 | ```js 275 | // Sample code 276 | ``` 277 | 278 | ### `function` send(message) 279 | 280 | - `object` **message** 281 | 282 | message to send to server 283 | 284 | > This function does things! 285 | 286 | ```js 287 | // Sample code 288 | ``` 289 | 290 | ### `function` close() 291 | 292 | > This function does things! 293 | 294 | ```js 295 | // Sample code 296 | ``` 297 | 298 | ## `object` tcpServer 299 | 300 | TCP server socket instance on browser that bridges native calls. 301 | 302 | ### `string` id 303 | 304 | Unique socket id assigned by window to specify instance, primarly used to dispatch calls. 305 | 306 | ```js 307 | // Sample code 308 | ``` 309 | 310 | ### `function` onOpen(listenAddress) 311 | 312 | - `string` **listenAddress** 313 | 314 | local bound address 315 | 316 | > This function does things! 317 | 318 | ```js 319 | // Sample code 320 | ``` 321 | 322 | ### `function` onConnect(clientAddress) 323 | 324 | - `string` **clientAddress** 325 | 326 | connected client address 327 | 328 | > This function does things! 329 | 330 | ```js 331 | // Sample code 332 | ``` 333 | 334 | ### `function` onReceive(clientAddress, message) 335 | 336 | - `string` **clientAddress** 337 | 338 | sender client address 339 | 340 | - `object` **message** 341 | 342 | message received from client 343 | 344 | > This function does things! 345 | 346 | ```js 347 | // Sample code 348 | ``` 349 | 350 | ### `function` onDisconnect(clientAddress, error) 351 | 352 | - `string` **clientAddress** 353 | 354 | disconnected client address 355 | 356 | - `string` **error** _(optional)_ 357 | 358 | error message, will be `undefined` if connection closed with [`function` drop()](#function-dropclientaddress) 359 | 360 | > This function does things! 361 | 362 | ```js 363 | // Sample code 364 | ``` 365 | 366 | ### `function` onClose(error) 367 | 368 | - `string` **error** _(optional)_ 369 | 370 | error message, will be `undefined` if socket closed with [`function` close()](#function-close-2) 371 | 372 | > This function does things! 373 | 374 | ```js 375 | // Sample code 376 | ``` 377 | 378 | ### `function` send(clientAddress, message) 379 | 380 | - `string` **clientAddress** 381 | 382 | target client address 383 | 384 | - `object` **message** 385 | 386 | message to send to client 387 | 388 | > This function does things! 389 | 390 | ```js 391 | // Sample code 392 | ``` 393 | 394 | ### `function` drop(clientAddress) 395 | 396 | - `string` **clientAddress** 397 | 398 | target client address 399 | 400 | > This function does things! 401 | 402 | ```js 403 | // Sample code 404 | ``` 405 | 406 | ### `function` close() 407 | 408 | > This function does things! 409 | 410 | ```js 411 | // Sample code 412 | ``` 413 | -------------------------------------------------------------------------------- /Example/script.js: -------------------------------------------------------------------------------- 1 | var udpPeerElem = undefined; 2 | var tcpClientElem = undefined; 3 | var tcpServerElem = undefined; 4 | 5 | var inputElem = undefined; 6 | 7 | var logsElem = undefined; 8 | 9 | function init() { 10 | console.log(`init()`); 11 | 12 | setTimeout(function () { 13 | if (window.socketify) { 14 | document.getElementById("page").style.display = "block"; 15 | } else { 16 | document.getElementById("unavailable").style.display = "block"; 17 | } 18 | }, 100); 19 | 20 | udpPeerElem = document.getElementById("udpPeer"); 21 | tcpClientElem = document.getElementById("tcpClient"); 22 | tcpServerElem = document.getElementById("tcpServer"); 23 | 24 | inputElem = document.getElementById("input"); 25 | 26 | logsElem = document.getElementById("logs"); 27 | } 28 | 29 | var udpPeer = undefined; 30 | var tcpClient = undefined; 31 | var tcpServer = undefined; 32 | 33 | var activeType = "udpPeer"; 34 | 35 | function switchTo(type) { 36 | console.log(`switchTo(${type}) - activeType: ${activeType}`); 37 | 38 | if (type === activeType) { 39 | return; 40 | } 41 | 42 | closeClick(); 43 | 44 | switch (type) { 45 | case "udpPeer": { 46 | activeType = type; 47 | 48 | udpPeerElem.className = "active"; 49 | tcpClientElem.className = ""; 50 | tcpServerElem.className = ""; 51 | } break; 52 | case "tcpClient": { 53 | activeType = type; 54 | 55 | udpPeerElem.className = ""; 56 | tcpClientElem.className = "active"; 57 | tcpServerElem.className = ""; 58 | } break; 59 | case "tcpServer": { 60 | activeType = type; 61 | 62 | udpPeerElem.className = ""; 63 | tcpClientElem.className = ""; 64 | tcpServerElem.className = "active"; 65 | } break; 66 | } 67 | 68 | console.log(`~switchTo(${type}) - activeType: ${activeType}`); 69 | } 70 | 71 | function getInput() { 72 | var input = inputElem.value; 73 | inputElem.value = ""; 74 | return input; 75 | } 76 | 77 | var logIDs = []; 78 | var lastLogID = 0; 79 | 80 | function addLog(log) { 81 | if (!log) { 82 | return; 83 | } 84 | 85 | var isScrolledToBottom = logsElem.scrollHeight - logsElem.clientHeight <= logsElem.scrollTop + 1; 86 | 87 | var logID = lastLogID++; 88 | if (lastLogID > 1024) { 89 | lastLogID = 0; 90 | } 91 | 92 | var logElem = document.createElement("div"); 93 | logElem.id = `log-${logID}`; 94 | { 95 | if (log.type) { 96 | var typeElem = document.createElement("span"); 97 | typeElem.className = "type"; 98 | typeElem.innerHTML = log.type; 99 | logElem.appendChild(typeElem); 100 | } 101 | 102 | if (log.outEvent) { 103 | var outEventElem = document.createElement("span"); 104 | outEventElem.className = "outEvent"; 105 | outEventElem.innerHTML = log.outEvent + " →"; 106 | logElem.appendChild(outEventElem); 107 | } 108 | 109 | if (log.inEvent) { 110 | var inEventElem = document.createElement("span"); 111 | inEventElem.className = "inEvent"; 112 | inEventElem.innerHTML = log.inEvent + " ←"; 113 | logElem.appendChild(inEventElem); 114 | } 115 | 116 | if (log.address) { 117 | var addressElem = document.createElement("span"); 118 | addressElem.innerHTML = `address{${log.address}}`; 119 | logElem.appendChild(addressElem); 120 | } 121 | 122 | var payloadElem = undefined; 123 | if (log.message || log.error) { 124 | payloadElem = document.createElement("div"); 125 | payloadElem.className = "payload"; 126 | } 127 | 128 | if (log.message) { 129 | var messageElem = document.createElement("span"); 130 | messageElem.innerHTML = `message{${log.message.length} chars}`; 131 | logElem.appendChild(messageElem); 132 | 133 | if (payloadElem) { 134 | var messagePayloadElem = document.createElement("div"); 135 | messagePayloadElem.innerText = log.message; 136 | payloadElem.appendChild(messagePayloadElem); 137 | } 138 | } 139 | 140 | if (log.error) { 141 | var errorElem = document.createElement("span"); 142 | errorElem.className = "error"; 143 | errorElem.innerHTML = `error{${log.error.length} chars}`; 144 | logElem.appendChild(errorElem); 145 | 146 | if (payloadElem) { 147 | var errorPayloadElem = document.createElement("div"); 148 | errorPayloadElem.className = "error"; 149 | errorPayloadElem.innerText = log.error; 150 | payloadElem.appendChild(errorPayloadElem); 151 | console.log("error payload"); 152 | } 153 | } 154 | 155 | if (log.usage) { 156 | var usageElem = document.createElement("span"); 157 | usageElem.className = "error"; 158 | usageElem.innerHTML = log.usage; 159 | logElem.appendChild(usageElem); 160 | } 161 | 162 | if (payloadElem) { 163 | logElem.appendChild(payloadElem); 164 | } 165 | } 166 | logsElem.appendChild(logElem); 167 | 168 | logIDs.push(logID); 169 | if (logIDs.length > 32) { 170 | var topLogID = logIDs.shift(); 171 | if (topLogID !== undefined) { 172 | logsElem.removeChild(document.getElementById(`log-${topLogID}`)); 173 | } 174 | } 175 | 176 | if (isScrolledToBottom) { 177 | logsElem.scrollTop = logsElem.scrollHeight - logsElem.clientHeight; 178 | } 179 | } 180 | 181 | function openClick(input) { 182 | switch (activeType) { 183 | case "udpPeer": { 184 | if (!udpPeer) { 185 | addLog({ 186 | type: "udpPeer", 187 | outEvent: "open", 188 | address: input 189 | }); 190 | console.log(`[udpPeer] open: ${input}`); 191 | udpPeer = socketify.udpPeer(input, { 192 | onOpen: function (address) { 193 | addLog({ 194 | type: "udpPeer", 195 | inEvent: "onOpen", 196 | address: address 197 | }); 198 | console.log(`[udpPeer] onOpen: ${address}`); 199 | }, 200 | onReceive: function (address, message) { 201 | addLog({ 202 | type: "udpPeer", 203 | inEvent: "onReceive", 204 | address: address, 205 | message: message 206 | }); 207 | console.log(`[udpPeer] onReceive <${address}>: ${message}`); 208 | }, 209 | onClose: function (error) { 210 | udpPeer = undefined; 211 | addLog({ 212 | type: "udpPeer", 213 | inEvent: "onClose", 214 | error: error 215 | }); 216 | console.log(`[udpPeer] onClose: ${error}`); 217 | } 218 | }); 219 | } 220 | } break; 221 | case "tcpClient": { 222 | if (!tcpClient) { 223 | addLog({ 224 | type: "tcpClient", 225 | outEvent: "open", 226 | address: input 227 | }); 228 | console.log(`[tcpClient] open: ${input}`); 229 | tcpClient = socketify.tcpClient(input, { 230 | onOpen: function (address) { 231 | addLog({ 232 | type: "tcpClient", 233 | inEvent: "onOpen", 234 | address: address 235 | }); 236 | console.log(`[tcpClient] onOpen: ${address}`); 237 | }, 238 | onReceive: function (message) { 239 | addLog({ 240 | type: "tcpClient", 241 | inEvent: "onReceive", 242 | message: message 243 | }); 244 | console.log(`[tcpClient] onReceive: ${message}`); 245 | }, 246 | onClose: function (error) { 247 | tcpClient = undefined; 248 | addLog({ 249 | type: "tcpClient", 250 | inEvent: "onClose", 251 | error: error 252 | }); 253 | console.log(`[tcpClient] onClose: ${error}`); 254 | } 255 | }); 256 | } 257 | } break; 258 | case "tcpServer": { 259 | if (!tcpServer) { 260 | addLog({ 261 | type: "tcpServer", 262 | outEvent: "open", 263 | address: input 264 | }); 265 | console.log(`[tcpServer] open: ${input}`); 266 | tcpServer = socketify.tcpServer(input, { 267 | onOpen: function (address) { 268 | addLog({ 269 | type: "tcpServer", 270 | inEvent: "onOpen", 271 | address: address 272 | }); 273 | console.log(`[tcpServer] onOpen: ${address}`); 274 | }, 275 | onConnect: function (address) { 276 | addLog({ 277 | type: "tcpServer", 278 | inEvent: "onConnect", 279 | address: address 280 | }); 281 | console.log(`[tcpServer] onConnect <${address}>`); 282 | }, 283 | onReceive: function (address, message) { 284 | addLog({ 285 | type: "tcpServer", 286 | inEvent: "onReceive", 287 | address: address, 288 | message: message 289 | }); 290 | console.log(`[tcpServer] onReceive <${address}>: ${message}`); 291 | }, 292 | onDisconnect: function (address, error) { 293 | addLog({ 294 | type: "tcpServer", 295 | inEvent: "onDisconnect", 296 | address: address, 297 | error: error 298 | }); 299 | console.log(`[tcpServer] onDisconnect <${address}>: ${error}`); 300 | }, 301 | onClose: function (error) { 302 | tcpServer = undefined; 303 | addLog({ 304 | type: "tcpServer", 305 | inEvent: "onClose", 306 | error: error 307 | }); 308 | console.log(`[tcpServer] onClose: ${error}`); 309 | } 310 | }); 311 | } 312 | } break; 313 | } 314 | } 315 | 316 | function sendClick(input) { 317 | console.log(`sendClick(${input})`); 318 | 319 | switch (activeType) { 320 | case "udpPeer": { 321 | if (udpPeer) { 322 | var blocks = input.split(' '); 323 | if (blocks.length > 1) { 324 | var message = input.substring(blocks[0].length + 1); 325 | if (message.length > 0) { 326 | addLog({ 327 | type: "udpPeer", 328 | outEvent: "send", 329 | address: blocks[0], 330 | message: message 331 | }); 332 | console.log(`[udpPeer] send <${blocks[0]}>: ${message}`); 333 | udpPeer.send(blocks[0], message); 334 | } 335 | } 336 | } 337 | } break; 338 | case "tcpClient": { 339 | if (tcpClient) { 340 | if (input.length > 0) { 341 | addLog({ 342 | type: "tcpClient", 343 | outEvent: "send", 344 | message: input 345 | }); 346 | console.log(`[tcpClient] send: ${message}`); 347 | tcpClient.send(input); 348 | } 349 | } 350 | } break; 351 | case "tcpServer": { 352 | if (tcpServer) { 353 | var blocks = input.split(' '); 354 | if (blocks.length > 1) { 355 | var message = input.substring(blocks[0].length + 1); 356 | if (message.length > 0) { 357 | addLog({ 358 | type: "tcpServer", 359 | outEvent: "send", 360 | address: blocks[0], 361 | message: message 362 | }); 363 | console.log(`[tcpServer] send <${blocks[0]}>: ${message}`); 364 | tcpServer.send(blocks[0], message); 365 | } 366 | } 367 | } 368 | } break; 369 | } 370 | } 371 | 372 | function dropClick(input) { 373 | console.log(`dropClick(${input})`); 374 | 375 | switch (activeType) { 376 | case "udpPeer": { 377 | 378 | } break; 379 | case "tcpClient": { 380 | 381 | } break; 382 | case "tcpServer": { 383 | if (tcpServer) { 384 | if (input.length > 0) { 385 | addLog({ 386 | type: "tcpServer", 387 | outEvent: "drop", 388 | address: input 389 | }); 390 | console.log(`[tcpServer] drop: ${input}`); 391 | tcpServer.drop(input); 392 | } 393 | } 394 | } break; 395 | } 396 | } 397 | 398 | function closeClick() { 399 | console.log(`closeClick()`); 400 | 401 | switch (activeType) { 402 | case "udpPeer": { 403 | if (udpPeer) { 404 | addLog({ 405 | type: "udpPeer", 406 | outEvent: "close" 407 | }); 408 | console.log(`[udpPeer] close`); 409 | 410 | udpPeer.close(); 411 | udpPeer = undefined; 412 | } 413 | } break; 414 | case "tcpClient": { 415 | if (tcpClient) { 416 | addLog({ 417 | type: "tcpClient", 418 | outEvent: "close" 419 | }); 420 | console.log(`[tcpClient] close`); 421 | 422 | tcpClient.close(); 423 | tcpClient = undefined; 424 | } 425 | } break; 426 | case "tcpServer": { 427 | if (tcpServer) { 428 | addLog({ 429 | type: "tcpServer", 430 | outEvent: "close" 431 | }); 432 | console.log(`[tcpServer] close`); 433 | 434 | tcpServer.close(); 435 | tcpServer = undefined; 436 | } 437 | } break; 438 | } 439 | } --------------------------------------------------------------------------------