react-native-zeroconf 0.16.1 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/NOTICE +0 -3
  2. package/README.md +3 -3
  3. package/android/build.gradle +0 -3
  4. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroConfImplFactory.java +1 -1
  5. package/android/src/main/java/com/balthazargronon/RCTZeroconf/Zeroconf.java +12 -2
  6. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfModule.java +129 -4
  7. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfOptions.java +77 -0
  8. package/android/src/main/java/com/balthazargronon/RCTZeroconf/dnssd/DnssdImpl.java +654 -0
  9. package/android/src/main/java/com/balthazargronon/RCTZeroconf/nsd/NsdServiceImpl.java +393 -39
  10. package/android/src/main/java/com/balthazargronon/RCTZeroconf/nsd/ServiceInfoCallbacks.java +50 -13
  11. package/android/src/main/java/com/balthazargronon/RCTZeroconf/nsd/ServiceTypesQuery.java +296 -0
  12. package/android/src/main/java/com/github/druk/dnssd/DNSSDEmbedded.java +1 -1
  13. package/android/src/main/jni/mdnsresponder/mDNSShared/dnssd_clientshim.c +59 -22
  14. package/dist/index.js +235 -48
  15. package/index.d.ts +105 -7
  16. package/ios/RNZeroconf/RNZeroconf.m +285 -22
  17. package/package.json +28 -4
  18. package/src/index.js +214 -44
  19. package/windows/ExperimentalFeatures.props +33 -0
  20. package/windows/NuGet.config +11 -0
  21. package/windows/RNZeroconf/RNZeroconf.cpp +228 -0
  22. package/windows/RNZeroconf/RNZeroconf.def +3 -0
  23. package/windows/RNZeroconf/RNZeroconf.h +70 -0
  24. package/windows/RNZeroconf/RNZeroconf.rc +0 -0
  25. package/windows/RNZeroconf/RNZeroconf.vcxproj +150 -0
  26. package/windows/RNZeroconf/RNZeroconf.vcxproj.filters +50 -0
  27. package/windows/RNZeroconf/ReactPackageProvider.cpp +20 -0
  28. package/windows/RNZeroconf/ReactPackageProvider.h +24 -0
  29. package/windows/RNZeroconf/ReactPackageProvider.idl +9 -0
  30. package/windows/RNZeroconf/ZeroconfCore.cpp +1095 -0
  31. package/windows/RNZeroconf/ZeroconfCore.h +167 -0
  32. package/windows/RNZeroconf/pch.cpp +1 -0
  33. package/windows/RNZeroconf/pch.h +30 -0
  34. package/windows/RNZeroconf/resource.h +5 -0
  35. package/windows/RNZeroconf/targetver.h +8 -0
  36. package/windows/RNZeroconf.sln +43 -0
  37. package/android/src/main/java/com/balthazargronon/RCTZeroconf/rx2dnssd/DnssdImpl.java +0 -305
  38. package/android/src/main/java/com/github/druk/rx2dnssd/BonjourService.java +0 -400
  39. package/android/src/main/java/com/github/druk/rx2dnssd/Rx2BrowseListener.java +0 -55
  40. package/android/src/main/java/com/github/druk/rx2dnssd/Rx2Dnssd.java +0 -127
  41. package/android/src/main/java/com/github/druk/rx2dnssd/Rx2DnssdCommon.java +0 -280
  42. package/android/src/main/java/com/github/druk/rx2dnssd/Rx2DnssdEmbedded.java +0 -31
  43. package/android/src/main/java/com/github/druk/rx2dnssd/Rx2QueryListener.java +0 -70
  44. package/android/src/main/java/com/github/druk/rx2dnssd/Rx2RegisterListener.java +0 -47
  45. package/android/src/main/java/com/github/druk/rx2dnssd/Rx2ResolveListener.java +0 -51
