/** -*- objc -*- * * Author: Sergei Golovin * * TestWebServer is intended to be used in the tests where a web service is * needed. The class's instance is instantiated with the method * -[initWithAddress:port:mode:extra] and then started with the call -[start:]. * When started the TestWebServer starts in it's turn a SimpleWebServer instance * in the same thread unless it is initialized with the argument 'mode' set to * 'YES' to run it in a detached thread. * * It is designed to call the delegate's callbacks (if implemented) of * the TestWebServerDelegate protocol for every request made to * the SimpleWebServer's assigned address on the SimpleWebServer's assigned port * (by default localhost and 1234 respectively). However it currently doesn't * handle any request on it's own. Instead the class uses a collection of * handlers. Every handler makes a custom response. So the TestWebServer only * has to dispatch a request to a corresponding handler and then to send back * the handler's response to the SimpleWebServer instance. See RequestHandler * for more information. * * TestWebServer can be supplied with additional parameters via the argument * 'extra' of the initializer -[initWithAddress:port:mode:extra]. * See that method's description for the currently supported key-value pairs. * * The pattern of use: * ---------------------------------------------------------------------------- * NSDictionary *extra = [NSDictionary dictionaryWithObjectsAndKeys: * @"https", @"Protocol", * nil]; * TestWebServer *server = [[TestWebServer alloc] initWithAddress: @"localhost" * port: @"1234" * mode: NO * extra: extra]; * ADelegateClass *del = [ADelegateClass new]; * [server setDelegate: del]; * [server start: extra]; * .... * * .... * [server stop]; * DESTROY(server); * DESTROY(del); * ---------------------------------------------------------------------------- * * * The TestWebServer can response to the following resource paths: * * /index * displays the page with all supported resources * /withoutauth/ * if it is a part of the request's URL then the TestWebServer * will NOT check authentication/authorization of the client. * * /204/ * /301/ * /400/ * /401/ * /402/ * /404/ * /405/ * /409/ * /500/ * /505/ * /507/ * a try to access to this resources will lead the TestWebServer * to produce one of the pre-defined responses with the status code * is equal to the corresponding number. * The pre-defined responses are: * 204 "" (empty string) * 301 "Redirect to " * Returns in the header 'Location' a new which by default * has the same protocol, address but the port is incremented by * 1 (e.g. http://localhost:1235/ with other parameters set to * their default values). * 400 "You have issued a request with invalid data" * 401 "Invalid login or password" * 403 "Check your balance" * 404 "The resource you are trying to access is not found" * 405 "You can't do that" * 409 "The resource you are trying to access is busy" * 500 "System error" * 505 "There is network protocol inconsistency" * 507 "Insufficient storage" * * If you want more verbosity call the method -[setDebug: YES]. * */ #import "SimpleWebServer.h" #import @class RequestHandler; @interface TestWebServer : NSObject { /* the debug mode flag */ BOOL _debug; /* the IP address to attach to... see DEFAULTADDRESS at the beginning * of the implementaion */ NSString *_address; /* the port to listen on... see DEFAULTPORT in the beginning * of the implementaion */ NSString *_port; /* whether the TestWebServer listens for HTTPS requests */ BOOL _isSecure; /* the login for basic authentication */ NSString *_login; /* the password for basic authentication */ NSString *_password; /* holds the extra argument which is for a future use... * Currently it is expected to be a dictionary with predetermined * keys ( see the description of -[initWithAddress:port:mode:extra:]) */ id _extra; /* the flag used as a trigger to stop the detached thread */ BOOL _threadToQuit; /* holds the SimpleWebServer accepting incoming connections */ SimpleWebServer *_server; /* holds the detached thread the SimpleWebServer is running on... * nil if it runs in the same thread as the calling code */ NSThread *_serverThread; /* the delegate ... NOT RETAINED... * see below the protocol TestWebServerDelegate */ id _delegate; /* the lock used for synchronization between threads mainly * to wait for the SimpleWebServer is started/stopped... * the condition list: * READY - the initial condition * STARTED - the SimpleWebServer has been started; * STOPPED - the SimpleWebServer has been stopped; */ NSConditionLock *_lock; /* the traversal map used to pick a right handler from the request's path */ NSMutableDictionary *_traversalMap; } /** * Initializes the intance with default parameters. */ - (id)init; /** * Initializes an instance with it's SimpleWebServer accepting connection on * the specified address and port. The mode decides whether to run * the SimpleWebServer on the detached thread (NO means it won't be detached). * The argument extra is for future enhancement and could be of the class * NSDictionary whose key-value pairs set various parameters. * The current code supports the following extra keys: * 'Login' - the login for basic authentication * 'Password' - the password for basic authentication * 'Protocol' - 'http' means waiting for HTTP requests * 'https' - for HTTPS requests */ - (id)initWithAddress:(NSString *)address port:(NSString *)port mode:(BOOL)detached extra:(id)extra; - (void)dealloc; /** * Starts the SimpleWebServer accepting incoming connections. * The supplied argument is for future enhancement and currently has no meaning. * It isn't retained by the method. */ - (void)start:(id)extra; /** * Stops the SimpleWebServer. The instance ceases to accept incoming connections. */ - (void)stop; /* SimpleWebServerDelegate */ /** * The main job of request-response cycle is done here. The method is called * when all request's bytes are read. It must be supplied with the incoming * request and the response document which is about to be sent back to the web * client. The handling code within the method would change the supplied response. */ - (BOOL) processRequest:(GSMimeDocument *)request response:(GSMimeDocument *)response for:(SimpleWebServer *)server; /* end of SimpleWebServerDelegate */ /* getters */ /** * Returns the address which SimpleWebServer is bound to. */ - (NSString *)address; /** * Returns the port which the SimpleWebServer listens on. */ - (NSString *)port; /** * Returns the login for basic authentication. */ - (NSString *)login; /** * Returns the password for basic authentication. */ - (NSString *)password; /** * Returns YES if the instance waits for HTTPS requests. */ - (BOOL)isSecure; /* end of getters */ /* setters */ /** * Sets the delegate implementing TestWebServerDelegate protocol. * The argument isn't retained by the method. */ - (void)setDelegate:(id)delegate; /** * Sets the debug mode (more verbose). */ - (void)setDebug:(BOOL)mode; /* end of setters */ @end /* TestWebServer */ /** * The protocol's methods are called during processing of a request. */ @protocol TestWebServerDelegate /** * Called by the handler when it has got the supplied request * from the supplied TestWebServer instance. */ - (void)handler:(id)handler gotRequest:(GSMimeDocument *)request with:(TestWebServer *)server; /** * Called by the handler when it is going to send the response * on an unauthorized request (an invalid request or no credentials are supplied) * via the supplied TestWebServer. */ - (void)handler:(id)handler willSendUnauthorized:(GSMimeDocument *)response with:(TestWebServer *)server; /** * Called by the handler when it has got the supplied request * with valid credentials from the supplied TestWebServer instance. */ - (void)handler:(id)handler gotAuthorized:(GSMimeDocument *)request with:(TestWebServer *)server; /** * Called by the handler when it is going to send the response * on any request except an unauthorized one via the supplied TestWebServer. */ - (void)handler:(id)handler willSend:(GSMimeDocument *)response with:(TestWebServer *)server; /** * Called when the timeout is exceeded. */ - (void)timeoutExceededByHandler:(id)handler; @end /* TestWebServerDelegate */