forked from couchbaselabs/CouchCocoa
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCouchConnectionChangeTracker.m
125 lines (103 loc) · 3.78 KB
/
CouchConnectionChangeTracker.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//
// CouchConnectionChangeTracker.m
// CouchCocoa
//
// Created by Jens Alfke on 12/1/11.
// Copyright (c) 2011 Couchbase, Inc. All rights reserved.
//
// <http://wiki.apache.org/couchdb/HTTP_database_API#Changes>
#import "CouchConnectionChangeTracker.h"
#import "CouchInternal.h"
@implementation CouchConnectionChangeTracker
- (BOOL) start {
// For some reason continuous mode doesn't work with CFNetwork.
if (_mode == kContinuous)
_mode = kLongPoll;
_inputBuffer = [[NSMutableData alloc] init];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL: self.changesFeedURL];
request.cachePolicy = NSURLRequestReloadIgnoringCacheData;
request.timeoutInterval = 6.02e23;
_connection = [[NSURLConnection connectionWithRequest: request delegate: self] retain];
[_connection start];
COUCHLOG(@"%@: Started... <%@>", self, request.URL);
return YES;
}
- (void) clearConnection {
[_connection autorelease];
_connection = nil;
[_inputBuffer release];
_inputBuffer = nil;
_status = 0;
}
- (void) stopped {
COUCHLOG2(@"%@: Stopped", self);
[self clearConnection];
[super stopped];
}
- (void) stop {
[_connection cancel];
[super stop];
}
- (void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
COUCHLOG2(@"%@: didReceiveAuthenticationChallenge", self);
if (challenge.previousFailureCount == 0) {
NSURLCredential* credential = self.authCredential;
if (credential) {
[challenge.sender useCredential: credential forAuthenticationChallenge: challenge];
return;
}
}
// give up
[challenge.sender cancelAuthenticationChallenge: challenge];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
_status = (int) ((NSHTTPURLResponse*)response).statusCode;
COUCHLOG3(@"%@: Got response, status %d", self, _status);
if (_status >= 300) {
Warn(@"%@: Got status %i", self, _status);
[self stop];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
COUCHLOG3(@"%@: Got %lu bytes", self, (unsigned long)data.length);
[_inputBuffer appendData: data];
if (_mode == kContinuous) {
// In continuous mode, break input into lines and parse each as JSON:
for (;;) {
const char* start = _inputBuffer.bytes;
const char* eol = strnstr(start, "\n", _inputBuffer.length);
if (!eol)
break; // Wait till we have a complete line
ptrdiff_t lineLength = eol - start;
NSData* chunk = [[[NSData alloc] initWithBytes: start
length: lineLength] autorelease];
[_inputBuffer replaceBytesInRange: NSMakeRange(0, lineLength + 1)
withBytes: NULL length: 0];
// Finally! Send the line to the database to parse:
[self receivedChunk: chunk];
}
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
Warn(@"%@: Got error %@", self, error);
[self stopped];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (_mode != kContinuous) {
int status = _status;
NSData* input = [_inputBuffer retain];
COUCHLOG3(@"%@: Got entire body, %u bytes", self, (unsigned)input.length);
BOOL responseOK = [self receivedPollResponse: input];
[input release];
[self clearConnection];
if (_mode == kLongPoll && status == 200 && responseOK)
[self start]; // Next poll...
else
[self stopped];
} else {
[self stopped];
}
}
@end