Core Foundation Sockets  v0.2(28.0)
 All Classes Files Functions Variables Properties
CFStreamPair.m
Go to the documentation of this file.
1 // CFSockets CFStreamPair.m
2 //
3 // Copyright © 2012, 2013, Roy Ratcliffe, Pioneering Software, United Kingdom
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the “Software”), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OF ANY KIND, EITHER
16 // EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO
18 // EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
19 // OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 // DEALINGS IN THE SOFTWARE.
22 //
23 //------------------------------------------------------------------------------
24 
25 #import "CFStreamPair.h"
26 
27 @interface CFStreamPair()
28 
29 // You would not normally access the buffers directly. The following exposes the
30 // buffer implementation, albeit away from the public header: just a pair of
31 // mutable data objects.
32 @property(strong, NS_NONATOMIC_IOSONLY) NSMutableData *requestBuffer;
33 @property(strong, NS_NONATOMIC_IOSONLY) NSMutableData *responseBuffer;
34 
35 @end
36 
37 @implementation CFStreamPair
38 
39 @synthesize delegate = _delegate;
40 
41 // streams
42 @synthesize requestStream = _requestStream;
43 @synthesize responseStream = _responseStream;
44 
45 // buffers
46 @synthesize requestBuffer = _requestBuffer;
47 @synthesize responseBuffer = _responseBuffer;
48 
49 // designated initialiser
50 - (id)init
51 {
52  if ((self = [super init]))
53  {
54  // Sets up the request and response buffers at the outset. You can ask
55  // for available request bytes even before the request stream
56  // opens. Similarly, you can send response bytes even before the
57  // response opens. Hard to imagine exactly why however. Still, there is
58  // nothing to say that we can assume that the response stream will open
59  // before the request opens, or vice versa; indeed, the delegate may
60  // even respond by sending some bytes even before the response stream
61  // becomes ready. The buffer pair make such behaviour a valid pattern.
62  [self setRequestBuffer:[NSMutableData data]];
63  [self setResponseBuffer:[NSMutableData data]];
64  }
65  return self;
66 }
67 
68 - (void)dealloc
69 {
70  [self close];
71 }
72 
73 // convenience initialiser
74 - (id)initWithRequestStream:(NSInputStream *)requestStream responseStream:(NSOutputStream *)responseStream
75 {
76  self = [self init];
77  if (self)
78  {
79  [self setRequestStream:requestStream];
80  [self setResponseStream:responseStream];
81  }
82  return self;
83 }
84 
85 - (id)initWithSocketNativeHandle:(NSSocketNativeHandle)socketNativeHandle
86 {
87  if ((self = [self init]))
88  {
89  CFReadStreamRef readStream = NULL;
90  CFWriteStreamRef writeStream = NULL;
91  CFStreamCreatePairWithSocket(kCFAllocatorDefault, socketNativeHandle, &readStream, &writeStream);
92  if (readStream && writeStream)
93  {
94  CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
95  CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
96  [self setRequestStream:CFBridgingRelease(readStream)];
97  [self setResponseStream:CFBridgingRelease(writeStream)];
98  }
99  else
100  {
101  if (readStream) CFRelease(readStream);
102  if (writeStream) CFRelease(writeStream);
103 
104  // Something went wrong. Answer nil. Bear in mind however that this
105  // does not mean that the de-allocation method will not run: it
106  // will run.
107  self = nil;
108  }
109  }
110  return self;
111 }
112 
113 - (void)open
114 {
115  for (NSStream *stream in [NSArray arrayWithObjects:[self requestStream], [self responseStream], nil])
116  {
117  [stream setDelegate:self];
118 
119  [stream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
120  [stream open];
121  }
122 }
123 
124 - (void)close
125 {
126  // Send -close first. Closing may trigger events. Let the stream emit all
127  // events until closing finishes. Might be wise to check the stream status
128  // first, before attempting to close the stream. The de-allocator invokes
129  // -close and therefore may send a double-close if the pair has already
130  // received an explicit -close message.
131  for (NSStream *stream in [NSArray arrayWithObjects:[self requestStream], [self responseStream], nil])
132  {
133  [stream close];
134  [stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
135  }
136 }
137 
139 {
140  NSData *bytes = [[self requestBuffer] copy];
141  [[self requestBuffer] setLength:0];
142  return bytes;
143 }
144 
145 - (NSString *)receiveLineUsingEncoding:(NSStringEncoding)encoding
146 {
147  // The implementation first converts all the request bytes to a string. This
148  // could be risky for multi-byte characters. The implementation effectively
149  // assumes that multi-byte characters do not cross buffer boundaries.
150  //
151  // When the request range has length equal to zero, sending
152  // -lineRangeForRange: searches for the first line. The final bit is
153  // tricky. How to dissect the line from any remaining characters? Simply
154  // convert the remaining characters back to data using the given encoding.
155  NSString *result;
156  NSString *requestString = [[NSString alloc] initWithData:[self requestBuffer] encoding:encoding];
157  NSRange lineRange = [requestString lineRangeForRange:NSMakeRange(0, 0)];
158  if (lineRange.length)
159  {
160  [[self requestBuffer] setData:[[requestString substringFromIndex:lineRange.length] dataUsingEncoding:encoding]];
161  result = [requestString substringToIndex:lineRange.length];
162  }
163  else
164  {
165  result = nil;
166  }
167  return result;
168 }
169 
170 - (void)sendBytes:(NSData *)responseBytes
171 {
172  [[self responseBuffer] appendData:responseBytes];
173 
174  // Trigger a "has space available" event if the response stream reports
175  // available space at this point.
176  if ([[self responseStream] hasSpaceAvailable])
177  {
178  [self hasSpaceAvailable];
179  }
180 }
181 
182 #pragma mark -
183 #pragma mark Overrides
184 
186 {
187  if ([[self requestBuffer] length])
188  {
189  id delegate = [self delegate];
190  if (delegate && [delegate respondsToSelector:@selector(streamPair:hasBytesAvailable:)])
191  {
192  [delegate streamPair:self hasBytesAvailable:[[self requestBuffer] length]];
193  }
194  }
195 }
196 
198 {
199  // Note that writing zero bytes to the response stream closes the
200  // connection. Therefore, avoid sending nothing unless you want to close.
201  if ([[self responseBuffer] length])
202  {
203  [self sendBytes];
204  }
205 }
206 
207 - (void)sendBytes
208 {
209  NSMutableData *responseBuffer = [self responseBuffer];
210  NSInteger bytesSent = [[self responseStream] write:[responseBuffer bytes] maxLength:[responseBuffer length]];
211  if (bytesSent > 0)
212  {
213  [responseBuffer replaceBytesInRange:NSMakeRange(0, bytesSent) withBytes:NULL length:0];
214  }
215 }
216 
217 - (void)handleRequestEvent:(NSStreamEvent)eventCode
218 {
219  switch (eventCode)
220  {
221  case NSStreamEventHasBytesAvailable:
222  {
223  uint8_t bytes[4096];
224  NSInteger bytesAvailable = [[self requestStream] read:bytes maxLength:sizeof(bytes)];
225  // Do not send a -read:maxLength message unless the stream reports
226  // that it has bytes available. Always send this message at least
227  // once when the bytes-available event fires, i.e. right now. The
228  // stream event indicates that available bytes have already been
229  // sensed. Avoid asking again.
230  //
231  // What happens however if more bytes arrive while reading, or the
232  // available bytes overflow the stack-based temporary buffer? In
233  // these cases, after reading, ask if more bytes exist. Issue
234  // another read if they do, and repeat while they do.
235  while (bytesAvailable > 0)
236  {
237  [[self requestBuffer] appendBytes:bytes length:bytesAvailable];
238  if ([[self requestStream] hasBytesAvailable])
239  {
240  bytesAvailable = [[self requestStream] read:bytes maxLength:sizeof(bytes)];
241  }
242  else
243  {
244  bytesAvailable = 0;
245  }
246  }
247  // Please note, the delegate can receive an has-bytes-available
248  // event immediately followed by an error event.
249  [self hasBytesAvailable];
250  if (bytesAvailable < 0)
251  {
252 
253  }
254  break;
255  }
256  default:
257  ;
258  }
259 
260  id delegate = [self delegate];
261  if (delegate && [delegate respondsToSelector:@selector(streamPair:handleRequestEvent:)])
262  {
263  [delegate streamPair:self handleRequestEvent:eventCode];
264  }
265 }
266 
267 - (void)handleResponseEvent:(NSStreamEvent)eventCode
268 {
269  switch (eventCode)
270  {
271  case NSStreamEventHasSpaceAvailable:
272  {
273  [self hasSpaceAvailable];
274  break;
275  }
276  default:
277  ;
278  }
279 
280  id delegate = [self delegate];
281  if (delegate && [delegate respondsToSelector:@selector(streamPair:handleResponseEvent:)])
282  {
283  [delegate streamPair:self handleResponseEvent:eventCode];
284  }
285 }
286 
287 #pragma mark -
288 #pragma mark Stream Delegate
289 
290 - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
291 {
292  if (stream == [self requestStream])
293  {
294  [self handleRequestEvent:eventCode];
295  }
296  else if (stream == [self responseStream])
297  {
298  [self handleResponseEvent:eventCode];
299  }
300  else
301  {
302  ;
303  }
304 }
305 
306 @end