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