BLIP/BLIPRequest.m
author Jens Alfke <jens@mooseyard.com>
Sat May 24 21:26:09 2008 -0700 (2008-05-24)
changeset 3 76f302097a75
parent 1 8267d5c429c4
child 18 3be241de1630
permissions -rw-r--r--
Added a BLIP port of Apple's "CocoaEcho" sample code.
     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];
   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         NSDictionary *userInfo = error.userInfo;
   203         for( NSString *key in userInfo ) {
   204             id value = $castIf(NSString,[userInfo objectForKey: key]);
   205             if( value )
   206                 [errorProps setValue: value ofProperty: key];
   207         }
   208         [errorProps setValue: error.domain ofProperty: @"Error-Domain"];
   209         [errorProps setValue: $sprintf(@"%i",error.code) ofProperty: @"Error-Code"];
   210         setObj(&_properties,errorProps);
   211         [errorProps release];
   212         
   213     } else {
   214         _flags |= kBLIP_RPY;
   215         [self.mutableProperties setAllProperties: nil];
   216     }
   217 }
   218 
   219 - (void) setError: (NSError*)error
   220 {
   221     Assert(_isMine && _isMutable);
   222     [self _setError: error];
   223 }
   224 
   225 
   226 - (BOOL) send
   227 {
   228     Assert(_connection,@"%@ has no connection to send over",self);
   229     Assert(!_sent,@"%@ was already sent",self);
   230     [self _encode];
   231     return (self.sent = [(BLIPWriter*)_connection.writer sendMessage: self]);
   232 }
   233 
   234 
   235 @synthesize onComplete=_onComplete;
   236 
   237 
   238 - (void) setComplete: (BOOL)complete
   239 {
   240     [super setComplete: complete];
   241     if( complete && _onComplete ) {
   242         @try{
   243             [_onComplete invokeWithSender: self];
   244         }catchAndReport(@"BLIPResponse onComplete target");
   245     }
   246 }
   247 
   248 
   249 - (void) _connectionClosed
   250 {
   251     [super _connectionClosed];
   252     if( !_isMine ) {
   253         // Change incoming response to an error:
   254         _isMutable = YES;
   255         [_properties autorelease];
   256         _properties = [_properties mutableCopy];
   257         [self _setError: BLIPMakeError(kBLIPError_Disconnected,
   258                                          @"Connection closed before response was received")];
   259         _isMutable = NO;
   260     }
   261 }
   262 
   263 
   264 @end
   265 
   266 
   267 /*
   268  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   269  
   270  Redistribution and use in source and binary forms, with or without modification, are permitted
   271  provided that the following conditions are met:
   272  
   273  * Redistributions of source code must retain the above copyright notice, this list of conditions
   274  and the following disclaimer.
   275  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   276  and the following disclaimer in the documentation and/or other materials provided with the
   277  distribution.
   278  
   279  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   280  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   281  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   282  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   283  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   284  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   285  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   286  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   287  */