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