0.6.0: Ability to handle simultaneous requests; limited React Native QT support
This commit is contained in:
parent
a120f59261
commit
f3381bb5a2
|
@ -1,12 +1,13 @@
|
|||
# react-native-http-bridge
|
||||
|
||||
Simple HTTP server for [React Native](https://github.com/facebook/react-native)
|
||||
Simple HTTP server for [React Native](https://github.com/facebook/react-native).
|
||||
Created for [Status.im](https://github.com/status-im/status-react).
|
||||
|
||||
Since 0.5.0 supports and handles GET, POST, PUT and DELETE requests.
|
||||
The library can be useful for handling requests with `application/json` content type
|
||||
(and this is the only content type we support at the current stage) and returning different responses.
|
||||
|
||||
Created for [Status.im](https://github.com/status-im/status-react).
|
||||
Since 0.6.0 can handle millions of requests at the same time and also includes some very basic support for [React Native QT](https://github.com/status-im/react-native-desktop).
|
||||
|
||||
## Install
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@ allprojects {
|
|||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.1"
|
||||
compileSdkVersion 26
|
||||
buildToolsVersion "26.0.2"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
@ -20,14 +21,13 @@ public class Server extends NanoHTTPD {
|
|||
private static final String TAG = "HttpServer";
|
||||
private static final String SERVER_EVENT_ID = "httpServerResponseReceived";
|
||||
|
||||
private Map<String, ReadableMap> response;
|
||||
private ReactContext reactContext;
|
||||
private Response requestResponse;
|
||||
private Map<String, Response> responses;
|
||||
|
||||
public Server(ReactContext context, int port) {
|
||||
super(port);
|
||||
reactContext = context;
|
||||
response = new HashMap<>();
|
||||
responses = new HashMap<>();
|
||||
|
||||
Log.d(TAG, "Server started");
|
||||
}
|
||||
|
@ -36,11 +36,12 @@ public class Server extends NanoHTTPD {
|
|||
public Response serve(IHTTPSession session) {
|
||||
Log.d(TAG, "Request received!");
|
||||
|
||||
requestResponse = null;
|
||||
Random rand = new Random();
|
||||
String requestId = String.format("%d:%d", System.currentTimeMillis(), rand.nextInt(1000000));
|
||||
|
||||
WritableMap request;
|
||||
try {
|
||||
request = fillRequestMap(session);
|
||||
request = fillRequestMap(session, requestId);
|
||||
} catch (Exception e) {
|
||||
return newFixedLengthResponse(
|
||||
Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, e.getMessage()
|
||||
|
@ -49,25 +50,28 @@ public class Server extends NanoHTTPD {
|
|||
|
||||
this.sendEvent(reactContext, SERVER_EVENT_ID, request);
|
||||
|
||||
while (requestResponse == null) {
|
||||
while (responses.get(requestId) == null) {
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Exception while waiting: " + e);
|
||||
}
|
||||
}
|
||||
return requestResponse;
|
||||
Response response = responses.get(requestId);
|
||||
responses.remove(requestId);
|
||||
return response;
|
||||
}
|
||||
|
||||
public void respond(int code, String type, String body) {
|
||||
requestResponse = newFixedLengthResponse(Status.lookup(code), type, body);
|
||||
public void respond(String requestId, int code, String type, String body) {
|
||||
requestResponses.put(requestId, newFixedLengthResponse(Status.lookup(code), type, body));
|
||||
}
|
||||
|
||||
private WritableMap fillRequestMap(IHTTPSession session) throws Exception {
|
||||
private WritableMap fillRequestMap(IHTTPSession session, String requestId) throws Exception {
|
||||
Method method = session.getMethod();
|
||||
WritableMap request = Arguments.createMap();
|
||||
request.putString("url", session.getUri());
|
||||
request.putString("type", method.name());
|
||||
request.putString("requestId", requestId);
|
||||
|
||||
Map<String, String> files = new HashMap<>();
|
||||
session.parseBody(files);
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="android" name="Android">
|
||||
<configuration />
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/java" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,7 @@
|
|||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
set(REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_TYPE_NAMES ${REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_TYPE_NAMES}
|
||||
\"RCTHttpServer\" PARENT_SCOPE)
|
||||
|
||||
set(REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_SRC ${REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_SRC}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rcthttpserver.cpp PARENT_SCOPE)
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* Copyright (c) 2017-present, Status Research and Development GmbH.
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "rcthttpserver.h"
|
||||
#include "bridge.h"
|
||||
#include "eventdispatcher.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QByteArray>
|
||||
#include <QVariantMap>
|
||||
|
||||
namespace {
|
||||
struct RegisterQMLMetaType {
|
||||
RegisterQMLMetaType() {
|
||||
qRegisterMetaType<RCTHttpServer*>();
|
||||
}
|
||||
} registerMetaType;
|
||||
} // namespace
|
||||
|
||||
class RCTHttpServerPrivate {
|
||||
public:
|
||||
Bridge* bridge = nullptr;
|
||||
};
|
||||
|
||||
RCTHttpServer::RCTHttpServer(QObject* parent) : QObject(parent), d_ptr(new RCTHttpServerPrivate) {}
|
||||
|
||||
RCTHttpServer::~RCTHttpServer() {}
|
||||
|
||||
void RCTHttpServer::setBridge(Bridge* bridge) {
|
||||
Q_D(RCTHttpServer);
|
||||
d->bridge = bridge;
|
||||
}
|
||||
|
||||
QString RCTHttpServer::moduleName() {
|
||||
return "RCTHttpServer";
|
||||
}
|
||||
|
||||
QList<ModuleMethod*> RCTHttpServer::methodsToExport() {
|
||||
return QList<ModuleMethod*>{};
|
||||
}
|
||||
|
||||
QVariantMap RCTHttpServer::constantsToExport() {
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
void RCTHttpServer::start(int port, QString serviceName) {
|
||||
|
||||
}
|
||||
|
||||
void RCTHttpServer::stop() {
|
||||
|
||||
}
|
||||
|
||||
void RCTHttpServer::respond(QString requestId, int code, QString type, QString body) {
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* Copyright (c) 2017-present, Status Research and Development GmbH.
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RCTHTTPSERVER_H
|
||||
#define RCTHTTPSERVER_H
|
||||
|
||||
#include "moduleinterface.h"
|
||||
|
||||
#include <QVariantMap>
|
||||
|
||||
class RCTHttpServerPrivate;
|
||||
class RCTHttpServer : public QObject, public ModuleInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(ModuleInterface)
|
||||
|
||||
Q_DECLARE_PRIVATE(RCTHttpServer)
|
||||
|
||||
public:
|
||||
Q_INVOKABLE RCTHttpServer(QObject* parent = 0);
|
||||
~RCTHttpServer();
|
||||
|
||||
void setBridge(Bridge* bridge) override;
|
||||
|
||||
QString moduleName() override;
|
||||
QList<ModuleMethod*> methodsToExport() override;
|
||||
QVariantMap constantsToExport() override;
|
||||
|
||||
Q_INVOKABLE void start(int port, QString serviceName);
|
||||
Q_INVOKABLE void stop();
|
||||
Q_INVOKABLE void respond(QString requestId, int code, QString type, QString body);
|
||||
|
||||
private:
|
||||
QScopedPointer<RCTHttpServerPrivate> d_ptr;
|
||||
};
|
||||
|
||||
#endif // RCTHTTPSERVER_H
|
|
@ -22,7 +22,7 @@ module.exports = {
|
|||
DeviceEventEmitter.removeListener('httpServerResponseReceived');
|
||||
},
|
||||
|
||||
respond: function (code, type, body) {
|
||||
Server.respond(code, type, body);
|
||||
respond: function (requestId, code, type, body) {
|
||||
Server.respond(requestId, code, type, body);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
#import "WGCDWebServerDataResponse.h"
|
||||
#import "WGCDWebServerDataRequest.h"
|
||||
#import "WGCDWebServerPrivate.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
@interface RCTHttpServer : NSObject <RCTBridgeModule> {
|
||||
WGCDWebServer* _webServer;
|
||||
WGCDWebServerDataResponse* _requestResponse;
|
||||
NSMutableDictionary* _requestResponses;
|
||||
}
|
||||
@end
|
||||
|
||||
|
@ -25,38 +26,48 @@ RCT_EXPORT_MODULE();
|
|||
|
||||
- (void)initResponseReceivedFor:(WGCDWebServer *)server forType:(NSString*)type {
|
||||
[server addDefaultHandlerForMethod:type requestClass:[WGCDWebServerDataRequest class] processBlock:^WGCDWebServerResponse *(WGCDWebServerRequest* request) {
|
||||
_requestResponse = NULL;
|
||||
|
||||
long long milliseconds = (long long)([[NSDate date] timeIntervalSince1970] * 1000.0);
|
||||
int r = arc4random_uniform(1000000);
|
||||
NSString *requestId = [NSString stringWithFormat:@"%lld:%d", milliseconds, r];
|
||||
|
||||
// it's a weird way of doing it, fix it
|
||||
@try {
|
||||
if ([WGCDWebServerTruncateHeaderValue(request.contentType) isEqualToString:@"application/json"]) {
|
||||
WGCDWebServerDataRequest* dataRequest = (WGCDWebServerDataRequest*)request;
|
||||
[self.bridge.eventDispatcher sendAppEventWithName:@"httpServerResponseReceived"
|
||||
body:@{@"postData": dataRequest.jsonObject,
|
||||
body:@{@"requestId": requestId,
|
||||
@"postData": dataRequest.jsonObject,
|
||||
@"type": type,
|
||||
@"url": request.URL.relativeString}];
|
||||
} else {
|
||||
[self.bridge.eventDispatcher sendAppEventWithName:@"httpServerResponseReceived"
|
||||
body:@{@"type": type,
|
||||
body:@{@"requestId": requestId,
|
||||
@"type": type,
|
||||
@"url": request.URL.relativeString}];
|
||||
}
|
||||
} @catch (NSException *exception) {
|
||||
[self.bridge.eventDispatcher sendAppEventWithName:@"httpServerResponseReceived"
|
||||
body:@{@"type": type,
|
||||
body:@{@"requestId": requestId,
|
||||
@"type": type,
|
||||
@"url": request.URL.relativeString}];
|
||||
}
|
||||
|
||||
while (_requestResponse == NULL) {
|
||||
while ([_requestResponses objectForKey:requestId] == NULL) {
|
||||
[NSThread sleepForTimeInterval:0.01f];
|
||||
}
|
||||
return _requestResponse;
|
||||
|
||||
WGCDWebServerDataResponse* response = [_requestResponses objectForKey:requestId];
|
||||
[_requestResponses removeObjectForKey:requestId];
|
||||
return response;
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(start:(NSInteger) port
|
||||
serviceName:(NSString *) serviceName)
|
||||
{
|
||||
RCTLogInfo(@"Running HTTP bridge server: %d", port);
|
||||
RCTLogInfo(@"Running HTTP bridge server: %ld", port);
|
||||
_requestResponses = [[NSMutableDictionary alloc] init];
|
||||
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
_webServer = [[WGCDWebServer alloc] init];
|
||||
|
@ -81,13 +92,16 @@ RCT_EXPORT_METHOD(stop)
|
|||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(respond:(NSInteger) code
|
||||
RCT_EXPORT_METHOD(respond: (NSString *) requestId
|
||||
code: (NSInteger) code
|
||||
type: (NSString *) type
|
||||
body: (NSString *) body)
|
||||
{
|
||||
NSData* data = [body dataUsingEncoding:NSUTF8StringEncoding];
|
||||
_requestResponse = [[WGCDWebServerDataResponse alloc] initWithData:data contentType:type];
|
||||
_requestResponse.statusCode = code;
|
||||
WGCDWebServerDataResponse* requestResponse = [[WGCDWebServerDataResponse alloc] initWithData:data contentType:type];
|
||||
requestResponse.statusCode = code;
|
||||
|
||||
[_requestResponses setObject:requestResponse forKey:requestId];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
{
|
||||
"name": "react-native-http-bridge",
|
||||
"version": "0.5.2",
|
||||
"version": "0.6.0",
|
||||
"description": "A simple HTTP debug server for React Native apps",
|
||||
"main": "httpServer.js",
|
||||
"repository": {
|
||||
|
|
Loading…
Reference in New Issue