This commit is contained in:
Ari Lazier 2015-10-08 12:23:42 -06:00
parent 1991d9df95
commit c8c8a883bf
2 changed files with 98 additions and 88 deletions

View File

@ -55,14 +55,21 @@ RCT_EXPORT_MODULE()
GCDWebServer *webServer = [[GCDWebServer alloc] init]; GCDWebServer *webServer = [[GCDWebServer alloc] init];
RJSRPCServer *rpcServer = [[RJSRPCServer alloc] init]; RJSRPCServer *rpcServer = [[RJSRPCServer alloc] init];
// Add a handler to respond to GET requests on any URL // Add a handler to respond to POST requests on any URL
[webServer addDefaultHandlerForMethod:@"POST" [webServer addDefaultHandlerForMethod:@"POST"
requestClass:[GCDWebServerDataRequest class] requestClass:[GCDWebServerDataRequest class]
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) { processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:[(GCDWebServerDataRequest *)request data] options:0 error:nil]; NSError *error;
GCDWebServerDataResponse *response = [GCDWebServerDataResponse responseWithJSONObject:[rpcServer performRequest:request.path args:json]]; NSData *data = [(GCDWebServerDataRequest *)request data];
[response setValue:@"http://localhost:8081" forAdditionalHeader:@"Access-Control-Allow-Origin"]; NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
return response; if (error) {
NSLog(@"%@", error);
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_UnprocessableEntity underlyingError:error message:@"Invalid RPC request"];
}
GCDWebServerDataResponse *response = [GCDWebServerDataResponse responseWithJSONObject:[rpcServer performRequest:request.path args:json]];
[response setValue:@"http://localhost:8081" forAdditionalHeader:@"Access-Control-Allow-Origin"];
return response;
}]; }];
[webServer startWithPort:8082 bonjourName:nil]; [webServer startWithPort:8082 bonjourName:nil];

View File

