rhodes 7.4.1 → 7.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +5 -5
  2. data/CREDITS +3 -1
  3. data/LICENSE +1 -1
  4. data/README.md +3 -1
  5. data/Rakefile +52 -43
  6. data/SECURITY.md +19 -0
  7. data/appveyor.yml +60 -7
  8. data/azure-pipelines.yml +67 -0
  9. data/extensions/rhoconnect-push/ext/rhoconnect-push/platform/android/src/com/rhomobile/rhoelements/ans/ANSManager.java +2 -2
  10. data/lib/commonAPI/barcode/ext/platform/android/AndroidManifest.xml +3 -1
  11. data/lib/commonAPI/barcode/ext/platform/android/src/com/google/barcodereader/BarcodeCaptureActivity.java +11 -13
  12. data/lib/commonAPI/barcode/ext/platform/android/src/com/google/barcodereader/BarcodeGraphicTracker.java +1 -1
  13. data/lib/commonAPI/barcode/ext/platform/android/src/com/google/barcodereader/ui/camera/CameraSource.java +3 -3
  14. data/lib/commonAPI/barcode/ext/platform/android/src/com/google/barcodereader/ui/camera/CameraSourcePreview.java +1 -1
  15. data/lib/commonAPI/barcode/ext.yml +4 -8
  16. data/lib/commonAPI/coreapi/ext/platform/android/Rakefile +0 -12
  17. data/lib/commonAPI/coreapi/ext/platform/android/src/com/rho/notification/Notification.java +4 -2
  18. data/lib/commonAPI/coreapi/ext/platform/android/src/com/rho/notification/NotificationScheduler.java +3 -3
  19. data/lib/commonAPI/coreapi/ext/platform/android/src/com/rho/notification/NotificationSingleton.java +1 -1
  20. data/lib/commonAPI/coreapi/ext/push.xml +5 -2
  21. data/lib/commonAPI/mediacapture/ext/camera.xml +4 -9
  22. data/lib/commonAPI/mediacapture/ext/platform/android/ApplicationFileProvider.erb +1 -1
  23. data/lib/commonAPI/mediacapture/ext/platform/android/ext_java.files +1 -9
  24. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraExtension.java +0 -2
  25. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraFactory.java +19 -24
  26. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraObject.java +317 -729
  27. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraRhoListener.java +240 -434
  28. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/{CameraSingletonObject.java → CameraSingleton.java} +68 -74
  29. data/lib/commonAPI/printing_zebra/ext/platform/android/src/com/rhomobile/printing/zebra/impl/ZebraPrintingProviderImpl.java +2 -1
  30. data/lib/extensions/fcm-push/ext/android/ApplicationManifestAdds.erb +15 -17
  31. data/lib/extensions/fcm-push/ext/android/Rakefile +59 -34
  32. data/lib/extensions/fcm-push/ext/android/ext_java.files +0 -1
  33. data/lib/extensions/fcm-push/ext/android/src/com/rhomobile/rhodes/fcm/FCMFacade.java +13 -17
  34. data/lib/extensions/fcm-push/ext/android/src/com/rhomobile/rhodes/fcm/FCMIntentService.java +81 -80
  35. data/lib/extensions/fcm-push/ext/android/src/com/rhomobile/rhodes/fcm/FCMListener.java +0 -16
  36. data/lib/extensions/fcm-push/ext.yml +1 -1
  37. data/lib/extensions/gmaps/ext/platform/android/ApplicationManifestAdds.erb +1 -0
  38. data/lib/extensions/gmaps/ext/platform/android/src/com/rhomobile/rhodes/gmaps/GMapActivity.java +12 -4
  39. data/lib/extensions/gmaps/ext.yml +3 -1
  40. data/platform/android/Rhodes/jni/src/MethodResultJni.cpp +4 -0
  41. data/platform/android/Rhodes/src/com/rhomobile/rhodes/RhodesActivity.java +3 -3
  42. data/platform/android/Rhodes/src/com/rhomobile/rhodes/RhodesService.java +3 -3
  43. data/platform/android/Rhodes/src/com/rhomobile/rhodes/alert/StatusNotification.java +1 -1
  44. data/platform/android/Rhodes/src/com/rhomobile/rhodes/geolocation/GeoLocation.java +14 -14
  45. data/platform/android/Rhodes/src/com/rhomobile/rhodes/geolocation/GeoLocationImpl.java +18 -9
  46. data/platform/android/Rhodes/src/com/rhomobile/rhodes/osfunctionality/AndroidFunctionality26.java +1 -1
  47. data/platform/android/build/{aab_builder.rb → aapt2_helper.rb} +94 -37
  48. data/platform/android/build/android.rake +124 -191
  49. data/platform/android/build/android_tools.rb +9 -9
  50. data/platform/android/build/androidcommon.rb +18 -7
  51. data/platform/android/build/build_tools_finder.rb +46 -0
  52. data/platform/android/build/config.yml +5 -0
  53. data/platform/android/build/dex_builder.rb +88 -0
  54. data/platform/android/build/hostplatform.rb +9 -0
  55. data/platform/android/build/manifest_generator.rb +1 -0
  56. data/platform/android/build/maven_deps_extractor.rb +22 -21
  57. data/platform/android/build/ndkwrapper.rb +80 -51
  58. data/platform/iphone/Classes/AppManager/AppManager.m +3 -1
  59. data/platform/iphone/Classes/Camera/PickImageDelegate.h +2 -0
  60. data/platform/iphone/Classes/Camera/PickImageDelegate.m +45 -23
  61. data/platform/iphone/Classes/CocoaServer/CCocoaServer.h +27 -0
  62. data/platform/iphone/Classes/CocoaServer/CCocoaServer.m +107 -0
  63. data/platform/iphone/Classes/CocoaServer/RhoHTTPConnection.h +16 -0
  64. data/platform/iphone/Classes/CocoaServer/RhoHTTPConnection.m +226 -0
  65. data/platform/iphone/Classes/RhoWKWebView.mm +78 -11
  66. data/platform/iphone/Classes/RhoWebViewFabrique.m +6 -1
  67. data/platform/iphone/Classes/Rhodes.m +3 -0
  68. data/platform/iphone/CocoaHTTPServer/Core/Categories/DDData.h +14 -0
  69. data/platform/iphone/CocoaHTTPServer/Core/Categories/DDData.m +158 -0
  70. data/platform/iphone/CocoaHTTPServer/Core/Categories/DDNumber.h +12 -0
  71. data/platform/iphone/CocoaHTTPServer/Core/Categories/DDNumber.m +88 -0
  72. data/platform/iphone/CocoaHTTPServer/Core/Categories/DDRange.h +56 -0
  73. data/platform/iphone/CocoaHTTPServer/Core/Categories/DDRange.m +104 -0
  74. data/platform/iphone/CocoaHTTPServer/Core/HTTPAuthenticationRequest.h +45 -0
  75. data/platform/iphone/CocoaHTTPServer/Core/HTTPAuthenticationRequest.m +195 -0
  76. data/platform/iphone/CocoaHTTPServer/Core/HTTPConnection.h +120 -0
  77. data/platform/iphone/CocoaHTTPServer/Core/HTTPConnection.m +2708 -0
  78. data/platform/iphone/CocoaHTTPServer/Core/HTTPLogging.h +136 -0
  79. data/platform/iphone/CocoaHTTPServer/Core/HTTPMessage.h +48 -0
  80. data/platform/iphone/CocoaHTTPServer/Core/HTTPMessage.m +113 -0
  81. data/platform/iphone/CocoaHTTPServer/Core/HTTPResponse.h +149 -0
  82. data/platform/iphone/CocoaHTTPServer/Core/HTTPServer.h +205 -0
  83. data/platform/iphone/CocoaHTTPServer/Core/HTTPServer.m +772 -0
  84. data/platform/iphone/CocoaHTTPServer/Core/Mime/MultipartFormDataParser.h +65 -0
  85. data/platform/iphone/CocoaHTTPServer/Core/Mime/MultipartFormDataParser.m +529 -0
  86. data/platform/iphone/CocoaHTTPServer/Core/Mime/MultipartMessageHeader.h +33 -0
  87. data/platform/iphone/CocoaHTTPServer/Core/Mime/MultipartMessageHeader.m +86 -0
  88. data/platform/iphone/CocoaHTTPServer/Core/Mime/MultipartMessageHeaderField.h +23 -0
  89. data/platform/iphone/CocoaHTTPServer/Core/Mime/MultipartMessageHeaderField.m +211 -0
  90. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPAsyncFileResponse.h +75 -0
  91. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPAsyncFileResponse.m +405 -0
  92. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPDataResponse.h +13 -0
  93. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPDataResponse.m +79 -0
  94. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPDynamicFileResponse.h +52 -0
  95. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPDynamicFileResponse.m +292 -0
  96. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPErrorResponse.h +9 -0
  97. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPErrorResponse.m +38 -0
  98. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPFileResponse.h +25 -0
  99. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPFileResponse.m +237 -0
  100. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPRedirectResponse.h +12 -0
  101. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPRedirectResponse.m +73 -0
  102. data/platform/iphone/CocoaHTTPServer/Core/WebSocket.h +105 -0
  103. data/platform/iphone/CocoaHTTPServer/Core/WebSocket.m +791 -0
  104. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/DAVConnection.h +7 -0
  105. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/DAVConnection.m +160 -0
  106. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/DAVResponse.h +11 -0
  107. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/DAVResponse.m +372 -0
  108. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/DELETEResponse.h +7 -0
  109. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/DELETEResponse.m +49 -0
  110. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/PUTResponse.h +8 -0
  111. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/PUTResponse.m +69 -0
  112. data/platform/iphone/CocoaHTTPServer/LICENSE.txt +18 -0
  113. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaAsyncSocket/About.txt +4 -0
  114. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaAsyncSocket/GCDAsyncSocket.h +1226 -0
  115. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaAsyncSocket/GCDAsyncSocket.m +8528 -0
  116. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/About.txt +33 -0
  117. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDASLLogger.h +41 -0
  118. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDASLLogger.m +99 -0
  119. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDAbstractDatabaseLogger.h +102 -0
  120. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDAbstractDatabaseLogger.m +727 -0
  121. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDFileLogger.h +334 -0
  122. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDFileLogger.m +1353 -0
  123. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDLog.h +601 -0
  124. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDLog.m +1083 -0
  125. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDTTYLogger.h +167 -0
  126. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDTTYLogger.m +1479 -0
  127. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/ContextFilterLogFormatter.h +65 -0
  128. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/ContextFilterLogFormatter.m +191 -0
  129. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/DispatchQueueLogFormatter.h +116 -0
  130. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/DispatchQueueLogFormatter.m +251 -0
  131. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/README.txt +7 -0
  132. data/platform/iphone/Framework/Rhodes/Rhodes.xcodeproj/project.pbxproj +1 -1
  133. data/platform/iphone/RhoAppBaseLib/RhoAppBaseLib.xcodeproj/project.pbxproj +364 -0
  134. data/platform/iphone/rbuild/iphone.rake +4 -0
  135. data/platform/osx/bin/RhoSimulator/RhoSimulator.app.zip +0 -0
  136. data/platform/shared/common/RhodesApp.cpp +26 -1
  137. data/platform/shared/net/HttpServer.cpp +20 -0
  138. data/platform/shared/qt/RhoSimulator.pro +1 -1
  139. data/platform/shared/ruby/osx/ruby/config.h +2 -0
  140. data/platform/shared/ruby/win32/win32.c +10 -3
  141. data/platform/win32/RhoSimulator/RhoSimulator.exe +0 -0
  142. data/rakefile.rb +52 -43
  143. data/res/build-tools/RhoRuby.exe +0 -0
  144. data/res/build-tools/aapt2/linux/aapt2 +0 -0
  145. data/res/build-tools/aapt2/osx/aapt2 +0 -0
  146. data/res/generators/templates/application/AndroidManifest.erb +2 -1
  147. data/res/generators/templates/application/build.yml +4 -1
  148. data/res/generators/templates/application/rhoconfig.txt +5 -0
  149. data/rhobuild.yml.example +4 -4
  150. data/rhodes.gemspec +1 -2
  151. data/version +1 -1
  152. metadata +80 -35
  153. data/lib/commonAPI/mediacapture/ext/platform/android/adds/res/drawable/camera_btn.xml +0 -14
  154. data/lib/commonAPI/mediacapture/ext/platform/android/adds/res/layout/camera_land.xml +0 -23
  155. data/lib/commonAPI/mediacapture/ext/platform/android/adds/res/layout/camera_port.xml +0 -23
  156. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraActivity.java +0 -156
  157. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraEclair.java +0 -227
  158. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraGingerbread.java +0 -152
  159. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraPreview.java +0 -183
  160. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraSingletonEclair.java +0 -14
  161. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraSingletonGingerbread.java +0 -60
  162. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/ICameraObject.java +0 -20
  163. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/ICameraSingletonObject.java +0 -8
  164. data/lib/extensions/fcm-push/ext/android/src/com/rhomobile/rhodes/fcm/FCMTokenRefresherService.java +0 -27
  165. data/platform/android/Rhodes/AndroidManifest.xml.erb +0 -89
