1//
2// Copyright (c) ZeroC, Inc. All rights reserved.
3//
4
5#include "Transceiver.h"
6#include "EndpointI.h"
7
8#include <IceIAP/ConnectionInfo.h>
9
10#include <Ice/LocalException.h>
11#include <Ice/ProtocolInstance.h>
12#include <Ice/Buffer.h>
13
14#import <Foundation/NSRunLoop.h>
15#import <Foundation/NSError.h>
16#import <Foundation/NSString.h>
17
18using namespace std;
19using namespace Ice;
20using namespace IceInternal;
21
22@interface iAPTransceiverCallback : NSObject<NSStreamDelegate>
23{
24@private
25
26    SelectorReadyCallback* callback;
27}
28-(id) init:(SelectorReadyCallback*)cb;
29@end
30
31@implementation iAPTransceiverCallback
32-(id) init:(SelectorReadyCallback*)cb
33{
34    if(![super init])
35    {
36        return nil;
37    }
38    callback = cb;
39    return self;
40}
41
42- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
43{
44    switch(eventCode)
45    {
46    case NSStreamEventHasBytesAvailable:
47        callback->readyCallback(SocketOperationRead);
48        break;
49    case NSStreamEventHasSpaceAvailable:
50        callback->readyCallback(SocketOperationWrite);
51        break;
52    case NSStreamEventOpenCompleted:
53        if([[stream class] isSubclassOfClass:[NSInputStream class]])
54        {
55            callback->readyCallback(static_cast<SocketOperation>(SocketOperationConnect | SocketOperationRead));
56        }
57        else
58        {
59            callback->readyCallback(static_cast<SocketOperation>(SocketOperationConnect | SocketOperationWrite));
60        }
61        break;
62    default:
63        if([[stream class] isSubclassOfClass:[NSInputStream class]])
64        {
65            callback->readyCallback(SocketOperationRead, -1); // Error
66        }
67        else
68        {
69            callback->readyCallback(SocketOperationWrite, -1); // Error
70        }
71    }
72}
73@end
74
75void
76IceObjC::iAPTransceiver::initStreams(SelectorReadyCallback* callback)
77{
78    _callback = [[iAPTransceiverCallback alloc] init:callback];
79    [_writeStream setDelegate:_callback];
80    [_readStream setDelegate:_callback];
81}
82
83SocketOperation
84IceObjC::iAPTransceiver::registerWithRunLoop(SocketOperation op)
85{
86    IceUtil::Mutex::Lock sync(_mutex);
87    SocketOperation readyOp = SocketOperationNone;
88    if(op & SocketOperationConnect)
89    {
90        if([_writeStream streamStatus] != NSStreamStatusNotOpen || [_readStream streamStatus] != NSStreamStatusNotOpen)
91        {
92            return SocketOperationConnect;
93        }
94
95        _opening = true;
96
97        [_writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
98        [_readStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
99
100        _writeStreamRegistered = true; // Note: this must be set after the schedule call
101        _readStreamRegistered = true; // Note: this must be set after the schedule call
102
103        [_writeStream open];
104        [_readStream open];
105    }
106    else
107    {
108        if(op & SocketOperationWrite)
109        {
110            if([_writeStream hasSpaceAvailable])
111            {
112                readyOp = static_cast<SocketOperation>(readyOp | SocketOperationWrite);
113            }
114            else if(!_writeStreamRegistered)
115            {
116                [_writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
117                _writeStreamRegistered = true; // Note: this must be set after the schedule call
118                if([_writeStream hasSpaceAvailable])
119                {
120                    readyOp = static_cast<SocketOperation>(readyOp | SocketOperationWrite);
121                }
122            }
123        }
124
125        if(op & SocketOperationRead)
126        {
127            if([_readStream hasBytesAvailable])
128            {
129                readyOp = static_cast<SocketOperation>(readyOp | SocketOperationRead);
130            }
131            else if(!_readStreamRegistered)
132            {
133                [_readStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
134                _readStreamRegistered = true; // Note: this must be set after the schedule call
135                if([_readStream hasBytesAvailable])
136                {
137                    readyOp = static_cast<SocketOperation>(readyOp | SocketOperationRead);
138                }
139            }
140        }
141    }
142    return readyOp;
143}
144
145SocketOperation
146IceObjC::iAPTransceiver::unregisterFromRunLoop(SocketOperation op, bool error)
147{
148    IceUtil::Mutex::Lock sync(_mutex);
149    _error |= error;
150
151    if(_opening)
152    {
153        // Wait for the stream to be ready for write
154        if(op == SocketOperationWrite)
155        {
156            _writeStreamRegistered = false;
157        }
158
159        //
160        // We don't wait for the stream to be ready for read (even if
161        // it's a client connection) because there's no guarantees that
162        // the server might actually send data right away. If we use
163        // the WebSocket transport, the server actually waits for the
164        // client to write the HTTP upgrade request.
165        //
166        //if(op & SocketOperationRead && (_fd != INVALID_SOCKET || !(op & SocketOperationConnect)))
167        if(op == (SocketOperationRead | SocketOperationConnect))
168        {
169            _readStreamRegistered = false;
170        }
171
172        if(error || (!_readStreamRegistered && !_writeStreamRegistered))
173        {
174            [_writeStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
175            [_readStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
176            _opening = false;
177            return SocketOperationConnect;
178        }
179        else
180        {
181            return SocketOperationNone;
182        }
183    }
184    else
185    {
186        if(op & SocketOperationWrite && _writeStreamRegistered)
187        {
188            [_writeStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
189            _writeStreamRegistered = false;
190        }
191
192        if(op & SocketOperationRead && _readStreamRegistered)
193        {
194            [_readStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
195            _readStreamRegistered = false;
196        }
197    }
198    return op;
199}
200
201void
202IceObjC::iAPTransceiver::closeStreams()
203{
204    [_writeStream setDelegate:nil];
205    [_readStream setDelegate:nil];
206
207    [_callback release];
208    _callback = 0;
209
210    [_writeStream close];
211    [_readStream close];
212}
213
214IceInternal::NativeInfoPtr
215IceObjC::iAPTransceiver::getNativeInfo()
216{
217    return this;
218}
219
220SocketOperation
221IceObjC::iAPTransceiver::initialize(Buffer& /*readBuffer*/, Buffer& /*writeBuffer*/)
222{
223    IceUtil::Mutex::Lock sync(_mutex);
224    if(_state == StateNeedConnect)
225    {
226        _state = StateConnectPending;
227        return SocketOperationConnect;
228    }
229
230    if(_state <= StateConnectPending)
231    {
232        if(_error)
233        {
234            checkErrorStatus(_writeStream, __FILE__, __LINE__);
235            checkErrorStatus(_readStream, __FILE__, __LINE__);
236        }
237        _state = StateConnected;
238    }
239    assert(_state == StateConnected);
240    return SocketOperationNone;
241}
242
243SocketOperation
244IceObjC::iAPTransceiver::closing(bool initiator, const Ice::LocalException&)
245{
246    // If we are initiating the connection closure, wait for the peer
247    // to close the TCP/IP connection. Otherwise, close immediately.
248    return initiator ? SocketOperationRead : SocketOperationNone;
249}
250
251void
252IceObjC::iAPTransceiver::close()
253{
254}
255
256SocketOperation
257IceObjC::iAPTransceiver::write(Buffer& buf)
258{
259    IceUtil::Mutex::Lock sync(_mutex);
260    if(_error)
261    {
262        checkErrorStatus(_writeStream, __FILE__, __LINE__);
263    }
264
265    size_t packetSize = buf.b.end() - buf.i;
266    while(buf.i != buf.b.end())
267    {
268        if(![_writeStream hasSpaceAvailable])
269        {
270            return SocketOperationWrite;
271        }
272        assert([_writeStream streamStatus] >= NSStreamStatusOpen);
273
274        NSInteger ret = [_writeStream write:reinterpret_cast<const UInt8*>(&*buf.i) maxLength:packetSize];
275        if(ret == SOCKET_ERROR)
276        {
277            checkErrorStatus(_writeStream, __FILE__, __LINE__);
278            if(noBuffers() && packetSize > 1024)
279            {
280                packetSize /= 2;
281            }
282            continue;
283        }
284
285        buf.i += ret;
286
287        if(packetSize > static_cast<size_t>(buf.b.end() - buf.i))
288        {
289            packetSize = buf.b.end() - buf.i;
290        }
291    }
292
293    return SocketOperationNone;
294}
295
296SocketOperation
297IceObjC::iAPTransceiver::read(Buffer& buf)
298{
299    IceUtil::Mutex::Lock sync(_mutex);
300    if(_error)
301    {
302        checkErrorStatus(_readStream, __FILE__, __LINE__);
303    }
304
305    size_t packetSize = buf.b.end() - buf.i;
306    while(buf.i != buf.b.end())
307    {
308        if(![_readStream hasBytesAvailable] && [_readStream streamStatus] != NSStreamStatusError)
309        {
310            return SocketOperationRead;
311        }
312        assert([_readStream streamStatus] >= NSStreamStatusOpen);
313
314        NSInteger ret = [_readStream read:reinterpret_cast<UInt8*>(&*buf.i) maxLength:packetSize];
315        if(ret == 0)
316        {
317            throw ConnectionLostException(__FILE__, __LINE__);
318        }
319
320        if(ret == SOCKET_ERROR)
321        {
322            checkErrorStatus(_readStream, __FILE__, __LINE__);
323            if(noBuffers() && packetSize > 1024)
324            {
325                packetSize /= 2;
326            }
327            continue;
328        }
329
330        buf.i += ret;
331
332        if(packetSize > static_cast<size_t>(buf.b.end() - buf.i))
333        {
334            packetSize = buf.b.end() - buf.i;
335        }
336    }
337
338    return SocketOperationNone;
339}
340
341string
342IceObjC::iAPTransceiver::protocol() const
343{
344    return _instance->protocol();
345}
346
347string
348IceObjC::iAPTransceiver::toString() const
349{
350    return _desc;
351}
352
353string
354IceObjC::iAPTransceiver::toDetailedString() const
355{
356    return toString();
357}
358
359Ice::ConnectionInfoPtr
360IceObjC::iAPTransceiver::getInfo() const
361{
362    IceIAP::ConnectionInfoPtr info = ICE_MAKE_SHARED(IceIAP::ConnectionInfo);
363    info->manufacturer = [_session.accessory.manufacturer UTF8String];
364    info->name = [_session.accessory.name UTF8String];
365    info->modelNumber = [_session.accessory.modelNumber UTF8String];
366    info->firmwareRevision = [_session.accessory.firmwareRevision UTF8String];
367    info->hardwareRevision = [_session.accessory.hardwareRevision UTF8String];
368    info->protocol = [_session.protocolString UTF8String];
369    return info;
370}
371
372void
373IceObjC::iAPTransceiver::checkSendSize(const Buffer& /*buf*/)
374{
375}
376
377void
378IceObjC::iAPTransceiver::setBufferSize(int, int)
379{
380}
381
382IceObjC::iAPTransceiver::iAPTransceiver(const ProtocolInstancePtr& instance, EASession* session) :
383    StreamNativeInfo(INVALID_SOCKET),
384    _instance(instance),
385    _session([session retain]),
386    _readStream([session inputStream]),
387    _writeStream([session outputStream]),
388    _readStreamRegistered(false),
389    _writeStreamRegistered(false),
390    _error(false),
391    _state(StateNeedConnect)
392{
393    ostringstream os;
394    os << "name = " << [session.accessory.name UTF8String] << "\n";
395    os << "protocol = " << [session.protocolString UTF8String];
396    _desc = os.str();
397}
398
399IceObjC::iAPTransceiver::~iAPTransceiver()
400{
401    [_session release];
402}
403
404void
405IceObjC::iAPTransceiver::checkErrorStatus(NSStream* stream, const char* file, int line)
406{
407    NSStreamStatus status = [stream streamStatus];
408    if(status == NSStreamStatusAtEnd)
409    {
410        throw ConnectionLostException(file, line);
411    }
412
413    assert(status == NSStreamStatusError);
414
415    NSError* err = [stream streamError];
416    NSString* domain = [err domain];
417    if([domain compare:NSPOSIXErrorDomain] == NSOrderedSame)
418    {
419        errno = [err code];
420        [err release];
421        if(interrupted() || noBuffers())
422        {
423            return;
424        }
425        else if(connectionRefused())
426        {
427            ConnectionRefusedException ex(file, line);
428            ex.error = getSocketErrno();
429            throw ex;
430        }
431        else if(connectFailed())
432        {
433            ConnectFailedException ex(file, line);
434            ex.error = getSocketErrno();
435            throw ex;
436        }
437        else
438        {
439            SocketException ex(file, line);
440            ex.error = getSocketErrno();
441            throw ex;
442        }
443    }
444
445    // Otherwise throw a generic exception.
446    CFNetworkException ex(file, line);
447    ex.domain = [domain UTF8String];
448    ex.error = [err code];
449    [err release];
450    throw ex;
451}
452