/scgi/Server.vala
http://libzee.googlecode.com/ · Vala · 215 lines · 74 code · 36 blank · 105 comment · 12 complexity · 5358adacf682077e7138f0a70800d44e MD5 · raw file
- /**
- * Copyright (c) 2011 George 'kha0s' Zelenskiy <admin.kha0s@gmail.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- * --------------------------------------------------------------------------------
- * Part: Zee.SCGI.Server
- * Package: libzee-scgi
- *
- * Implements simple, threaded, asynchronous, signal-based SCGI server interface.
- *
- * To use it, create a Zee.SCGI.Server instance, providing working host, port
- * and maximum number of concurrent threads. Provide atleast one incoming
- * request handler by Zee.SCGI.Server.attach_request_handler() and start()
- * the server.
- *
- * On every new connection, after request body decomposition all request handlers
- * attached to handle_request signal will be called successively.
- *
- * Note that once attached request handler cannot be removed from handle_request
- * signal due to signal-delegate bug (?) in ValaC.
- */
- namespace Zee.SCGI {
- /**
- * Threaded asynchronous signal-based SCGI server
- */
- public class Server {
- /**** Variables ****/
- /**
- * Main loop to hold
- */
- protected MainLoop _loop;
- /**
- * Maximum numer of threads
- */
- protected int _max_threads;
- /**
- * Server's internet address
- */
- protected InetSocketAddress _address;
- /**
- * Server's socket service
- */
- protected ThreadedSocketService _service;
-
-
-
- /**** Signals ****/
- /**
- * Decomposed SCGI request handle signal
- */
- protected signal void _handle_request_signal(Request req);
-
-
-
- /**** Delegates ****/
- /**
- * Request handler's delegate
- */
- public delegate void RequestHandler(Request req);
-
-
-
- /**** Constructors ****/
- /**
- * Initializes a new instance of Zee.SCGI.Server
- *
- * @param address string representing a server's IP
- * @param port server's port to listen on
- * @param max_threads maximum number of threads server will operate
- */
- public Server(string address, uint16 port, int max_threads) {
- InetAddress addr = new InetAddress.from_string(address);
-
- this._address = new InetSocketAddress(addr, port);
- this._max_threads = max_threads;
- }
-
-
-
- /**** Properties ****/
- /**
- * Internet address current instance is configured to work on
- */
- public InetSocketAddress address {
- get { return this._address; }
- }
-
- /**
- * Maximum number of threads
- */
- public int max_threads {
- get { return this._max_threads; }
- }
-
-
-
- /**** Methods ****/
- /**
- * Attaches a new method/function satisfying Zee.SCGI.Server.RequestHandler
- * delegate to handle all incoming requests
- *
- * @param handler function to handle decomposed requests with
- */
- public void attach_request_handler(RequestHandler handler) {
- //this._handle_request_signal.connect(handler);
- this._handle_request_signal.connect((req) => handler(req));
- }
-
- /**
- * Starts SCGI server with current configuration
- *
- * @throws Error if specified internet address is not available or
- * cannot be configured GLib.Error will be thrown
- */
- public void start() throws Error {
- this._service = new ThreadedSocketService(this._max_threads);
-
- // [TODO]: Wrap the generic GLib error
- this._service.add_address(this._address, SocketType.STREAM, SocketProtocol.TCP, null, null);
-
- this._service.incoming.connect(on_incoming_connection);
- this._service.start();
-
- (this._loop = new MainLoop(null, false)).run();
- }
-
- /**
- * Asynchronously dispatches incoming connection
- *
- * @param conn socket connection associated with request
- * @return true in all cases
- */
- protected bool on_incoming_connection(SocketConnection conn) {
- handle_request_async.begin(conn);
- return true;
- }
-
- /**
- * Decomposes incoming SCGI request and calls handle_request signal subscribers
- *
- * @param conn socket connection associated with request
- * @throws IOError if input stream is not available or request is invalid
- * GLib.IOError is thrown
- */
- protected async void handle_request_async(SocketConnection conn) throws IOError {
- Request req = new Request();
-
- DataInputStream input = new DataInputStream(conn.input_stream);
- DataOutputStream output = new DataOutputStream(conn.output_stream);
-
- StringBuilder builder = new StringBuilder();
- uchar temp;
-
- try {
- while ((temp = input.read_byte(null)) != ':') {
- builder.append_c((char)temp);
- }
-
- int length = int.parse(builder.str);
-
- if (length > 0) {
- uchar[] buffer = new uchar[length];
-
- // [TODO]: May need to async-optimize the following
-
- if (input.read(buffer) == length) {
- req.input = input;
- req.output = output;
-
- string key = null;
- var temp_builder = new StringBuilder();
-
- for (int i = 0; i < length; i++) {
- if (buffer[i] == '\0') {
- if (key == null) {
- key = temp_builder.str;
- } else {
- req.params.insert(key, temp_builder.str);
- key = null;
- }
-
- temp_builder.erase(0, -1);
- } else {
- temp_builder.append_c((char)buffer[i]);
- }
- }
- }
- }
-
- input.read_byte(null);
- } catch (Error e) {
- throw new IOError.FAILED(e.message);
- }
-
- this._handle_request_signal(req);
- }
- }
- }