@@ -0,0 +1,167 @@
1
+ // Zeroconf on Windows with the DNS-SD functions of windns.h (Windows 10 1809 and later).
2
+ // No React Native dependency: RNZeroconf.cpp bridges it to JavaScript, test/windows/harness.cpp tests it.
3
+ #pragma once
4
+
5
+ #ifndef UNICODE
6
+ #define UNICODE
7
+ #endif
8
+ #ifndef _UNICODE
9
+ #define _UNICODE
10
+ #endif
11
+ #ifndef WIN32_LEAN_AND_MEAN
12
+ #define WIN32_LEAN_AND_MEAN
13
+ #endif
14
+ #ifndef NOMINMAX
15
+ #define NOMINMAX
16
+ #endif
17
+
18
+ #include <winsock2.h>
19
+ #include <ws2tcpip.h>
20
+ #include <windows.h>
21
+ #include <windns.h>
22
+
23
+ #include <atomic>
24
+ #include <cstdint>
25
+ #include <functional>
26
+ #include <map>
27
+ #include <memory>
28
+ #include <mutex>
29
+ #include <set>
30
+ #include <string>
31
+ #include <utility>
32
+ #include <vector>
33
+
34
+ namespace rnzeroconf {
35
+
36
+ using TxtPairs = std::vector<std::pair<std::wstring, std::wstring>>;
37
+
38
+ struct Service {
39
+ std::wstring name;
40
+ std::wstring fullName;
41
+ std::wstring host;
42
+ uint16_t port = 0;
43
+ std::vector<std::wstring> addresses;
44
+ TxtPairs txt;
45
+ };
46
+
47
+ // { message, code, domain, serviceName } as on the other platforms.
48
+ // domain "Windows": code is a Win32 / DNS_STATUS number. domain "RNZeroconf": stringCode is set.
49
+ struct Error {
50
+ std::wstring domain;
51
+ int64_t code = 0;
52
+ std::wstring stringCode;
53
+ std::wstring message;
54
+ std::wstring serviceName;
55
+ };
56
+
57
+ using ServiceCallback = std::function<void(const Service &)>;
58
+ using ErrorCallback = std::function<void(const Error &)>;
59
+
60
+ // Called from DNS-SD threads. scanId is empty for errors that are not about a scan.
61
+ struct Events {
62
+ std::function<void(const std::string &scanId)> start;
63
+ std::function<void(const std::string &scanId)> stop;
64
+ std::function<void(const std::string &scanId, const std::wstring &name)> found;
65
+ std::function<void(const std::string &scanId, const std::wstring &name)> remove;
66
+ std::function<void(const std::string &scanId, const Service &)> resolved;
67
+ std::function<void(const Service &)> published;
68
+ std::function<void(const Service &)> unpublished;
69
+ std::function<void(const std::string &scanId, const Error &)> error;
70
+ };
71
+
72
+ struct Operation;
73
+ struct Browse;
74
+ struct Resolve;
75
+ struct Publication;
76
+
77
+ class Zeroconf {
78
+ public:
79
+ explicit Zeroconf(Events events);
80
+ ~Zeroconf();
81
+
82
+ Zeroconf(const Zeroconf &) = delete;
83
+ Zeroconf &operator=(const Zeroconf &) = delete;
84
+
85
+ // type and protocol without underscore ("http", "tcp"). "services._dns-sd" / "udp" lists service types
86
+ void Scan(
87
+ const std::string &scanId,
88
+ const std::wstring &type,
89
+ const std::wstring &protocol,
90
+ const std::wstring &domain,
91
+ const std::wstring &subtype,
92
+ const std::wstring &networkInterface,
93
+ double resolveTimeoutSeconds = 5);
94
+ void Stop(const std::string &scanId);
95
+ void StopAll();
96
+
97
+ void Publish(
98
+ const std::wstring &type,
99
+ const std::wstring &protocol,
100
+ const std::wstring &domain,
101
+ const std::wstring &name,
102
+ uint16_t port,
103
+ const TxtPairs &txt,
104
+ const std::wstring &networkInterface,
105
+ ServiceCallback resolve,
106
+ ErrorCallback reject);
107
+ void Unpublish(const std::wstring &name, ServiceCallback resolve, ErrorCallback reject);
108
+ // windns.h has no update: unpublishes and publishes again under the same name
109
+ void Update(const std::wstring &name, const TxtPairs &txt, ServiceCallback resolve, ErrorCallback reject);
110
+ void UnpublishAll();
111
+
112
+ void ResolveService(
113
+ const std::wstring &name,
114
+ const std::wstring &type,
115
+ const std::wstring &protocol,
116
+ const std::wstring &domain,
117
+ const std::wstring &networkInterface,
118
+ double timeoutSeconds,
119
+ ServiceCallback resolve,
120
+ ErrorCallback reject);
121
+
122
+ // Callbacks of windns.h, routed through a registry of live operations
123
+ void OnBrowse(const std::shared_ptr<Browse> &browse, DWORD status, PDNS_RECORD records);
124
+ void OnResolve(const std::shared_ptr<Resolve> &resolve, DWORD status, PDNS_SERVICE_INSTANCE instance);
125
+ void OnRegister(const std::shared_ptr<Publication> &publication, DWORD status, PDNS_SERVICE_INSTANCE instance);
126
+
127
+ private:
128
+ using After = std::vector<std::function<void()>>;
129
+
130
+ void StopLocked(const std::string &scanId, After &after);
131
+ void StartResolve(const std::shared_ptr<Resolve> &resolve, After &after);
132
+ void StartScanResolve(const std::shared_ptr<Browse> &browse, const std::shared_ptr<Resolve> &resolve, After &after);
133
+ void CancelResolve(const std::shared_ptr<Resolve> &resolve, After &after);
134
+ void PublishAs(
135
+ const std::wstring &regType,
136
+ const std::wstring &domain,
137
+ const std::wstring &requestedName,
138
+ uint16_t port,
139
+ const TxtPairs &txt,
140
+ ULONG interfaceIndex,
141
+ bool announce,
142
+ ServiceCallback resolve,
143
+ ErrorCallback reject);
144
+ void UnpublishWith(const std::wstring &name, bool announce, ServiceCallback resolve, ErrorCallback reject);
145
+ Service PublishedService(const Publication &publication) const;
146
+ // Service type scans also list the types this app publishes, the network doesn't answer them back
147
+ void SyncLocalType(const std::wstring &regType, const std::wstring &domain, After &after);
148
+ bool LocallyPublished(const std::wstring &typeKey) const;
149
+ bool InterfaceIndex(const std::wstring &networkInterface, ULONG &index, Error &error) const;
150
+
151
+ Events events_;
152
+ // False once destroyed, for the resolveService() timeout threads
153
+ std::shared_ptr<std::atomic<bool>> alive_;
154
+ std::recursive_mutex mutex_;
155
+ std::map<std::string, std::shared_ptr<Browse>> browses_;
156
+ // Keyed by the published name, which can differ from the requested one
157
+ std::map<std::wstring, std::shared_ptr<Publication>> publications_;
158
+ std::vector<std::shared_ptr<Resolve>> singleResolves_;
159
+ std::wstring hostName_;
160
+ };
161
+
162
+ // Helpers shared with the bridge and the harness
163
+ std::wstring Widen(const std::string &value);
164
+ std::string Narrow(const std::wstring &value);
165
+ std::wstring DescribeStatus(DWORD status);
166
+
167
+ } // namespace rnzeroconf
@@ -0,0 +1 @@
1
+ #include "pch.h"
@@ -0,0 +1,30 @@
1
+ // pch.h : include file for standard system include files,
2
+ // or project specific include files that are used frequently, but
3
+ // are changed infrequently
4
+ //
5
+
6
+ #pragma once
7
+
8
+ #include "targetver.h"
9
+
10
+ #define NOMINMAX 1
11
+ #define WIN32_LEAN_AND_MEAN 1
12
+ #define WINRT_LEAN_AND_MEAN 1
13
+
14
+ // Windows Header Files
15
+ #include <windows.h>
16
+ #undef GetCurrentTime
17
+ #include <unknwn.h>
18
+
19
+ // WinRT Header Files
20
+ #include <winrt/base.h>
21
+ #include <CppWinRTIncludes.h>
22
+ #include <winrt/Microsoft.ReactNative.h>
23
+
24
+ // C RunTime Header Files
25
+ #include <malloc.h>
26
+ #include <memory.h>
27
+ #include <stdlib.h>
28
+ #include <tchar.h>
29
+
30
+ // Reference additional headers your project requires here
@@ -0,0 +1,5 @@
1
+ //
2
+ // Microsoft Visual C++ generated include file.
3
+ // Used by RNZeroconf.rc
4
+
5
+ #pragma once
@@ -0,0 +1,8 @@
1
+ #pragma once
2
+
3
+ // Including SDKDDKVer.h defines the highest available Windows platform.
4
+
5
+ // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
6
+ // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
7
+
8
+ #include <SDKDDKVer.h>
@@ -0,0 +1,43 @@
1
+ 
2
+ Microsoft Visual Studio Solution File, Format Version 12.00
3
+ # Visual Studio Version 17
4
+ VisualStudioVersion = 17.3.32929.385
5
+ MinimumVisualStudioVersion = 10.0.40219.1
6
+ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RNZeroconf", "RNZeroconf\RNZeroconf.vcxproj", "{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}"
7
+ EndProject
8
+ Global
9
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
10
+ Debug|x64 = Debug|x64
11
+ Debug|x86 = Debug|x86
12
+ Debug|ARM64 = Debug|ARM64
13
+ Release|x64 = Release|x64
14
+ Release|x86 = Release|x86
15
+ Release|ARM64 = Release|ARM64
16
+ EndGlobalSection
17
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
18
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|x64.ActiveCfg = Debug|x64
19
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|x64.Build.0 = Debug|x64
20
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|x64.Deploy.0 = Debug|x64
21
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|x86.ActiveCfg = Debug|Win32
22
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|x86.Build.0 = Debug|Win32
23
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|x86.Deploy.0 = Debug|Win32
24
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|ARM64.ActiveCfg = Debug|ARM64
25
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|ARM64.Build.0 = Debug|ARM64
26
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|ARM64.Deploy.0 = Debug|ARM64
27
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|x64.ActiveCfg = Release|x64
28
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|x64.Build.0 = Release|x64
29
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|x64.Deploy.0 = Release|x64
30
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|x86.ActiveCfg = Release|Win32
31
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|x86.Build.0 = Release|Win32
32
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|x86.Deploy.0 = Release|Win32
33
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|ARM64.ActiveCfg = Release|ARM64
34
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|ARM64.Build.0 = Release|ARM64
35
+ {9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|ARM64.Deploy.0 = Release|ARM64
36
+ EndGlobalSection
37
+ GlobalSection(SolutionProperties) = preSolution
38
+ HideSolutionNode = FALSE
39
+ EndGlobalSection
40
+ GlobalSection(ExtensibilityGlobals) = postSolution
41
+ SolutionGuid = {D43FAD39-F619-437D-BB40-04A3982ACB6A}
42
+ EndGlobalSection
43
+ EndGlobal
@@ -1,305 +0,0 @@
1
- package com.balthazargronon.RCTZeroconf.rx2dnssd;
2
-
3
- import android.annotation.SuppressLint;
4
- import android.content.Context;
5
- import android.net.wifi.WifiManager;
6
- import android.util.Log;
7
-
8
- import com.balthazargronon.RCTZeroconf.Zeroconf;
9
- import com.balthazargronon.RCTZeroconf.ZeroconfModule;
10
- import com.facebook.react.bridge.Promise;
11
- import com.facebook.react.bridge.ReactApplicationContext;
12
- import com.facebook.react.bridge.ReadableArray;
13
- import com.facebook.react.bridge.WritableArray;
14
- import com.facebook.react.bridge.WritableMap;
15
- import com.facebook.react.bridge.WritableNativeArray;
16
- import com.facebook.react.bridge.WritableNativeMap;
17
- import com.github.druk.dnssd.DNSSDException;
18
- import com.github.druk.rx2dnssd.BonjourService;
19
- import com.github.druk.rx2dnssd.Rx2Dnssd;
20
- import com.github.druk.rx2dnssd.Rx2DnssdEmbedded;
21
-
22
- import java.net.InetAddress;
23
- import java.util.ArrayList;
24
- import java.util.HashMap;
25
- import java.util.LinkedHashMap;
26
- import java.util.concurrent.ConcurrentHashMap;
27
- import java.util.Locale;
28
- import java.util.Map;
29
- import java.util.List;
30
-
31
- import javax.annotation.Nullable;
32
-
33
- import io.reactivex.Flowable;
34
- import io.reactivex.android.schedulers.AndroidSchedulers;
35
- import io.reactivex.disposables.Disposable;
36
- import io.reactivex.schedulers.Schedulers;
37
-
38
- public class DnssdImpl implements Zeroconf {
39
- private static final String TAG = "DnssdImpl";
40
-
41
- private Rx2Dnssd rxDnssd;
42
-
43
-
44
- private Map<String, BonjourService> mPublishedServices;
45
- private Map<String, Disposable> mRegisteredDisposables;
46
-
47
- // Running browses, keyed by the id of the JS instance that started them
48
- private final Map<String, Disposable> mBrowses = new ConcurrentHashMap<>();
49
-
50
- private ZeroconfModule zeroconfModule;
51
-
52
- private ReactApplicationContext reactApplicationContext;
53
- private WifiManager.MulticastLock multicastLock;
54
-
55
- public DnssdImpl(ZeroconfModule zeroconfModule, ReactApplicationContext reactApplicationContext) {
56
- this.zeroconfModule = zeroconfModule;
57
- this.reactApplicationContext = reactApplicationContext;
58
- mPublishedServices = new HashMap<String, BonjourService>();
59
- mRegisteredDisposables = new HashMap<String, Disposable>();
60
- rxDnssd = createDnssd(reactApplicationContext);
61
- }
62
-
63
- /**
64
- * Creates the Rx2Dnssd implementation.
65
- * Always uses embedded mDNSResponder since it works across all Android versions.
66
- * The daemonic version (not bundled) is unreliable on Android as the
67
- * system daemon at /dev/socket/mdnsd doesn't exist on most devices.
68
- */
69
- private Rx2Dnssd createDnssd(Context context) {
70
- return new Rx2DnssdEmbedded(context);
71
- }
72
-
73
- @Override
74
- public void scan(final String scanId, String type, String protocol, String domain) {
75
- this.stop(scanId);
76
- acquireMulticastLock();
77
-
78
- String serviceType = getServiceType(type, protocol);
79
- Log.d(TAG, "Starting DNSSD scan " + scanId + " for: " + serviceType);
80
-
81
- sendScanEvent(ZeroconfModule.EVENT_START, new WritableNativeMap(), scanId);
82
-
83
- // A service is reported once per network interface, count them so found/remove are emitted once
84
- final Map<String, Integer> foundInterfaces = new HashMap<>();
85
- Disposable browse = rxDnssd.browse(serviceType, "local.")
86
- .doOnNext(bonjourService -> {
87
- String name = bonjourService.getServiceName();
88
- Integer count = foundInterfaces.get(name);
89
- int interfaces = count == null ? 0 : count;
90
- if (!bonjourService.isLost()) {
91
- foundInterfaces.put(name, interfaces + 1);
92
- if (interfaces == 0) {
93
- sendScanEvent(ZeroconfModule.EVENT_FOUND, serviceNameToMap(bonjourService), scanId);
94
- }
95
- } else if (interfaces <= 1) {
96
- foundInterfaces.remove(name);
97
- sendScanEvent(ZeroconfModule.EVENT_REMOVE, serviceNameToMap(bonjourService), scanId);
98
- } else {
99
- foundInterfaces.put(name, interfaces - 1);
100
- }
101
- })
102
- .filter(bonjourService -> !bonjourService.isLost())
103
- // Resolve each service independently so a single failure doesn't end the whole browse
104
- .flatMap(bonjourService -> Flowable.just(bonjourService)
105
- .compose(rxDnssd.resolve())
106
- .compose(rxDnssd.queryRecords())
107
- .onErrorResumeNext((Throwable throwable) -> {
108
- Log.e(TAG, "Error resolving service: ", throwable);
109
- sendError(throwable, "Resolving service " + bonjourService.getServiceName() + " failed: ", bonjourService.getServiceName(), scanId);
110
- return Flowable.empty();
111
- }))
112
- .subscribeOn(Schedulers.io())
113
- .observeOn(AndroidSchedulers.mainThread())
114
- .subscribe(bonjourService -> {
115
- // Lost services were filtered out above, a lost service could still come back from resolve
116
- if (bonjourService.isLost()) {
117
- return;
118
- }
119
- sendScanEvent(ZeroconfModule.EVENT_RESOLVE, serviceInfoToMap(bonjourService), scanId);
120
- }, throwable -> {
121
- Log.e(TAG, "Error browsing services: ", throwable);
122
- sendError(throwable, "Browsing services failed: ", null, scanId);
123
- if (mBrowses.remove(scanId) != null) {
124
- releaseMulticastLock();
125
- }
126
- sendScanEvent(ZeroconfModule.EVENT_STOP, new WritableNativeMap(), scanId);
127
- });
128
- mBrowses.put(scanId, browse);
129
- }
130
-
131
- private void sendScanEvent(String eventName, WritableMap body, String scanId) {
132
- body.putString(ZeroconfModule.KEY_SCAN_ID, scanId);
133
- zeroconfModule.sendEvent(reactApplicationContext, eventName, body);
134
- }
135
-
136
- private void acquireMulticastLock() {
137
- if (multicastLock == null) {
138
- @SuppressLint("WifiManagerLeak") WifiManager wifi = (WifiManager) reactApplicationContext.getSystemService(Context.WIFI_SERVICE);
139
- multicastLock = wifi.createMulticastLock("multicastLock");
140
- multicastLock.setReferenceCounted(true);
141
- }
142
- multicastLock.acquire();
143
- }
144
-
145
- private void releaseMulticastLock() {
146
- if (multicastLock != null && multicastLock.isHeld()) {
147
- multicastLock.release();
148
- }
149
- }
150
-
151
- /**
152
- * DNSSD errors carry their DNSServiceErrorType code, anything else is reported as an exception
153
- */
154
- private void sendError(Throwable throwable, String prefix, @Nullable String serviceName, @Nullable String scanId) {
155
- if (throwable instanceof DNSSDException) {
156
- DNSSDException dnssdException = (DNSSDException) throwable;
157
- zeroconfModule.sendError(ZeroconfModule.ERROR_DOMAIN_DNSSD, dnssdException.getErrorCode(), prefix + throwable.getMessage(), serviceName, scanId);
158
- } else {
159
- zeroconfModule.sendError(ZeroconfModule.ERROR_DOMAIN_LIBRARY, ZeroconfModule.ERROR_CODE_EXCEPTION, prefix + throwable.getMessage(), serviceName, scanId);
160
- }
161
- }
162
-
163
- private void rejectWith(@Nullable Promise promise, Throwable throwable, String prefix, @Nullable String serviceName) {
164
- if (throwable instanceof DNSSDException) {
165
- ZeroconfModule.reject(promise, ZeroconfModule.ERROR_DOMAIN_DNSSD, ((DNSSDException) throwable).getErrorCode(), prefix + throwable.getMessage(), serviceName);
166
- } else {
167
- ZeroconfModule.reject(promise, ZeroconfModule.ERROR_DOMAIN_LIBRARY, ZeroconfModule.ERROR_CODE_EXCEPTION, prefix + throwable.getMessage(), serviceName);
168
- }
169
- }
170
-
171
- private WritableMap serviceNameToMap(BonjourService serviceInfo) {
172
- WritableMap service = new WritableNativeMap();
173
- service.putString(ZeroconfModule.KEY_SERVICE_NAME, serviceInfo.getServiceName());
174
- return service;
175
- }
176
-
177
- private String getServiceType(String type, String protocol) {
178
- return String.format("_%s._%s", type, protocol);
179
- }
180
-
181
- private WritableMap serviceInfoToMap(BonjourService serviceInfo) {
182
- WritableMap service = new WritableNativeMap();
183
- service.putString(ZeroconfModule.KEY_SERVICE_NAME, serviceInfo.getServiceName());
184
- final List<InetAddress> hostList = serviceInfo.getInetAddresses();
185
- final String hostname = serviceInfo.getHostname();
186
- final String fullServiceName = hostname != null
187
- ? hostname + serviceInfo.getRegType()
188
- : serviceInfo.getServiceName();
189
- service.putString(ZeroconfModule.KEY_SERVICE_HOST, hostname != null ? hostname : serviceInfo.getServiceName());
190
-
191
- WritableArray addresses = new WritableNativeArray();
192
- for (InetAddress host : hostList) {
193
- addresses.pushString(host.getHostAddress());
194
- }
195
-
196
- service.putArray(ZeroconfModule.KEY_SERVICE_ADDRESSES, addresses);
197
- service.putString(ZeroconfModule.KEY_SERVICE_FULL_NAME, fullServiceName);
198
- service.putInt(ZeroconfModule.KEY_SERVICE_PORT, serviceInfo.getPort());
199
-
200
- WritableMap txtRecords = new WritableNativeMap();
201
-
202
- Map<String, String> attributes = serviceInfo.getTxtRecords();
203
- for (String key : attributes.keySet()) {
204
- String recordValue = attributes.get(key);
205
- txtRecords.putString(String.format(Locale.getDefault(), "%s", key), String.format(Locale.getDefault(), "%s", recordValue != null ? recordValue : ""));
206
- }
207
-
208
- service.putMap(ZeroconfModule.KEY_SERVICE_TXT, txtRecords);
209
-
210
- return service;
211
- }
212
-
213
- @Override
214
- public void stop(String scanId) {
215
- Disposable browse = mBrowses.remove(scanId);
216
- if (browse == null) {
217
- return;
218
- }
219
- browse.dispose();
220
- releaseMulticastLock();
221
- sendScanEvent(ZeroconfModule.EVENT_STOP, new WritableNativeMap(), scanId);
222
- }
223
-
224
- @Override
225
- public void stopAll() {
226
- for (String scanId : new ArrayList<>(mBrowses.keySet())) {
227
- stop(scanId);
228
- }
229
- }
230
-
231
- @Override
232
- public void unregisterService(String serviceName, @Nullable Promise promise) {
233
-
234
- BonjourService bs = mPublishedServices.get(serviceName);
235
- Disposable registerDisposable = mRegisteredDisposables.get(serviceName);
236
- if (bs == null && registerDisposable == null) {
237
- ZeroconfModule.reject(promise, ZeroconfModule.ERROR_DOMAIN_LIBRARY, ZeroconfModule.ERROR_CODE_NOT_PUBLISHED, "Service " + serviceName + " is not published", serviceName);
238
- return;
239
- }
240
-
241
- if (bs != null) {
242
- zeroconfModule.sendEvent(reactApplicationContext, ZeroconfModule.EVENT_UNREGISTERED, serviceInfoToMap(bs));
243
- mPublishedServices.remove(serviceName);
244
- }
245
-
246
- if (registerDisposable != null && !registerDisposable.isDisposed()) {
247
- registerDisposable.dispose();
248
- }
249
- mRegisteredDisposables.remove(serviceName);
250
-
251
- // Disposing unregisters synchronously
252
- if (promise != null) {
253
- promise.resolve(bs != null ? serviceInfoToMap(bs) : null);
254
- }
255
- }
256
-
257
- @Override
258
- public void registerService(String type, String protocol, String domain, String name, int port, ReadableArray txt, Promise promise) {
259
- BonjourService bs = new BonjourService.Builder(0, 0, name, getServiceType(type, protocol), null)
260
- .port(port)
261
- .dnsRecords(getTxtRecordMap(txt))
262
- .build();
263
-
264
- // Resolved once, the registration stream can emit again later
265
- final Promise[] registerPromise = { promise };
266
- Disposable registerDisposable = rxDnssd.register(bs)
267
- .subscribeOn(Schedulers.io())
268
- .observeOn(AndroidSchedulers.mainThread())
269
- .subscribe(bonjourService -> {
270
- Log.i(TAG, "Registered service " + bonjourService.toString());
271
-
272
- mPublishedServices.put(bs.getServiceName(), bs);
273
- zeroconfModule.sendEvent(reactApplicationContext, ZeroconfModule.EVENT_PUBLISHED, serviceInfoToMap(bonjourService));
274
- if (registerPromise[0] != null) {
275
- registerPromise[0].resolve(serviceInfoToMap(bonjourService));
276
- registerPromise[0] = null;
277
- }
278
- }, throwable -> {
279
- Log.e(TAG, "Error registering service: ", throwable);
280
- sendError(throwable, "Registering service " + name + " failed: ", name, null);
281
- rejectWith(registerPromise[0], throwable, "Registering service " + name + " failed: ", name);
282
- registerPromise[0] = null;
283
- mRegisteredDisposables.remove(name);
284
- });
285
-
286
- mRegisteredDisposables.put(name, registerDisposable);
287
- }
288
-
289
- @Override
290
- public void unregisterAllServices() {
291
- for (String serviceName : new ArrayList<>(mRegisteredDisposables.keySet())) {
292
- unregisterService(serviceName, null);
293
- }
294
- }
295
-
296
- private Map<String, String> getTxtRecordMap(ReadableArray txt) {
297
- // LinkedHashMap keeps the TXT record order
298
- Map<String, String> txtMap = new LinkedHashMap<>();
299
- for (int i = 0; i < txt.size(); i++) {
300
- ReadableArray pair = txt.getArray(i);
301
- txtMap.put(pair.getString(0), pair.getString(1));
302
- }
303
- return txtMap;
304
- }
305
- }