diff --git a/templates/remotecontrol_android.html b/templates/remotecontrol_android.html
index 48d1e42..37eed41 100644
--- a/templates/remotecontrol_android.html
+++ b/templates/remotecontrol_android.html
@@ -399,14 +399,14 @@
增加快捷命令
- 下载截图
+ 下载截图
刷新
-
@@ -527,7 +527,7 @@ 增加快捷命令
$.ajax({
method: "post",
- url: this.source.url + "/app/install?udid=" + this.udid,
+ url: this.formatProviderUrl('/app/install') + "&udid=" + this.udid,
data: {
url: this.app.installUrl,
launch: this.app.launch,
@@ -546,9 +546,15 @@ 增加快捷命令
this.app.finished = true
})
},
+ formatAtxAgentUrl(path) {
+ return `${this.deviceAtxAgentUrl}${path}?host=${this.deviceHost}&port=${this.devicePort}`
+ },
+ formatProviderUrl(path) {
+ return `${this.deviceProviderUrl}${path}?host=${this.providerHost}&port=${this.providerPort}`
+ },
appLaunch(packageName) {
$.ajax({
- url: `${this.deviceUrl}/session/${packageName}`,
+ url: this.formatAtxAgentUrl(`/session/${packageName}`),
method: "post"
}).then(ret => {
console.log(ret)
@@ -593,9 +599,8 @@ 增加快捷命令
})
},
loadWhatsinput(callback) {
- console.log(this.whatsInputUrl)
let defer = $.Deferred()
- let ws = new WebSocket(this.whatsInputUrl)
+ let ws = new WebSocket(this.whatsInputWSUrl)
this.websockets.winput = ws;
ws.onopen = (ev) => {
defer.resolve()
@@ -702,7 +707,7 @@ 增加快捷命令
// })
$.ajax({
method: "get",
- url: this.deviceUrl + "/info/rotation"
+ url: this.formatAtxAgentUrl("/info/rotation")
}).done(ret => {
this.$notify({
message: "Rotation updated",
@@ -730,7 +735,7 @@ 增加快捷命令
runShell(command) {
return $.ajax({
method: "get",
- url: this.deviceUrl + "/shell",
+ url: this.formatAtxAgentUrl("/shell"),
data: {
"command": command,
},
@@ -761,7 +766,7 @@ 增加快捷命令
},
loadTerminal() {
let term;
- let ws = new WebSocket("ws://" + this.address + "/term");
+ let ws = new WebSocket(this.deviceAtxAgentTermWSUrl);
ws.binaryType = "arraybuffer"
function ab2str(buf) {
@@ -858,7 +863,7 @@ 增加快捷命令
versionName: "",
versionCode: "",
}
- $.getJSON(`${this.deviceUrl}/packages/${packageName}/info`).then(ret => {
+ $.getJSON(this.formatAtxAgentUrl(`/packages/${packageName}/info`)).then(ret => {
if (ret.success && ret.data) {
item.label = ret.data.label
item.versionName = ret.data.versionName
@@ -892,7 +897,7 @@ 增加快捷命令
this.closeSyncTouchpad()
},
mirrorDisplay() {
- let ws = new WebSocket(this.deviceUrl.replace(/^http/, "ws") + '/minicap');
+ let ws = new WebSocket(`${this.deviceAtxAgentCapWSUrl}`);
this.websockets.screen = ws;
ws.onopen = (ev) => {
@@ -941,7 +946,7 @@ 增加快捷命令
bounds: {}
}
- let ws = new WebSocket("ws://" + this.address + "/minitouch")
+ let ws = new WebSocket(this.deviceAtxAgentTouchWSUrl)
this.websockets.touchpad = ws
ws.onopen = (ret) => {
@@ -1445,20 +1450,51 @@ 增加快捷命令
address() {
return this.source.atxAgentAddress
},
- deviceUrl() {
- return "http://" + this.address
+ providerHost() {
+ return this.source.url.split('/')[2].split(':')[0]
+ },
+ providerPort() {
+ return this.source.url.split(':')[2]
+ },
+ deviceHost() {
+ return this.address.split(':')[0]
+ },
+ devicePort() {
+ return this.address.split(':')[1]
+ },
+ deviceWhatsInputPort() {
+ return this.source.whatsInputAddress.split(':')[1]
+ },
+ deviceAtxAgentCapWSUrl() {
+ var host = window.location.host;
+ return `ws://${host}/websocket/atxagent/minicap?host=${this.deviceHost}&port=${this.devicePort}`
+ },
+ deviceAtxAgentTouchWSUrl() {
+ var host = window.location.host;
+ return `ws://${host}/websocket/atxagent/minitouch?host=${this.deviceHost}&port=${this.devicePort}`
+ },
+ deviceAtxAgentTermWSUrl() {
+ var host = window.location.host;
+ return `ws://${host}/websocket/atxagent/term?host=${this.deviceHost}&port=${this.devicePort}`
+ },
+ deviceAtxAgentUrl() {
+ var host = window.location.host;
+ return "http://" + host + '/api/v1/atxagent'
},
- remoteTerminal() {
- return "http://" + this.address + "/term"
+ deviceProviderUrl() {
+ var host = window.location.host;
+ return `http://${host}/api/v1/provider`
},
- screenshotUrl() {
- return "http://" + this.address + "/screenshot/0"
+ screenshotAtxAgentUrl() {
+ var host = window.location.host;
+ return `http://${host}/api/v1/atxagent/screenshot/0?host=${this.deviceHost}&port=${this.devicePort}`
},
remoteConnectAddr() {
return "adb connect " + this.source.remoteConnectAddress
},
- whatsInputUrl() {
- return "ws://" + this.source.whatsInputAddress
+ whatsInputWSUrl() {
+ var host = window.location.host;
+ return `ws://${host}/websocket/whatsinput?host=${this.deviceHost}&port=${this.deviceWhatsInputPort}`
},
displayLinked() {
return this.websockets.screen !== null;
diff --git a/web/urls.py b/web/urls.py
index 2502384..c0fb70b 100644
--- a/web/urls.py
+++ b/web/urls.py
@@ -9,7 +9,8 @@
APIDeviceListHandler, APIDevicePropertiesHandler,
APIUserDeviceActiveHandler, APIUserDeviceHandler,
AppleDeviceListHandler, DeviceChangesWSHandler,
- DeviceItemHandler, DeviceListHandler)
+ DeviceItemHandler, DeviceListHandler, AndroidProviderProxyHandler,
+ AndroidDeviceWSProxyHandler, AndroidDeviceAtxAgentProxyHandler)
from .views.group import (APIGroupUserListHandler, APIUserGroupListHandler,
UserGroupCreateHandler)
from .views.provider import ProviderHeartbeatWSHandler
@@ -35,6 +36,9 @@
(r"/websocket/devicechanges", DeviceChangesWSHandler),
(r"/websocket/heartbeat", ProviderHeartbeatWSHandler),
+ (r"/websocket/atxagent/(minicap|term|minitouch)", AndroidDeviceWSProxyHandler),
+ (r"/websocket/whatsinput", AndroidDeviceWSProxyHandler),
+
# For compability of atx-server-1
(r"/list", make_redirect_handler("/api/v1/devices")),
# RESP API
@@ -47,6 +51,9 @@
(r"/api/v1/user/devices/([^/]+)/active", APIUserDeviceActiveHandler), # GET
(r"/api/v1/user/settings", APIUserSettingsHandler), # GET, PUT
(r"/api/v1/admins", APIAdminListHandler), # GET, POST
+ (r"/api/v1/atxagent/(.*)", AndroidDeviceAtxAgentProxyHandler),
+ (r"/api/v1/provider/(app/install|cold)", AndroidProviderProxyHandler),
+
## Group API
# (r"/api/v1/user/groups/([^/]+)", APIUserGroupHandler), # GET, POST, DELETE TODO(ssx)
(r"/api/v1/user/groups", APIUserGroupListHandler), # GET, POST
diff --git a/web/views/device.py b/web/views/device.py
index 9fed87a..dd7c212 100644
--- a/web/views/device.py
+++ b/web/views/device.py
@@ -6,12 +6,15 @@
import urllib
from functools import wraps
from typing import Union
+from six.moves import http_cookies as Cookie
+from urllib.parse import urlencode
import rethinkdb as rdb
import tornado.websocket
from logzero import logger
from rethinkdb import r
from tornado import gen
+from tornado.platform.asyncio import to_asyncio_future
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
from tornado.ioloop import IOLoop
from tornado.web import HTTPError, authenticated
@@ -523,3 +526,102 @@ async def open(self, udid):
return
self.write_message("device is yours")
+
+
+class CheckMixin(object):
+ def check_host_port(self):
+ host = self.get_argument('host', None)
+ port = self.get_argument('port', None)
+ if not host or not port:
+ hcookie = self.request.headers.get('cookie')
+ if hcookie:
+ cookie = Cookie.SimpleCookie()
+ for hcookie_part in hcookie.split(';'):
+ hcookie_part = hcookie_part.lstrip()
+ try:
+ cookie.load(hcookie_part)
+ except Cookie.CookieError:
+ logger.warning('Found malformed cookie')
+ else:
+ if 'host' in cookie:
+ host = cookie['host'].value
+ if 'port' in cookie:
+ port = cookie['port'].value
+ return host, port
+
+
+class AndroidDeviceAtxAgentProxyHandler(CheckMixin, AuthRequestHandler):
+ """ device atx agent proxy """
+ async def do_proxy(self, method, *args, **kwargs):
+ host, port = self.check_host_port()
+ if not host or not port:
+ self.set_status(400) # bad request
+ self.write_json({
+ "success": False,
+ "description": "Missing host and port"
+ })
+ arguments = self.request.query_arguments # 内部是List[bytes]格式
+ arguments.pop('host', None)
+ arguments.pop('port', None)
+ params = {name: self.get_argument(name) for name in arguments}
+ try:
+ if params:
+ url = f'http://{host}:{port}/{args[0]}?{urlencode(params)}'
+ else:
+ url = f'http://{host}:{port}/{args[0]}'
+ logger.info(f'Forward http {method} from {self.request.uri} <--> {url}')
+ headers = self.request.headers
+ response = await tornado.httpclient.AsyncHTTPClient().fetch(
+ url, method=method, headers=headers, body=self.request.body, allow_nonstandard_methods=True, **kwargs)
+ self.set_cookie("host", host)
+ self.set_cookie("port", port)
+ self.write(response.body)
+ except Exception as e:
+ print(e)
+ self.write_error(500)
+
+ async def get(self, *args, **kwargs):
+ await self.do_proxy('GET', *args, **kwargs)
+
+ async def post(self, *args, **kwargs):
+ await self.do_proxy('POST', *args, **kwargs)
+
+
+AndroidProviderProxyHandler = AndroidDeviceAtxAgentProxyHandler
+
+
+class AndroidDeviceWSProxyHandler(CheckMixin, tornado.websocket.WebSocketHandler):
+ def __init__(self, *args, **kwargs):
+ super(AndroidDeviceWSProxyHandler, self).__init__(*args, **kwargs)
+ self._client = None
+
+ def check_origin(self, origin):
+ return True
+
+ async def open(self, *args, **kwargs):
+ host, port = self.check_host_port()
+ if not host or not port:
+ self.write_message("Missing host and port")
+ self.close()
+ return
+
+ schema = {"http":"ws", "https":"wss"}[self.request.protocol]
+ if args:
+ target_url = f'{schema}://{host}:{port}/{args[0]}'
+ else:
+ target_url = f'{schema}://{host}:{port}/'
+ logger.info(f'Forward websocket from {self.request.uri} <--> {target_url}')
+ self._client = await to_asyncio_future(
+ tornado.websocket.websocket_connect(target_url, on_message_callback=self.on_target_message))
+
+ def on_message(self, message):
+ if self._client is not None:
+ self._client.write_message(message, binary=isinstance(message, bytes))
+
+ def on_target_message(self, message):
+ if message:
+ self.write_message(message, binary=isinstance(message, bytes))
+
+ def on_close(self):
+ if self._client is not None:
+ self._client.close()