2
Copyright (C) 2000-2005 SKYRIX Software AG
4
This file is part of SOPE.
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
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.
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
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>
29
@implementation WOResponse
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;
38
return [super version] + 1 /* v6 */;
41
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
42
static BOOL didInit = NO;
45
NSAssert2([super version] == 5,
46
@"invalid superclass (%@) version %i !",
47
NSStringFromClass([self superclass]), [super version]);
49
dontZip = [ud boolForKey:@"WODontZipResponse"];
50
debugZip = [ud boolForKey:@"WODebugZipResponse"];
53
+ (WOResponse *)responseWithRequest:(WORequest *)_request {
54
return [[(WOResponse *)[self alloc] initWithRequest:_request] autorelease];
58
if ((self = [super init])) {
60
[self setHTTPVersion:@"HTTP/1.1"];
61
//[self setHeader:@"text/html" forKey:@"content-type"];
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]];
76
- (void)setStatus:(unsigned int)_status {
77
self->status = _status;
79
- (unsigned int)status {
85
static __inline__ char *weekdayName(int 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 !";
93
static __inline__ char *monthName(int 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 !";
103
- (void)disableClientCaching {
105
OSX Prof: 7.1% of WOSession -appendToResponse !
107
/* HTTP/1.1 caching directive, prevents browser from caching dynamic pages */
108
static NSTimeZone *gmt = nil;
110
if (gmt == nil) gmt = [[NSTimeZone timeZoneWithAbbreviation:@"GMT"] retain];
112
[self logWithFormat:@"disabled client caching: %@ ..", self];
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.
125
now = [[NSCalendarDate alloc] initWithTimeIntervalSinceNow:-3600.0];
126
[now setTimeZone:gmt];
130
"%s, %02i %s %04i %02i:%02i:%02i GMT",
132
"%s, %02li %s %04li %02li:%02li:%02li GMT",
134
weekdayName([now dayOfWeek]),
136
monthName([now monthOfYear]),
137
[now yearOfCommonEra],
138
[now hourOfDay], [now minuteOfHour], [now secondOfMinute]);
141
s = [[NSString alloc] initWithCString:buf];
142
[self setHeader:s forKey:@"expires"];
145
[self setHeader:@"no-cache" forKey:@"cache-control"];
146
[self setHeader:@"no-cache" forKey:@"pragma"];
151
- (NSString *)contentString {
154
if (NSStringClass == Nil)
155
NSStringClass = [NSString class];
157
s = [[NSStringClass alloc] initWithData:[self content]
158
encoding:[self contentEncoding]];
159
return [s autorelease];
162
/* WOActionResults */
164
- (WOResponse *)generateResponse {
170
- (BOOL)shouldZipResponseToRequest:(WORequest *)_rq {
171
NSString *contentType;
172
NSString *acceptEncoding;
176
if (debugZip) [self logWithFormat:@"Zipping of response disabled"];
180
if ((body = [self content]) == nil)
182
if (![body isKindOfClass:[NSData class]])
184
if ([body length] < OWMinimumZipSize) {
187
@"content length is below minimum size for zipping (%i vs %i)",
188
[body length], OWMinimumZipSize];
193
contentType = [self headerForKey:@"content-type"];
195
if ([self headerForKey:@"content-encoding"] != nil) {
196
/* already applied some content-encoding */
198
[self logWithFormat:@"Do not zip, already has a 'content-encoding'!"];
201
if ([contentType hasPrefix:@"application"]) {
202
/* browser often seem to have problems with zipped bodies */
204
[self logWithFormat:@"Do not zip, is 'application/' MIME type."];
207
if ([contentType hasPrefix:@"image"]) {
208
/* do not zip images (usually already compressed ...) */
210
[self logWithFormat:@"Do not zip, is an image."];
217
acceptEncoding = [[_rq headerForKey:@"accept-encoding"] stringValue];
218
if (acceptEncoding == nil) {
221
@"Do not zip, browser sent no 'accept-encoding' header."];
225
// TODO: improve naive parsing of accept header
226
if ([acceptEncoding rangeOfString:@"gzip"].length == 0) {
229
@"Do not zip, browser does not understand 'gzip' encoding: %@",
237
- (NSData *)zipResponse {
238
NSMutableDictionary *ui;
240
NSData *zipped = nil;
244
if ((body = [self content]) == nil) return nil;
250
if ((zipped = [body gzipWithLevel:OWDefaultZipLevel]) == nil) {
252
[self logWithFormat:@"gzip refused to zip body ..."];
256
/* check if it's smaller */
257
if ((int)[zipped length] >= len) {
260
@"zipped length is larger than raw length (%i vs %i)",
261
[zipped length], len];
263
return body; /* it's not */
266
/* it is smaller .. */
270
@"zipped content %i => %i bytes (gain: %-.2g%%).",
271
len, [zipped length],
272
(double)(100.0 - (((double)[zipped length]) /
273
(((double)len) / 100.0)))];
277
[self setHeader:@"gzip" forKey:@"content-encoding"];
281
if ((ui = [[self userInfo] mutableCopy]) == nil)
282
ui = [[NSMutableDictionary alloc] initWithCapacity:2];
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"];
290
[self setUserInfo:ui];
291
[ui release]; ui = nil;
292
[self setContent:zipped];
299
- (NSString *)description {
303
ms = [NSMutableString stringWithCapacity:64];
304
[ms appendFormat:@"<0x%p[%@]:", self,
305
NSStringFromClass((Class)*(void**)self)];
307
[ms appendFormat:@" status=%i", [self status]];
308
[ms appendFormat:@" headers=%@", [self headers]];
310
if ((data = [self content])) {
311
if ([data length] == 0)
312
[ms appendString:@" empty-content"];
314
[ms appendFormat:@" content-size=%i", [data length]];
317
[ms appendString:@" no-content"];
319
[ms appendString:@">"];
323
@end /* WOResponse */