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