Merge pull request #10 from gunyarakun/fix-invalid-return
[cocotron.git] / Foundation / NSStream / NSOutputStream_socket.m
blob148a34c60b245b532baabd36a31f867c4f4d54f0
1 /* Copyright (c) 2006-2007 Christopher J. W. Lloyd <cjwl@objc.net>
3 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
8 #import <Foundation/NSOutputStream_socket.h>
9 #import <Foundation/NSSocket.h>
10 #import <Foundation/NSSelectInputSource.h>
11 #import <Foundation/NSSelectSet.h>
12 #import <Foundation/NSError.h>
13 #import <Foundation/NSRunLoop.h>
14 #import <Foundation/NSRaise.h>
15 #import <Foundation/NSData.h>
16 #import <Foundation/CFSSLHandler.h>
17 #import <CFNetwork/CFSocketStream.h>
18 #import <Foundation/NSRaiseException.h>
20 @implementation NSOutputStream_socket
22 -initWithSocket:(NSSocket *)socket streamStatus:(NSStreamStatus)status {
23    _delegate=self;
24    _error=nil;
25    _status=status;
26    _socket=[socket retain];
27    _inputSource=nil;
28    return self;
31 -(void)dealloc {
32    [_error release];
33    [_socket release];
34    [_inputSource release];
35    [super dealloc];
38 -(int)fileDescriptor {
39    return [_socket fileDescriptor];
42 -delegate {
43    return _delegate;
46 -(void)setClientEvents:(CFOptionFlags)events callBack:(CFWriteStreamClientCallBack)callBack context:(CFStreamClientContext *)context {
47    _events=events;
48    _callBack=callBack;
50    if(context!=NULL && context->info!=NULL && context->retain!=NULL)
51     context->retain(context->info);
53    _context.version=0;
54    if(_context.info!=NULL && _context.release!=NULL)
55     _context.release(_context.info);
56    _context.info=NULL;
57    _context.retain=NULL;
58    _context.release=NULL;
60    if(context!=NULL)
61     _context=*context;
64 -(void)setDelegate:delegate {
65    _delegate=delegate;
66    if(_delegate==nil)
67     _delegate=self;
70 -propertyForKey:(NSString *)key {
71    if([key isEqualToString:(NSString *)kCFStreamPropertySocketNativeHandle]){
72     CFSocketNativeHandle value=(_socket==nil)?-1:[_socket fileDescriptor];
74     return [NSData dataWithBytes:&value length:sizeof(value)];
75    }
77    NSUnimplementedMethod();
78    return nil;
81 -(BOOL)setProperty:property forKey:(NSString *)key {
82    if([key isEqualToString:(NSString *)kCFStreamPropertySSLSettings])
83     return [_socket setSSLProperties:(CFDictionaryRef)property];
85    NSUnimplementedMethod();
86    return NO;
89 -(void)open {
90    if(_status==NSStreamStatusNotOpen){
91     _status=NSStreamStatusOpening;
92    }
95 -(NSError *)streamError {
96    return _error;
99 -(NSStreamStatus)streamStatus {
100    return _status;
103 -(void)scheduleInRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode {
104    if(_inputSource==nil){
105     _inputSource=[[NSSelectInputSource alloc] initWithSocket:_socket];
106     [_inputSource setDelegate:self];
107     [_inputSource setSelectEventMask:NSSelectWriteEvent];
108    }
110    [runLoop addInputSource:_inputSource forMode:mode];
113 -(void)removeFromRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode {
114    if(_inputSource!=nil)
115     [runLoop removeInputSource:_inputSource forMode:mode];
118 -(void)close {
119    [_inputSource setSelectEventMask:0];
120    [_inputSource invalidate];
121    [_socket close];
124 static BOOL socketHasSpaceAvailable(NSSocket *socket){
125    NSSelectSet *selectSet=[[[NSSelectSet alloc] init] autorelease];
126    NSSelectSet *outputSet;
128    [selectSet addObjectForWrite:socket];
129    if([selectSet waitForSelectWithOutputSet:&outputSet beforeDate:[NSDate date]]==nil)
130     return [outputSet containsObjectForWrite:socket];
132    return NO;
135 -(BOOL)hasSpaceAvailable {
136    if(_status==NSStreamStatusOpen){
138     CFSSLHandler *sslHandler=[_socket sslHandler];
140     if(sslHandler==nil)
141      return socketHasSpaceAvailable(_socket);
142     else {
143      if([sslHandler writeBytesAvailable]==0)
144       return YES;
145      else if(socketHasSpaceAvailable(_socket)){
146       [sslHandler transferOneBufferFromSSLToSocket:_socket];
147      }
148     }
149    }
151    return NO;
154 -(NSInteger)write:(const uint8_t *)buffer maxLength:(NSUInteger)length {
156    if(_status!=NSStreamStatusOpen && _status!=NSStreamStatusOpening)
157     return -1;
159    CFSSLHandler *sslHandler=[_socket sslHandler];
161    if(sslHandler==nil){
162     [_inputSource setSelectEventMask:[_inputSource selectEventMask]|NSSelectWriteEvent];
163     return [_socket write:buffer maxLength:length];
164    }
165    else {
167     [sslHandler runHandshakeIfNeeded:_socket];
169     NSInteger check=[sslHandler writePlaintext:buffer maxLength:length];
171     if(check!=length)
172      NSCLog("failure writePlaintext:%d=%d",length,check);
174     [sslHandler runWithSocket:_socket];
175     [_inputSource setSelectEventMask:[_inputSource selectEventMask]|NSSelectWriteEvent];
177     return check;
178    }
182 - (void)selectInputSource:(NSSelectInputSource *)inputSource selectEvent:(NSUInteger)selectEvent
184     NSStreamEvent event;
186     switch (_status) {
187         case NSStreamStatusOpening:
188             _status = NSStreamStatusOpen;
189             event = NSStreamEventOpenCompleted;
190             break;
192         case NSStreamStatusOpen:
193             if(![self hasSpaceAvailable]) {
194                 event = NSStreamEventNone;
195             } else {
196                 event = NSStreamEventHasSpaceAvailable;
197                 /* Streams only signal when space is available once, then it reactivates on a write, so we turn it off before notifying */
198                 [_inputSource setSelectEventMask:[_inputSource selectEventMask] &~ NSSelectWriteEvent];
199             }
200             break;
202         case NSStreamStatusAtEnd:
203             event = NSStreamEventEndEncountered;
204             break;
206         default:
207             event = NSStreamEventNone;
208             break;
209     }
211     if (event != NSStreamEventNone) {
212         if (_callBack != NULL) {
213             if (_events & event) {
214                 _callBack((CFWriteStreamRef)self, (CFStreamEventType)event, _context.info);
215             }
216         } else {
217             if ([_delegate respondsToSelector:@selector(stream:handleEvent:)]) {
218                 [_delegate stream:self handleEvent:event];
219             }
220         }
221     }
225 @end