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 {
26 _socket=[socket retain];
34 [_inputSource release];
38 -(int)fileDescriptor {
39 return [_socket fileDescriptor];
46 -(void)setClientEvents:(CFOptionFlags)events callBack:(CFWriteStreamClientCallBack)callBack context:(CFStreamClientContext *)context {
50 if(context!=NULL && context->info!=NULL && context->retain!=NULL)
51 context->retain(context->info);
54 if(_context.info!=NULL && _context.release!=NULL)
55 _context.release(_context.info);
58 _context.release=NULL;
64 -(void)setDelegate:delegate {
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)];
77 NSUnimplementedMethod();
81 -(BOOL)setProperty:property forKey:(NSString *)key {
82 if([key isEqualToString:(NSString *)kCFStreamPropertySSLSettings])
83 return [_socket setSSLProperties:(CFDictionaryRef)property];
85 NSUnimplementedMethod();
90 if(_status==NSStreamStatusNotOpen){
91 _status=NSStreamStatusOpening;
95 -(NSError *)streamError {
99 -(NSStreamStatus)streamStatus {
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];
110 [runLoop addInputSource:_inputSource forMode:mode];
113 -(void)removeFromRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode {
114 if(_inputSource!=nil)
115 [runLoop removeInputSource:_inputSource forMode:mode];
119 [_inputSource setSelectEventMask:0];
120 [_inputSource invalidate];
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];
135 -(BOOL)hasSpaceAvailable {
136 if(_status==NSStreamStatusOpen){
138 CFSSLHandler *sslHandler=[_socket sslHandler];
141 return socketHasSpaceAvailable(_socket);
143 if([sslHandler writeBytesAvailable]==0)
145 else if(socketHasSpaceAvailable(_socket)){
146 [sslHandler transferOneBufferFromSSLToSocket:_socket];
154 -(NSInteger)write:(const uint8_t *)buffer maxLength:(NSUInteger)length {
156 if(_status!=NSStreamStatusOpen && _status!=NSStreamStatusOpening)
159 CFSSLHandler *sslHandler=[_socket sslHandler];
162 [_inputSource setSelectEventMask:[_inputSource selectEventMask]|NSSelectWriteEvent];
163 return [_socket write:buffer maxLength:length];
167 [sslHandler runHandshakeIfNeeded:_socket];
169 NSInteger check=[sslHandler writePlaintext:buffer maxLength:length];
172 NSCLog("failure writePlaintext:%d=%d",length,check);
174 [sslHandler runWithSocket:_socket];
175 [_inputSource setSelectEventMask:[_inputSource selectEventMask]|NSSelectWriteEvent];
182 - (void)selectInputSource:(NSSelectInputSource *)inputSource selectEvent:(NSUInteger)selectEvent
187 case NSStreamStatusOpening:
188 _status = NSStreamStatusOpen;
189 event = NSStreamEventOpenCompleted;
192 case NSStreamStatusOpen:
193 if(![self hasSpaceAvailable]) {
194 event = NSStreamEventNone;
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];
202 case NSStreamStatusAtEnd:
203 event = NSStreamEventEndEncountered;
207 event = NSStreamEventNone;
211 if (event != NSStreamEventNone) {
212 if (_callBack != NULL) {
213 if (_events & event) {
214 _callBack((CFWriteStreamRef)self, (CFStreamEventType)event, _context.info);
217 if ([_delegate respondsToSelector:@selector(stream:handleEvent:)]) {
218 [_delegate stream:self handleEvent:event];