@@ -0,0 +1,772 @@
1
+ #import "HTTPServer.h"
2
+ #import "GCDAsyncSocket.h"
3
+ #import "HTTPConnection.h"
4
+ #import "WebSocket.h"
5
+ #import "HTTPLogging.h"
6
+
7
+ #if ! __has_feature(objc_arc)
8
+ #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
9
+ #endif
10
+
11
+ // Log levels: off, error, warn, info, verbose
12
+ // Other flags: trace
13
+ static const int httpLogLevel = HTTP_LOG_LEVEL_INFO; // | HTTP_LOG_FLAG_TRACE;
14
+
15
+ @interface HTTPServer (PrivateAPI)
16
+
17
+ - (void)unpublishBonjour;
18
+ - (void)publishBonjour;
19
+
20
+ + (void)startBonjourThreadIfNeeded;
21
+ + (void)performBonjourBlock:(dispatch_block_t)block;
22
+
23
+ @end
24
+
25
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
26
+ #pragma mark -
27
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
28
+
29
+ @implementation HTTPServer
30
+
31
+ /**
32
+ * Standard Constructor.
33
+ * Instantiates an HTTP server, but does not start it.
34
+ **/
35
+ - (id)init
36
+ {
37
+ if ((self = [super init]))
38
+ {
39
+ HTTPLogTrace();
40
+
41
+ // Setup underlying dispatch queues
42
+ serverQueue = dispatch_queue_create("HTTPServer", NULL);
43
+ connectionQueue = dispatch_queue_create("HTTPConnection", NULL);
44
+
45
+ IsOnServerQueueKey = &IsOnServerQueueKey;
46
+ IsOnConnectionQueueKey = &IsOnConnectionQueueKey;
47
+
48
+ void *nonNullUnusedPointer = (__bridge void *)self; // Whatever, just not null
49
+
50
+ dispatch_queue_set_specific(serverQueue, IsOnServerQueueKey, nonNullUnusedPointer, NULL);
51
+ dispatch_queue_set_specific(connectionQueue, IsOnConnectionQueueKey, nonNullUnusedPointer, NULL);
52
+
53
+ // Initialize underlying GCD based tcp socket
54
+ asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:serverQueue];
55
+
56
+ // Use default connection class of HTTPConnection
57
+ connectionClass = [HTTPConnection self];
58
+
59
+ // By default bind on all available interfaces, en1, wifi etc
60
+ interface = nil;
61
+
62
+ // Use a default port of 0
63
+ // This will allow the kernel to automatically pick an open port for us
64
+ port = 0;
65
+
66
+ // Configure default values for bonjour service
67
+
68
+ // Bonjour domain. Use the local domain by default
69
+ domain = @"local.";
70
+
71
+ // If using an empty string ("") for the service name when registering,
72
+ // the system will automatically use the "Computer Name".
73
+ // Passing in an empty string will also handle name conflicts
74
+ // by automatically appending a digit to the end of the name.
75
+ name = @"";
76
+
77
+ // Initialize arrays to hold all the HTTP and webSocket connections
78
+ connections = [[NSMutableArray alloc] init];
79
+ webSockets = [[NSMutableArray alloc] init];
80
+
81
+ connectionsLock = [[NSLock alloc] init];
82
+ webSocketsLock = [[NSLock alloc] init];
83
+
84
+ // Register for notifications of closed connections
85
+ [[NSNotificationCenter defaultCenter] addObserver:self
86
+ selector:@selector(connectionDidDie:)
87
+ name:HTTPConnectionDidDieNotification
88
+ object:nil];
89
+
90
+ // Register for notifications of closed websocket connections
91
+ [[NSNotificationCenter defaultCenter] addObserver:self
92
+ selector:@selector(webSocketDidDie:)
93
+ name:WebSocketDidDieNotification
94
+ object:nil];
95
+
96
+ isRunning = NO;
97
+ }
98
+ return self;
99
+ }
100
+
101
+ /**
102
+ * Standard Deconstructor.
103
+ * Stops the server, and clients, and releases any resources connected with this instance.
104
+ **/
105
+ - (void)dealloc
106
+ {
107
+ HTTPLogTrace();
108
+
109
+ // Remove notification observer
110
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
111
+
112
+ // Stop the server if it's running
113
+ [self stop];
114
+
115
+ // Release all instance variables
116
+
117
+ #if !OS_OBJECT_USE_OBJC
118
+ dispatch_release(serverQueue);
119
+ dispatch_release(connectionQueue);
120
+ #endif
121
+
122
+ [asyncSocket setDelegate:nil delegateQueue:NULL];
123
+ }
124
+
125
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
126
+ #pragma mark Server Configuration
127
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
128
+
129
+ /**
130
+ * The document root is filesystem root for the webserver.
131
+ * Thus requests for /index.html will be referencing the index.html file within the document root directory.
132
+ * All file requests are relative to this document root.
133
+ **/
134
+ - (NSString *)documentRoot
135
+ {
136
+ __block NSString *result;
137
+
138
+ dispatch_sync(serverQueue, ^{
139
+ result = documentRoot;
140
+ });
141
+
142
+ return result;
143
+ }
144
+
145
+ - (void)setDocumentRoot:(NSString *)value
146
+ {
147
+ HTTPLogTrace();
148
+
149
+ // Document root used to be of type NSURL.
150
+ // Add type checking for early warning to developers upgrading from older versions.
151
+
152
+ if (value && ![value isKindOfClass:[NSString class]])
153
+ {
154
+ HTTPLogWarn(@"%@: %@ - Expecting NSString parameter, received %@ parameter",
155
+ THIS_FILE, THIS_METHOD, NSStringFromClass([value class]));
156
+ return;
157
+ }
158
+
159
+ NSString *valueCopy = [value copy];
160
+
161
+ dispatch_async(serverQueue, ^{
162
+ documentRoot = valueCopy;
163
+ });
164
+
165
+ }
166
+
167
+ /**
168
+ * The connection class is the class that will be used to handle connections.
169
+ * That is, when a new connection is created, an instance of this class will be intialized.
170
+ * The default connection class is HTTPConnection.
171
+ * If you use a different connection class, it is assumed that the class extends HTTPConnection
172
+ **/
173
+ - (Class)connectionClass
174
+ {
175
+ __block Class result;
176
+
177
+ dispatch_sync(serverQueue, ^{
178
+ result = connectionClass;
179
+ });
180
+
181
+ return result;
182
+ }
183
+
184
+ - (void)setConnectionClass:(Class)value
185
+ {
186
+ HTTPLogTrace();
187
+
188
+ dispatch_async(serverQueue, ^{
189
+ connectionClass = value;
190
+ });
191
+ }
192
+
193
+ /**
194
+ * What interface to bind the listening socket to.
195
+ **/
196
+ - (NSString *)interface
197
+ {
198
+ __block NSString *result;
199
+
200
+ dispatch_sync(serverQueue, ^{
201
+ result = interface;
202
+ });
203
+
204
+ return result;
205
+ }
206
+
207
+ - (void)setInterface:(NSString *)value
208
+ {
209
+ NSString *valueCopy = [value copy];
210
+
211
+ dispatch_async(serverQueue, ^{
212
+ interface = valueCopy;
213
+ });
214
+
215
+ }
216
+
217
+ /**
218
+ * The port to listen for connections on.
219
+ * By default this port is initially set to zero, which allows the kernel to pick an available port for us.
220
+ * After the HTTP server has started, the port being used may be obtained by this method.
221
+ **/
222
+ - (UInt16)port
223
+ {
224
+ __block UInt16 result;
225
+
226
+ dispatch_sync(serverQueue, ^{
227
+ result = port;
228
+ });
229
+
230
+ return result;
231
+ }
232
+
233
+ - (UInt16)listeningPort
234
+ {
235
+ __block UInt16 result;
236
+
237
+ dispatch_sync(serverQueue, ^{
238
+ if (isRunning)
239
+ result = [asyncSocket localPort];
240
+ else
241
+ result = 0;
242
+ });
243
+
244
+ return result;
245
+ }
246
+
247
+ - (void)setPort:(UInt16)value
248
+ {
249
+ HTTPLogTrace();
250
+
251
+ dispatch_async(serverQueue, ^{
252
+ port = value;
253
+ });
254
+ }
255
+
256
+ /**
257
+ * Domain on which to broadcast this service via Bonjour.
258
+ * The default domain is @"local".
259
+ **/
260
+ - (NSString *)domain
261
+ {
262
+ __block NSString *result;
263
+
264
+ dispatch_sync(serverQueue, ^{
265
+ result = domain;
266
+ });
267
+
268
+ return result;
269
+ }
270
+
271
+ - (void)setDomain:(NSString *)value
272
+ {
273
+ HTTPLogTrace();
274
+
275
+ NSString *valueCopy = [value copy];
276
+
277
+ dispatch_async(serverQueue, ^{
278
+ domain = valueCopy;
279
+ });
280
+
281
+ }
282
+
283
+ /**
284
+ * The name to use for this service via Bonjour.
285
+ * The default name is an empty string,
286
+ * which should result in the published name being the host name of the computer.
287
+ **/
288
+ - (NSString *)name
289
+ {
290
+ __block NSString *result;
291
+
292
+ dispatch_sync(serverQueue, ^{
293
+ result = name;
294
+ });
295
+
296
+ return result;
297
+ }
298
+
299
+ - (NSString *)publishedName
300
+ {
301
+ __block NSString *result;
302
+
303
+ dispatch_sync(serverQueue, ^{
304
+
305
+ if (netService == nil)
306
+ {
307
+ result = nil;
308
+ }
309
+ else
310
+ {
311
+
312
+ dispatch_block_t bonjourBlock = ^{
313
+ result = [[netService name] copy];
314
+ };
315
+
316
+ [[self class] performBonjourBlock:bonjourBlock];
317
+ }
318
+ });
319
+
320
+ return result;
321
+ }
322
+
323
+ - (void)setName:(NSString *)value
324
+ {
325
+ NSString *valueCopy = [value copy];
326
+
327
+ dispatch_async(serverQueue, ^{
328
+ name = valueCopy;
329
+ });
330
+
331
+ }
332
+
333
+ /**
334
+ * The type of service to publish via Bonjour.
335
+ * No type is set by default, and one must be set in order for the service to be published.
336
+ **/
337
+ - (NSString *)type
338
+ {
339
+ __block NSString *result;
340
+
341
+ dispatch_sync(serverQueue, ^{
342
+ result = type;
343
+ });
344
+
345
+ return result;
346
+ }
347
+
348
+ - (void)setType:(NSString *)value
349
+ {
350
+ NSString *valueCopy = [value copy];
351
+
352
+ dispatch_async(serverQueue, ^{
353
+ type = valueCopy;
354
+ });
355
+
356
+ }
357
+
358
+ /**
359
+ * The extra data to use for this service via Bonjour.
360
+ **/
361
+ - (NSDictionary *)TXTRecordDictionary
362
+ {
363
+ __block NSDictionary *result;
364
+
365
+ dispatch_sync(serverQueue, ^{
366
+ result = txtRecordDictionary;
367
+ });
368
+
369
+ return result;
370
+ }
371
+
372
+ - (void)setTXTRecordDictionary:(NSDictionary *)value
373
+ {
374
+ HTTPLogTrace();
375
+
376
+ NSDictionary *valueCopy = [value copy];
377
+
378
+ dispatch_async(serverQueue, ^{
379
+
380
+ txtRecordDictionary = valueCopy;
381
+
382
+ // Update the txtRecord of the netService if it has already been published
383
+ if (netService)
384
+ {
385
+ NSNetService *theNetService = netService;
386
+ NSData *txtRecordData = nil;
387
+ if (txtRecordDictionary)
388
+ txtRecordData = [NSNetService dataFromTXTRecordDictionary:txtRecordDictionary];
389
+
390
+ dispatch_block_t bonjourBlock = ^{
391
+ [theNetService setTXTRecordData:txtRecordData];
392
+ };
393
+
394
+ [[self class] performBonjourBlock:bonjourBlock];
395
+ }
396
+ });
397
+
398
+ }
399
+
400
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
401
+ #pragma mark Server Control
402
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
403
+
404
+ - (BOOL)start:(NSError **)errPtr
405
+ {
406
+ HTTPLogTrace();
407
+
408
+ __block BOOL success = YES;
409
+ __block NSError *err = nil;
410
+
411
+ dispatch_sync(serverQueue, ^{ @autoreleasepool {
412
+
413
+ success = [asyncSocket acceptOnInterface:interface port:port error:&err];
414
+ if (success)
415
+ {
416
+ HTTPLogInfo(@"%@: Started HTTP server on port %hu", THIS_FILE, [asyncSocket localPort]);
417
+
418
+ isRunning = YES;
419
+ [self publishBonjour];
420
+ }
421
+ else
422
+ {
423
+ HTTPLogError(@"%@: Failed to start HTTP Server: %@", THIS_FILE, err);
424
+ }
425
+ }});
426
+
427
+ if (errPtr)
428
+ *errPtr = err;
429
+
430
+ return success;
431
+ }
432
+
433
+ - (void)stop
434
+ {
435
+ [self stop:NO];
436
+ }
437
+
438
+ - (void)stop:(BOOL)keepExistingConnections
439
+ {
440
+ HTTPLogTrace();
441
+
442
+ dispatch_sync(serverQueue, ^{ @autoreleasepool {
443
+
444
+ // First stop publishing the service via bonjour
445
+ [self unpublishBonjour];
446
+
447
+ // Stop listening / accepting incoming connections
448
+ [asyncSocket disconnect];
449
+ isRunning = NO;
450
+
451
+ if (!keepExistingConnections)
452
+ {
453
+ // Stop all HTTP connections the server owns
454
+ [connectionsLock lock];
455
+ for (HTTPConnection *connection in connections)
456
+ {
457
+ [connection stop];
458
+ }
459
+ [connections removeAllObjects];
460
+ [connectionsLock unlock];
461
+
462
+ // Stop all WebSocket connections the server owns
463
+ [webSocketsLock lock];
464
+ for (WebSocket *webSocket in webSockets)
465
+ {
466
+ [webSocket stop];
467
+ }
468
+ [webSockets removeAllObjects];
469
+ [webSocketsLock unlock];
470
+ }
471
+ }});
472
+ }
473
+
474
+ - (BOOL)isRunning
475
+ {
476
+ __block BOOL result;
477
+
478
+ dispatch_sync(serverQueue, ^{
479
+ result = isRunning;
480
+ });
481
+
482
+ return result;
483
+ }
484
+
485
+ - (void)addWebSocket:(WebSocket *)ws
486
+ {
487
+ [webSocketsLock lock];
488
+
489
+ HTTPLogTrace();
490
+ [webSockets addObject:ws];
491
+
492
+ [webSocketsLock unlock];
493
+ }
494
+
495
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
496
+ #pragma mark Server Status
497
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
498
+
499
+ /**
500
+ * Returns the number of http client connections that are currently connected to the server.
501
+ **/
502
+ - (NSUInteger)numberOfHTTPConnections
503
+ {
504
+ NSUInteger result = 0;
505
+
506
+ [connectionsLock lock];
507
+ result = [connections count];
508
+ [connectionsLock unlock];
509
+
510
+ return result;
511
+ }
512
+
513
+ /**
514
+ * Returns the number of websocket client connections that are currently connected to the server.
515
+ **/
516
+ - (NSUInteger)numberOfWebSocketConnections
517
+ {
518
+ NSUInteger result = 0;
519
+
520
+ [webSocketsLock lock];
521
+ result = [webSockets count];
522
+ [webSocketsLock unlock];
523
+
524
+ return result;
525
+ }
526
+
527
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
528
+ #pragma mark Incoming Connections
529
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
530
+
531
+ - (HTTPConfig *)config
532
+ {
533
+ // Override me if you want to provide a custom config to the new connection.
534
+ //
535
+ // Generally this involves overriding the HTTPConfig class to include any custom settings,
536
+ // and then having this method return an instance of 'MyHTTPConfig'.
537
+
538
+ // Note: Think you can make the server faster by putting each connection on its own queue?
539
+ // Then benchmark it before and after and discover for yourself the shocking truth!
540
+ //
541
+ // Try the apache benchmark tool (already installed on your Mac):
542
+ // $ ab -n 1000 -c 1 http://localhost:<port>/some_path.html
543
+
544
+ return [[HTTPConfig alloc] initWithServer:self documentRoot:documentRoot queue:connectionQueue];
545
+ }
546
+
547
+ - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
548
+ {
549
+ HTTPConnection *newConnection = (HTTPConnection *)[[connectionClass alloc] initWithAsyncSocket:newSocket
550
+ configuration:[self config]];
551
+ [connectionsLock lock];
552
+ [connections addObject:newConnection];
553
+ [connectionsLock unlock];
554
+
555
+ [newConnection start];
556
+ }
557
+
558
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
559
+ #pragma mark Bonjour
560
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
561
+
562
+ - (void)publishBonjour
563
+ {
564
+ HTTPLogTrace();
565
+
566
+ NSAssert(dispatch_get_specific(IsOnServerQueueKey) != NULL, @"Must be on serverQueue");
567
+
568
+ if (type)
569
+ {
570
+ netService = [[NSNetService alloc] initWithDomain:domain type:type name:name port:[asyncSocket localPort]];
571
+ [netService setDelegate:self];
572
+
573
+ NSNetService *theNetService = netService;
574
+ NSData *txtRecordData = nil;
575
+ if (txtRecordDictionary)
576
+ txtRecordData = [NSNetService dataFromTXTRecordDictionary:txtRecordDictionary];
577
+
578
+ dispatch_block_t bonjourBlock = ^{
579
+
580
+ [theNetService removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
581
+ [theNetService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
582
+ [theNetService publish];
583
+
584
+ // Do not set the txtRecordDictionary prior to publishing!!!
585
+ // This will cause the OS to crash!!!
586
+ if (txtRecordData)
587
+ {
588
+ [theNetService setTXTRecordData:txtRecordData];
589
+ }
590
+ };
591
+
592
+ [[self class] startBonjourThreadIfNeeded];
593
+ [[self class] performBonjourBlock:bonjourBlock];
594
+ }
595
+ }
596
+
597
+ - (void)unpublishBonjour
598
+ {
599
+ HTTPLogTrace();
600
+
601
+ NSAssert(dispatch_get_specific(IsOnServerQueueKey) != NULL, @"Must be on serverQueue");
602
+
603
+ if (netService)
604
+ {
605
+ NSNetService *theNetService = netService;
606
+
607
+ dispatch_block_t bonjourBlock = ^{
608
+
609
+ [theNetService stop];
610
+ };
611
+
612
+ [[self class] performBonjourBlock:bonjourBlock];
613
+
614
+ netService = nil;
615
+ }
616
+ }
617
+
618
+ /**
619
+ * Republishes the service via bonjour if the server is running.
620
+ * If the service was not previously published, this method will publish it (if the server is running).
621
+ **/
622
+ - (void)republishBonjour
623
+ {
624
+ HTTPLogTrace();
625
+
626
+ dispatch_async(serverQueue, ^{
627
+
628
+ [self unpublishBonjour];
629
+ [self publishBonjour];
630
+ });
631
+ }
632
+
633
+ /**
634
+ * Called when our bonjour service has been successfully published.
635
+ * This method does nothing but output a log message telling us about the published service.
636
+ **/
637
+ - (void)netServiceDidPublish:(NSNetService *)ns
638
+ {
639
+ // Override me to do something here...
640
+ //
641
+ // Note: This method is invoked on our bonjour thread.
642
+
643
+ HTTPLogInfo(@"Bonjour Service Published: domain(%@) type(%@) name(%@)", [ns domain], [ns type], [ns name]);
644
+ }
645
+
646
+ /**
647
+ * Called if our bonjour service failed to publish itself.
648
+ * This method does nothing but output a log message telling us about the published service.
649
+ **/
650
+ - (void)netService:(NSNetService *)ns didNotPublish:(NSDictionary *)errorDict
651
+ {
652
+ // Override me to do something here...
653
+ //
654
+ // Note: This method in invoked on our bonjour thread.
655
+
656
+ HTTPLogWarn(@"Failed to Publish Service: domain(%@) type(%@) name(%@) - %@",
657
+ [ns domain], [ns type], [ns name], errorDict);
658
+ }
659
+
660
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
661
+ #pragma mark Notifications
662
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
663
+
664
+ /**
665
+ * This method is automatically called when a notification of type HTTPConnectionDidDieNotification is posted.
666
+ * It allows us to remove the connection from our array.
667
+ **/
668
+ - (void)connectionDidDie:(NSNotification *)notification
669
+ {
670
+ // Note: This method is called on the connection queue that posted the notification
671
+
672
+ [connectionsLock lock];
673
+
674
+ HTTPLogTrace();
675
+ [connections removeObject:[notification object]];
676
+
677
+ [connectionsLock unlock];
678
+ }
679
+
680
+ /**
681
+ * This method is automatically called when a notification of type WebSocketDidDieNotification is posted.
682
+ * It allows us to remove the websocket from our array.
683
+ **/
684
+ - (void)webSocketDidDie:(NSNotification *)notification
685
+ {
686
+ // Note: This method is called on the connection queue that posted the notification
687
+
688
+ [webSocketsLock lock];
689
+
690
+ HTTPLogTrace();
691
+ [webSockets removeObject:[notification object]];
692
+
693
+ [webSocketsLock unlock];
694
+ }
695
+
696
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
697
+ #pragma mark Bonjour Thread
698
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
699
+
700
+ /**
701
+ * NSNetService is runloop based, so it requires a thread with a runloop.
702
+ * This gives us two options:
703
+ *
704
+ * - Use the main thread
705
+ * - Setup our own dedicated thread
706
+ *
707
+ * Since we have various blocks of code that need to synchronously access the netservice objects,
708
+ * using the main thread becomes troublesome and a potential for deadlock.
709
+ **/
710
+
711
+ static NSThread *bonjourThread;
712
+
713
+ + (void)startBonjourThreadIfNeeded
714
+ {
715
+ HTTPLogTrace();
716
+
717
+ static dispatch_once_t predicate;
718
+ dispatch_once(&predicate, ^{
719
+
720
+ HTTPLogVerbose(@"%@: Starting bonjour thread...", THIS_FILE);
721
+
722
+ bonjourThread = [[NSThread alloc] initWithTarget:self
723
+ selector:@selector(bonjourThread)
724
+ object:nil];
725
+ [bonjourThread start];
726
+ });
727
+ }
728
+
729
+ + (void)bonjourThread
730
+ {
731
+ @autoreleasepool {
732
+
733
+ HTTPLogVerbose(@"%@: BonjourThread: Started", THIS_FILE);
734
+
735
+ // We can't run the run loop unless it has an associated input source or a timer.
736
+ // So we'll just create a timer that will never fire - unless the server runs for 10,000 years.
737
+ #pragma clang diagnostic push
738
+ #pragma clang diagnostic ignored "-Wundeclared-selector"
739
+ [NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow]
740
+ target:self
741
+ selector:@selector(donothingatall:)
742
+ userInfo:nil
743
+ repeats:YES];
744
+ #pragma clang diagnostic pop
745
+
746
+ [[NSRunLoop currentRunLoop] run];
747
+
748
+ HTTPLogVerbose(@"%@: BonjourThread: Aborted", THIS_FILE);
749
+
750
+ }
751
+ }
752
+
753
+ + (void)executeBonjourBlock:(dispatch_block_t)block
754
+ {
755
+ HTTPLogTrace();
756
+
757
+ NSAssert([NSThread currentThread] == bonjourThread, @"Executed on incorrect thread");
758
+
759
+ block();
760
+ }
761
+
762
+ + (void)performBonjourBlock:(dispatch_block_t)block
763
+ {
764
+ HTTPLogTrace();
765
+
766
+ [self performSelector:@selector(executeBonjourBlock:)
767
+ onThread:bonjourThread
768
+ withObject:block
769
+ waitUntilDone:YES];
770
+ }
771
+
772
+ @end