Files
Monkey-Test/WebDriverAgent/WebDriverAgentLib/Routing/FBWebServer.m
T
2019-01-25 17:01:52 +08:00

194 lines
5.9 KiB
Objective-C
Executable File

/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "FBWebServer.h"
#import <RoutingHTTPServer/RoutingConnection.h>
#import <RoutingHTTPServer/RoutingHTTPServer.h>
#import "FBCommandHandler.h"
#import "FBErrorBuilder.h"
#import "FBExceptionHandler.h"
#import "FBRouteRequest.h"
#import "FBRuntimeUtils.h"
#import "FBSession.h"
#import "FBUnknownCommands.h"
#import "FBConfiguration.h"
#import "FBLogger.h"
#import "XCUIDevice+FBHelpers.h"
static NSString *const FBServerURLBeginMarker = @"ServerURLHere->";
static NSString *const FBServerURLEndMarker = @"<-ServerURLHere";
@interface FBHTTPConnection : RoutingConnection
@end
@implementation FBHTTPConnection
- (void)handleResourceNotFound
{
[FBLogger logFmt:@"Received request for %@ which we do not handle", self.requestURI];
[super handleResourceNotFound];
}
@end
@interface FBWebServer ()
@property (nonatomic, strong) FBExceptionHandler *exceptionHandler;
@property (nonatomic, strong) RoutingHTTPServer *server;
@property (atomic, assign) BOOL keepAlive;
@end
@implementation FBWebServer
+ (NSArray<Class<FBCommandHandler>> *)collectCommandHandlerClasses
{
NSArray *handlersClasses = FBClassesThatConformsToProtocol(@protocol(FBCommandHandler));
NSMutableArray *handlers = [NSMutableArray array];
for (Class aClass in handlersClasses) {
if ([aClass respondsToSelector:@selector(shouldRegisterAutomatically)]) {
if (![aClass shouldRegisterAutomatically]) {
continue;
}
}
[handlers addObject:aClass];
}
return handlers.copy;
}
- (void)startServing
{
[FBLogger logFmt:@"Built at %s %s", __DATE__, __TIME__];
self.exceptionHandler = [FBExceptionHandler new];
[self startHTTPServer];
self.keepAlive = YES;
NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
while (self.keepAlive &&
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
}
- (void)startHTTPServer
{
self.server = [[RoutingHTTPServer alloc] init];
[self.server setRouteQueue:dispatch_get_main_queue()];
[self.server setDefaultHeader:@"Server" value:@"WebDriverAgent/1.0"];
[self.server setConnectionClass:[FBHTTPConnection self]];
[self registerRouteHandlers:[self.class collectCommandHandlerClasses]];
[self registerServerKeyRouteHandlers];
NSRange serverPortRange = FBConfiguration.bindingPortRange;
NSError *error;
BOOL serverStarted = NO;
for (NSUInteger index = 0; index < serverPortRange.length; index++) {
NSInteger port = serverPortRange.location + index;
[self.server setPort:(UInt16)port];
serverStarted = [self attemptToStartServer:self.server onPort:port withError:&error];
if (serverStarted) {
break;
}
[FBLogger logFmt:@"Failed to start web server on port %ld with error %@", (long)port, [error description]];
}
if (!serverStarted) {
[FBLogger logFmt:@"Last attempt to start web server failed with error %@", [error description]];
abort();
}
[FBLogger logFmt:@"%@http://%@:%d%@", FBServerURLBeginMarker, [XCUIDevice sharedDevice].fb_wifiIPAddress ?: @"localhost", [self.server port], FBServerURLEndMarker];
}
- (void)stopServing
{
[FBSession.activeSession kill];
if (self.server.isRunning) {
[self.server stop:NO];
}
self.keepAlive = NO;
}
- (BOOL)attemptToStartServer:(RoutingHTTPServer *)server onPort:(NSInteger)port withError:(NSError **)error
{
server.port = (UInt16)port;
NSError *innerError = nil;
BOOL started = [server start:&innerError];
if (!started) {
if (!error) {
return NO;
}
NSString *description = @"Unknown Error when Starting server";
if ([innerError.domain isEqualToString:NSPOSIXErrorDomain] && innerError.code == EADDRINUSE) {
description = [NSString stringWithFormat:@"Unable to start web server on port %ld", (long)port];
}
return
[[[[FBErrorBuilder builder]
withDescription:description]
withInnerError:innerError]
buildError:error];
}
return YES;
}
- (void)registerRouteHandlers:(NSArray *)commandHandlerClasses
{
for (Class<FBCommandHandler> commandHandler in commandHandlerClasses) {
NSArray *routes = [commandHandler routes];
for (FBRoute *route in routes) {
[self.server handleMethod:route.verb withPath:route.path block:^(RouteRequest *request, RouteResponse *response) {
NSDictionary *arguments = [NSJSONSerialization JSONObjectWithData:request.body options:NSJSONReadingMutableContainers error:NULL];
FBRouteRequest *routeParams = [FBRouteRequest
routeRequestWithURL:request.url
parameters:request.params
arguments:arguments ?: @{}
];
[FBLogger verboseLog:routeParams.description];
@try {
[route mountRequest:routeParams intoResponse:response];
}
@catch (NSException *exception) {
[self handleException:exception forResponse:response];
}
}];
}
}
}
- (void)handleException:(NSException *)exception forResponse:(RouteResponse *)response
{
if ([self.exceptionHandler webServer:self handleException:exception forResponse:response]) {
return;
}
id<FBResponsePayload> payload = FBResponseWithErrorFormat(@"%@\n\n%@", exception.description, exception.callStackSymbols);
[payload dispatchWithResponse:response];
}
- (void)registerServerKeyRouteHandlers
{
[self.server get:@"/health" withBlock:^(RouteRequest *request, RouteResponse *response) {
[response respondWithString:@"I-AM-ALIVE"];
}];
[self.server get:@"/wda/shutdown" withBlock:^(RouteRequest *request, RouteResponse *response) {
[response respondWithString:@"Shutting down"];
[self.delegate webServerDidRequestShutdown:self];
}];
[self registerRouteHandlers:@[FBUnknownCommands.class]];
}
@end