Appium源码解析之一——建立连接

源码解析

Posted by Mio4kon on 2017-06-18

本地运行Appium源码

建立Appium连接

根据启动命令可以知道程序入口点在lib/main.js中.

[appium:lib/main.js]

1
2
3
4
5
6
7
async function main (args = null) {
...
let router = getAppiumRouter(args);
let server = await baseServer(router, args.port, args.address);
...
return server;
}

这里其实主要做了两件事.获取router和开启server.

让我们看getAppiumRouter到底做了什么?

[appium:lib/appium.js]

1
2
3
4
function getAppiumRouter (args) {
let appium = new AppiumDriver(args);
return routeConfiguringFunction(appium);
}

这里会生成AppiumDriver,然后将driver配置给route,那么driver具体是做什么的呢?

打开源码你会发现提供了各种方法,由于代码太多了.我们直接来看有哪些方法:

可以看出里面的方法都挺重要的.很多都是之后我们需要分析到的.现在我们先记住这个AppiumDriver可以给我提供类似createSession,executeCommand这些方法.

生成了AppiumDriver会把它传给routeConfiguringFunction.routeConfiguringFunctionmjsonwp.js的一个方法.我们看一下它的具体实现:

Tips: routeConfiguringFunction指向的是 node_modules/appium-base-driver/build/lib/mjsonwp/mjsonwp.js,但是build目录下都是最终生成的代码,会很多乱七八糟的东西会扰乱我们阅读源码.所以建议直接到node_modules/appium-base-driver/lib/mjsonwp/mjsonwp.js中看源码,后面源码的都是这种情况

[appium-base-driver:lib/mjsonwp/mjsonwp.js]

1
2
3
4
5
6
7
8
9
10
11
12
function routeConfiguringFunction (driver) {
...
// return a function which will add all the routes to the driver
return function (app) {
for (let [path, methods] of _.toPairs(METHOD_MAP)) {
for (let [method, spec] of _.toPairs(methods)) {
// set up the express route handler
buildHandler(app, method, path, spec, driver, isSessionCommand(spec.command));
}
}
};
}

主要逻辑都是在buildHandler中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function buildHandler (app, method, path, spec, driver, isSessCmd) {
let asyncHandler = async (req, res) => {
let jsonObj = req.body;
let httpResBody = {};
let httpStatus = 200;
let newSessionId;
try {
...
if (driver.executeCommand) {
//[1]
driverRes = await driver.executeCommand(spec.command, ...args);
} else {
driverRes = await driver.execute(spec.command, ...args);
}
if (spec.command === 'createSession') {
newSessionId = driverRes[0];
driverRes = driverRes[1];
}
// add the method to the app
app[method.toLowerCase()](path, (req, res) => {
B.resolve(asyncHandler(req, res)).done();
});
}

这里面实际上就是服务器响应的处理逻辑,而逻辑是通过[1]driver.executeCommand来执行的.
executeCommand方法实际上就是之前AppiumDriver中提供的方法.这个之后会具体分析.

那么command具体是什么? 发现它是取自spec中的.回到之前的routeConfiguringFunction方法.
可以看到spec其实都是来自于METHOD_MAP.

[appium-base-driver:/lib/mjsonwp/routes.js]

这个就不用太多解释了.根据客户端的不同请求来执行不同的command.appium正是这种C/S架构.

OK,到此为止我们已经知道了入口文件main.jsgetAppiumRouter(args)方法具体的内容了.

现在我们来分析await baseServer(router, args.port, args.address)到底做了什么.其实猜都能猜到它开启了appium服务.

[appium-base-driver:lib/express/server.js]

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
async function server (configureRoutes, port, hostname = null) {
// create the actual http server
let app = express();
//[1]
let httpServer = http.createServer(app);
let close = httpServer.close.bind(httpServer);
httpServer.close = async () => {
return await new Promise((resolve, reject) => {
httpServer.on('close', resolve);
close((err) => {
if (err) reject(err);
});
});
};
return await new Promise((resolve, reject) => {
httpServer.on('error', (err) => {
...
//[2]
configureServer(app, configureRoutes);
let serverArgs = [port];
if (hostname) {
// If the hostname is omitted, the server will accept
// connections on any IP address
serverArgs.push(hostname);
}
httpServer.listen(...serverArgs, (err) => {
if (err) {
reject(err);
}
resolve(httpServer);
});
});
}

[1]http.createServer(app),的确印证了我们的猜测–开启了一个服务.其中用了node的express框架,有兴趣的可以了解下.

再来看看[2]configureServer(app, configureRoutes)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function configureServer (app, configureRoutes) {
// set up static assets
app.use(favicon(path.resolve(STATIC_DIR, 'favicon.ico')));
app.use(express.static(STATIC_DIR));
// crash routes, for testing
app.use('/wd/hub/produce_error', produceError);
app.use('/wd/hub/crash', produceCrash);
...
//[1]
configureRoutes(app);
...
}

里面处理了各种异常,很重要一点绑定了之前创建的路由[1]configureRoutes(app),configureRoutes就是之前我们获得router.

appium服务器的创建以及响应的处理我们都已经在代码中一一找到了.下一篇将具体针对创建session这个请求来看一看整个过程是如何处理的.

下一篇地址: Appium源码解析之二——第一个请求