~ubuntu-branches/ubuntu/saucy/sope/saucy

« back to all changes in this revision

Viewing changes to sope-appserver/NGObjWeb/WOResponse.m

  • Committer: Package Import Robot
  • Author(s): Jeroen Dekkers
  • Date: 2012-05-09 15:39:17 UTC
  • Revision ID: package-import@ubuntu.com-20120509153917-nr4jlm8mktma1yv3
Tags: upstream-1.3.14
ImportĀ upstreamĀ versionĀ 1.3.14

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
  Copyright (C) 2000-2005 SKYRIX Software AG
 
3
 
 
4
  This file is part of SOPE.
 
5
 
 
6
  SOPE is free software; you can redistribute it and/or modify it under
 
7
  the terms of the GNU Lesser General Public License as published by the
 
8
  Free Software Foundation; either version 2, or (at your option) any
 
9
  later version.
 
10
 
 
11
  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
 
12
  WARRANTY; without even the implied warranty of MERCHANTABILITY or
 
13
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 
14
  License for more details.
 
15
 
 
16
  You should have received a copy of the GNU Lesser General Public
 
17
  License along with SOPE; see the file COPYING.  If not, write to the
 
18
  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 
19
  02111-1307, USA.
 
20
*/
 
21
 
 
22
#include <NGObjWeb/WOResponse.h>
 
23
#include <NGObjWeb/WORequest.h>
 
24
#include <NGObjWeb/WOCookie.h>
 
25
#include <NGExtensions/NSData+gzip.h>
 
26
#import <EOControl/EOControl.h>
 
27
#include "common.h"
 
28
 
 
29
@implementation WOResponse
 
30
 
 
31
static Class         NSStringClass = Nil;
 
32
static unsigned char OWDefaultZipLevel = 3;
 
33
static unsigned int  OWMinimumZipSize  = 1024;
 
34
static BOOL          dontZip  = NO;
 
35
static BOOL          debugZip = NO;
 
36
 
 
37
+ (int)version {
 
38
  return [super version] + 1 /* v6 */;
 
39
}
 
40
+ (void)initialize {
 
41
  NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
 
42
  static BOOL didInit = NO;
 
43
  if (didInit) return;
 
44
  didInit = YES;
 
45
  NSAssert2([super version] == 5,
 
46
            @"invalid superclass (%@) version %i !",
 
47
            NSStringFromClass([self superclass]), [super version]);
 
48
  
 
49
  dontZip  = [ud boolForKey:@"WODontZipResponse"];
 
50
  debugZip = [ud boolForKey:@"WODebugZipResponse"];
 
51
}
 
52
 
 
53
+ (WOResponse *)responseWithRequest:(WORequest *)_request {
 
54
  return [[(WOResponse *)[self alloc] initWithRequest:_request] autorelease];
 
55
}
 
56
 
 
57
- (id)init {
 
58
  if ((self = [super init])) {
 
59
    [self setStatus:200];
 
60
    [self setHTTPVersion:@"HTTP/1.1"];
 
61
    //[self setHeader:@"text/html" forKey:@"content-type"];
 
62
  }
 
63
  return self;
 
64
}
 
65
 
 
66
- (id)initWithRequest:(WORequest *)_request {
 
67
  if ((self = [self init])) {
 
68
    // don't fake being the request protocol, but rather stay to the truth ;-)
 
69
    // [self setHTTPVersion:[_request httpVersion]];
 
70
  }
 
71
  return self;
 
72
}
 
73
 
 
74
/* HTTP */
 
75
 
 
76
- (void)setStatus:(unsigned int)_status {
 
77
  self->status = _status;
 
78
}
 
79
- (unsigned int)status {
 
80
  return self->status;
 
81
}
 
82
 
 
83
/* client caching */
 
84
 
 
85
static __inline__ char *weekdayName(int dow) {
 
86
  switch (dow) {
 
87
    case 0: return "Sun"; case 1: return "Mon"; case 2: return "Tue";
 
88
    case 3: return "Wed"; case 4: return "Thu"; case 5: return "Fri";
 
89
    case 6: return "Sat"; case 7: return "Sun";
 
90
    default: return "UNKNOWN DAY OF WEEK !";
 
91
  }
 
92
}
 
93
static __inline__ char *monthName(int m) {
 
94
  switch (m) {
 
95
    case  1:  return "Jan"; case  2:  return "Feb"; case  3:  return "Mar";
 
96
    case  4:  return "Apr"; case  5:  return "May"; case  6:  return "Jun";
 
97
    case  7:  return "Jul"; case  8:  return "Aug"; case  9:  return "Sep";
 
98
    case 10:  return "Oct"; case 11:  return "Nov"; case 12:  return "Dec";
 
99
    default: return "UNKNOWN MONTH !";
 
100
  }
 
101
}
 
