BLIP/BLIPRequest.m
author Jens Alfke <jens@mooseyard.com>
Fri May 23 17:37:36 2008 -0700 (2008-05-23)
changeset 0 9d67172bb323
child 1 8267d5c429c4
permissions -rw-r--r--
First checkin after breaking out of Cloudy
jens@0
     1
//
jens@0
     2
//  BLIPRequest.m
jens@0
     3
//  MYNetwork
jens@0
     4
//
jens@0
     5
//  Created by Jens Alfke on 5/22/08.
jens@0
     6
//  Copyright 2008 Jens Alfke. All rights reserved.
jens@0
     7
//
jens@0
     8
jens@0
     9
#import "BLIPRequest.h"
jens@0
    10
#import "BLIP_Internal.h"
jens@0
    11
#import "BLIPWriter.h"
jens@0
    12
#import "BLIPReader.h"
jens@0
    13
#import "Target.h"
jens@0
    14
#import "ExceptionUtils.h"
jens@0
    15
jens@0
    16
jens@0
    17
@implementation BLIPRequest
jens@0
    18
jens@0
    19
jens@0
    20
- (id) _initWithConnection: (BLIPConnection*)connection
jens@0
    21
                      body: (NSData*)body 
jens@0
    22
                properties: (NSDictionary*)properties
jens@0
    23
{
jens@0
    24
    self = [self _initWithConnection: connection
jens@0
    25
                              isMine: YES
jens@0
    26
                               flags: kBLIP_MSG
jens@0
    27
                              number: 0
jens@0
    28
                                body: body];
jens@0
    29
    if( self ) {
jens@0
    30
        _isMutable = YES;
jens@0
    31
        if( body )
jens@0
    32
            self.body = body;
jens@0
    33
        if( properties )
jens@0
    34
            [self.mutableProperties setAllProperties: properties];
jens@0
    35
    }
jens@0
    36
    return self;
jens@0
    37
}
jens@0
    38
jens@0
    39
+ (BLIPRequest*) requestWithBody: (NSData*)body
jens@0
    40
{
jens@0
    41
    return [[[self alloc] _initWithConnection: nil body: body properties: nil] autorelease];
jens@0
    42
}
jens@0
    43
jens@0
    44
+ (BLIPRequest*) requestWithBody: (NSData*)body
jens@0
    45
                      properties: (NSDictionary*)properties
jens@0
    46
{
jens@0
    47
    return [[[self alloc] _initWithConnection: nil body: body properties: properties] autorelease];
jens@0
    48
}
jens@0
    49
jens@0
    50
jens@0
    51
- (void) dealloc
jens@0
    52
{
jens@0
    53
    [_response release];
jens@0
    54
    [super dealloc];
jens@0
    55
}
jens@0
    56
jens@0
    57
jens@0
    58
- (BOOL) noReply                            {return (_flags & kBLIP_NoReply) != 0;}
jens@0
    59
- (void) setNoReply: (BOOL)noReply          {[self _setFlag: kBLIP_NoReply value: noReply];}
jens@0
    60
- (BLIPConnection*) connection              {return _connection;}
jens@0
    61
jens@0
    62
- (void) setConnection: (BLIPConnection*)conn
jens@0
    63
{
jens@0
    64
    Assert(_isMine && !_sent,@"Connection can only be set before sending");
jens@0
    65
    setObj(&_connection,conn);
jens@0
    66
}
jens@0
    67
jens@0
    68
jens@0
    69
- (BLIPResponse*) send
jens@0
    70
{
jens@0
    71
    Assert(_connection,@"%@ has no connection to send over",self);
jens@0
    72
    Assert(!_sent,@"%@ was already sent",self);
jens@0
    73
    [self _encode];
jens@0
    74
    BLIPResponse *response = self.response;
jens@0
    75
    if( [(BLIPWriter*)_connection.writer sendRequest: self response: response] )
jens@0
    76
        self.sent = YES;
jens@0
    77
    else
jens@0
    78
        response = nil;
jens@0
    79
    return response;
jens@0
    80
}
jens@0
    81
jens@0
    82
jens@0
    83
- (BLIPResponse*) response
jens@0
    84
{
jens@0
    85
    if( ! _response && ! self.noReply )
jens@0
    86
        _response = [[BLIPResponse alloc] _initWithRequest: self];
jens@0
    87
    return _response;
jens@0
    88
}
jens@0
    89
