mirror of
https://github.com/gnustep/libs-sqlclient.git
synced 2025-02-14 15:40:59 +00:00
Simplify library ... move portions into WebServer and Performance libraries
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/sqlclient/trunk@22006 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
bbab465478
commit
c9ed820419
12 changed files with 36 additions and 3660 deletions
|
@ -1,3 +1,9 @@
|
|||
2005-11-14 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
Factor out WebServer into separate library, and timer and caching
|
||||
stuff into Performance library. Make this library depend on the
|
||||
Performance library.
|
||||
|
||||
2005-10-27 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* WebServer.m: Add more accurate timestamps and implement request
|
||||
|
|
30
GNUmakefile
30
GNUmakefile
|
@ -3,7 +3,7 @@ include $(GNUSTEP_MAKEFILES)/common.make
|
|||
-include config.make
|
||||
|
||||
PACKAGE_NAME = SQLClient
|
||||
PACKAGE_VERSION = 1.1.0
|
||||
PACKAGE_VERSION = 1.2.0
|
||||
CVS_MODULE_NAME = gnustep/dev-libs/SQLClient
|
||||
CVS_TAG_NAME = SQLClient
|
||||
|
||||
|
@ -12,35 +12,16 @@ TEST_TOOL_NAME=
|
|||
LIBRARY_NAME=SQLClient
|
||||
DOCUMENT_NAME=SQLClient
|
||||
|
||||
SQLClient_INTERFACE_VERSION=1.1
|
||||
SQLClient_INTERFACE_VERSION=1.2
|
||||
|
||||
SQLClient_OBJC_FILES = SQLClient.m
|
||||
SQLClient_LIBRARIES_DEPEND_UPON =
|
||||
SQLClient_LIBRARIES_DEPEND_UPON = -lPerformance
|
||||
SQLClient_HEADER_FILES = SQLClient.h
|
||||
SQLClient_AGSDOC_FILES = SQLClient.h
|
||||
|
||||
# Optional Java wrappers for the library
|
||||
JAVA_WRAPPER_NAME = SQLClient
|
||||
|
||||
#
|
||||
# Assume that the use of the gnu runtime means we have the gnustep
|
||||
# base library and can use its extensions to build WebServer stuff.
|
||||
#
|
||||
ifeq ($(OBJC_RUNTIME_LIB),gnu)
|
||||
APPLE=0
|
||||
else
|
||||
APPLE=1
|
||||
endif
|
||||
|
||||
ifeq ($(APPLE),1)
|
||||
ADDITIONAL_OBJC_LIBS += -lgnustep-baseadd
|
||||
SQLClient_LIBRARIES_DEPEND_UPON = -lgnustep-baseadd
|
||||
else
|
||||
SQLClient_OBJC_FILES += WebServer.m WebServerBundles.m
|
||||
SQLClient_HEADER_FILES += WebServer.h
|
||||
SQLClient_AGSDOC_FILES += WebServer.h
|
||||
endif
|
||||
|
||||
SQLClient_HEADER_FILES_INSTALL_DIR = SQLClient
|
||||
|
||||
BUNDLE_NAME=
|
||||
|
@ -148,11 +129,6 @@ Oracle_libs_BUNDLE_LIBS += -lclntsh \
|
|||
Oracle_libs_PRINCIPAL_CLASS = SQLClientOracle_libs
|
||||
endif
|
||||
|
||||
TEST_TOOL_NAME+=testWebServer
|
||||
testWebServer_OBJC_FILES = testWebServer.m
|
||||
testWebServer_TOOL_LIBS += -lSQLClient
|
||||
testWebServer_LIB_DIRS += -L./obj
|
||||
|
||||
-include GNUmakefile.preamble
|
||||
|
||||
include $(GNUSTEP_MAKEFILES)/library.make
|
||||
|
|
3
Performance.import
Normal file
3
Performance.import
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
import gnu.gnustep.Performance.*;
|
||||
|
32
README
32
README
|
@ -80,35 +80,3 @@ should be entered on the GNUstep project page
|
|||
<http://savannah.gnu.org/support/?group-gnustep>
|
||||
|
||||
|
||||
|
||||
The WebServer class provides the framework for a GNUstep program to act as
|
||||
an HTTP or HTTPS server for simple applications.
|
||||
It does not attempt to be a general-purpose web server, but is rather
|
||||
intended to permit a program to easily handle requests from automated
|
||||
systems which are intended to control, monitor, or use the services
|
||||
provided by the program in which the class is embedded.
|
||||
The emphasis is on making it robust/reliable/simple, so you can rapidly
|
||||
develop software using it. It is a single-threaded, single-process
|
||||
system using asynchronous I/O, so you can easily run it under debug
|
||||
in gdb to fix any bugs in your delegate object.
|
||||
In particular of course, it may be used in conjunction with the
|
||||
SQLClient class to implement web-based database applications.
|
||||
|
||||
The class is controlled by a few straightforward settings and basically
|
||||
operates by handing over requests to its delegate. The delegate must at
|
||||
least implement the -processRequest:response:for: method.
|
||||
|
||||
Built-in facilities include -
|
||||
|
||||
* Parsing of parameter string in request URL
|
||||
* Parsing of url encoded form data in a POST request
|
||||
* Parsing of form encoded data in a POST request
|
||||
* Substitution into template pages on output
|
||||
* SSL support
|
||||
* Limit access by IP address
|
||||
* Limit total number of simultaneous connections
|
||||
* Limit number of simultaneous connectionsform one address
|
||||
* Limit idle time permitted on a connection
|
||||
* Limit size of request headers permitted
|
||||
* Limit size of request body permitted
|
||||
|
||||
|
|
144
SQLClient.h
144
SQLClient.h
|
@ -38,12 +38,6 @@
|
|||
faster, easier to use, and easier to add new database backends for
|
||||
than JDBC).
|
||||
</p>
|
||||
<p>
|
||||
The library also provides a framework for making such simple database
|
||||
applications readily available as web applications using the
|
||||
[WebServer] class, which allows your applications to act as stand-alone
|
||||
web servers.
|
||||
</p>
|
||||
<p>
|
||||
The major features of the SQLClient library are -
|
||||
</p>
|
||||
|
@ -177,12 +171,12 @@
|
|||
|
||||
#include <GNUstepBase/GNUstep.h>
|
||||
|
||||
@class GSCache;
|
||||
@class NSData;
|
||||
@class NSDate;
|
||||
@class NSMutableSet;
|
||||
@class NSRecursiveLock;
|
||||
@class NSString;
|
||||
@class SQLCache;
|
||||
@class SQLTransaction;
|
||||
|
||||
/**
|
||||
|
@ -322,7 +316,7 @@ extern unsigned SQLClientTimeTick();
|
|||
NSTimeInterval _lastOperation;
|
||||
NSTimeInterval _duration;
|
||||
unsigned int _debugging; /** The current debugging level */
|
||||
SQLCache *_cache; /** The cache for query results */
|
||||
GSCache *_cache; /** The cache for query results */
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -960,136 +954,6 @@ extern unsigned SQLClientTimeTick();
|
|||
- (void) setDurationLogging: (NSTimeInterval)threshold;
|
||||
@end
|
||||
|
||||
/**
|
||||
* The SQLCache class is used to maintain a cache of objects.<br />
|
||||
* When full, old objects are removed to make room for new ones
|
||||
* on a least-recently-used basis.<br />
|
||||
* Cache sizes may be limited by the number of objects in the cache,
|
||||
* or by the memory used by the cache, or both. Calculation of the
|
||||
* size of items in the cache is relatively expensive, so caches are
|
||||
* only limited by number of objects in the default case.<br />
|
||||
* Objects stored in the cache may be given a limited lifetime,
|
||||
* in which case an attempt to fetch an <em>expired</em> object
|
||||
* from the cache will cause it to be removed from the cache instead.
|
||||
*/
|
||||
@interface SQLCache : NSObject
|
||||
|
||||
/**
|
||||
* Return all the current cache instances... useful if you want to do
|
||||
* something to all cache instances in your process.
|
||||
*/
|
||||
+ (NSArray*) allCaches;
|
||||
|
||||
/**
|
||||
* Return a report on all SQLCache instances ... calls the -description
|
||||
* method of the individual cache instances to get a report on each one.
|
||||
*/
|
||||
+ (NSString*) description;
|
||||
|
||||
/**
|
||||
* Return the count of objects currently in the cache.
|
||||
*/
|
||||
- (unsigned) currentObjects;
|
||||
|
||||
/**
|
||||
* Return the total size of the objects currently in the cache.
|
||||
*/
|
||||
- (unsigned) currentSize;
|
||||
|
||||
/**
|
||||
* Return the default lifetime for items set in the cache.<br />
|
||||
* A value of zero means that items are not purged based on lifetime.
|
||||
*/
|
||||
- (unsigned) lifetime;
|
||||
|
||||
/**
|
||||
* Return the maximum number of items in the cache.<br />
|
||||
* A value of zero means there is no limit.
|
||||
*/
|
||||
- (unsigned) maxObjects;
|
||||
|
||||
/**
|
||||
* Return the maximum tital size of items in the cache.<br />
|
||||
* A value of zero means there is no limit.
|
||||
*/
|
||||
- (unsigned) maxSize;
|
||||
|
||||
/**
|
||||
* Return the name of this instance (as set using -setName:)
|
||||
*/
|
||||
- (NSString*) name;
|
||||
|
||||
/**
|
||||
* Return the cached value for the specified key, or nil if there
|
||||
* is no value in the cache.
|
||||
*/
|
||||
- (id) objectForKey: (NSString*)aKey;
|
||||
|
||||
/**
|
||||
* Remove all items whose lifetimes have passed
|
||||
* (if lifetimes are in use for the cache).<br />
|
||||
* The 'current' timestamp is that of the last call to SQLClientTimeNow(),
|
||||
* which is usually the timestmap of the most recent SQL query.
|
||||
*/
|
||||
- (void) purge;
|
||||
|
||||
/**
|
||||
* Sets the lifetime (seconds) for items added to the cache. If this
|
||||
* is set to zero then items are not removed from the cache based on
|
||||
* lifetimes when the cache is full and an object is added, though
|
||||
* <em>expired</em> items are still removed when an attempt to retrieve
|
||||
* them is made.
|
||||
*/
|
||||
- (void) setLifetime: (unsigned)max;
|
||||
|
||||
/**
|
||||
* Sets the maximum number of objects in the cache. If this is non-zero
|
||||
* then an attempt to set an object in a full cache will result in the
|
||||
* least recently used item in the cache being removed.
|
||||
*/
|
||||
- (void) setMaxObjects: (unsigned)max;
|
||||
|
||||
/**
|
||||
* Sets the maximum total size for objects in the cache. If this is non-zero
|
||||
* then an attempt to set an object whose size would exceed the cache limit
|
||||
* will result in the least recently used items in the cache being removed.
|
||||
*/
|
||||
- (void) setMaxSize: (unsigned)max;
|
||||
|
||||
/**
|
||||
* Sets the name of this instance.
|
||||
*/
|
||||
- (void) setName: (NSString*)name;
|
||||
|
||||
/**
|
||||
* Sets (or replaces)the cached value for the specified key.
|
||||
*/
|
||||
- (void) setObject: (id)anObject forKey: (NSString*)aKey;
|
||||
|
||||
/**
|
||||
* Sets (or replaces)the cached value for the specified key, giving
|
||||
* the value the specified lifetime (in seconds). A lifetime of zero
|
||||
* means that the item is not limited by lifetime.
|
||||
*/
|
||||
- (void) setObject: (id)anObject
|
||||
forKey: (NSString*)aKey
|
||||
lifetime: (unsigned)lifetime;
|
||||
|
||||
/**
|
||||
* Called by -setObject:forKey:lifetime: to make space for a new
|
||||
* object in the cache (also when the cache is resized).<br />
|
||||
* This will, if a lifetime is set (see the -setLifetime: method)
|
||||
* first purge all <em>expired</em> objects from the cache, then
|
||||
* (if necessary) remove objects from the cache until the number
|
||||
* of objects and size of cache meet the limits specified.<br />
|
||||
* If the objects argument is zero then all objects are removed from
|
||||
* the cache.<br />
|
||||
* The size argument is used <em>only</em> if a maximum size is set
|
||||
* for the cache.
|
||||
*/
|
||||
- (void) shrinkObjects: (unsigned)objects andSize: (unsigned)size;
|
||||
@end
|
||||
|
||||
/**
|
||||
* This category porovides methods for caching the results of queries
|
||||
* in order to reduce the number of client-server trips and the database
|
||||
|
@ -1102,7 +966,7 @@ extern unsigned SQLClientTimeTick();
|
|||
* Returns the cache used by the receiver for storing the results of
|
||||
* requests made through it.
|
||||
*/
|
||||
- (SQLCache*) cache;
|
||||
- (GSCache*) cache;
|
||||
|
||||
/**
|
||||
* If the result of the query is already cached and is still valid,
|
||||
|
@ -1148,7 +1012,7 @@ extern unsigned SQLClientTimeTick();
|
|||
* If aCache is nil, the current cache is released, and a new cache will
|
||||
* be automatically created as soon as there is a need to cache anything.
|
||||
*/
|
||||
- (void) setCache: (SQLCache*)aCache;
|
||||
- (void) setCache: (GSCache*)aCache;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -1,36 +1,14 @@
|
|||
{ /* -*-c-*- */
|
||||
"prerequisite libraries" = (
|
||||
"gnustep-base"
|
||||
"gnustep-base",
|
||||
"Performance"
|
||||
);
|
||||
types = {
|
||||
NSTimeInterval = double;
|
||||
};
|
||||
classes = (
|
||||
{
|
||||
"java name" = "gnu.gnustep.SQLClient.SQLCache";
|
||||
"objective-c name" = "SQLCache";
|
||||
"class methods" = (
|
||||
"allCaches"
|
||||
);
|
||||
"instance methods" = (
|
||||
"currentObjects",
|
||||
"currentSize",
|
||||
"lifetime",
|
||||
"maxObjects",
|
||||
"maxSize",
|
||||
"name",
|
||||
"objectForKey:",
|
||||
"purge",
|
||||
"setLifetime:",
|
||||
"setMaxObjects:",
|
||||
"setMaxSize:",
|
||||
"setName:",
|
||||
"setObject:forKey:",
|
||||
"setObject:forKey:lifetime:",
|
||||
"shrinkObjects:andSize:"
|
||||
);
|
||||
},
|
||||
{
|
||||
"file to include in preamble java code" = "Performance.import";
|
||||
"java name" = "gnu.gnustep.SQLClient.SQLClient";
|
||||
"objective-c name" = "SQLClient";
|
||||
"class methods" = (
|
||||
|
|
594
SQLClient.m
594
SQLClient.m
|
@ -48,6 +48,7 @@
|
|||
#include <Foundation/NSTimer.h>
|
||||
|
||||
#include <GNUstepBase/GSLock.h>
|
||||
#include <Performance/Performance.h>
|
||||
|
||||
#include "SQLClient.h"
|
||||
|
||||
|
@ -59,126 +60,6 @@ static NSNull *null = nil;
|
|||
|
||||
static Class NSStringClass = 0;
|
||||
static Class NSDateClass = 0;
|
||||
static SEL tiSel = 0;
|
||||
static NSTimeInterval (*tiImp)(Class,SEL) = 0;
|
||||
|
||||
static NSTimeInterval baseTime = 0;
|
||||
static NSTimeInterval lastTime = 0;
|
||||
|
||||
inline NSTimeInterval SQLClientTimeLast()
|
||||
{
|
||||
return lastTime;
|
||||
}
|
||||
|
||||
inline NSTimeInterval SQLClientTimeNow()
|
||||
{
|
||||
if (baseTime == 0)
|
||||
{
|
||||
NSDateClass = [NSDate class];
|
||||
tiSel = @selector(timeIntervalSinceReferenceDate);
|
||||
tiImp
|
||||
= (NSTimeInterval (*)(Class,SEL))[NSDateClass methodForSelector: tiSel];
|
||||
baseTime = lastTime = (*tiImp)(NSDateClass, tiSel);
|
||||
return baseTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSTimeInterval now = (*tiImp)(NSDateClass, tiSel);
|
||||
|
||||
/*
|
||||
* If the clock has been reset so that time has gone backwards,
|
||||
* we adjust the baseTime so that lastTime >= baseTime is true.
|
||||
*/
|
||||
if (now < lastTime)
|
||||
{
|
||||
baseTime -= (lastTime - now);
|
||||
}
|
||||
lastTime = now;
|
||||
return lastTime;
|
||||
}
|
||||
}
|
||||
|
||||
inline NSTimeInterval SQLClientTimeStart()
|
||||
{
|
||||
if (baseTime == 0)
|
||||
{
|
||||
return SQLClientTimeNow();
|
||||
}
|
||||
return baseTime;
|
||||
}
|
||||
|
||||
inline unsigned SQLClientTimeTick()
|
||||
{
|
||||
NSTimeInterval start = SQLClientTimeStart();
|
||||
|
||||
return (SQLClientTimeLast() - start) + 1;
|
||||
}
|
||||
|
||||
@interface NSArray (SizeInBytes)
|
||||
- (unsigned) sizeInBytes: (NSMutableSet*)exclude;
|
||||
@end
|
||||
|
||||
@interface NSData (SizeInBytes)
|
||||
- (unsigned) sizeInBytes: (NSMutableSet*)exclude;
|
||||
@end
|
||||
|
||||
@interface NSObject (SizeInBytes)
|
||||
- (unsigned) sizeInBytes: (NSMutableSet*)exclude;
|
||||
@end
|
||||
|
||||
@interface NSString (SizeInBytes)
|
||||
- (unsigned) sizeInBytes: (NSMutableSet*)exclude;
|
||||
@end
|
||||
|
||||
@implementation NSArray (SizeInBytes)
|
||||
- (unsigned) sizeInBytes: (NSMutableSet*)exclude
|
||||
{
|
||||
if ([exclude member: self] != nil)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned count = [self count];
|
||||
unsigned size = [super sizeInBytes: exclude] + count*sizeof(void*);
|
||||
|
||||
if (exclude == nil)
|
||||
{
|
||||
exclude = [NSMutableSet setWithCapacity: 8];
|
||||
}
|
||||
[exclude addObject: self];
|
||||
while (count-- > 0)
|
||||
{
|
||||
size += [[self objectAtIndex: count] sizeInBytes: exclude];
|
||||
}
|
||||
return size;
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation NSData (SizeInBytes)
|
||||
- (unsigned) sizeInBytes: (NSMutableSet*)exclude
|
||||
{
|
||||
if ([exclude member: self] != nil) return 0;
|
||||
return [super sizeInBytes: exclude] + [self length];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation NSObject (SizeInBytes)
|
||||
- (unsigned) sizeInBytes: (NSMutableSet*)exclude
|
||||
{
|
||||
if ([exclude member: self] != nil) return 0;
|
||||
return isa->instance_size;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation NSString (SizeInBytes)
|
||||
- (unsigned) sizeInBytes: (NSMutableSet*)exclude
|
||||
{
|
||||
if ([exclude member: self] != nil) return 0;
|
||||
return [super sizeInBytes: exclude] + sizeof(unichar) * [self length];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SQLRecord
|
||||
+ (id) allocWithZone: (NSZone*)aZone
|
||||
|
@ -189,7 +70,7 @@ inline unsigned SQLClientTimeTick()
|
|||
|
||||
+ (void) initialize
|
||||
{
|
||||
SQLClientTimeNow();
|
||||
GSTickerTimeNow();
|
||||
if (null == nil)
|
||||
{
|
||||
null = [NSNull new];
|
||||
|
@ -542,7 +423,7 @@ static unsigned int maxConnections = 8;
|
|||
|
||||
+ (void) initialize
|
||||
{
|
||||
SQLClientTimeNow();
|
||||
GSTickerTimeNow();
|
||||
if (null == nil)
|
||||
{
|
||||
null = [NSNull new];
|
||||
|
@ -1215,10 +1096,10 @@ static void quoteString(NSMutableString *s)
|
|||
|
||||
if (_duration >= 0)
|
||||
{
|
||||
start = SQLClientTimeNow();
|
||||
start = GSTickerTimeNow();
|
||||
}
|
||||
[self backendExecute: info];
|
||||
_lastOperation = SQLClientTimeNow();
|
||||
_lastOperation = GSTickerTimeNow();
|
||||
[_statements addObject: statement];
|
||||
if (_duration >= 0)
|
||||
{
|
||||
|
@ -1289,10 +1170,10 @@ static void quoteString(NSMutableString *s)
|
|||
|
||||
if (_duration >= 0)
|
||||
{
|
||||
start = SQLClientTimeNow();
|
||||
start = GSTickerTimeNow();
|
||||
}
|
||||
result = [self backendQuery: stmt];
|
||||
_lastOperation = SQLClientTimeNow();
|
||||
_lastOperation = GSTickerTimeNow();
|
||||
if (_duration >= 0)
|
||||
{
|
||||
NSTimeInterval d;
|
||||
|
@ -1787,7 +1668,7 @@ static void quoteString(NSMutableString *s)
|
|||
*/
|
||||
+ (void) _tick: (NSTimer*)t
|
||||
{
|
||||
SQLClientTimeNow();
|
||||
GSTickerTimeNow();
|
||||
}
|
||||
@end
|
||||
|
||||
|
@ -1906,11 +1787,11 @@ static void quoteString(NSMutableString *s)
|
|||
|
||||
@implementation SQLClient (Caching)
|
||||
|
||||
- (SQLCache*) cache
|
||||
- (GSCache*) cache
|
||||
{
|
||||
if (_cache == nil)
|
||||
{
|
||||
_cache = [SQLCache new];
|
||||
_cache = [GSCache new];
|
||||
}
|
||||
return _cache;
|
||||
}
|
||||
|
@ -1942,8 +1823,8 @@ static void quoteString(NSMutableString *s)
|
|||
[lock lock];
|
||||
NS_DURING
|
||||
{
|
||||
NSTimeInterval start = SQLClientTimeNow();
|
||||
SQLCache *c = [self cache];
|
||||
NSTimeInterval start = GSTickerTimeNow();
|
||||
GSCache *c = [self cache];
|
||||
id toCache = nil;
|
||||
|
||||
if (seconds < 0)
|
||||
|
@ -1958,7 +1839,7 @@ static void quoteString(NSMutableString *s)
|
|||
if (result == nil)
|
||||
{
|
||||
result = toCache = [self backendQuery: stmt];
|
||||
_lastOperation = SQLClientTimeNow();
|
||||
_lastOperation = GSTickerTimeNow();
|
||||
if (_duration >= 0)
|
||||
{
|
||||
NSTimeInterval d;
|
||||
|
@ -2002,7 +1883,7 @@ static void quoteString(NSMutableString *s)
|
|||
return result;
|
||||
}
|
||||
|
||||
- (void) setCache: (SQLCache*)aCache
|
||||
- (void) setCache: (GSCache*)aCache
|
||||
{
|
||||
ASSIGN(_cache, aCache);
|
||||
}
|
||||
|
@ -2125,450 +2006,3 @@ static void quoteString(NSMutableString *s)
|
|||
}
|
||||
@end
|
||||
|
||||
|
||||
@interface SQLCItem : NSObject
|
||||
{
|
||||
@public
|
||||
SQLCItem *next;
|
||||
SQLCItem *prev;
|
||||
unsigned when;
|
||||
unsigned size;
|
||||
NSString *key;
|
||||
id object;
|
||||
}
|
||||
+ (SQLCItem*) newWithObject: (id)anObject forKey: (NSString*)aKey;
|
||||
@end
|
||||
|
||||
@implementation SQLCItem
|
||||
+ (SQLCItem*) newWithObject: (id)anObject forKey: (NSString*)aKey
|
||||
{
|
||||
SQLCItem *i;
|
||||
|
||||
i = (SQLCItem*)NSAllocateObject(self, 0, NSDefaultMallocZone());
|
||||
i->object = RETAIN(anObject);
|
||||
i->key = [aKey copy];
|
||||
return i;
|
||||
}
|
||||
- (void) dealloc
|
||||
{
|
||||
RELEASE(key);
|
||||
RELEASE(object);
|
||||
NSDeallocateObject(self);
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@implementation SQLCache
|
||||
|
||||
static NSHashTable *SQLCacheInstances = 0;
|
||||
static NSLock *SQLCacheLock = nil;
|
||||
|
||||
typedef struct {
|
||||
unsigned currentObjects;
|
||||
unsigned currentSize;
|
||||
unsigned lifetime;
|
||||
unsigned maxObjects;
|
||||
unsigned maxSize;
|
||||
unsigned hits;
|
||||
unsigned misses;
|
||||
NSMapTable *contents;
|
||||
SQLCItem *first;
|
||||
NSString *name;
|
||||
NSMutableSet *exclude;
|
||||
} SQLC;
|
||||
#define my ((SQLC*)&self[1])
|
||||
|
||||
/*
|
||||
* Add item to linked list starting at *first
|
||||
*/
|
||||
static void appendItem(SQLCItem *item, SQLCItem **first)
|
||||
{
|
||||
if (*first == nil)
|
||||
{
|
||||
item->next = item->prev = item;
|
||||
*first = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
(*first)->prev->next = item;
|
||||
item->prev = (*first)->prev;
|
||||
(*first)->prev = item;
|
||||
item->next = *first;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove item from linked list starting at *first
|
||||
*/
|
||||
static void removeItem(SQLCItem *item, SQLCItem **first)
|
||||
{
|
||||
if (*first == item)
|
||||
{
|
||||
if (item->next == item)
|
||||
{
|
||||
*first = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
*first = item->next;
|
||||
}
|
||||
}
|
||||
item->next->prev = item->prev;
|
||||
item->prev->next = item->next;
|
||||
item->prev = item->next = item;
|
||||
}
|
||||
|
||||
+ (NSArray*) allCaches
|
||||
{
|
||||
NSArray *a;
|
||||
|
||||
[SQLCacheLock lock];
|
||||
a = NSAllHashTableObjects(SQLCacheInstances);
|
||||
[SQLCacheLock unlock];
|
||||
return a;
|
||||
}
|
||||
|
||||
+ (id) alloc
|
||||
{
|
||||
return [self allocWithZone: NSDefaultMallocZone()];
|
||||
}
|
||||
|
||||
+ (id) allocWithZone: (NSZone*)z
|
||||
{
|
||||
SQLCache *c;
|
||||
|
||||
c = (SQLCache*)NSAllocateObject(self, sizeof(SQLC), z);
|
||||
[SQLCacheLock lock];
|
||||
NSHashInsert(SQLCacheInstances, (void*)c);
|
||||
[SQLCacheLock unlock];
|
||||
return c;
|
||||
}
|
||||
|
||||
+ (NSString*) description
|
||||
{
|
||||
NSMutableString *ms;
|
||||
NSHashEnumerator e;
|
||||
SQLCache *c;
|
||||
|
||||
ms = [NSMutableString stringWithString: [super description]];
|
||||
[SQLCacheLock lock];
|
||||
e = NSEnumerateHashTable(SQLCacheInstances);
|
||||
while ((c = (SQLCache*)NSNextHashEnumeratorItem(&e)) != nil)
|
||||
{
|
||||
[ms appendFormat: @"\n%@", [c description]];
|
||||
}
|
||||
NSEndHashTableEnumeration(&e);
|
||||
[SQLCacheLock unlock];
|
||||
return ms;
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
SQLClientTimeNow();
|
||||
if (null == nil)
|
||||
{
|
||||
null = [NSNull new];
|
||||
}
|
||||
if (SQLCacheInstances == 0)
|
||||
{
|
||||
SQLCacheLock = [NSLock new];
|
||||
SQLCacheInstances
|
||||
= NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0);
|
||||
}
|
||||
}
|
||||
|
||||
- (unsigned) currentObjects
|
||||
{
|
||||
return my->currentObjects;
|
||||
}
|
||||
|
||||
- (unsigned) currentSize
|
||||
{
|
||||
return my->currentSize;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[SQLCacheLock lock];
|
||||
if (my->contents != 0)
|
||||
{
|
||||
[self shrinkObjects: 0 andSize: 0];
|
||||
NSFreeMapTable(my->contents);
|
||||
}
|
||||
RELEASE(my->exclude);
|
||||
RELEASE(my->name);
|
||||
NSHashRemove(SQLCacheInstances, (void*)self);
|
||||
NSDeallocateObject(self);
|
||||
[SQLCacheLock unlock];
|
||||
}
|
||||
|
||||
- (NSString*) description
|
||||
{
|
||||
NSString *n = my->name;
|
||||
|
||||
if (n == nil)
|
||||
{
|
||||
n = [super description];
|
||||
}
|
||||
return [NSString stringWithFormat:
|
||||
@" %@\n"
|
||||
@" Items: %u(%u)\n"
|
||||
@" Size: %u(%u)\n"
|
||||
@" Life: %u\n"
|
||||
@" Hit: %u\n"
|
||||
@" Miss: %u\n",
|
||||
n,
|
||||
my->currentObjects, my->maxObjects,
|
||||
my->currentSize, my->maxSize,
|
||||
my->lifetime,
|
||||
my->hits,
|
||||
my->misses];
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
my->contents = NSCreateMapTable(NSObjectMapKeyCallBacks,
|
||||
NSObjectMapValueCallBacks, 0);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (unsigned) lifetime
|
||||
{
|
||||
return my->lifetime;
|
||||
}
|
||||
|
||||
- (unsigned) maxObjects
|
||||
{
|
||||
return my->maxObjects;
|
||||
}
|
||||
|
||||
- (unsigned) maxSize
|
||||
{
|
||||
return my->maxSize;
|
||||
}
|
||||
|
||||
- (NSString*) name
|
||||
{
|
||||
return my->name;
|
||||
}
|
||||
|
||||
- (id) objectForKey: (NSString*)aKey
|
||||
{
|
||||
SQLCItem *item;
|
||||
unsigned when = SQLClientTimeTick();
|
||||
|
||||
item = (SQLCItem*)NSMapGet(my->contents, aKey);
|
||||
if (item == nil)
|
||||
{
|
||||
my->misses++;
|
||||
return nil;
|
||||
}
|
||||
removeItem(item, &my->first);
|
||||
if (item->when > 0 && item->when < when)
|
||||
{
|
||||
my->currentObjects--;
|
||||
if (my->maxSize > 0)
|
||||
{
|
||||
my->currentSize -= item->size;
|
||||
}
|
||||
NSMapRemove(my->contents, (void*)item->key);
|
||||
my->misses++;
|
||||
return nil; // Lifetime expired.
|
||||
}
|
||||
|
||||
// Least recently used ... move to end of list.
|
||||
appendItem(item, &my->first);
|
||||
my->hits++;
|
||||
return item->object;
|
||||
}
|
||||
|
||||
- (void) purge
|
||||
{
|
||||
unsigned when = SQLClientTimeTick();
|
||||
|
||||
if (my->contents != 0)
|
||||
{
|
||||
NSMapEnumerator e;
|
||||
SQLCItem *i;
|
||||
NSString *k;
|
||||
|
||||
e = NSEnumerateMapTable(my->contents);
|
||||
while (NSNextMapEnumeratorPair(&e, (void**)&k, (void**)&i) != 0)
|
||||
{
|
||||
if (i->when > 0 && i->when < when)
|
||||
{
|
||||
removeItem(i, &my->first);
|
||||
my->currentObjects--;
|
||||
if (my->maxSize > 0)
|
||||
{
|
||||
my->currentSize -= i->size;
|
||||
}
|
||||
NSMapRemove(my->contents, (void*)i->key);
|
||||
}
|
||||
}
|
||||
NSEndMapTableEnumeration(&e);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setLifetime: (unsigned)max
|
||||
{
|
||||
my->lifetime = max;
|
||||
}
|
||||
|
||||
- (void) setMaxObjects: (unsigned)max
|
||||
{
|
||||
my->maxObjects = max;
|
||||
if (my->currentObjects > my->maxObjects)
|
||||
{
|
||||
[self shrinkObjects: my->maxObjects
|
||||
andSize: my->maxSize];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setMaxSize: (unsigned)max
|
||||
{
|
||||
if (max > 0 && my->maxSize == 0)
|
||||
{
|
||||
NSMapEnumerator e = NSEnumerateMapTable(my->contents);
|
||||
SQLCItem *i;
|
||||
NSString *k;
|
||||
unsigned size = 0;
|
||||
|
||||
if (my->exclude == nil)
|
||||
{
|
||||
my->exclude = [NSMutableSet new];
|
||||
}
|
||||
while (NSNextMapEnumeratorPair(&e, (void**)&k, (void**)&i) != 0)
|
||||
{
|
||||
if (i->size == 0)
|
||||
{
|
||||
[my->exclude removeAllObjects];
|
||||
i->size = [i->object sizeInBytes: my->exclude];
|
||||
}
|
||||
if (i->size > max)
|
||||
{
|
||||
/*
|
||||
* Item in cache is too big for new size limit ...
|
||||
* Remove it.
|
||||
*/
|
||||
removeItem(i, &my->first);
|
||||
NSMapRemove(my->contents, (void*)i->key);
|
||||
my->currentObjects--;
|
||||
continue;
|
||||
}
|
||||
size += i->size;
|
||||
}
|
||||
NSEndMapTableEnumeration(&e);
|
||||
my->currentSize = size;
|
||||
}
|
||||
else if (max == 0)
|
||||
{
|
||||
my->currentSize = 0;
|
||||
}
|
||||
my->maxSize = max;
|
||||
if (my->currentSize > my->maxSize)
|
||||
{
|
||||
[self shrinkObjects: my->maxObjects
|
||||
andSize: my->maxSize];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setName: (NSString*)name
|
||||
{
|
||||
ASSIGN(my->name, name);
|
||||
}
|
||||
|
||||
- (void) setObject: (id)anObject forKey: (NSString*)aKey
|
||||
{
|
||||
[self setObject: anObject forKey: aKey lifetime: my->lifetime];
|
||||
}
|
||||
|
||||
- (void) setObject: (id)anObject
|
||||
forKey: (NSString*)aKey
|
||||
lifetime: (unsigned)lifetime
|
||||
{
|
||||
SQLCItem *item;
|
||||
unsigned maxObjects = my->maxObjects;
|
||||
unsigned maxSize = my->maxSize;
|
||||
unsigned addObjects = (anObject == nil ? 0 : 1);
|
||||
unsigned addSize = 0;
|
||||
|
||||
item = (SQLCItem*)NSMapGet(my->contents, aKey);
|
||||
if (item != nil)
|
||||
{
|
||||
removeItem(item, &my->first);
|
||||
my->currentObjects--;
|
||||
if (my->maxSize > 0)
|
||||
{
|
||||
my->currentSize -= item->size;
|
||||
}
|
||||
NSMapRemove(my->contents, (void*)aKey);
|
||||
}
|
||||
|
||||
if (maxSize > 0 || maxObjects > 0)
|
||||
{
|
||||
if (maxSize > 0)
|
||||
{
|
||||
if (my->exclude == nil)
|
||||
{
|
||||
my->exclude = [NSMutableSet new];
|
||||
}
|
||||
[my->exclude removeAllObjects];
|
||||
addSize = [anObject sizeInBytes: my->exclude];
|
||||
if (addSize > maxSize)
|
||||
{
|
||||
return; // Object too big to cache.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (addObjects > 0)
|
||||
{
|
||||
/*
|
||||
* Make room for new object.
|
||||
*/
|
||||
[self shrinkObjects: maxObjects - addObjects
|
||||
andSize: maxSize - addSize];
|
||||
item = [SQLCItem newWithObject: anObject forKey: aKey];
|
||||
if (lifetime > 0)
|
||||
{
|
||||
item->when = SQLClientTimeTick() + lifetime;
|
||||
}
|
||||
item->size = addSize;
|
||||
NSMapInsert(my->contents, (void*)item->key, (void*)item);
|
||||
appendItem(item, &my->first);
|
||||
my->currentObjects += addObjects;
|
||||
my->currentSize += addSize;
|
||||
RELEASE(item);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) shrinkObjects: (unsigned)objects andSize: (unsigned)size
|
||||
{
|
||||
unsigned newSize = [self currentSize];
|
||||
unsigned newObjects = [self currentObjects];
|
||||
|
||||
if (newObjects > objects || (my->maxSize > 0 && newSize > size))
|
||||
{
|
||||
[self purge];
|
||||
newSize = [self currentSize];
|
||||
newObjects = [self currentObjects];
|
||||
while (newObjects > objects || (my->maxSize > 0 && newSize > size))
|
||||
{
|
||||
SQLCItem *item;
|
||||
|
||||
item = my->first;
|
||||
removeItem(item, &my->first);
|
||||
newObjects--;
|
||||
if (my->maxSize > 0)
|
||||
{
|
||||
newSize -= item->size;
|
||||
}
|
||||
NSMapRemove(my->contents, (void*)item->key);
|
||||
}
|
||||
my->currentObjects = newObjects;
|
||||
my->currentSize = newSize;
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
|
|
611
WebServer.h
611
WebServer.h
|
@ -1,611 +0,0 @@
|
|||
/**
|
||||
Copyright (C) 2004 Free Software Foundation, Inc.
|
||||
|
||||
Written by: Richard Frith-Macdonald <rfm@gnu.org>
|
||||
Date: June 2004
|
||||
|
||||
This file is part of the SQLClient Library.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
||||
|
||||
<title>WebServer documentation</title>
|
||||
<chapter>
|
||||
<heading>The WebServer class</heading>
|
||||
<section>
|
||||
<heading>What is the WebServer class?</heading>
|
||||
<p>
|
||||
The WebServer class provides the framework for a GNUstep program to
|
||||
act as an HTTP or HTTPS server for simple applications.<br />
|
||||
It does not attempt to be a general-purpose web server, but is rather
|
||||
intended to permit a program to easily handle requests from automated
|
||||
systems which are intended to control, monitor, or use the services
|
||||
provided by the program in which the class is embedded.<br />
|
||||
The emphasis is on making it robust/reliable/simple, so you can rapidly
|
||||
develop software using it. It is a single-threaded, single-process
|
||||
system using asynchronous I/O, so you can easily run it under
|
||||
debug in gdb to fix any bugs in your delegate object.<br />
|
||||
In particular of course, it may be used in conjunction with the
|
||||
[SQLClient] class to implement web-based database applications.
|
||||
</p>
|
||||
<p>
|
||||
The class is controlled by a few straightforward settings and
|
||||
basically operates by handing over requests to its delegate.
|
||||
The delegate must at least implement the
|
||||
[(WebServerDelegate)-processRequest:response:for:] method.
|
||||
</p>
|
||||
<p>
|
||||
Built-in facilities include -
|
||||
</p>
|
||||
<list>
|
||||
<item>Parsing of parameter string in request URL</item>
|
||||
<item>Parsing of url encoded form data in a POST request</item>
|
||||
<item>Parsing of form encoded data in a POST request</item>
|
||||
<item>Substitution into template pages on output</item>
|
||||
<item>SSL support</item>
|
||||
<item>HTTP Basic authentication</item>
|
||||
<item>Limit access by IP address</item>
|
||||
<item>Limit total number of simultaneous connections</item>
|
||||
<item>Limit number of simultaneous connectionsform one address</item>
|
||||
<item>Limit idle time permitted on a connection</item>
|
||||
<item>Limit size of request headers permitted</item>
|
||||
<item>Limit size of request body permitted</item>
|
||||
</list>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
$Date$ $Revision$
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_WEBSERVER_H
|
||||
#define INCLUDED_WEBSERVER_H
|
||||
|
||||
#include <Foundation/NSObject.h>
|
||||
#include <Foundation/NSMapTable.h>
|
||||
#include <Foundation/NSDictionary.h>
|
||||
#include <Foundation/NSFileHandle.h>
|
||||
#include <Foundation/NSNotification.h>
|
||||
#include <Foundation/NSArray.h>
|
||||
#include <Foundation/NSSet.h>
|
||||
#include <Foundation/NSTimer.h>
|
||||
#include <GNUstepBase/GSMime.h>
|
||||
|
||||
@class WebServer;
|
||||
|
||||
/**
|
||||
* This protocol is implemented by a delegate of a WebServer instance
|
||||
* in order to allow the delegate to process requests which arrive
|
||||
* at the server.
|
||||
*/
|
||||
@protocol WebServerDelegate
|
||||
/**
|
||||
* Process the http request whose headers and data are provided in
|
||||
* a GSMimeDocument.<br />
|
||||
* Extra headers are created as follows -
|
||||
* <deflist>
|
||||
* <term>x-http-method</term>
|
||||
* <desc>The method from the HTTP request (eg. GET or POST)</desc>
|
||||
* <term>x-http-path</term>
|
||||
* <desc>The path from the HTTP request, or an empty string if
|
||||
* there was no path.</desc>
|
||||
* <term>x-http-query</term>
|
||||
* <desc>The query string from the HTTP request or an empty string
|
||||
* if there was no query.</desc>
|
||||
* <term>x-http-version</term>
|
||||
* <desc>The version from the HTTP request.</desc>
|
||||
* <term>x-local-address</term>
|
||||
* <desc>The IP address of the local host receiving the request.</desc>
|
||||
* <term>x-local-port</term>
|
||||
* <desc>The port of the local host receiving the request.</desc>
|
||||
* <term>x-remote-address</term>
|
||||
* <desc>The IP address of the host that the request came from.</desc>
|
||||
* <term>x-remote-port</term>
|
||||
* <desc>The port of the host that the request came from.</desc>
|
||||
* <term>x-http-username</term>
|
||||
* <desc>The username from the 'authorization' header if the request
|
||||
* supplied http basic authentication.</desc>
|
||||
* <term>x-http-password</term>
|
||||
* <desc>The password from the 'authorization' header if the request
|
||||
* supplied http basic authentication.</desc>
|
||||
* </deflist>
|
||||
* On completion, the method must modify response to contain the data
|
||||
* and headers to be sent out.<br />
|
||||
* The 'content-length' header need not be set in the response as it will
|
||||
* be overridden anyway.<br />
|
||||
* The special 'http' header will be used as the response/status line.
|
||||
* If not supplied, 'HTTP/1.1 200 Success' or 'HTTP/1.1 204 No Content' will
|
||||
* be used as the response line, depending on whether the data is empty or
|
||||
* not.<br />
|
||||
* If an exception is raised by this method, the response produced will
|
||||
* be set to 'HTTP/1.0 500 Internal Server Error' and the connection will
|
||||
* be closed.
|
||||
*/
|
||||
- (BOOL) processRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (WebServer*)http;
|
||||
/**
|
||||
* Log an error or warning ... if the delegate does not implement this
|
||||
* method, the message is logged to stderr using the NSLog function.
|
||||
*/
|
||||
- (void) webAlert: (NSString*)message for: (WebServer*)http;
|
||||
@end
|
||||
|
||||
/**
|
||||
* <p>You create an instance of the WebServer class in order to handle
|
||||
* incoming http or https requests on a single port.
|
||||
* </p>
|
||||
* <p>Before use, it must be configured using the -setPort:secure: method
|
||||
* to specify the port and if/how ssl is to be used.
|
||||
* </p>
|
||||
* <p>You must also set a delegate to handle incoming requests,
|
||||
* and may specify a maximum number of simultaneous connections
|
||||
* which may be in progress etc.
|
||||
* </p>
|
||||
* <p>In addition to the options which may be set directly in the class,
|
||||
* you can provide some configuration via the standard NSDefaults class.
|
||||
* This information is set at initialisation of an instance and the
|
||||
* class recognises the following defaults keys -
|
||||
* </p>
|
||||
* <deflist>
|
||||
* <term>WebServerHosts</term>
|
||||
* <desc>An array of host IP addresses to list the mhosts permitted to
|
||||
* send requests to the server. If defined, requests from other hosts
|
||||
* will be rejected. It's better to use firewalling to control this
|
||||
* sort of thing.
|
||||
* </desc>
|
||||
* <term>WebServerQuiet</term>
|
||||
* <desc>An array of host IP addresses to refrain from logging ...
|
||||
* this is useful if (for instance) you have a monitoring process which
|
||||
* sends requests to the server to be sure it's alive, and don't want
|
||||
* to log all the connections from this monitor.<br />
|
||||
* Not only do we refrain from logging anything but exceptional events
|
||||
* about these hosts, connections and requests by these hosts are not
|
||||
* counted in statistics we generate.
|
||||
* </desc>
|
||||
* </deflist>
|
||||
*/
|
||||
@interface WebServer : NSObject
|
||||
{
|
||||
@private
|
||||
NSNotificationCenter *_nc;
|
||||
NSString *_port;
|
||||
BOOL _accepting;
|
||||
BOOL _verbose;
|
||||
BOOL _durations;
|
||||
NSDictionary *_sslConfig;
|
||||
NSArray *_quiet;
|
||||
NSArray *_hosts;
|
||||
unsigned int _substitutionLimit;
|
||||
unsigned int _maxBodySize;
|
||||
unsigned int _maxRequestSize;
|
||||
unsigned int _maxSessions;
|
||||
unsigned int _maxPerHost;
|
||||
id _delegate;
|
||||
NSFileHandle *_listener;
|
||||
NSMapTable *_sessions;
|
||||
unsigned _handled;
|
||||
unsigned _requests;
|
||||
NSString *_root;
|
||||
NSTimer *_ticker;
|
||||
NSTimeInterval _sessionTimeout;
|
||||
NSTimeInterval _ticked;
|
||||
NSCountedSet *_perHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called for each incoming request, and checks that the
|
||||
* requested resource is accessible (basic user/password access control).<br />
|
||||
* The method returns YES if access is granted, or returns NO and sets the
|
||||
* appropriate response values if access is refused.<br />
|
||||
* If access is refused by this method, the delegate is not informed of the
|
||||
* request at all ... so this forms an initial access control mechanism,
|
||||
* but if it is passed, the delegate is still free to implement its own
|
||||
* additional access control within the
|
||||
* [(WebServerDelegate)-processRequest:response:for:] method.<br />
|
||||
* The access control is managed by the <code>WebServerAccess</code>
|
||||
* user default, which is a dictionary whose keys are paths, and whose
|
||||
* values are dictionaries specifying the access control for those paths.
|
||||
* Access control is done on the basis of the longest matching path.<br />
|
||||
* Each access control dictionary contains an authentication realm string
|
||||
* (keyed on <em>Realm</em>) and a dictionary containing username/password
|
||||
* pairs (keyed on <em>Users</em>) or a dictionary containing information
|
||||
* to perform a database lookup of username and password
|
||||
* (keyed on <em>UserDB</em>).<br />
|
||||
* eg.
|
||||
* <example>
|
||||
* WebServerAccess = {
|
||||
* "" = {
|
||||
* Realm = "general";
|
||||
* Users = {
|
||||
* Fred = 1942;
|
||||
* };
|
||||
* };
|
||||
* "/private" = {
|
||||
* Realm = "private";
|
||||
* UserDB = {
|
||||
* // System will contact database using SQLClient and lookup password
|
||||
* // The SQLClient library must be linked in and used by the tool
|
||||
* // using WebServer ... it is not linked in by the WebServer library.
|
||||
* Name = databasename;
|
||||
* Table = tablename;
|
||||
* UsernameField = fielname1;
|
||||
* PasswordField = fielname2;
|
||||
* };
|
||||
* };
|
||||
* };
|
||||
* </example>
|
||||
*/
|
||||
- (BOOL) accessRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response;
|
||||
|
||||
/**
|
||||
* Decode an application/x-www-form-urlencoded form and store its
|
||||
* contents into the supplied dictionary.<br />
|
||||
* The resulting dictionary keys are strings.<br />
|
||||
* The resulting dictionary values are arrays of NSData objects.<br />
|
||||
* You probably don't need to call this method yourself ... more likely
|
||||
* you will use the -parameters: method instead.<br />
|
||||
* NB. For forms POSTed using <code>multipart/form-data</code> you don't
|
||||
* need to perform any explicit decoding as this will already have been
|
||||
* done for you and the decoded form will be presented as the request
|
||||
* GSMimeDocument. The fields of the form will be the component parts
|
||||
* of the content of the request and can be accessed using the standard
|
||||
* GSMimeDocument methods.<br />
|
||||
* This method returns the number of fields actually decoded.
|
||||
*/
|
||||
- (unsigned) decodeURLEncodedForm: (NSData*)data
|
||||
into: (NSMutableDictionary*)dict;
|
||||
|
||||
/**
|
||||
* Encode an application/x-www-form-urlencoded form and store its
|
||||
* representation in the supplied data object.<br />
|
||||
* The dictionary contains the form, with keys as data objects or strings,
|
||||
* and values as arrays of values to be placed in the data.
|
||||
* Each value in the array may be a data object or a string.<br />
|
||||
* As a special case, a value may be a data object or a string rather
|
||||
* than an array ... this is treated like an array of one value.<br />
|
||||
* All non data keys and values are convertd to data using utf-8 encoding.<br />
|
||||
* This method returns the number of values actually encoded.
|
||||
*/
|
||||
- (unsigned) encodeURLEncodedForm: (NSDictionary*)dict
|
||||
into: (NSMutableData*)data;
|
||||
|
||||
/**
|
||||
* Returns YES if the server is for HTTPS (encrypted connections),
|
||||
* NO otherwise.
|
||||
*/
|
||||
- (BOOL) isSecure;
|
||||
|
||||
/**
|
||||
* Extracts request parameters from the http query string and from the
|
||||
* request body (if it was application/x-www-form-urlencoded or
|
||||
* multipart/form-data) and return the extracted parameters as a
|
||||
* mutable dictionary whose keys are the parameter names and whose
|
||||
* values are arrays containing the data for each parameter.<br />
|
||||
* You should call this no more than once per request, storing the result
|
||||
* and using it as an argument to the methods used to extract particular
|
||||
* parameters.<br />
|
||||
* Parameters from the request data are <em>added</em> to any found in the
|
||||
* query string.<br />
|
||||
* Values provided as <code>multipart/form-data</code> are also available
|
||||
* in a more flexible format as the content of the request.
|
||||
*/
|
||||
- (NSMutableDictionary*) parameters: (GSMimeDocument*)request;
|
||||
|
||||
/**
|
||||
* Returns the index'th data parameter for the specified name.<br />
|
||||
* Matching of names is case-insensitive<br />
|
||||
* If there are no data items for the name, or if the index is
|
||||
* too large for the number of items which exist, this returns nil.
|
||||
*/
|
||||
- (NSData*) parameter: (NSString*)name
|
||||
at: (unsigned)index
|
||||
from: (NSDictionary*)params;
|
||||
|
||||
/**
|
||||
* Calls -parameter:at:from: with an index of zero.
|
||||
*/
|
||||
- (NSData*) parameter: (NSString*)name from: (NSDictionary*)params;
|
||||
|
||||
/**
|
||||
* Calls -parameterString:at:from:charset: with a nil charset so that
|
||||
* UTF-8 encoding is used for string conversion.
|
||||
*/
|
||||
- (NSString*) parameterString: (NSString*)name
|
||||
at: (unsigned)index
|
||||
from: (NSDictionary*)params;
|
||||
/**
|
||||
* Calls -parameter:at:from: and, if the result is non-nil
|
||||
* converts the data to a string using the specified mime
|
||||
* characterset, (if charset is nil, UTF-8 is used).
|
||||
*/
|
||||
- (NSString*) parameterString: (NSString*)name
|
||||
at: (unsigned)index
|
||||
from: (NSDictionary*)params
|
||||
charset: (NSString*)charset;
|
||||
/**
|
||||
* Calls -parameterString:at:from:charset: with an index of zero and
|
||||
* a nil value for charset (which causes data to be treated as UTF-8).
|
||||
*/
|
||||
- (NSString*) parameterString: (NSString*)name
|
||||
from: (NSDictionary*)params;
|
||||
|
||||
/**
|
||||
* Calls -parameterString:at:from:charset: with an index of zero.
|
||||
*/
|
||||
- (NSString*) parameterString: (NSString*)name
|
||||
from: (NSDictionary*)params
|
||||
charset: (NSString*)charset;
|
||||
|
||||
/**
|
||||
* Loads a template file from disk and places it in aResponse as content
|
||||
* whose mime type is determined from the file extension using the
|
||||
* provided mapping (or a simple built-in default mapping if map is nil).<br />
|
||||
* If you have a dedicated web server for handling static pages (eg images)
|
||||
* it is better to use that rather than vending static pages using this
|
||||
* method. It's unlikley that this method can be as efficient as a dedicated
|
||||
* server. However this mechanism is adequate for moderate throughputs.
|
||||
*/
|
||||
- (BOOL) produceResponse: (GSMimeDocument*)aResponse
|
||||
fromStaticPage: (NSString*)aPath
|
||||
using: (NSDictionary*)map;
|
||||
|
||||
/**
|
||||
* Loads a template file from disk and places it in aResponse as content
|
||||
* of type 'text/html' with a charset of 'utf-8'.<br />
|
||||
* The argument aPath is a path relative to the root path set using
|
||||
* the -setRoot: method.<br />
|
||||
* Substitutes values into the template from map using the
|
||||
* -substituteFrom:using:into:depth: method.<br />
|
||||
* Returns NO if them template could not be read or if any substitution
|
||||
* failed. In this case no value is set in the response.<br />
|
||||
* If the response is actually text of another type, or you want another
|
||||
* characterset used, you can change the content type header in the
|
||||
* request after you call this method.
|
||||
*/
|
||||
- (BOOL) produceResponse: (GSMimeDocument*)aResponse
|
||||
fromTemplate: (NSString*)aPath
|
||||
using: (NSDictionary*)map;
|
||||
|
||||
/**
|
||||
* Sets the delegate object which processes requests for the receiver.
|
||||
*/
|
||||
- (void) setDelegate: (id)anObject;
|
||||
|
||||
/**
|
||||
* Sets a flag to determine whether logging of request and session
|
||||
* durations is to be performed.<br />
|
||||
* If this is YES then the duration of requests and sessions will
|
||||
* be logged using the [(WebServerDelegate)-webAlert:for:] method.<br />
|
||||
* The request duration is calculated from the point where the first byte
|
||||
* of data in the request is read to the point where the response has
|
||||
* been completely written.<br />
|
||||
* This is useful for debugging and where a full audit trail is required.
|
||||
*/
|
||||
- (void) setDurationLogging: (BOOL)aFlag;
|
||||
|
||||
/**
|
||||
* Sets the maximum size of an uploaded request body.<br />
|
||||
* The default is 4M bytes.<br />
|
||||
*/
|
||||
- (void) setMaxBodySize: (unsigned)max;
|
||||
|
||||
/**
|
||||
* Sets the maximum size of an incoming request (including all headers,
|
||||
* but not the body).<br />
|
||||
* The default is 8K bytes.<br />
|
||||
*/
|
||||
- (void) setMaxRequestSize: (unsigned)max;
|
||||
|
||||
/**
|
||||
* Sets the maximum number of simultaneous sessions with clients.<br />
|
||||
* The default is 32.<br />
|
||||
* A value of zero permits unlimited connections.
|
||||
*/
|
||||
- (void) setMaxSessions: (unsigned)max;
|
||||
|
||||
/**
|
||||
* Sets the maximum number of simultaneous sessions with a particular
|
||||
* remote host.<br />
|
||||
* The default is 8.<br />
|
||||
* A value of zero permits unlimited connections.
|
||||
*/
|
||||
- (void) setMaxSessionsPerHost: (unsigned)max;
|
||||
|
||||
/**
|
||||
* Sets the port and security information for the receiver ... without
|
||||
* this the receiver will not listen for incoming requests.<br />
|
||||
* If secure is nil then the receiver listens on aPort for HTTP requests.<br />
|
||||
* If secure is not nil, the receiver listens for HTTPS instead.<br />
|
||||
* If secure is a dictionary containing <code>CertificateFile</code>,
|
||||
* <code>KeyFile</code> and <code>Password</code> then the server will
|
||||
* use the specified certificate and key files (which it will access
|
||||
* using the password).<br />
|
||||
* The <em>secure</em> dictionary may also contain other dictionaries
|
||||
* keyed on IP addresses, and if the address that an incoming connection
|
||||
* arrived on matches the key of a dictionary, that dictionary is used
|
||||
* to provide the certificate information, with the top-level values
|
||||
* being used as a fallback.<br />
|
||||
* This method returns YES on success, NO on failure ... if it returns NO
|
||||
* then the receiver will <em>not</em> be capable of handling incoming
|
||||
* web requests!<br />
|
||||
* Typically a failure will be due to an invalid port being specified ...
|
||||
* a port may not already be in use and may not be in the range up to 1024
|
||||
* (unless running as the super-user).
|
||||
*/
|
||||
- (BOOL) setPort: (NSString*)aPort secure: (NSDictionary*)secure;
|
||||
|
||||
/**
|
||||
* Sets the maximum recursion depth allowed for subsititutions into
|
||||
* templates. This defaults to 4.
|
||||
*/
|
||||
- (void) setSubstitutionLimit: (unsigned)depth;
|
||||
|
||||
/**
|
||||
* Set root path for loading template files from.<br />
|
||||
* Templates may only be loaded from within this directory.
|
||||
*/
|
||||
- (void) setRoot: (NSString*)aPath;
|
||||
|
||||
/**
|
||||
* Sets the time after which an idle session should be shut down.<br />
|
||||
* Default is 30.0
|
||||
*/
|
||||
- (void) setSessionTimeout: (NSTimeInterval)aDelay;
|
||||
|
||||
/**
|
||||
* Sets a flag to determine whether verbose logging is to be performed.<br />
|
||||
* If this is YES then all incoming requests and their responses will
|
||||
* be logged using the [(WebServerDelegate)-webAlert:for:] method.<br />
|
||||
* Setting this to YES automatically sets duration logging to YES as well,
|
||||
* though you can then call -setDurationLogging: to set it back to NO.<br />
|
||||
* This is useful for debugging and where a full audit trail is required.
|
||||
*/
|
||||
- (void) setVerbose: (BOOL)aFlag;
|
||||
|
||||
/**
|
||||
* Perform substituations replacing the markup in aTemplate with the
|
||||
* values supplied by map and appending the results to the result.<br />
|
||||
* Substitutions are recursive, and the depth argument is used to
|
||||
* specify the current recursion depth (you should normally call this
|
||||
* method with a depth of zero at the start of processing a template).<br />
|
||||
* Any value inside SGML comment delimiters ('<!--' and '-->') is
|
||||
* treated as a possible key in map and the entire comment is replaced
|
||||
* by the corresponding map value (unless it is nil). Recursive substitution
|
||||
* is done unless the mapped value <em>starts</em> with an SGML comment.<br />
|
||||
* While the map is nominally a dictionary, in fact it may be any
|
||||
* object which responds to the objectForKey: method by returning
|
||||
* an NSString or nil.<br />
|
||||
* The method returns YES on success, NO on failure (depth too great).<br />
|
||||
* You don't normally need to use this method directly ... call the
|
||||
* -produceResponse:fromTemplate:using: method instead.
|
||||
*/
|
||||
- (BOOL) substituteFrom: (NSString*)aTemplate
|
||||
using: (NSDictionary*)map
|
||||
into: (NSMutableString*)result
|
||||
depth: (unsigned)depth;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* WebServerBundles is an example delegate for the WebServer class.<br />
|
||||
* This is intended to act as a convenience for a scheme where the
|
||||
* WebServer instance in a program is configured by values obtained
|
||||
* from the user defaults system, and incoming requests may be handled
|
||||
* by different delegate objects depending on the path information
|
||||
* supplied in the request. The WebServerBundles intance is responsible
|
||||
* for loading the bundles (based on information in the WebServerBundles
|
||||
* dictionary in the user defaults system) and for forwarding requests
|
||||
* to the appropriate bundles for processing.<br />
|
||||
* If a request comes in which is not an exact match for the path of any
|
||||
* handler, the request path is repeatedly shortened by chopping off the
|
||||
* last path component until a matching handler is found.<br />
|
||||
* The paths in the dictionary must <em>not</em> end with a slash...
|
||||
* an empty string will match all requests which do not match a handler
|
||||
* with a longer path.
|
||||
* <example>
|
||||
* </example>
|
||||
*/
|
||||
@interface WebServerBundles : NSObject <WebServerDelegate>
|
||||
{
|
||||
NSMutableDictionary *_handlers;
|
||||
WebServer *_http;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a notification that the defaults have been updated ... change
|
||||
* WebServer configuration if necessary.<br />
|
||||
* <list>
|
||||
* <item>
|
||||
* WebServerPort must be used to specify the port that the server
|
||||
* listens on. See [WebServer-setPort:secure:] for details.
|
||||
* </item>
|
||||
* <item>
|
||||
* WebServerSecure may be supplied to make the server operate as an
|
||||
* HTTPS server rather than an HTTP server.
|
||||
* See [WebServer-setPort:secure:] for details.
|
||||
* </item>
|
||||
* <item>
|
||||
* WebServerBundles is a dictionary keyed on path strings, whose
|
||||
* values are dictionaries, each containing per-handler configuration
|
||||
* information and the name of the bundle containing the code to handle
|
||||
* requests sent to the path. NB. the bundle name listed should
|
||||
* omit the <code>.bundle</code> extension.
|
||||
* </item>
|
||||
* </list>
|
||||
* Returns YES on success, NO on failure (if the port of the WebServer
|
||||
* cannot be set).
|
||||
*/
|
||||
- (BOOL) defaultsUpdate: (NSNotification *)aNotification;
|
||||
|
||||
/**
|
||||
* Returns the handler to be used for the specified path, or nil if there
|
||||
* is no handler available.<br />
|
||||
* If the info argument is non-null, it is used to return additional
|
||||
* information, either the path actually matched, or an error string.
|
||||
*/
|
||||
- (id) handlerForPath: (NSString*)path info: (NSString**)info;
|
||||
|
||||
/**
|
||||
* Return dictionary of all handlers by name (path in request which maps
|
||||
* to that handler instance).
|
||||
*/
|
||||
- (NSMutableDictionary*) handlers;
|
||||
|
||||
/**
|
||||
* Return the WebServer instance that the receiver is acting as a
|
||||
* delegate for.
|
||||
*/
|
||||
- (WebServer*) http;
|
||||
|
||||
/** <init />
|
||||
* Initialises the receiver as the delegate of http and configures
|
||||
* the WebServer based upon the settings found in the user defaults
|
||||
* system by using the -defaultsUpdate: method.
|
||||
*/
|
||||
- (id) initAsDelegateOf: (WebServer*)http;
|
||||
|
||||
/**
|
||||
* Handles an incoming request by forwarding it to another handler.<br />
|
||||
* If a direct mapping is available from the path in the request to
|
||||
* an existing handler, that handler is used to process the request.
|
||||
* Otherwise, the WebServerBundles dictionary (obtained from the
|
||||
* defaults system) is used to map the request path to configuration
|
||||
* information listing the bundle containing the handler to be used.<br />
|
||||
* The configuration information is a dictionary containing the name
|
||||
* of the bundle (keyed on 'Name'), and this is used to locate the
|
||||
* bundle in the applications resources.<br />
|
||||
* Before a request is passed on to a handler, two extra headers are set
|
||||
* in it ... <code>x-http-path-base</code> and <code>x-http-path-info</code>
|
||||
* being the actual path matched for the handler, and the remainder of the
|
||||
* path after that base part.
|
||||
*/
|
||||
- (BOOL) processRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (WebServer*)http;
|
||||
|
||||
/**
|
||||
* Registers an object as the handler for a particular path.<br />
|
||||
* Registering a nil handler destroys any existing handler for the path.
|
||||
*/
|
||||
- (void) registerHandler: (id)handler forPath: (NSString*)path;
|
||||
|
||||
/**
|
||||
* Just write to stderr using NSLog.
|
||||
*/
|
||||
- (void) webAlert: (NSString*)message for: (WebServer*)http;
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
1910
WebServer.m
1910
WebServer.m
File diff suppressed because it is too large
Load diff
|
@ -1,248 +0,0 @@
|
|||
/**
|
||||
Copyright (C) 2004 Free Software Foundation, Inc.
|
||||
|
||||
Written by: Richard Frith-Macdonald <rfm@gnu.org>
|
||||
Date: June 2004
|
||||
|
||||
This file is part of the SQLClient Library.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
||||
|
||||
$Date$ $Revision$
|
||||
*/
|
||||
#include <Foundation/Foundation.h>
|
||||
#include "WebServer.h"
|
||||
|
||||
@implementation WebServerBundles
|
||||
- (void) dealloc
|
||||
{
|
||||
RELEASE(_http);
|
||||
RELEASE(_handlers);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (BOOL) defaultsUpdate: (NSNotification *)aNotification
|
||||
{
|
||||
NSUserDefaults *defs = [aNotification object];
|
||||
NSString *port;
|
||||
NSDictionary *secure;
|
||||
|
||||
port = [defs stringForKey: @"WebServerPort"];
|
||||
if ([port length] == 0)
|
||||
{
|
||||
return NO; // Can't make web server active.
|
||||
}
|
||||
secure = [defs dictionaryForKey: @"WebServerSecure"];
|
||||
return [_http setPort: port secure: secure];
|
||||
}
|
||||
|
||||
- (id) handlerForPath: (NSString*)path info: (NSString**)info
|
||||
{
|
||||
NSString *error = nil;
|
||||
NSMutableDictionary *handlers;
|
||||
id handler;
|
||||
|
||||
if (info != 0)
|
||||
{
|
||||
*info = path;
|
||||
}
|
||||
handlers = [self handlers];
|
||||
handler = [handlers objectForKey: path];
|
||||
if (handler == nil)
|
||||
{
|
||||
NSUserDefaults *defs;
|
||||
NSDictionary *conf;
|
||||
NSDictionary *byPath;
|
||||
|
||||
defs = [NSUserDefaults standardUserDefaults];
|
||||
conf = [defs dictionaryForKey: @"WebServerBundles"];
|
||||
byPath = [conf objectForKey: path];
|
||||
if ([byPath isKindOfClass: [NSDictionary class]] == NO)
|
||||
{
|
||||
NSRange r;
|
||||
|
||||
r = [path rangeOfString: @"/" options: NSBackwardsSearch];
|
||||
if (r.length > 0)
|
||||
{
|
||||
path = [path substringToIndex: r.location];
|
||||
handler = [self handlerForPath: path info: info];
|
||||
}
|
||||
else
|
||||
{
|
||||
error = [NSString stringWithFormat:
|
||||
@"Unable to find handler in Bundles config for '%@'", path];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *name;
|
||||
|
||||
name = [byPath objectForKey: @"Name"];
|
||||
|
||||
if ([name length] == 0)
|
||||
{
|
||||
error = [NSString stringWithFormat:
|
||||
@"Unable to find Name in Bundles config for '%@'", path];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSBundle *mb = [NSBundle mainBundle];
|
||||
NSString *p = [mb pathForResource: name ofType: @"bundle"];
|
||||
NSBundle *b = [NSBundle bundleWithPath: p];
|
||||
Class c = [b principalClass];
|
||||
|
||||
if (c == 0)
|
||||
{
|
||||
error = [NSString stringWithFormat:
|
||||
@"Unable to find class in '%@' for '%@'", p, path];
|
||||
}
|
||||
else
|
||||
{
|
||||
handler = [c new];
|
||||
[self registerHandler: handler forPath: path];
|
||||
RELEASE(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (handler == nil && info != 0)
|
||||
{
|
||||
*info = error;
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary*) handlers
|
||||
{
|
||||
if (_handlers == nil)
|
||||
{
|
||||
_handlers = [NSMutableDictionary new];
|
||||
}
|
||||
return _handlers;
|
||||
}
|
||||
|
||||
- (WebServer*) http
|
||||
{
|
||||
return _http;
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
return [self initAsDelegateOf: nil];
|
||||
}
|
||||
|
||||
- (id) initAsDelegateOf: (WebServer*)http
|
||||
{
|
||||
if (http == nil)
|
||||
{
|
||||
DESTROY(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
|
||||
NSNotification *n;
|
||||
|
||||
ASSIGN(_http, http);
|
||||
[_http setDelegate: self];
|
||||
|
||||
/*
|
||||
* Watch for config changes, and set initial config by sending a
|
||||
* faked change notification.
|
||||
*/
|
||||
[nc addObserver: self
|
||||
selector: @selector(defaultsUpdate:)
|
||||
name: NSUserDefaultsDidChangeNotification
|
||||
object: defs];
|
||||
n = [NSNotification
|
||||
notificationWithName: NSUserDefaultsDidChangeNotification
|
||||
object: defs
|
||||
userInfo: nil];
|
||||
if ([self defaultsUpdate: n] == NO)
|
||||
{
|
||||
DESTROY(self);
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* We handle the incoming requests here.
|
||||
*/
|
||||
- (BOOL) processRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (WebServer*)http
|
||||
{
|
||||
NSString *path;
|
||||
NSString *info;
|
||||
id handler;
|
||||
|
||||
path = [[request headerNamed: @"x-http-path"] value];
|
||||
handler = [self handlerForPath: path info: &info];
|
||||
if (handler == nil)
|
||||
{
|
||||
NSString *error = @"bad path";
|
||||
|
||||
/*
|
||||
* Log the error message.
|
||||
*/
|
||||
[self webAlert: info for: (WebServer*)http];
|
||||
|
||||
/*
|
||||
* Return status code 400 (Bad Request) with the informative error
|
||||
*/
|
||||
error = [NSString stringWithFormat: @"HTTP/1.0 400 %@", error];
|
||||
[response setHeader: @"http" value: error parameters: nil];
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *extra = [path substringFromIndex: [info length]];
|
||||
|
||||
/*
|
||||
* Provide extra information about the exact path used to match
|
||||
* the handler, and any remaining path information beyond it.
|
||||
*/
|
||||
[request setHeader: @"x-http-path-base"
|
||||
value: info
|
||||
parameters: nil];
|
||||
[request setHeader: @"x-http-path-info"
|
||||
value: extra
|
||||
parameters: nil];
|
||||
|
||||
return [handler processRequest: request
|
||||
response: response
|
||||
for: http];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) registerHandler: (id)handler forPath: (NSString*)path
|
||||
{
|
||||
if (handler == nil)
|
||||
{
|
||||
[[self handlers] removeObjectForKey: path];
|
||||
}
|
||||
else
|
||||
{
|
||||
[[self handlers] setObject: handler forKey: path];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) webAlert: (NSString*)message for: (WebServer*)http
|
||||
{
|
||||
NSLog(@"%@", message);
|
||||
}
|
||||
@end
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <Performance/GSCache.h>
|
||||
#include "SQLClient.h"
|
||||
|
||||
int
|
||||
|
@ -233,7 +234,7 @@ main()
|
|||
r0 = [db cache: 1 query: @"select * from xxx", nil];
|
||||
r1 = [db cache: 1 query: @"select * from xxx", nil];
|
||||
NSCAssert([r0 lastObject] == [r1 lastObject], @"Cache failed");
|
||||
sleep(1);
|
||||
sleep(2);
|
||||
records = [db cache: 1 query: @"select * from xxx", nil];
|
||||
NSCAssert([r0 lastObject] != [records lastObject], @"Lifetime failed");
|
||||
|
||||
|
@ -258,7 +259,7 @@ main()
|
|||
}
|
||||
}
|
||||
|
||||
NSLog(@"Records - %@", [SQLCache class]);
|
||||
NSLog(@"Records - %@", [GSCache class]);
|
||||
}
|
||||
|
||||
RELEASE(pool);
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
/**
|
||||
Copyright (C) 2005 Free Software Foundation, Inc.
|
||||
|
||||
Written by: Richard Frith-Macdonald <rfm@gnu.org>
|
||||
Date: September 2005
|
||||
|
||||
This file is part of the SQLClient Library.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
||||
|
||||
$Date$ $Revision$
|
||||
*/
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <GNUstepBase/GSMime.h>
|
||||
#include "WebServer.h"
|
||||
|
||||
@interface Handler: NSObject
|
||||
- (BOOL) processRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (WebServer*)http;
|
||||
@end
|
||||
@implementation Handler
|
||||
- (BOOL) processRequest: (GSMimeDocument*)request
|
||||
response: (GSMimeDocument*)response
|
||||
for: (WebServer*)http
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [[NSString alloc] initWithData: [request rawMimeData]
|
||||
encoding: NSISOLatin1StringEncoding];
|
||||
NSLog(@"Got request -\n%@\n", s);
|
||||
[response setContent: s type: @"text/plain" name: nil];
|
||||
RELEASE(s);
|
||||
|
||||
return YES;
|
||||
}
|
||||
@end
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
CREATE_AUTORELEASE_POOL(pool);
|
||||
WebServer *server;
|
||||
Handler *handler;
|
||||
NSUserDefaults *defs;
|
||||
|
||||
defs = [NSUserDefaults standardUserDefaults];
|
||||
[defs registerDefaults:
|
||||
[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
@"80", @"Port",
|
||||
nil]
|
||||
];
|
||||
|
||||
server = [WebServer new];
|
||||
{
|
||||
NSData *d = [NSData dataWithContentsOfFile: @"/home/richard/web.log"];
|
||||
NSMutableDictionary *p = [NSMutableDictionary dictionary];
|
||||
[server decodeURLEncodedForm: d into: p];
|
||||
NSLog(@"Params: %@", p);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
handler = [Handler new];
|
||||
[server setDelegate: handler];
|
||||
[server setPort: [defs stringForKey: @"Port"] secure: nil];
|
||||
|
||||
[[NSRunLoop currentRunLoop] run];
|
||||
|
||||
RELEASE(pool);
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in a new issue