102
 
 
103
- (void)disableClientCaching {
 
104
  /*
 
105
    OSX Prof: 7.1% of WOSession -appendToResponse !
 
106
  */
 
107
  /* HTTP/1.1 caching directive, prevents browser from caching dynamic pages */
 
108
  static NSTimeZone *gmt = nil;
 
109
  
 
110
  if (gmt == nil) gmt = [[NSTimeZone timeZoneWithAbbreviation:@"GMT"] retain];
 
111
#if DEBUG && 0
 
112
  [self logWithFormat:@"disabled client caching: %@ ..", self];
 
113
#endif
 
114
  
 
115
  /*
 
116
    Set expire time to one hour before now to catch inconsitencies between
 
117
    client and server time. Not using -description of NSCalendarDate to
 
118
    avoid locales and to improve performance.
 
119
  */
 
120
  {
 
121
    NSCalendarDate *now;
 
122
    NSString *s;
 
123
    char buf[32];
 
124
    
 
125
    now = [[NSCalendarDate alloc] initWithTimeIntervalSinceNow:-3600.0];
 
126
    [now setTimeZone:gmt];
 
127
    
 
128
    sprintf(buf,
 
129
#if GS_64BIT_OLD
 
130
            "%s, %02i %s %04i %02i:%02i:%02i GMT",
 
131
#else
 
132
            "%s, %02li %s %04li %02li:%02li:%02li GMT",
 
133
#endif
 
134
            weekdayName([now dayOfWeek]),
 
135
            [now dayOfMonth], 
 
136
            monthName([now monthOfYear]),
 
137
            [now yearOfCommonEra],
 
138
            [now hourOfDay], [now minuteOfHour], [now secondOfMinute]);
 
139
    [now release];
 
140
    
 
141
    s = [[NSString alloc] initWithCString:buf];
 
142
    [self setHeader:s forKey:@"expires"];
 
143
    [s release];
 
144
  }
 
145
  [self setHeader:@"no-cache" forKey:@"cache-control"];
 
146
  [self setHeader:@"no-cache" forKey:@"pragma"];
 
147
}
 
148
 
 
149
/* WO methods */
 
150
 
 
151
- (NSString *)contentString {
 
152
  NSString *s;
 
153
  
 
154
  if (NSStringClass == Nil)
 
155
    NSStringClass = [NSString class];
 
156
  
 
157
  s = [[NSStringClass alloc] initWithData:[self content]
 
158
                             encoding:[self contentEncoding]];
 
159
  return [s autorelease];
 
160
}
 
161
 
 
162
/* WOActionResults */
 
163
 
 
164
- (WOResponse *)generateResponse {
 
165
  return self;
 
166
}
 
167
 
 
168
/* zipping */
 
169
 
 
170
- (BOOL)shouldZipResponseToRequest:(WORequest *)_rq {
 
171
  NSString *contentType;
 
172
  NSString *acceptEncoding;
 
173
  id       body;
 
174
  
 
175
  if (dontZip) {
 
176
    if (debugZip) [self logWithFormat:@"Zipping of response disabled"];
 
177
    return NO;
 
178
  }
 
179
  
 
180
  if ((body = [self content]) == nil)  
 
181
    return NO;
 
182
  if (![body isKindOfClass:[NSData class]])
 
183
    return NO;
 
184
  if ([body length] < OWMinimumZipSize) {
 
185
    if (debugZip) {
 
186
      [self logWithFormat:
 
187
              @"content length is below minimum size for zipping (%i vs %i)",
 
188
              [body length], OWMinimumZipSize];
 
189
    }
 
190
    return NO;
 
191
  }
 
192
  
 
193
  contentType = [self headerForKey:@"content-type"];
 
194
  
 
195
  if ([self headerForKey:@"content-encoding"] != nil) {
 
196
    /* already applied some content-encoding */
 
197
    if (debugZip)
 
198
      [self logWithFormat:@"Do not zip, already has a 'content-encoding'!"];
 
199
    return NO;
 
200
  }
 
201
  if ([contentType hasPrefix:@"application"]) {
 
202
    /* browser often seem to have problems with zipped bodies */
 
203
    if (debugZip)
 
204
      [self logWithFormat:@"Do not zip, is 'application/' MIME type."];
 
205
    return NO;
 
206
  }
 
207
  if ([contentType hasPrefix:@"image"]) {
 
208
    /* do not zip images (usually already compressed ...) */
 
209
    if (debugZip)
 
210
      [self logWithFormat:@"Do not zip, is an image."];
 
211
    return NO;
 
212
  }
 
213
  
 
214
  if (_rq == nil)
 
215
    return YES;
 
216
  
 
217
  acceptEncoding = [[_rq headerForKey:@"accept-encoding"] stringValue];
 
218
  if (acceptEncoding == nil) {
 
219
    if (debugZip) {
 
220
      [self logWithFormat:
 
221
              @"Do not zip, browser sent no 'accept-encoding' header."];
 
222
    }
 
223
    return NO;
 
224
  }
 
225
  // TODO: improve naive parsing of accept header
 
226
  if ([acceptEncoding rangeOfString:@"gzip"].length == 0) {
 
227
    if (debugZip) {
 
228
      [self logWithFormat:
 
229
              @"Do not zip, browser does not understand 'gzip' encoding: %@",
 
230
              acceptEncoding];
 
231
    }
 
232
    return NO;
 
233
  }
 
234
  return YES;
 
235
}
 