jens@0
    90
- (void) deferResponse
jens@0
    91
{
jens@0
    92
    // This will allocate _response, causing -repliedTo to become YES, so BLIPConnection won't
jens@0
    93
    // send an automatic empty response after the current request handler returns.
jens@0
    94
    LogTo(BLIP,@"Deferring response to %@",self);
jens@0
    95
    [self response];
jens@0
    96
}
jens@0
    97
jens@0
    98
- (BOOL) repliedTo
jens@0
    99
{
jens@0
   100
    return _response != nil;
jens@0
   101
}
jens@0
   102
jens@0
   103
- (void) respondWithData: (NSData*)data                   {self.response.body = data; [self.response send];}
jens@0
   104
- (void) respondWithString: (NSString*)string             {[self respondWithData: [string dataUsingEncoding: NSUTF8StringEncoding]];}
jens@0
   105
- (void) respondWithError: (NSError*)error                {self.response.error = error; [self.response send];}
jens@0
   106
jens@0
   107
- (void) respondWithErrorCode: (int)errorCode message: (NSString*)errorMessage
jens@0
   108
{
jens@0
   109
    [self respondWithError: BLIPMakeError(errorCode, @"%@",errorMessage)];
jens@0
   110
}
jens@0
   111
jens@0
   112
- (void) respondWithException: (NSException*)exception
jens@0
   113
{
jens@0
   114
    [self respondWithError: BLIPMakeError(kBLIPError_HandlerFailed, @"%@", exception.reason)];
jens@0
   115
}
jens@0
   116
jens@0
   117
jens@0
   118
@end
jens@0
   119
jens@0
   120
jens@0
   121
jens@0
   122
jens@0
   123
#pragma mark -
jens@0
   124
@implementation BLIPResponse
jens@0
   125
jens@0
   126
- (id) _initWithRequest: (BLIPRequest*)request
jens@0
   127
{
jens@0
   128
    Assert(request);
jens@0
   129
    self = [super _initWithConnection: request.connection
jens@0
   130
                               isMine: !request.isMine
jens@0
   131
                                flags: kBLIP_RPY | kBLIP_MoreComing
jens@0
   132
                               number: request.number
jens@0
   133
                                 body: nil];
jens@0
   134
    if (self != nil) {
jens@0
   135
        if( _isMine ) {
jens@0
   136
            _isMutable = YES;
jens@0
   137
            if( request.urgent )
jens@0
   138
                _flags |= kBLIP_Urgent;
jens@0
   139
        } else {
jens@0
   140
            _flags |= kBLIP_MoreComing;
jens@0
   141
        }
jens@0
   142
    }
jens@0
   143
    return self;
jens@0
   144
}
jens@0
   145
jens@0
   146
- (void) dealloc
jens@0
   147
{
jens@0
   148
    [_error release];
jens@0
   149
    [_onComplete release];
jens@0
   150
    [super dealloc];
jens@0
   151
}
jens@0
   152
jens@0
   153
jens@0
   154
- (NSError*) error
jens@0
   155
{
jens@0
   156
    if( ! (_flags & kBLIP_ERR) )
jens@0
   157
        return nil;
jens@0
   158
    
jens@0
   159
    NSMutableDictionary *userInfo = [[self.properties allProperties] mutableCopy];
jens@0
   160
    NSString *domain = [userInfo objectForKey: @"Error-Domain"];
jens@0
   161
    int code = [[userInfo objectForKey: @"Error-Code"] intValue];
jens@0
   162
    if( domain==nil || code==0 ) {
jens@0
   163
        domain = BLIPErrorDomain;
jens@0
   164
        if( code==0 )
jens@0
   165
            code = kBLIPError_Unspecified;
jens@0
   166
    }
jens@0
   167
    [userInfo removeObjectForKey: @"Error-Domain"];
jens@0
   168
    [userInfo removeObjectForKey: @"Error-Code"];
jens@0
   169
    return [NSError errorWithDomain: domain code: code userInfo: userInfo];
jens@0
   170
}
jens@0
   171
jens@0
   172
