PageRenderTime 56ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/scgi/Server.vala

http://libzee.googlecode.com/
Vala | 215 lines | 74 code | 36 blank | 105 comment | 12 complexity | 5358adacf682077e7138f0a70800d44e MD5 | raw file
  1. /**
  2. * Copyright (c) 2011 George 'kha0s' Zelenskiy <admin.kha0s@gmail.com>
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. * THE SOFTWARE.
  21. * --------------------------------------------------------------------------------
  22. * Part: Zee.SCGI.Server
  23. * Package: libzee-scgi
  24. *
  25. * Implements simple, threaded, asynchronous, signal-based SCGI server interface.
  26. *
  27. * To use it, create a Zee.SCGI.Server instance, providing working host, port
  28. * and maximum number of concurrent threads. Provide atleast one incoming
  29. * request handler by Zee.SCGI.Server.attach_request_handler() and start()
  30. * the server.
  31. *
  32. * On every new connection, after request body decomposition all request handlers
  33. * attached to handle_request signal will be called successively.
  34. *
  35. * Note that once attached request handler cannot be removed from handle_request
  36. * signal due to signal-delegate bug (?) in ValaC.
  37. */
  38. namespace Zee.SCGI {
  39. /**
  40. * Threaded asynchronous signal-based SCGI server
  41. */
  42. public class Server {
  43. /**** Variables ****/
  44. /**
  45. * Main loop to hold
  46. */
  47. protected MainLoop _loop;
  48. /**
  49. * Maximum numer of threads
  50. */
  51. protected int _max_threads;
  52. /**
  53. * Server's internet address
  54. */
  55. protected InetSocketAddress _address;
  56. /**
  57. * Server's socket service
  58. */
  59. protected ThreadedSocketService _service;
  60. /**** Signals ****/
  61. /**
  62. * Decomposed SCGI request handle signal
  63. */
  64. protected signal void _handle_request_signal(Request req);
  65. /**** Delegates ****/
  66. /**
  67. * Request handler's delegate
  68. */
  69. public delegate void RequestHandler(Request req);
  70. /**** Constructors ****/
  71. /**
  72. * Initializes a new instance of Zee.SCGI.Server
  73. *
  74. * @param address string representing a server's IP
  75. * @param port server's port to listen on
  76. * @param max_threads maximum number of threads server will operate
  77. */
  78. public Server(string address, uint16 port, int max_threads) {
  79. InetAddress addr = new InetAddress.from_string(address);
  80. this._address = new InetSocketAddress(addr, port);
  81. this._max_threads = max_threads;
  82. }
  83. /**** Properties ****/
  84. /**
  85. * Internet address current instance is configured to work on
  86. */
  87. public InetSocketAddress address {
  88. get { return this._address; }
  89. }
  90. /**
  91. * Maximum number of threads
  92. */
  93. public int max_threads {
  94. get { return this._max_threads; }
  95. }
  96. /**** Methods ****/
  97. /**
  98. * Attaches a new method/function satisfying Zee.SCGI.Server.RequestHandler
  99. * delegate to handle all incoming requests
  100. *
  101. * @param handler function to handle decomposed requests with
  102. */
  103. public void attach_request_handler(RequestHandler handler) {
  104. //this._handle_request_signal.connect(handler);
  105. this._handle_request_signal.connect((req) => handler(req));
  106. }
  107. /**
  108. * Starts SCGI server with current configuration
  109. *
  110. * @throws Error if specified internet address is not available or
  111. * cannot be configured GLib.Error will be thrown
  112. */
  113. public void start() throws Error {
  114. this._service = new ThreadedSocketService(this._max_threads);
  115. // [TODO]: Wrap the generic GLib error
  116. this._service.add_address(this._address, SocketType.STREAM, SocketProtocol.TCP, null, null);
  117. this._service.incoming.connect(on_incoming_connection);
  118. this._service.start();
  119. (this._loop = new MainLoop(null, false)).run();
  120. }
  121. /**
  122. * Asynchronously dispatches incoming connection
  123. *
  124. * @param conn socket connection associated with request
  125. * @return true in all cases
  126. */
  127. protected bool on_incoming_connection(SocketConnection conn) {
  128. handle_request_async.begin(conn);
  129. return true;
  130. }
  131. /**
  132. * Decomposes incoming SCGI request and calls handle_request signal subscribers
  133. *
  134. * @param conn socket connection associated with request
  135. * @throws IOError if input stream is not available or request is invalid
  136. * GLib.IOError is thrown
  137. */
  138. protected async void handle_request_async(SocketConnection conn) throws IOError {
  139. Request req = new Request();
  140. DataInputStream input = new DataInputStream(conn.input_stream);
  141. DataOutputStream output = new DataOutputStream(conn.output_stream);
  142. StringBuilder builder = new StringBuilder();
  143. uchar temp;
  144. try {
  145. while ((temp = input.read_byte(null)) != ':') {
  146. builder.append_c((char)temp);
  147. }
  148. int length = int.parse(builder.str);
  149. if (length > 0) {
  150. uchar[] buffer = new uchar[length];
  151. // [TODO]: May need to async-optimize the following
  152. if (input.read(buffer) == length) {
  153. req.input = input;
  154. req.output = output;
  155. string key = null;
  156. var temp_builder = new StringBuilder();
  157. for (int i = 0; i < length; i++) {
  158. if (buffer[i] == '\0') {
  159. if (key == null) {
  160. key = temp_builder.str;
  161. } else {
  162. req.params.insert(key, temp_builder.str);
  163. key = null;
  164. }
  165. temp_builder.erase(0, -1);
  166. } else {
  167. temp_builder.append_c((char)buffer[i]);
  168. }
  169. }
  170. }
  171. }
  172. input.read_byte(null);
  173. } catch (Error e) {
  174. throw new IOError.FAILED(e.message);
  175. }
  176. this._handle_request_signal(req);
  177. }
  178. }
  179. }