236
 
 
237
- (NSData *)zipResponse {
 
238
  NSMutableDictionary *ui;
 
239
  NSNumber *zlen;
 
240
  NSData *zipped = nil;
 
241
  int    len;
 
242
  id     body;
 
243
  
 
244
  if ((body = [self content]) == nil) return nil;
 
245
  
 
246
  len = [body length];
 
247
 
 
248
  /* zip body data */
 
249
  
 
250
  if ((zipped = [body gzipWithLevel:OWDefaultZipLevel]) == nil) {
 
251
    if (debugZip)
 
252
      [self logWithFormat:@"gzip refused to zip body ..."];
 
253
    return body;
 
254
  }
 
255
  
 
256
  /* check if it's smaller */
 
257
  if ((int)[zipped length] >= len) {
 
258
    if (debugZip) {
 
259
      [self logWithFormat:
 
260
              @"zipped length is larger than raw length (%i vs %i)",
 
261
              [zipped length], len];
 
262
    }
 
263
    return body; /* it's not */
 
264
  }
 
265
  
 
266
  /* it is smaller .. */
 
267
      
 
268
  if (debugZip) {
 
269
    [self logWithFormat:
 
270
            @"zipped content %i => %i bytes (gain: %-.2g%%).",
 
271
            len, [zipped length],
 
272
            (double)(100.0 - (((double)[zipped length]) /
 
273
                              (((double)len) / 100.0)))];
 
274
  }
 
275
  
 
276
  body = zipped;
 
277
  [self setHeader:@"gzip" forKey:@"content-encoding"];
 
278
 
 
279
  /* statistics */
 
280
  
 
281
  if ((ui = [[self userInfo] mutableCopy]) == nil)
 
282
    ui = [[NSMutableDictionary alloc] initWithCapacity:2];
 
283
  
 
284
  [ui setObject:zipped forKey:@"WOZippedContent"];
 
285
  zlen = [NSNumber numberWithUnsignedInt:len];
 
286
  [ui setObject:zlen   forKey:@"WOResponseUnzippedLength"];
 
287
  zlen = [NSNumber numberWithUnsignedInt:[zipped length]];
 
288
  [ui setObject:zlen   forKey:@"WOResponseZippedLength"];
 
289
  
 
290
  [self setUserInfo:ui];
 
291
  [ui release]; ui = nil;
 
292
  [self setContent:zipped];
 
293
  
 
294
  return zipped;
 
295
}
 
296
 
 
297
/* description */
 
298
 
 
299
- (NSString *)description {
 
300
  NSMutableString *ms;
 
301
  NSData *data;
 
302
  
 
303
  ms = [NSMutableString stringWithCapacity:64];
 
304
  [ms appendFormat:@"<0x%p[%@]:", self,
 
305
        NSStringFromClass((Class)*(void**)self)];
 
306
  
 
307
  [ms appendFormat:@" status=%i",  [self status]];
 
308
  [ms appendFormat:@" headers=%@", [self headers]];
 
309
 
 
310
  if ((data = [self content])) {
 
311
    if ([data length] == 0)
 
312
      [ms appendString:@" empty-content"];
 
313
    else
 
314
      [ms appendFormat:@" content-size=%i", [data length]];
 
315
  }
 
316
  else
 
317
    [ms appendString:@" no-content"];
 
318
  
 
319
  [ms appendString:@">"];
 
320
  return ms;
 
321
}
 
322
 
 
323
@end /* WOResponse */