@ -32,135 +32,135 @@
#include "shared_realm.hpp" #include "shared_realm.hpp"
#include "results.hpp" #include "results.hpp"
using RPCObjectID = long; using RPCObjectID = u_int64_t;
using RPCRequest = std::function<NSDictionary *(NSDictionary *dictionary)>; using RPCRequest = std::function<NSDictionary *(NSDictionary *dictionary)>;
@implementation RJSRPCServer { @implementation RJSRPCServer {
JSGlobalContextRef s_context; JSGlobalContextRef _context;
std::map<std::string, RPCRequest> s_requests; std::map<std::string, RPCRequest> _requests;
std::map<RPCObjectID, JSObjectRef> s_objects; std::map<RPCObjectID, JSObjectRef> _objects;
} }
- (instancetype)init { - (instancetype)init {
self = [super init]; self = [super init];
if (self) { if (self) {
s_context = JSGlobalContextCreate(NULL); _context = JSGlobalContextCreate(NULL);
s_requests["/create_realm"] = [=](NSDictionary *dict) { _requests["/create_realm"] = [=](NSDictionary *dict) {
// We should have a method for serializing schema rather than relying on JSValue
JSValueRef value = [[JSValue valueWithObject:dict JSValueRef value = [[JSValue valueWithObject:dict
inContext:[JSContext contextWithJSGlobalContextRef:s_context]] JSValueRef]; inContext:[JSContext contextWithJSGlobalContextRef:_context]] JSValueRef];
RPCObjectID realmId = [self storeObject:RealmConstructor(s_context, NULL, 1, &value, NULL)]; JSValueRef ex = NULL;
RPCObjectID realmId = [self storeObject:RealmConstructor(_context, NULL, 1, &value, &ex)];
if (ex) {
return @{@"error": @(RJSStringForValue(_context, ex).c_str())};
}
return @{@"result": @(realmId)}; return @{@"result": @(realmId)};
}; };
s_requests["/begin_transaction"] = [=](NSDictionary *dict) { _requests["/begin_transaction"] = [=](NSDictionary *dict) {
RPCObjectID realmId = [dict[@"realmId"] longValue]; RPCObjectID realmId = [dict[@"realmId"] unsignedLongValue];
RJSGetInternal<realm::SharedRealm *>(s_objects[realmId])->get()->begin_transaction(); RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->begin_transaction();
return @{}; return @{};
}; };
s_requests["/cancel_transaction"] = [=](NSDictionary *dict) { _requests["/cancel_transaction"] = [=](NSDictionary *dict) {
RPCObjectID realmId = [dict[@"realmId"] longValue]; RPCObjectID realmId = [dict[@"realmId"] unsignedLongValue];
RJSGetInternal<realm::SharedRealm *>(s_objects[realmId])->get()->cancel_transaction(); RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->cancel_transaction();
return @{}; return @{};
}; };
s_requests["/commit_transaction"] = [=](NSDictionary *dict) { _requests["/commit_transaction"] = [=](NSDictionary *dict) {
RPCObjectID realmId = [dict[@"realmId"] longValue]; RPCObjectID realmId = [dict[@"realmId"] unsignedLongValue];
RJSGetInternal<realm::SharedRealm *>(s_objects[realmId])->get()->commit_transaction(); RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->commit_transaction();
return @{}; return @{};
}; };
s_requests["/call_realm_method"] = [=](NSDictionary *dict) { _requests["/call_realm_method"] = [=](NSDictionary *dict) {
NSString *name = dict[@"name"]; NSString *name = dict[@"name"];
return [self performObjectMethod:name.UTF8String return [self performObjectMethod:name.UTF8String
classMethods:RJSRealmFuncs classMethods:RJSRealmFuncs
args:dict[@"arguments"] args:dict[@"arguments"]
objectId:[dict[@"realmId"] longValue]]; objectId:[dict[@"realmId"] unsignedLongValue]];
}; };
s_requests["/dispose_realm"] = [=](NSDictionary *dict) { _requests["/get_property"] = [=](NSDictionary *dict) {
RPCObjectID realmId = [dict[@"realmId"] longValue];
JSValueUnprotect(s_context, s_objects[realmId]);
s_objects.erase(realmId);
return @{};
};
s_requests["/get_property"] = [=](NSDictionary *dict) {
JSValueRef exception = NULL; JSValueRef exception = NULL;
NSString *name = dict[@"name"]; NSString *name = dict[@"name"];
JSStringRef propString = RJSStringForString(name.UTF8String); JSStringRef propString = RJSStringForString(name.UTF8String);
RPCObjectID objectId = [dict[@"objectId"] longValue]; RPCObjectID objectId = [dict[@"objectId"] unsignedLongValue];
JSValueRef propertyValue = ObjectGetProperty(s_context, s_objects[objectId], propString, &exception); JSValueRef propertyValue = ObjectGetProperty(_context, _objects[objectId], propString, &exception);
JSStringRelease(propString); JSStringRelease(propString);
if (exception) { if (exception) {
return @{@"error": @(RJSStringForValue(s_context, exception).c_str())}; return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
} }
return @{@"result": [self resultForJSValue:propertyValue]}; return @{@"result": [self resultForJSValue:propertyValue]};
}; };
s_requests["/set_property"] = [=](NSDictionary *dict) { _requests["/set_property"] = [=](NSDictionary *dict) {
JSStringRef propString = RJSStringForString([dict[@"name"] UTF8String]); JSStringRef propString = RJSStringForString([dict[@"name"] UTF8String]);
RPCObjectID realmId = [dict[@"objectId"] longValue]; RPCObjectID realmId = [dict[@"objectId"] unsignedLongValue];
JSValueRef value = [self valueFromDictionary:dict[@"value"]]; JSValueRef value = [self valueFromDictionary:dict[@"value"]];
JSValueRef exception = NULL; JSValueRef exception = NULL;
ObjectSetProperty(s_context, s_objects[realmId], propString, value, &exception); ObjectSetProperty(_context, _objects[realmId], propString, value, &exception);
JSStringRelease(propString); JSStringRelease(propString);
return exception ? @{@"error": @(RJSStringForValue(s_context, exception).c_str())} : @{}; return exception ? @{@"error": @(RJSStringForValue(_context, exception).c_str())} : @{};
}; };
s_requests["/dispose_object"] = [=](NSDictionary *dict) { _requests["/dispose_object"] = [=](NSDictionary *dict) {
RPCObjectID oid = [dict[@"realmId"] longValue]; RPCObjectID oid = [dict[@"id"] unsignedLongValue];
JSValueUnprotect(s_context, s_objects[oid]); JSValueUnprotect(_context, _objects[oid]);
s_objects.erase(oid); _objects.erase(oid);
return @{}; return @{};
}; };
s_requests["/get_results_size"] = [=](NSDictionary *dict) { _requests["/get_results_size"] = [=](NSDictionary *dict) {
RPCObjectID resultsId = [dict[@"resultsId"] longValue]; RPCObjectID resultsId = [dict[@"resultsId"] unsignedLongValue];
JSValueRef exception = NULL; JSValueRef exception = NULL;
static JSStringRef lengthPropertyName = JSStringCreateWithUTF8CString("length"); static JSStringRef lengthPropertyName = JSStringCreateWithUTF8CString("length");
JSValueRef lengthValue = ResultsGetProperty(s_context, s_objects[resultsId], lengthPropertyName, &exception); JSValueRef lengthValue = ResultsGetProperty(_context, _objects[resultsId], lengthPropertyName, &exception);
return @{@"result": @(JSValueToNumber(s_context, lengthValue, &exception))};
return @{@"result": @(JSValueToNumber(_context, lengthValue, &exception))};
}; };
s_requests["/get_results_item"] = [=](NSDictionary *dict) { _requests["/get_results_item"] = [=](NSDictionary *dict) {
RPCObjectID resultsId = [dict[@"resultsId"] longValue]; RPCObjectID resultsId = [dict[@"resultsId"] unsignedLongValue];
long index = [dict[@"index"] longValue]; long index = [dict[@"index"] longValue];
JSValueRef exception = NULL; JSValueRef exception = NULL;
JSStringRef indexPropertyName = JSStringCreateWithUTF8CString(std::to_string(index).c_str()); JSStringRef indexPropertyName = JSStringCreateWithUTF8CString(std::to_string(index).c_str());
JSValueRef objectValue = ResultsGetProperty(s_context, s_objects[resultsId], indexPropertyName, &exception); JSValueRef objectValue = ResultsGetProperty(_context, _objects[resultsId], indexPropertyName, &exception);
JSStringRelease(indexPropertyName); JSStringRelease(indexPropertyName);
if (exception) { if (exception) {
return @{@"error": @(RJSStringForValue(s_context, exception).c_str())}; return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
} }
return @{@"result": [self resultForJSValue:objectValue]}; return @{@"result": [self resultForJSValue:objectValue]};
}; };
s_requests["/get_list_size"] = [=](NSDictionary *dict) { _requests["/get_list_size"] = [=](NSDictionary *dict) {
RPCObjectID listId = [dict[@"listId"] longValue]; RPCObjectID listId = [dict[@"listId"] unsignedLongValue];
JSValueRef exception = NULL; JSValueRef exception = NULL;
static JSStringRef lengthPropertyName = JSStringCreateWithUTF8CString("length"); static JSStringRef lengthPropertyName = JSStringCreateWithUTF8CString("length");
JSValueRef lengthValue = ArrayGetProperty(s_context, s_objects[listId], lengthPropertyName, &exception); JSValueRef lengthValue = ArrayGetProperty(_context, _objects[listId], lengthPropertyName, &exception);
return @{@"result": @(JSValueToNumber(s_context, lengthValue, &exception))}; return @{@"result": @(JSValueToNumber(_context, lengthValue, &exception))};
}; };
s_requests["/get_list_item"] = [=](NSDictionary *dict) { _requests["/get_list_item"] = [=](NSDictionary *dict) {
RPCObjectID listId = [dict[@"listId"] longValue]; RPCObjectID listId = [dict[@"listId"] unsignedLongValue];
long index = [dict[@"index"] longValue]; long index = [dict[@"index"] longValue];
JSValueRef exception = NULL; JSValueRef exception = NULL;
JSStringRef indexPropertyName = JSStringCreateWithUTF8CString(std::to_string(index).c_str()); JSStringRef indexPropertyName = JSStringCreateWithUTF8CString(std::to_string(index).c_str());
JSValueRef objectValue = ArrayGetProperty(s_context, s_objects[listId], indexPropertyName, &exception); JSValueRef objectValue = ArrayGetProperty(_context, _objects[listId], indexPropertyName, &exception);
JSStringRelease(indexPropertyName); JSStringRelease(indexPropertyName);
if (exception) { if (exception) {
return @{@"error": @(RJSStringForValue(s_context, exception).c_str())}; return @{@"error": @(RJSStringForValue(_context, exception).c_str())};
} }
return @{@"result": [self resultForJSValue:objectValue]}; return @{@"result": [self resultForJSValue:objectValue]};
}; };
s_requests["/call_list_method"] = [=](NSDictionary *dict) { _requests["/call_list_method"] = [=](NSDictionary *dict) {
NSString *name = dict[@"name"]; NSString *name = dict[@"name"];
return [self performObjectMethod:name.UTF8String return [self performObjectMethod:name.UTF8String
classMethods:RJSArrayFuncs classMethods:RJSArrayFuncs
args:dict[@"arguments"] args:dict[@"arguments"]
objectId:[dict[@"listId"] longValue]]; objectId:[dict[@"listId"] unsignedLongValue]];
}; };
} }
return self; return self;
@ -168,10 +168,14 @@ using RPCRequest = std::function<NSDictionary *(NSDictionary *dictionary)>;
- (NSDictionary *)performRequest:(NSString *)name args:(NSDictionary *)args { - (NSDictionary *)performRequest:(NSString *)name args:(NSDictionary *)args {
// perform all realm ops on the main thread // perform all realm ops on the main thread
RPCRequest action = s_requests[name.UTF8String]; RPCRequest action = _requests[name.UTF8String];
__block id response; __block id response;
dispatch_sync(dispatch_get_main_queue(), ^{ dispatch_sync(dispatch_get_main_queue(), ^{
response = action(args); try {
response = action(args);
} catch (std::exception &exception) {
response = @{@"error": [@"exception thrown: " stringByAppendingString:@(exception.what())]};
}
}); });
return response; return response;
} }
@ -190,9 +194,9 @@ using RPCRequest = std::function<NSDictionary *(NSDictionary *dictionary)>;
while (methods[index].name) { while (methods[index].name) {
if (!strcmp(methods[index].name, name)) { if (!strcmp(methods[index].name, name)) {
JSValueRef ex = NULL; JSValueRef ex = NULL;
JSValueRef ret = methods[index].callAsFunction(s_context, NULL, s_objects[oid], count, argValues, &ex); JSValueRef ret = methods[index].callAsFunction(_context, NULL, _objects[oid], count, argValues, &ex);
if (ex) { if (ex) {
return @{@"error": @(RJSStringForValue(s_context, ex).c_str())}; return @{@"error": @(RJSStringForValue(_context, ex).c_str())};
} }
return @{@"result": [self resultForJSValue:ret]}; return @{@"result": [self resultForJSValue:ret]};
} }
@ -205,31 +209,31 @@ using RPCRequest = std::function<NSDictionary *(NSDictionary *dictionary)>;
- (RPCObjectID)storeObject:(JSObjectRef)object { - (RPCObjectID)storeObject:(JSObjectRef)object {
static RPCObjectID s_next_id = 1; static RPCObjectID s_next_id = 1;
RPCObjectID next_id = s_next_id++; RPCObjectID next_id = s_next_id++;
JSValueProtect(s_context, object); JSValueProtect(_context, object);
s_objects[next_id] = object; _objects[next_id] = object;
return next_id; return next_id;
} }
- (NSDictionary *)resultForJSValue:(JSValueRef)value { - (NSDictionary *)resultForJSValue:(JSValueRef)value {
switch (JSValueGetType(s_context, value)) { switch (JSValueGetType(_context, value)) {
case kJSTypeUndefined: case kJSTypeUndefined:
return @{}; return @{};
case kJSTypeNull: case kJSTypeNull:
return @{@"value": [NSNull null]}; return @{@"value": [NSNull null]};
case kJSTypeBoolean: case kJSTypeBoolean:
return @{@"value": @(JSValueToBoolean(s_context, value))}; return @{@"value": @(JSValueToBoolean(_context, value))};
case kJSTypeNumber: case kJSTypeNumber:
return @{@"value": @(JSValueToNumber(s_context, value, NULL))}; return @{@"value": @(JSValueToNumber(_context, value, NULL))};
case kJSTypeString: case kJSTypeString:
return @{@"value": @(RJSStringForValue(s_context, value).c_str())}; return @{@"value": @(RJSStringForValue(_context, value).c_str())};
case kJSTypeObject: case kJSTypeObject:
break; break;
} }
JSObjectRef jsObject = JSValueToObject(s_context, value, NULL); JSObjectRef jsObject = JSValueToObject(_context, value, NULL);
RPCObjectID oid = [self storeObject:jsObject]; RPCObjectID oid = [self storeObject:jsObject];
if (JSValueIsObjectOfClass(s_context, value, RJSObjectClass())) { if (JSValueIsObjectOfClass(_context, value, RJSObjectClass())) {
realm::Object *object = RJSGetInternal<realm::Object *>(jsObject); realm::Object *object = RJSGetInternal<realm::Object *>(jsObject);
return @{ return @{
@"type": @(RJSTypeGet(realm::PropertyTypeObject).c_str()), @"type": @(RJSTypeGet(realm::PropertyTypeObject).c_str()),
@ -237,7 +241,7 @@ using RPCRequest = std::function<NSDictionary *(NSDictionary *dictionary)>;
@"schema": [self objectSchemaToJSONObject:object->object_schema] @"schema": [self objectSchemaToJSONObject:object->object_schema]
}; };
} }
else if (JSValueIsObjectOfClass(s_context, value, RJSArrayClass())) { else if (JSValueIsObjectOfClass(_context, value, RJSArrayClass())) {
realm::ObjectArray *array = RJSGetInternal<realm::ObjectArray *>(jsObject); realm::ObjectArray *array = RJSGetInternal<realm::ObjectArray *>(jsObject);
return @{ return @{
@"type": @(RJSTypeGet(realm::PropertyTypeArray).c_str()), @"type": @(RJSTypeGet(realm::PropertyTypeArray).c_str()),
@ -246,7 +250,7 @@ using RPCRequest = std::function<NSDictionary *(NSDictionary *dictionary)>;
@"schema": [self objectSchemaToJSONObject:array->object_schema] @"schema": [self objectSchemaToJSONObject:array->object_schema]
}; };
} }
else if (JSValueIsObjectOfClass(s_context, value, RJSResultsClass())) { else if (JSValueIsObjectOfClass(_context, value, RJSResultsClass())) {
realm::Results *results = RJSGetInternal<realm::Results *>(jsObject); realm::Results *results = RJSGetInternal<realm::Results *>(jsObject);
return @{ return @{
@"type": @"ObjectTypesRESULTS", @"type": @"ObjectTypesRESULTS",
@ -255,12 +259,11 @@ using RPCRequest = std::function<NSDictionary *(NSDictionary *dictionary)>;
@"schema": [self objectSchemaToJSONObject:results->object_schema] @"schema": [self objectSchemaToJSONObject:results->object_schema]
}; };
} }
else if (RJSIsValueArray(s_context, value)) { else if (RJSIsValueArray(_context, value)) {
JSObjectRef jsObject = JSValueToObject(s_context, value, NULL); size_t length = RJSValidatedArrayLength(_context, jsObject);
size_t length = RJSValidatedArrayLength(s_context, jsObject);
NSMutableArray *array = [NSMutableArray new]; NSMutableArray *array = [NSMutableArray new];
for (unsigned int i = 0; i < length; i++) { for (unsigned int i = 0; i < length; i++) {
[array addObject:[self resultForJSValue:JSObjectGetPropertyAtIndex(s_context, jsObject, i, NULL)]]; [array addObject:[self resultForJSValue:JSObjectGetPropertyAtIndex(_context, jsObject, i, NULL)]];
} }
return @{@"value": array}; return @{@"value": array};
} }
@ -290,24 +293,24 @@ using RPCRequest = std::function<NSDictionary *(NSDictionary *dictionary)>;
- (JSValueRef)valueFromDictionary:(NSDictionary *)dict { - (JSValueRef)valueFromDictionary:(NSDictionary *)dict {
RPCObjectID oid = [dict[@"id"] longValue]; RPCObjectID oid = [dict[@"id"] longValue];
if (oid) { if (oid) {
return s_objects[oid]; return _objects[oid];
} }
id value = dict[@"value"]; id value = dict[@"value"];
if (!value) { if (!value) {
return JSValueMakeUndefined(s_context); return JSValueMakeUndefined(_context);
} }
else if ([value isKindOfClass:[NSNull class]]) { else if ([value isKindOfClass:[NSNull class]]) {
return JSValueMakeNull(s_context); return JSValueMakeNull(_context);
} }
else if ([value isKindOfClass:[@YES class]]) { else if ([value isKindOfClass:[@YES class]]) {
return JSValueMakeBoolean(s_context, [value boolValue]); return JSValueMakeBoolean(_context, [value boolValue]);
} }
else if ([value isKindOfClass:[NSNumber class]]) { else if ([value isKindOfClass:[NSNumber class]]) {
return JSValueMakeNumber(s_context, [value doubleValue]); return JSValueMakeNumber(_context, [value doubleValue]);
} }
else if ([value isKindOfClass:[NSString class]]) { else if ([value isKindOfClass:[NSString class]]) {
return RJSValueForString(s_context, std::string([value UTF8String])); return RJSValueForString(_context, std::string([value UTF8String]));
} }
else if ([value isKindOfClass:[NSArray class]]) { else if ([value isKindOfClass:[NSArray class]]) {
NSUInteger count = [value count]; NSUInteger count = [value count];
@ -317,23 +320,23 @@ using RPCRequest = std::function<NSDictionary *(NSDictionary *dictionary)>;
jsValues[i] = [self valueFromDictionary:value[i]]; jsValues[i] = [self valueFromDictionary:value[i]];
} }
return JSObjectMakeArray(s_context, count, jsValues, NULL); return JSObjectMakeArray(_context, count, jsValues, NULL);
} }
else if ([value isKindOfClass:[NSDictionary class]]) { else if ([value isKindOfClass:[NSDictionary class]]) {
JSObjectRef jsObject = JSObjectMake(s_context, NULL, NULL); JSObjectRef jsObject = JSObjectMake(_context, NULL, NULL);
for (NSString *key in value) { for (NSString *key in value) {
JSValueRef jsValue = [self valueFromDictionary:value[key]]; JSValueRef jsValue = [self valueFromDictionary:value[key]];
JSStringRef jsKey = JSStringCreateWithCFString((__bridge CFStringRef)key); JSStringRef jsKey = JSStringCreateWithCFString((__bridge CFStringRef)key);
JSObjectSetProperty(s_context, jsObject, jsKey, jsValue, 0, NULL); JSObjectSetProperty(_context, jsObject, jsKey, jsValue, 0, NULL);
JSStringRelease(jsKey); JSStringRelease(jsKey);
} }
return jsObject; return jsObject;
} }
return JSValueMakeUndefined(s_context); return JSValueMakeUndefined(_context);
} }
@end @end