- (void) _setError: (NSError*)error
jens@0
   173
{
jens@0
   174
    _flags &= ~kBLIP_TypeMask;
jens@0
   175
    if( error ) {
jens@0
   176
        // Setting this stuff is a PITA because this object might be technically immutable,
jens@0
   177
        // in which case the standard setters would barf if I called them.
jens@0
   178
        _flags |= kBLIP_ERR;
jens@0
   179
        setObj(&_body,nil);
jens@0
   180
        setObj(&_mutableBody,nil);
jens@0
   181
        
jens@0
   182
        BLIPMutableProperties *errorProps = [self.properties mutableCopy];
jens@0
   183
        NSDictionary *userInfo = error.userInfo;
jens@0
   184
        for( NSString *key in userInfo ) {
jens@0
   185
            id value = $castIf(NSString,[userInfo objectForKey: key]);
jens@0
   186
            if( value )
jens@0
   187
                [errorProps setValue: value ofProperty: key];
jens@0
   188
        }
jens@0
   189
        [errorProps setValue: error.domain ofProperty: @"Error-Domain"];
jens@0
   190
        [errorProps setValue: $sprintf(@"%i",error.code) ofProperty: @"Error-Code"];
jens@0
   191
        setObj(&_properties,errorProps);
jens@0
   192
        [errorProps release];
jens@0
   193
        
jens@0
   194
    } else {
jens@0
   195
        _flags |= kBLIP_RPY;
jens@0
   196
        [self.mutableProperties setAllProperties: nil];
jens@0
   197
    }
jens@0
   198
}
jens@0
   199
jens@0
   200
- (void) setError: (NSError*)error
jens@0
   201
{
jens@0
   202
    Assert(_isMine && _isMutable);
jens@0
   203
    [self _setError: error];
jens@0
   204
}
jens@0
   205
jens@0
   206
jens@0
   207
- (BOOL) send
jens@0
   208
{
jens@0
   209
    Assert(_connection,@"%@ has no connection to send over",self);
jens@0
   210
    Assert(!_sent,@"%@ was already sent",self);
jens@0
   211
    [self _encode];
jens@0
   212
    return (self.sent = [(BLIPWriter*)_connection.writer sendMessage: self]);
jens@0
   213
}
jens@0
   214
jens@0
   215
jens@0
   216
@synthesize onComplete=_onComplete;
jens@0
   217
jens@0
   218
jens@0
   219
- (void) setComplete: (BOOL)complete
jens@0
   220
{
jens@0
   221
    [super setComplete: complete];
jens@0
   222
    if( complete && _onComplete ) {
jens@0
   223
        @try{
jens@0
   224
            [_onComplete invokeWithSender: self];
jens@0
   225
        }catchAndReport(@"BLIPResponse onComplete target");
jens@0
   226
    }
jens@0
   227
}
jens@0
   228
jens@0
   229
jens@0
   230
- (void) _connectionClosed
jens@0
   231
{
jens@0
   232
    [super _connectionClosed];
jens@0
   233
    if( !_isMine ) {
jens@0
   234
        // Change incoming response to an error:
jens@0
   235
        _isMutable = YES;
jens@0
   236
        [_properties autorelease];
jens@0
   237
        _properties = [_properties mutableCopy];
jens@0
   238
        [self _setError: BLIPMakeError(kBLIPError_Disconnected,
jens@0
   239
                                         @"Connection closed before response was received")];
jens@0
   240
        _isMutable = NO;
jens@0
   241
    }
jens@0
   242
}
jens@0
   243
jens@0
   244
jens@0
   245
@end
jens@0
   246
jens@0
   247
jens@0
   248
/*
jens@0
   249
 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
jens@0
   250
 
jens@0
   251
 Redistribution and use in source and binary forms, with or without modification, are permitted
jens@0
   252
 provided that the following conditions are met:
jens@0
   253
 
jens@0
   254
 * Redistributions of source code must retain the above copyright notice, this list of conditions
jens@0
   255
 and the following disclaimer.
jens@0
   256
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
jens@0
   257
 and the following disclaimer in the documentation and/or other materials provided with the
jens@0
   258
 distribution.
jens@0
   259
 
jens@0
   260
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
jens@0
   261
 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
jens@0
   262
 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
jens@0
   263
 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
jens@0
   264
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
jens@0
   265
 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
jens@0
   266
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
jens@0
   267
 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
jens@0
   268
 */