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