@cappitolian/http-local-server-swifter 0.0.28 → 0.0.30
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.
- package/android/src/main/java/com/cappitolian/plugins/httplocalserviceswifter/HttpLocalServerSwifter.java +114 -34
- package/android/src/main/java/com/cappitolian/plugins/httplocalserviceswifter/HttpLocalServerSwifterPlugin.java +4 -1
- package/ios/Sources/HttpLocalServerSwifterPlugin/HttpLocalServerSwifter.swift +50 -3
- package/ios/Sources/HttpLocalServerSwifterPlugin/HttpLocalServerSwifterPlugin.swift +10 -4
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@ import org.json.JSONException;
|
|
|
17
17
|
import org.json.JSONObject;
|
|
18
18
|
|
|
19
19
|
import java.io.IOException;
|
|
20
|
-
import java.util.HashMap
|
|
20
|
+
import java.util.HashMap;>
|
|
21
21
|
import java.util.Map;
|
|
22
22
|
import java.util.UUID;
|
|
23
23
|
import java.util.concurrent.CompletableFuture;
|
|
@@ -28,67 +28,104 @@ import java.util.concurrent.TimeoutException;
|
|
|
28
28
|
import fi.iki.elonen.NanoHTTPD;
|
|
29
29
|
|
|
30
30
|
public class HttpLocalServerSwifter {
|
|
31
|
+
// Private static final properties
|
|
31
32
|
private static final String TAG = "HttpLocalServerSwifter";
|
|
32
33
|
private static final int DEFAULT_PORT = 8080;
|
|
33
34
|
private static final int DEFAULT_TIMEOUT_SECONDS = 5;
|
|
34
35
|
private static final String FALLBACK_IP = "127.0.0.1";
|
|
35
|
-
|
|
36
|
-
private
|
|
36
|
+
// Changed to String to transport the full JSON response from JS
|
|
37
|
+
private static final ConcurrentHashMap<String, CompletableFuture<String>> pendingResponses = new ConcurrentHashMap<>();
|
|
38
|
+
// Add inside LocalNanoServer
|
|
39
|
+
private static final ConcurrentHashMap<String, long[]> rateLimitMap = new ConcurrentHashMap<>();
|
|
40
|
+
private static final int RATE_LIMIT = 30; // requests
|
|
41
|
+
private static final long RATE_WINDOW_MS = 60_000; // per minute
|
|
42
|
+
|
|
43
|
+
// Private final properties
|
|
37
44
|
private final Plugin plugin;
|
|
38
45
|
private final int port;
|
|
39
46
|
private final int timeoutSeconds;
|
|
40
|
-
|
|
41
|
-
//
|
|
42
|
-
private
|
|
43
|
-
|
|
47
|
+
|
|
48
|
+
// Private properties
|
|
49
|
+
private LocalNanoServer server;
|
|
50
|
+
|
|
51
|
+
private boolean isRateLimited(String ip) {
|
|
52
|
+
long now = System.currentTimeMillis();
|
|
53
|
+
|
|
54
|
+
rateLimitMap.compute(ip, (key, timestamps) -> {
|
|
55
|
+
if (timestamps == null)
|
|
56
|
+
timestamps = new long[] { now, 1 };
|
|
57
|
+
else if (now - timestamps[0] > RATE_WINDOW_MS) {
|
|
58
|
+
timestamps[0] = now;
|
|
59
|
+
timestamps[1] = 1;
|
|
60
|
+
} else
|
|
61
|
+
timestamps[1]++;
|
|
62
|
+
|
|
63
|
+
return timestamps;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
long[] entry = rateLimitMap.get(ip);
|
|
67
|
+
|
|
68
|
+
return entry != null && entry[1] > RATE_LIMIT;
|
|
69
|
+
}
|
|
70
|
+
|
|
44
71
|
public HttpLocalServerSwifter(@NonNull Plugin plugin) {
|
|
45
72
|
this(plugin, DEFAULT_PORT, DEFAULT_TIMEOUT_SECONDS);
|
|
46
73
|
}
|
|
47
|
-
|
|
74
|
+
|
|
48
75
|
public HttpLocalServerSwifter(@NonNull Plugin plugin, int port, int timeoutSeconds) {
|
|
49
76
|
this.plugin = plugin;
|
|
50
77
|
this.port = port;
|
|
51
78
|
this.timeoutSeconds = timeoutSeconds;
|
|
52
79
|
}
|
|
53
|
-
|
|
80
|
+
|
|
54
81
|
public void connect(@NonNull PluginCall call) {
|
|
55
82
|
if (server != null && server.isAlive()) {
|
|
56
83
|
call.reject("Server is already running");
|
|
84
|
+
|
|
57
85
|
return;
|
|
58
86
|
}
|
|
59
|
-
|
|
87
|
+
|
|
60
88
|
try {
|
|
61
89
|
String localIp = getLocalIpAddress(plugin.getContext());
|
|
90
|
+
|
|
62
91
|
server = new LocalNanoServer(localIp, port, plugin, timeoutSeconds);
|
|
63
92
|
server.start();
|
|
64
|
-
|
|
93
|
+
|
|
65
94
|
JSObject response = new JSObject();
|
|
95
|
+
|
|
66
96
|
response.put("ip", localIp);
|
|
67
97
|
response.put("port", port);
|
|
98
|
+
|
|
68
99
|
call.resolve(response);
|
|
69
|
-
|
|
100
|
+
|
|
70
101
|
Log.i(TAG, "Server started at " + localIp + ":" + port);
|
|
71
102
|
} catch (IOException e) {
|
|
72
103
|
Log.e(TAG, "Failed to start server", e);
|
|
104
|
+
|
|
73
105
|
call.reject("Failed to start server: " + e.getMessage());
|
|
74
106
|
}
|
|
75
107
|
}
|
|
76
|
-
|
|
108
|
+
|
|
77
109
|
public void disconnect(@Nullable PluginCall call) {
|
|
78
110
|
if (server != null) {
|
|
79
111
|
server.stop();
|
|
112
|
+
|
|
80
113
|
server = null;
|
|
114
|
+
|
|
81
115
|
pendingResponses.clear();
|
|
116
|
+
|
|
82
117
|
Log.i(TAG, "Server stopped");
|
|
83
118
|
}
|
|
84
|
-
if (call != null)
|
|
119
|
+
if (call != null)
|
|
120
|
+
call.resolve();
|
|
85
121
|
}
|
|
86
|
-
|
|
122
|
+
|
|
87
123
|
/**
|
|
88
124
|
* Completes the future with the full JS response object (body, status, headers)
|
|
89
125
|
*/
|
|
90
126
|
public static void handleJsResponse(@NonNull String requestId, @NonNull JSObject responseData) {
|
|
91
127
|
CompletableFuture<String> future = pendingResponses.remove(requestId);
|
|
128
|
+
|
|
92
129
|
if (future != null && !future.isDone()) {
|
|
93
130
|
future.complete(responseData.toString());
|
|
94
131
|
Log.d(TAG, "Response object delivered to future for ID: " + requestId);
|
|
@@ -97,11 +134,19 @@ public class HttpLocalServerSwifter {
|
|
|
97
134
|
|
|
98
135
|
private @NonNull String getLocalIpAddress(@NonNull Context context) {
|
|
99
136
|
try {
|
|
100
|
-
WifiManager wifiManager = (WifiManager) context.getApplicationContext()
|
|
101
|
-
|
|
137
|
+
WifiManager wifiManager = (WifiManager) context.getApplicationContext()
|
|
138
|
+
.getSystemService(Context.WIFI_SERVICE);
|
|
139
|
+
|
|
140
|
+
if (wifiManager == null)
|
|
141
|
+
return FALLBACK_IP;
|
|
142
|
+
|
|
102
143
|
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
|
|
103
|
-
|
|
144
|
+
|
|
145
|
+
if (wifiInfo == null)
|
|
146
|
+
return FALLBACK_IP;
|
|
147
|
+
|
|
104
148
|
int ipAddress = wifiInfo.getIpAddress();
|
|
149
|
+
|
|
105
150
|
return ipAddress == 0 ? FALLBACK_IP : Formatter.formatIpAddress(ipAddress);
|
|
106
151
|
} catch (Exception e) {
|
|
107
152
|
return FALLBACK_IP;
|
|
@@ -111,27 +156,46 @@ public class HttpLocalServerSwifter {
|
|
|
111
156
|
private static class LocalNanoServer extends NanoHTTPD {
|
|
112
157
|
private final Plugin plugin;
|
|
113
158
|
private final int timeoutSeconds;
|
|
114
|
-
|
|
159
|
+
|
|
115
160
|
public LocalNanoServer(@NonNull String hostname, int port, @NonNull Plugin plugin, int timeoutSeconds) {
|
|
116
161
|
super(hostname, port);
|
|
117
162
|
this.plugin = plugin;
|
|
118
163
|
this.timeoutSeconds = timeoutSeconds;
|
|
119
164
|
}
|
|
120
|
-
|
|
165
|
+
|
|
121
166
|
@Override
|
|
122
167
|
public Response serve(@NonNull IHTTPSession session) {
|
|
168
|
+
if (Method.OPTIONS.equals(session.getMethod())) {
|
|
169
|
+
return createCorsResponse();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Rate limiting
|
|
173
|
+
String clientIp = session.getHeaders().getOrDefault("http-client-ip",
|
|
174
|
+
session.getHeaders().getOrDefault("remote-addr", "unknown"));
|
|
175
|
+
|
|
176
|
+
if (isRateLimited(clientIp)) {
|
|
177
|
+
Response r = newFixedLengthResponse(
|
|
178
|
+
Response.Status.lookup(429), "application/json",
|
|
179
|
+
"{\"success\":false,\"error\":\"Too many requests\"}");
|
|
180
|
+
|
|
181
|
+
addCorsHeaders(r);
|
|
182
|
+
|
|
183
|
+
return r;
|
|
184
|
+
}
|
|
185
|
+
|
|
123
186
|
// Native CORS Preflight handling for efficiency
|
|
124
187
|
if (Method.OPTIONS.equals(session.getMethod())) {
|
|
125
188
|
return createCorsResponse();
|
|
126
189
|
}
|
|
127
|
-
|
|
190
|
+
|
|
128
191
|
try {
|
|
129
192
|
String method = session.getMethod().name();
|
|
130
193
|
String path = session.getUri();
|
|
131
194
|
String body = extractBody(session);
|
|
195
|
+
|
|
132
196
|
Map<String, String> headers = session.getHeaders();
|
|
133
197
|
Map<String, String> params = session.getParms();
|
|
134
|
-
|
|
198
|
+
|
|
135
199
|
// Wait for TypeScript to process logic and provide the complex response
|
|
136
200
|
String jsResponseRaw = processRequest(method, path, body, headers, params);
|
|
137
201
|
return createDynamicResponse(jsResponseRaw);
|
|
@@ -141,24 +205,30 @@ public class HttpLocalServerSwifter {
|
|
|
141
205
|
}
|
|
142
206
|
|
|
143
207
|
/**
|
|
144
|
-
* Parses the JSON from JS and builds a NanoHTTPD Response with custom status
|
|
208
|
+
* Parses the JSON from JS and builds a NanoHTTPD Response with custom status
|
|
209
|
+
* and headers
|
|
145
210
|
*/
|
|
146
211
|
private Response createDynamicResponse(String jsResponseRaw) {
|
|
147
212
|
try {
|
|
148
213
|
JSONObject res = new JSONObject(jsResponseRaw);
|
|
149
214
|
String body = res.optString("body", "");
|
|
215
|
+
|
|
150
216
|
int statusCode = res.optInt("status", 200);
|
|
217
|
+
|
|
151
218
|
JSONObject customHeaders = res.optJSONObject("headers");
|
|
152
219
|
|
|
153
220
|
Response.IStatus status = Response.Status.lookup(statusCode);
|
|
154
|
-
Response response = newFixedLengthResponse(status != null ? status : Response.Status.OK,
|
|
155
|
-
|
|
221
|
+
Response response = newFixedLengthResponse(status != null ? status : Response.Status.OK,
|
|
222
|
+
"application/json", body);
|
|
223
|
+
|
|
156
224
|
// Add standard CORS headers
|
|
157
225
|
addCorsHeaders(response);
|
|
158
226
|
|
|
159
|
-
// Inject custom headers from TypeScript (allows overriding CORS or
|
|
227
|
+
// Inject custom headers from TypeScript (allows overriding CORS or
|
|
228
|
+
// Content-Type)
|
|
160
229
|
if (customHeaders != null) {
|
|
161
230
|
java.util.Iterator<String> keys = customHeaders.keys();
|
|
231
|
+
|
|
162
232
|
while (keys.hasNext()) {
|
|
163
233
|
String key = keys.next();
|
|
164
234
|
response.addHeader(key, customHeaders.getString(key));
|
|
@@ -173,32 +243,39 @@ public class HttpLocalServerSwifter {
|
|
|
173
243
|
|
|
174
244
|
private String extractBody(@NonNull IHTTPSession session) {
|
|
175
245
|
Method method = session.getMethod();
|
|
176
|
-
|
|
246
|
+
|
|
247
|
+
if (method != Method.POST && method != Method.PUT && method != Method.PATCH)
|
|
248
|
+
return null;
|
|
249
|
+
|
|
177
250
|
try {
|
|
178
251
|
HashMap<String, String> files = new HashMap<>();
|
|
179
252
|
session.parseBody(files);
|
|
180
253
|
String body = files.get("postData");
|
|
254
|
+
|
|
181
255
|
return (body == null || body.isEmpty()) ? session.getQueryParameterString() : body;
|
|
182
256
|
} catch (IOException | ResponseException e) {
|
|
183
257
|
return null;
|
|
184
258
|
}
|
|
185
259
|
}
|
|
186
|
-
|
|
187
|
-
private String processRequest(String method, String path, String body, Map<String, String> headers,
|
|
260
|
+
|
|
261
|
+
private String processRequest(String method, String path, String body, Map<String, String> headers,
|
|
262
|
+
Map<String, String> params) {
|
|
188
263
|
String requestId = UUID.randomUUID().toString();
|
|
189
264
|
JSObject requestData = new JSObject();
|
|
190
265
|
requestData.put("requestId", requestId);
|
|
191
266
|
requestData.put("method", method);
|
|
192
267
|
requestData.put("path", path);
|
|
193
|
-
|
|
194
|
-
|
|
268
|
+
|
|
269
|
+
if (body != null)
|
|
270
|
+
requestData.put("body", body);
|
|
271
|
+
|
|
195
272
|
CompletableFuture<String> future = new CompletableFuture<>();
|
|
196
273
|
pendingResponses.put(requestId, future);
|
|
197
|
-
|
|
274
|
+
|
|
198
275
|
if (plugin instanceof HttpLocalServerSwifterPlugin) {
|
|
199
276
|
((HttpLocalServerSwifterPlugin) plugin).fireOnRequest(requestData);
|
|
200
277
|
}
|
|
201
|
-
|
|
278
|
+
|
|
202
279
|
try {
|
|
203
280
|
return future.get(timeoutSeconds, TimeUnit.SECONDS);
|
|
204
281
|
} catch (Exception e) {
|
|
@@ -210,14 +287,17 @@ public class HttpLocalServerSwifter {
|
|
|
210
287
|
|
|
211
288
|
private Response createCorsResponse() {
|
|
212
289
|
Response response = newFixedLengthResponse(Response.Status.NO_CONTENT, "text/plain", "");
|
|
290
|
+
|
|
213
291
|
addCorsHeaders(response);
|
|
292
|
+
|
|
214
293
|
return response;
|
|
215
294
|
}
|
|
216
295
|
|
|
217
296
|
private void addCorsHeaders(@NonNull Response response) {
|
|
218
297
|
response.addHeader("Access-Control-Allow-Origin", "*");
|
|
219
298
|
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
|
|
220
|
-
response.addHeader("Access-Control-Allow-Headers",
|
|
299
|
+
response.addHeader("Access-Control-Allow-Headers",
|
|
300
|
+
"Origin, Content-Type, Accept, Authorization, X-Requested-With");
|
|
221
301
|
response.addHeader("Access-Control-Max-Age", "3600");
|
|
222
302
|
// Prevents TCP connection reuse. NanoHTTPD does not handle keep-alive
|
|
223
303
|
// correctly under rapid sequential requests, causing ERR_INVALID_HTTP_RESPONSE.
|
|
@@ -29,14 +29,17 @@ public class HttpLocalServerSwifterPlugin extends Plugin {
|
|
|
29
29
|
@PluginMethod
|
|
30
30
|
public void sendResponse(PluginCall call) {
|
|
31
31
|
String requestId = call.getString("requestId");
|
|
32
|
+
|
|
32
33
|
if (requestId == null || requestId.isEmpty()) {
|
|
33
34
|
call.reject("Missing requestId");
|
|
35
|
+
|
|
34
36
|
return;
|
|
35
37
|
}
|
|
36
|
-
|
|
38
|
+
|
|
37
39
|
// Pass the entire PluginCall data object to handleJsResponse
|
|
38
40
|
// This allows us to capture 'status' and 'headers' along with the 'body'
|
|
39
41
|
HttpLocalServerSwifter.handleJsResponse(requestId, call.getData());
|
|
42
|
+
|
|
40
43
|
call.resolve();
|
|
41
44
|
}
|
|
42
45
|
|
|
@@ -7,20 +7,43 @@ public protocol HttpLocalServerSwifterDelegate: AnyObject {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
@objc public class HttpLocalServerSwifter: NSObject {
|
|
10
|
-
|
|
11
|
-
private weak var delegate: HttpLocalServerSwifterDelegate?
|
|
12
|
-
|
|
10
|
+
// Private static properties
|
|
13
11
|
private static var pendingResponses = [String: (String) -> Void]()
|
|
14
12
|
private static let queue = DispatchQueue(
|
|
15
13
|
label: "com.cappitolian.HttpLocalServerSwifter.pendingResponses",
|
|
16
14
|
qos: .userInitiated
|
|
17
15
|
)
|
|
18
16
|
|
|
17
|
+
// Private properties
|
|
18
|
+
private var webServer: HttpServer?
|
|
19
|
+
private weak var delegate: HttpLocalServerSwifterDelegate?
|
|
19
20
|
private let defaultTimeout: TimeInterval = 10.0
|
|
20
21
|
private let defaultPort: UInt16 = 8080
|
|
21
22
|
|
|
23
|
+
private var rateLimitMap = [String: (start: Date, count: Int)]()
|
|
24
|
+
private let rateLimitQueue = DispatchQueue(label: "rateLimit", qos: .userInitiated)
|
|
25
|
+
private let rateLimit = 30
|
|
26
|
+
private let rateWindowSeconds: TimeInterval = 60
|
|
27
|
+
|
|
28
|
+
private func isRateLimited(ip: String) -> Bool {
|
|
29
|
+
return rateLimitQueue.sync {
|
|
30
|
+
let now = Date()
|
|
31
|
+
|
|
32
|
+
if let entry = rateLimitMap[ip], now.timeIntervalSince(entry.start) < rateWindowSeconds {
|
|
33
|
+
if entry.count >= rateLimit { return true }
|
|
34
|
+
|
|
35
|
+
rateLimitMap[ip] = (entry.start, entry.count + 1)
|
|
36
|
+
} else {
|
|
37
|
+
rateLimitMap[ip] = (now, 1)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
22
44
|
public init(delegate: HttpLocalServerSwifterDelegate) {
|
|
23
45
|
self.delegate = delegate
|
|
46
|
+
|
|
24
47
|
super.init()
|
|
25
48
|
}
|
|
26
49
|
|
|
@@ -33,6 +56,7 @@ public protocol HttpLocalServerSwifterDelegate: AnyObject {
|
|
|
33
56
|
self.stopServer()
|
|
34
57
|
|
|
35
58
|
let server = HttpServer()
|
|
59
|
+
|
|
36
60
|
self.webServer = server
|
|
37
61
|
|
|
38
62
|
server.middleware.append { [weak self] request in
|
|
@@ -41,11 +65,13 @@ public protocol HttpLocalServerSwifterDelegate: AnyObject {
|
|
|
41
65
|
if request.method == "OPTIONS" {
|
|
42
66
|
return self.corsResponse()
|
|
43
67
|
}
|
|
68
|
+
|
|
44
69
|
return self.processRequest(request)
|
|
45
70
|
}
|
|
46
71
|
|
|
47
72
|
do {
|
|
48
73
|
try server.start(self.defaultPort, forceIPv4: true)
|
|
74
|
+
|
|
49
75
|
let ip = Self.getWiFiAddress() ?? "127.0.0.1"
|
|
50
76
|
|
|
51
77
|
print("🚀 SWIFTER: Server running on http://\(ip):\(self.defaultPort)")
|
|
@@ -56,6 +82,7 @@ public protocol HttpLocalServerSwifterDelegate: AnyObject {
|
|
|
56
82
|
])
|
|
57
83
|
} catch {
|
|
58
84
|
print("❌ SWIFTER ERROR: \(error)")
|
|
85
|
+
|
|
59
86
|
call.reject("Could not start server: \(error.localizedDescription)")
|
|
60
87
|
}
|
|
61
88
|
}
|
|
@@ -65,6 +92,7 @@ public protocol HttpLocalServerSwifterDelegate: AnyObject {
|
|
|
65
92
|
@objc public func stopServer() {
|
|
66
93
|
webServer?.stop()
|
|
67
94
|
webServer = nil
|
|
95
|
+
|
|
68
96
|
// Drain pending futures so blocked threads can unblock on their semaphore timeout.
|
|
69
97
|
Self.queue.sync { Self.pendingResponses.removeAll() }
|
|
70
98
|
}
|
|
@@ -72,6 +100,7 @@ public protocol HttpLocalServerSwifterDelegate: AnyObject {
|
|
|
72
100
|
/// Stops the server and resolves the Capacitor call.
|
|
73
101
|
@objc public func disconnect(resolving call: CAPPluginCall) {
|
|
74
102
|
stopServer()
|
|
103
|
+
|
|
75
104
|
call.resolve()
|
|
76
105
|
}
|
|
77
106
|
|
|
@@ -96,6 +125,18 @@ public protocol HttpLocalServerSwifterDelegate: AnyObject {
|
|
|
96
125
|
// MARK: - Request processing
|
|
97
126
|
|
|
98
127
|
private func processRequest(_ request: HttpRequest) -> HttpResponse {
|
|
128
|
+
let clientIp = request.headers["x-forwarded-for"] ??
|
|
129
|
+
request.address ?? "unknown"
|
|
130
|
+
|
|
131
|
+
if isRateLimited(ip: clientIp) {
|
|
132
|
+
return .raw(429, "Too Many Requests", [
|
|
133
|
+
"Content-Type": "application/json",
|
|
134
|
+
"Access-Control-Allow-Origin": "*"
|
|
135
|
+
]) { writer in
|
|
136
|
+
try writer.write([UInt8](#"{"success":false,"error":"Too many requests"}"#.utf8))
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
99
140
|
let requestId = UUID().uuidString
|
|
100
141
|
|
|
101
142
|
// Use a protected local variable written only inside `queue.sync` inside
|
|
@@ -184,12 +225,15 @@ public protocol HttpLocalServerSwifterDelegate: AnyObject {
|
|
|
184
225
|
defer { freeifaddrs(ifaddr) }
|
|
185
226
|
|
|
186
227
|
var ptr = ifaddr
|
|
228
|
+
|
|
187
229
|
while let current = ptr {
|
|
188
230
|
let interface = current.pointee
|
|
231
|
+
|
|
189
232
|
if interface.ifa_addr.pointee.sa_family == UInt8(AF_INET),
|
|
190
233
|
String(cString: interface.ifa_name) == "en0"
|
|
191
234
|
{
|
|
192
235
|
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
|
236
|
+
|
|
193
237
|
getnameinfo(
|
|
194
238
|
interface.ifa_addr,
|
|
195
239
|
socklen_t(interface.ifa_addr.pointee.sa_len),
|
|
@@ -198,9 +242,12 @@ public protocol HttpLocalServerSwifterDelegate: AnyObject {
|
|
|
198
242
|
nil, 0,
|
|
199
243
|
NI_NUMERICHOST
|
|
200
244
|
)
|
|
245
|
+
|
|
201
246
|
address = String(cString: hostname)
|
|
247
|
+
|
|
202
248
|
break
|
|
203
249
|
}
|
|
250
|
+
|
|
204
251
|
ptr = interface.ifa_next
|
|
205
252
|
}
|
|
206
253
|
|
|
@@ -3,6 +3,10 @@ import Capacitor
|
|
|
3
3
|
|
|
4
4
|
@objc(HttpLocalServerSwifterPlugin)
|
|
5
5
|
public class HttpLocalServerSwifterPlugin: CAPPlugin, CAPBridgedPlugin, HttpLocalServerSwifterDelegate {
|
|
6
|
+
// Private properties
|
|
7
|
+
private var localServer: HttpLocalServerSwifter?
|
|
8
|
+
|
|
9
|
+
// Publi properties
|
|
6
10
|
public let identifier = "HttpLocalServerSwifterPlugin"
|
|
7
11
|
public let jsName = "HttpLocalServerSwifter"
|
|
8
12
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
@@ -11,14 +15,13 @@ public class HttpLocalServerSwifterPlugin: CAPPlugin, CAPBridgedPlugin, HttpLoca
|
|
|
11
15
|
CAPPluginMethod(name: "sendResponse", returnType: CAPPluginReturnPromise)
|
|
12
16
|
]
|
|
13
17
|
|
|
14
|
-
private var localServer: HttpLocalServerSwifter?
|
|
15
|
-
|
|
16
18
|
// MARK: - Plugin methods
|
|
17
19
|
|
|
18
20
|
@objc func connect(_ call: CAPPluginCall) {
|
|
19
21
|
if localServer == nil {
|
|
20
22
|
localServer = HttpLocalServerSwifter(delegate: self)
|
|
21
23
|
}
|
|
24
|
+
|
|
22
25
|
localServer?.connect(call)
|
|
23
26
|
}
|
|
24
27
|
|
|
@@ -32,14 +35,17 @@ public class HttpLocalServerSwifterPlugin: CAPPlugin, CAPBridgedPlugin, HttpLoca
|
|
|
32
35
|
@objc func sendResponse(_ call: CAPPluginCall) {
|
|
33
36
|
guard let requestId = call.getString("requestId"), !requestId.isEmpty else {
|
|
34
37
|
call.reject("Missing requestId")
|
|
38
|
+
|
|
35
39
|
return
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
let responseData = call.dictionaryRepresentation as? [String: Any] ?? [:]
|
|
43
|
+
|
|
39
44
|
HttpLocalServerSwifter.handleJsResponse(
|
|
40
45
|
requestId: requestId,
|
|
41
|
-
responseData:
|
|
46
|
+
responseData: responseData
|
|
42
47
|
)
|
|
48
|
+
|
|
43
49
|
call.resolve()
|
|
44
50
|
}
|
|
45
51
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cappitolian/http-local-server-swifter",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.30",
|
|
4
4
|
"description": "Runs a local HTTP server on your device, accessible over LAN. Supports connect, disconnect, GET, and POST methods with IP and port discovery.",
|
|
5
5
|
"main": "dist/plugin.cjs.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|