1212static CGFloat const ES_RETRY_INTERVAL = 1.0 ;
1313static CGFloat const ES_DEFAULT_TIMEOUT = 300.0 ;
1414
15- static NSString *const ESKeyValueDelimiter = @" : " ;
15+ static NSString *const ESKeyValueDelimiter = @" :" ;
1616static NSString *const ESEventSeparatorLFLF = @" \n\n " ;
1717static NSString *const ESEventSeparatorCRCR = @" \r\r " ;
1818static NSString *const ESEventSeparatorCRLFCRLF = @" \r\n\r\n " ;
2525
2626@interface EventSource () <NSURLConnectionDelegate , NSURLConnectionDataDelegate > {
2727 BOOL wasClosed;
28+ dispatch_queue_t messageQueue;
29+ dispatch_queue_t connectionQueue;
2830}
2931
3032@property (nonatomic , strong ) NSURL *eventURL;
@@ -34,39 +36,43 @@ @interface EventSource () <NSURLConnectionDelegate, NSURLConnectionDataDelegate>
3436@property (nonatomic , assign ) NSTimeInterval retryInterval;
3537@property (nonatomic , strong ) id lastEventID;
3638
37- - (void )open ;
39+ - (void )_open ;
40+ - (void )_dispatchEvent : (Event *)e ;
3841
3942@end
4043
4144@implementation EventSource
4245
43- + (id )eventSourceWithURL : (NSURL *)URL
46+ + (instancetype )eventSourceWithURL : (NSURL *)URL
4447{
4548 return [[EventSource alloc ] initWithURL: URL];
4649}
4750
48- + (id )eventSourceWithURL : (NSURL *)URL timeoutInterval : (NSTimeInterval )timeoutInterval
51+ + (instancetype )eventSourceWithURL : (NSURL *)URL timeoutInterval : (NSTimeInterval )timeoutInterval
4952{
5053 return [[EventSource alloc ] initWithURL: URL timeoutInterval: timeoutInterval];
5154}
5255
53- - (id )initWithURL : (NSURL *)URL
56+ - (instancetype )initWithURL : (NSURL *)URL
5457{
5558 return [self initWithURL: URL timeoutInterval: ES_DEFAULT_TIMEOUT];
5659}
5760
58- - (id )initWithURL : (NSURL *)URL timeoutInterval : (NSTimeInterval )timeoutInterval
61+ - (instancetype )initWithURL : (NSURL *)URL timeoutInterval : (NSTimeInterval )timeoutInterval
5962{
6063 self = [super init ];
6164 if (self) {
6265 _listeners = [NSMutableDictionary dictionary ];
6366 _eventURL = URL;
6467 _timeoutInterval = timeoutInterval;
6568 _retryInterval = ES_RETRY_INTERVAL;
66-
69+
70+ messageQueue = dispatch_queue_create (" co.cwbrn.eventsource-queue" , DISPATCH_QUEUE_SERIAL);
71+ connectionQueue = dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0 );
72+
6773 dispatch_time_t popTime = dispatch_time (DISPATCH_TIME_NOW, (int64_t )(_retryInterval * NSEC_PER_SEC));
68- dispatch_after (popTime, dispatch_get_main_queue () , ^(void ){
69- [self open ];
74+ dispatch_after (popTime, connectionQueue , ^(void ){
75+ [self _open ];
7076 });
7177 }
7278 return self;
@@ -96,14 +102,9 @@ - (void)onOpen:(EventSourceEventHandler)handler
96102 [self addEventListener: OpenEvent handler: handler];
97103}
98104
99- - (void )open
105+ - (void )onReadyStateChanged : (EventSourceEventHandler) handler
100106{
101- wasClosed = NO ;
102- NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: self .eventURL cachePolicy: NSURLRequestReloadIgnoringCacheData timeoutInterval: self .timeoutInterval];
103- if (self.lastEventID ) {
104- [request setValue: self .lastEventID forHTTPHeaderField: @" Last-Event-ID" ];
105- }
106- self.eventSource = [[NSURLConnection alloc ] initWithRequest: request delegate: self startImmediately: YES ];
107+ [self addEventListener: ReadyStateEvent handler: handler];
107108}
108109
109110- (void )close
@@ -122,94 +123,91 @@ - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLRespon
122123 Event *e = [Event new ];
123124 e.readyState = kEventStateOpen ;
124125
125- NSArray *openHandlers = self.listeners [OpenEvent];
126- for (EventSourceEventHandler handler in openHandlers) {
127- dispatch_async (dispatch_get_main_queue (), ^{
128- handler (e);
129- });
130- }
126+
127+ [self _dispatchEvent: e type: ReadyStateEvent];
128+ [self _dispatchEvent: e type: OpenEvent];
131129 }
132130}
133131
134132- (void )connection : (NSURLConnection *)connection didFailWithError : (NSError *)error
135133{
134+ self.eventSource = nil ;
135+
136+ if (wasClosed) {
137+ return ;
138+ }
139+
136140 Event *e = [Event new ];
137141 e.readyState = kEventStateClosed ;
138142 e.error = error;
139-
140- NSArray *errorHandlers = self.listeners [ErrorEvent];
141- for (EventSourceEventHandler handler in errorHandlers) {
142- dispatch_async (dispatch_get_main_queue (), ^{
143- handler (e);
144- });
145- }
146-
147- dispatch_time_t popTime = dispatch_time (DISPATCH_TIME_NOW, (int64_t )(self.retryInterval * NSEC_PER_SEC));
148- dispatch_after (popTime, dispatch_get_main_queue (), ^(void ){
149- [self open ];
143+
144+ [self _dispatchEvent: e type: ReadyStateEvent];
145+ [self _dispatchEvent: e type: ErrorEvent];
146+
147+ dispatch_time_t popTime = dispatch_time (DISPATCH_TIME_NOW, (int64_t )(_retryInterval * NSEC_PER_SEC));
148+ dispatch_after (popTime, connectionQueue, ^(void ){
149+ [self _open ];
150150 });
151151}
152152
153153- (void )connection : (NSURLConnection *)connection didReceiveData : (NSData *)data
154154{
155- __block NSString *eventString = [[NSString alloc ] initWithData: data encoding: NSUTF8StringEncoding];
156-
157- if ([eventString hasSuffix: ESEventSeparatorLFLF] ||
158- [eventString hasSuffix: ESEventSeparatorCRCR] ||
159- [eventString hasSuffix: ESEventSeparatorCRLFCRLF]) {
160- dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
161- eventString = [eventString stringByTrimmingCharactersInSet: [NSCharacterSet newlineCharacterSet ]];
162- NSMutableArray *components = [[eventString componentsSeparatedByString: ESEventKeyValuePairSeparator] mutableCopy ];
163-
164- Event *e = [Event new ];
165- e.readyState = kEventStateOpen ;
166-
167- for (NSString *component in components) {
168- if (component.length == 0 ) {
169- continue ;
170- }
171-
172- NSInteger index = [component rangeOfString: ESKeyValueDelimiter].location ;
173- if (index == NSNotFound || index == (component.length - 2 )) {
174- continue ;
175- }
176-
177- NSString *key = [component substringToIndex: index];
178- NSString *value = [component substringFromIndex: index + ESKeyValueDelimiter.length];
155+ NSString *eventString = [[NSString alloc ] initWithData: data encoding: NSUTF8StringEncoding];
156+ NSArray *lines = [eventString componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet ]];
157+
158+ Event *event = [Event new ];
159+ event.readyState = kEventStateOpen ;
160+
161+ for (NSString *line in lines) {
162+ if ([line hasPrefix: ESKeyValueDelimiter]) {
163+ continue ;
164+ }
165+
166+ if (!line || line.length == 0 ) {
167+ if (event.data != nil ) {
168+ dispatch_async (messageQueue, ^{
169+ [self _dispatchEvent: event];
170+ });
179171
180- if ([key isEqualToString: ESEventIDKey]) {
181- e.id = value;
182- self.lastEventID = e.id ;
183- } else if ([key isEqualToString: ESEventEventKey]) {
184- e.event = value;
172+ event = [Event new ];
173+ event.readyState = kEventStateOpen ;
174+ }
175+ continue ;
176+ }
177+
178+ @autoreleasepool {
179+ NSScanner *scanner = [NSScanner scannerWithString: line];
180+ scanner.charactersToBeSkipped = [NSCharacterSet whitespaceCharacterSet ];
181+
182+ NSString *key, *value;
183+ [scanner scanUpToString: ESKeyValueDelimiter intoString: &key];
184+ [scanner scanString: ESKeyValueDelimiter intoString: nil ];
185+ [scanner scanUpToCharactersFromSet: [NSCharacterSet newlineCharacterSet ] intoString: &value];
186+
187+ if (key && value) {
188+ if ([key isEqualToString: ESEventEventKey]) {
189+ event.event = value;
185190 } else if ([key isEqualToString: ESEventDataKey]) {
186- e.data = value;
191+ if (event.data != nil ) {
192+ event.data = [event.data stringByAppendingFormat: @" \n %@ " , value];
193+ } else {
194+ event.data = value;
195+ }
196+ } else if ([key isEqualToString: ESEventIDKey]) {
197+ event.id = value;
198+ self.lastEventID = event.id ;
187199 } else if ([key isEqualToString: ESEventRetryKey]) {
188200 self.retryInterval = [value doubleValue ];
189201 }
190202 }
191-
192- NSArray *messageHandlers = self.listeners [MessageEvent];
193- for (EventSourceEventHandler handler in messageHandlers) {
194- dispatch_async (dispatch_get_main_queue (), ^{
195- handler (e);
196- });
197- }
198-
199- if (e.event != nil ) {
200- NSArray *namedEventhandlers = self.listeners [e.event];
201- for (EventSourceEventHandler handler in namedEventhandlers) {
202- dispatch_async (dispatch_get_main_queue (), ^{
203- handler (e);
204- });
205- }
206- }
207- });
203+ }
208204 }
209205}
210206
211207- (void )connectionDidFinishLoading : (NSURLConnection *)connection
212208{
209+ self.eventSource = nil ;
210+
213211 if (wasClosed) {
214212 return ;
215213 }
@@ -219,15 +217,52 @@ - (void)connectionDidFinishLoading:(NSURLConnection *)connection
219217 e.error = [NSError errorWithDomain: @" "
220218 code: e.readyState
221219 userInfo: @{ NSLocalizedDescriptionKey : @" Connection with the event source was closed." }];
222-
223- NSArray *errorHandlers = self.listeners [ErrorEvent];
220+
221+ [self _dispatchEvent: e type: ReadyStateEvent];
222+ [self _dispatchEvent: e type: ErrorEvent];
223+
224+ dispatch_time_t popTime = dispatch_time (DISPATCH_TIME_NOW, (int64_t )(_retryInterval * NSEC_PER_SEC));
225+ dispatch_after (popTime, connectionQueue, ^(void ){
226+ [self _open ];
227+ });
228+ }
229+
230+ - (void )_open
231+ {
232+ wasClosed = NO ;
233+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: self .eventURL cachePolicy: NSURLRequestReloadIgnoringCacheData timeoutInterval: self .timeoutInterval];
234+ if (self.lastEventID ) {
235+ [request setValue: self .lastEventID forHTTPHeaderField: @" Last-Event-ID" ];
236+ }
237+ self.eventSource = [[NSURLConnection alloc ] initWithRequest: request delegate: self startImmediately: YES ];
238+
239+ Event *e = [Event new ];
240+ e.readyState = kEventStateConnecting ;
241+
242+ [self _dispatchEvent: e type: ReadyStateEvent];
243+
244+ if (![NSThread isMainThread ]) {
245+ CFRunLoopRun ();
246+ }
247+ }
248+
249+ - (void )_dispatchEvent : (Event *)event type : (NSString * const )type
250+ {
251+ NSArray *errorHandlers = self.listeners [type];
224252 for (EventSourceEventHandler handler in errorHandlers) {
225- dispatch_async (dispatch_get_main_queue () , ^{
226- handler (e );
253+ dispatch_async (connectionQueue , ^{
254+ handler (event );
227255 });
228256 }
229-
230- [self open ];
257+ }
258+
259+ - (void )_dispatchEvent : (Event *)event
260+ {
261+ [self _dispatchEvent: event type: MessageEvent];
262+
263+ if (event.event != nil ) {
264+ [self _dispatchEvent: event type: event.event];
265+ }
231266}
232267
233268@end
@@ -264,3 +299,4 @@ - (NSString *)description
264299NSString *const MessageEvent = @" message" ;
265300NSString *const ErrorEvent = @" error" ;
266301NSString *const OpenEvent = @" open" ;
302+ NSString *const ReadyStateEvent = @" readyState" ;
0 commit comments