[DRE-commits] [ruby-fcgi] 01/02: Imported Upstream version 0.9.2.1

Jonas Genannt jonas at brachium-system.net
Sun Dec 22 11:46:42 UTC 2013


This is an automated email from the git hooks/post-receive script.

hggh-guest pushed a commit to branch master
in repository ruby-fcgi.

commit 292badfc8f6a9e4ea4cd9c339df2f77aa5f3bb3b
Author: Jonas Genannt <jonas at brachium-system.net>
Date:   Sat Dec 21 17:03:43 2013 +0100

    Imported Upstream version 0.9.2.1
---
 LICENSE             |  21 ++
 README.rdoc         | 108 +++++++++
 README.signals      |  76 +++++++
 VERSION             |   1 +
 checksums.yaml.gz   | Bin 0 -> 268 bytes
 ext/fcgi/MANIFEST   |   3 +
 ext/fcgi/Makefile   |   1 +
 ext/fcgi/extconf.rb |   7 +
 ext/fcgi/fcgi.c     | 615 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fcgi.gemspec        |  34 +++
 lib/fcgi.rb         | 640 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 metadata.yml        |  65 ++++++
 test/helper.rb      |   9 +
 test/test_fcgi.rb   |   7 +
 14 files changed, 1587 insertions(+)

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0a5ac5a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+Copyright (c) 2009 saks
+Copyright (c) 2013 mva
+
+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.
diff --git a/README.rdoc b/README.rdoc
new file mode 100644
index 0000000..832b915
--- /dev/null
+++ b/README.rdoc
@@ -0,0 +1,108 @@
+= fcgi - The New generation of FastCGI library for Ruby.
+
+Version 0.9.2.1
+
+== Depends
+
+=== C version
+  * ((<libfcgi|URL:http://www.fastcgi.com/#TheDevKit>))(FastCGI Developer's Kit)
+
+=== Pure Ruby Version
+  * StringIO
+
+== Install
+
+  $ gem install fcgi
+
+== Usage
+=== Class Method
+--- FCGI.accept
+    Returns FCGI instance
+--- FCGI.each
+
+--- FCGI.each_request
+
+--- FCGI.is_cgi?
+
+--- FCGI.each_cgi
+    Automatically detects whether this program is running under the FastCGI
+    environment, and generates a 'CGI' type object for each request. Also
+    installs signal handlers for graceful handling of SIGPIPE (which may
+    occur if a client gives up on a request before it is complete) and
+    SIGUSR1 (generated by Apache for a 'graceful' exit)
+
+    If you are using the HTML output methods you can also pass the HTML type
+    e.g. FCGI.each_cgi('html3') do ... end
+
+    However, you should beware that the CGI library is quite slow when
+    used in this way, as it dynamically adds a large number of methods
+    to itself each time a new instance is created.
+
+=== Instance Method
+--- FCGI#finish
+    Finish
+
+--- FCGI#in
+    Returns Stream or StringIO
+
+--- FCGI#out
+    Returns Stream or StringIO
+
+--- FCGI#err
+    Returns Stream or StringIO
+
+--- FCGI#env
+    Returns Environment(Hash)
+
+== Sample
+
+Using the FastCGI native interface:
+
+  #!/usr/bin/ruby
+  require "fcgi"
+
+  FCGI.each {|request|
+    out = request.out
+    out.print "Content-Type: text/plain\r\n"
+    out.print "\r\n"
+    out.print Time.now.to_s
+    request.finish
+  }
+
+Using the CGI-compatible interface, which works both as a standalone CGI
+and under FastCGI with no modifications:
+
+  #!/usr/bin/ruby
+  require "fcgi"
+
+  FCGI.each_cgi {|cgi|
+    name = cgi['name'][0]
+    puts cgi.header
+    puts "You are #{name} " if name
+    puts "Connecting from #{cgi.remote_addr}"
+  }
+
+Note: you can't reference CGI environment variables using ENV when under
+FastCGI. It is recommended that you use the CGI-generated methods, e.g.
+cgi.remote_addr as above.
+
+If you need to access environment variables directly, perhaps extra ones set
+in your Apache config, then use cgi.env_table['REMOTE_ADDR'] instead. This
+isn't quite as portable because env_table is a private method in the
+standard CGI library.
+
+== License
+
+* ((<URL:http://www.ruby-lang.org/ja/LICENSE.txt>)) (Japanese)
+* ((<URL:http://www.ruby-lang.org/en/LICENSE.txt>)) (English)
+
+== Copyright
+
+  fcgi.c     0.1   Copyright (C) 1998-1999  Network Applied Communication Laboratory, Inc.
+             0.8   Copyright (C) 2002 MoonWolf <moonwolf at moonwolf.com>
+             0.9   Copyright (C) 2013 mva <mva at mva.name> Alpha, LLC
+
+  fastcgi.rb 0.7   Copyright (C) 2001 Eli Green
+  fcgi.rb    0.8   Copyright (C) 2002 MoonWolf <moonwolf at moonwolf.com>
+  fcgi.rb    0.8.5 Copyright (C) 2004 Minero Aoki
+
diff --git a/README.signals b/README.signals
new file mode 100644
index 0000000..60914ea
--- /dev/null
+++ b/README.signals
@@ -0,0 +1,76 @@
+= Handling of SIGUSR1
+
+When you request a 'graceful' restart of Apache, mod_fastcgi sends a SIGUSR1
+to each of the fastcgi worker processes. The intention is that each one
+should finish the current request, and then exit, at which point Apache will
+restart it. Of course, if the worker isn't doing anything, it should die
+immediately.
+
+This is implemented in the fcgi C library as follows:
+
+- a signal handler is installed for SIGUSR1, which just sets a flag
+  (shutdownPending) and returns
+- fcgi sits inside an accept() call waiting for a new request
+- if this accept() call terminates with EINTR, and this flag is set, then
+  it returns with no request
+
+Unfortunately, Ruby defeats this mechanism in at least two ways:
+
+1. Ruby installs its own signal handlers for a host of common signals,
+including USR1. The fcgi library will not install its own handler if it
+detects that a handler has already been set (i.e. the current handler is not
+SIG_DFL)
+
+2. When Ruby installs its own signal handlers, it does so with SA_RESTART
+set. This means that the accept() call does not terminate with EINTR; it is
+restarted automatically by the OS.
+
+When a signal comes in during the accept(), Ruby's own handler does nothing
+except store it in a queue to be processed later. It is only when the
+accept() call completes, i.e. when a genuine new request comes in, that Ruby
+takes action. Unfortunately it's too late by then, and if that
+already-accepted request is not honoured, a 500 Internal Error will be
+returned to the client.
+
+The simplest solution to this would be to remove Ruby's SIGUSR1 handler
+before initialising the FastCGI library.
+
+However, a cleaner solution is to call rb_thread_select before going into
+FastCGI's accept loop. If a signal happens during the select, it can be
+handled using Ruby's normal mechanisms. This also gives a very useful
+side-benefit, which is that FCGI::accept no longer blocks out other Ruby
+threads. The program below demonstrates this problem; its background logging
+thread is supposed to write a message every 10 seconds, but under older
+versions of ruby-fcgi it does not do so if it is waiting for a new request.
+
+  #!/usr/local/bin/ruby
+  require "fcgi"
+
+  Thread.new do
+    f = File.new("/tmp/fcgi.log","a")
+    f.sync=true
+    while true
+      f.puts "#{Time.now.to_s} pid #{$$}"
+      sleep 10
+    end
+  end
+
+  FCGI.each_cgi {|cgi|
+    name = cgi['name'][0]
+    puts cgi.header
+    puts "Hey! You are #{name} " if name
+    puts "Connecting from #{cgi.remote_addr}"
+  }
+
+Having protected the accept() with a ruby select(), you can then handle
+signals as follows:
+
+- call FCGI::accept  (it will raise an exception if USR1 is called)
+- install a USR1 handler
+- process the request
+- remove the USR1 handler
+
+The USR1 handler should set a flag to note if a USR1 signal came in while
+the request was being processed; you terminate the loop if it was set. The
+overall effect is that USR1 will cause the process to terminate, but without
+causing a half-completed request.
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..69667e5
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.9.2.1
diff --git a/checksums.yaml.gz b/checksums.yaml.gz
new file mode 100644
index 0000000..4ab3986
Binary files /dev/null and b/checksums.yaml.gz differ
diff --git a/ext/fcgi/MANIFEST b/ext/fcgi/MANIFEST
new file mode 100644
index 0000000..117ec96
--- /dev/null
+++ b/ext/fcgi/MANIFEST
@@ -0,0 +1,3 @@
+MANIFEST
+extconf.rb
+fcgi.c
diff --git a/ext/fcgi/Makefile b/ext/fcgi/Makefile
new file mode 100644
index 0000000..8a0a24b
--- /dev/null
+++ b/ext/fcgi/Makefile
@@ -0,0 +1 @@
+# Use ruby extconf.rb to generate makefile
\ No newline at end of file
diff --git a/ext/fcgi/extconf.rb b/ext/fcgi/extconf.rb
new file mode 100755
index 0000000..a181939
--- /dev/null
+++ b/ext/fcgi/extconf.rb
@@ -0,0 +1,7 @@
+require "mkmf"
+
+dir_config("fcgi")
+
+if (have_header("fcgiapp.h") || have_header("fastcgi/fcgiapp.h")) && have_library("fcgi", "FCGX_Accept")
+  create_makefile("fcgi")
+end
diff --git a/ext/fcgi/fcgi.c b/ext/fcgi/fcgi.c
new file mode 100755
index 0000000..e8e700b
--- /dev/null
+++ b/ext/fcgi/fcgi.c
@@ -0,0 +1,615 @@
+/*
+ * fcgi.c
+ * Copyright (C) 1998-1999  Network Applied Communication Laboratory, Inc.
+ * Copyright (C) 2002-2006  MoonWolf <moonwolf at moonwolf.com>
+ * Copyright (C) 2012-2014  mva <mva at mva.name>
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "ruby.h"
+
+#ifndef RSTRING_PTR
+#define RSTRING_PTR(str) (RSTRING(str)->ptr)
+#endif
+#ifndef RSTRING_LEN
+#define RSTRING_LEN(str) (RSTRING(str)->len)
+#endif
+
+#ifdef HAVE_FASTCGI_FCGIAPP_H
+#include <fastcgi/fcgiapp.h>
+#else
+#include "fcgiapp.h"
+#endif
+
+
+static VALUE cFCGI;
+static VALUE eFCGIError;
+static VALUE cFCGIStream;
+static VALUE eFCGIStreamError;
+static VALUE eFCGIStreamUnsupportedVersionError;
+static VALUE eFCGIStreamProtocolError;
+static VALUE eFCGIStreamParamsError;
+static VALUE eFCGIStreamCallSeqError;
+
+typedef struct fcgi_stream_data {
+  VALUE req;
+  FCGX_Stream *stream;
+} fcgi_stream_data;
+
+typedef struct fcgi_data {
+  FCGX_Request *req;
+  VALUE in;
+  VALUE out;
+  VALUE err;
+  VALUE env;
+} fcgi_data;
+
+static void fcgi_stream_mark(fcgi_stream_data *stream_data)
+{
+  rb_gc_mark(stream_data->req);
+}
+
+static void fcgi_stream_free(fcgi_stream_data *stream_data)
+{
+  free(stream_data);
+}
+
+static void fcgi_mark(fcgi_data *data)
+{
+  rb_gc_mark(data->in);
+  rb_gc_mark(data->out);
+  rb_gc_mark(data->err);
+  rb_gc_mark(data->env);
+}
+
+static void fcgi_free_req(fcgi_data *data)
+{
+  FCGX_Free(data->req, 1);
+  free(data->req);
+  free(data);
+}
+
+static VALUE fcgi_s_accept(VALUE self)
+{
+  int status;
+  FCGX_Request *req;
+  fd_set readfds;
+
+  req = ALLOC(FCGX_Request);
+
+  status = FCGX_InitRequest(req,0,0);
+  if (status != 0) {
+    rb_raise(eFCGIError, "FCGX_Init() failed");
+    return Qnil;
+  }
+
+  FD_ZERO(&readfds);
+  FD_SET(req->listen_sock, &readfds);
+  if (select(req->listen_sock+1, &readfds, NULL, NULL, NULL) < 1) {
+    return Qnil;
+  }
+
+  status = FCGX_Accept_r(req);
+  if (status >= 0) {
+    fcgi_data *data;
+    fcgi_stream_data *stream_data;
+    char      **env;
+    VALUE     obj,key, value;
+    char      *pkey,*pvalue;
+    int       flags, fd;
+
+    /* Unset NONBLOCKING */
+    fd = ((FCGX_Request*) req)->ipcFd;
+    flags = fcntl(fd, F_GETFL);
+
+    if (flags & O_NONBLOCK) {
+       fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+    }
+
+    obj = Data_Make_Struct(self, fcgi_data, fcgi_mark, fcgi_free_req, data);
+    data->req = req;
+    data->in = Data_Make_Struct(cFCGIStream, fcgi_stream_data, fcgi_stream_mark, fcgi_stream_free, stream_data);
+    stream_data->stream = req->in;
+    stream_data->req = obj;
+    data->out = Data_Make_Struct(cFCGIStream, fcgi_stream_data, fcgi_stream_mark, fcgi_stream_free, stream_data);
+    stream_data->stream = req->out;
+    stream_data->req = obj;
+    data->err = Data_Make_Struct(cFCGIStream, fcgi_stream_data, fcgi_stream_mark, fcgi_stream_free, stream_data);
+    stream_data->stream = req->err;
+    stream_data->req = obj;
+    data->env = rb_hash_new();
+    env = req->envp;
+    for (; *env; env++) {
+      int size = 0;
+      pkey = *env;
+      pvalue = pkey;
+      while( *(pvalue++) != '=') size++;
+      key   = rb_str_new(pkey, size);
+      value = rb_str_new2(pvalue);
+      OBJ_TAINT(key);
+      OBJ_TAINT(value);
+      rb_hash_aset(data->env, key, value);
+    }
+
+    return obj;
+  } else {
+    FCGX_Free(req, 1);
+    free(req);
+    return Qnil;
+  }
+}
+
+static VALUE fcgi_s_each(VALUE self)
+{
+  VALUE fcgi;
+
+  while ((fcgi = fcgi_s_accept(self)) != Qnil) {
+    rb_yield(fcgi);
+  }
+  return Qnil;
+}
+
+static VALUE fcgi_s_iscgi(VALUE self)
+{
+  if (FCGX_IsCGI()) {
+    return Qtrue;
+  } else {
+    return Qfalse;
+  }
+}
+
+static VALUE fcgi_in(VALUE self)
+{
+  fcgi_data *data;
+
+  Data_Get_Struct(self, fcgi_data, data);
+  return data->in;
+}
+
+static VALUE fcgi_out(VALUE self)
+{
+  fcgi_data *data;
+
+  Data_Get_Struct(self, fcgi_data, data);
+  return data->out;
+}
+
+static VALUE fcgi_err(VALUE self)
+{
+  fcgi_data *data;
+
+  Data_Get_Struct(self, fcgi_data, data);
+  return data->err;
+}
+
+static VALUE fcgi_env(VALUE self)
+{
+  fcgi_data *data;
+
+  Data_Get_Struct(self, fcgi_data, data);
+  return data->env;
+}
+
+static VALUE fcgi_finish(VALUE self)
+{
+  fcgi_data *data;
+  fcgi_stream_data *stream_data;
+
+  Data_Get_Struct(self, fcgi_data, data);
+
+  if (Qnil != data->in) {
+    Data_Get_Struct(data->in, fcgi_stream_data, stream_data);
+    stream_data->req = Qnil; stream_data->stream = NULL;
+  }
+  if (Qnil != data->out) {
+    Data_Get_Struct(data->out, fcgi_stream_data, stream_data);
+    stream_data->req = Qnil; stream_data->stream = NULL;
+  }
+  if (Qnil != data->err) {
+    Data_Get_Struct(data->err, fcgi_stream_data, stream_data);
+    stream_data->req = Qnil; stream_data->stream = NULL;
+  }
+
+  data->in = data->out = data->err = Qnil;
+  FCGX_Finish_r(data->req);
+
+  return Qtrue;
+}
+
+#define CHECK_STREAM_ERROR(stream) {\
+  int err = FCGX_GetError(stream);\
+	extern int errno; \
+  if (err) {\
+    if (err > 0) {\
+      rb_raise(eFCGIStreamError, "unknown error (syscall error)");\
+    }\
+    else {\
+      switch (err) {\
+      case FCGX_UNSUPPORTED_VERSION:\
+        rb_raise(eFCGIStreamUnsupportedVersionError, "unsupported version");\
+        break;\
+      case FCGX_PROTOCOL_ERROR:\
+        rb_raise(eFCGIStreamProtocolError, "protocol error");\
+        break;\
+      case FCGX_PARAMS_ERROR:\
+        rb_raise(eFCGIStreamProtocolError, "parameter error");\
+        break;\
+      case FCGX_CALL_SEQ_ERROR:\
+        rb_raise(eFCGIStreamCallSeqError, "preconditions are not met");\
+        break;\
+      default:\
+        rb_raise(eFCGIStreamError, "unknown error");\
+        break;\
+      }\
+    }\
+  }\
+}
+
+#define Data_Get_Stream(value, stream) do {\
+    fcgi_stream_data* _fsd;\
+    Data_Get_Struct(value, fcgi_stream_data, _fsd);\
+    if (NULL == (stream = _fsd->stream))\
+      rb_raise(eFCGIStreamError, "stream invalid as fastcgi request is already finished");\
+  } while (0)
+
+static VALUE fcgi_stream_putc(VALUE self, VALUE ch)
+{
+  FCGX_Stream *stream;
+  int c;
+
+  rb_secure(4);
+  Data_Get_Stream(self, stream);
+  if ((c = FCGX_PutChar(NUM2INT(ch), stream)) == EOF)
+    CHECK_STREAM_ERROR(stream);
+  return INT2NUM(c);
+}
+
+static VALUE fcgi_stream_write(VALUE self, VALUE str)
+{
+  FCGX_Stream *stream;
+  int len;
+
+  rb_secure(4);
+  Data_Get_Stream(self, stream);
+  str = rb_obj_as_string(str);
+  len = FCGX_PutStr(RSTRING_PTR(str), RSTRING_LEN(str), stream);
+  if (len == EOF) CHECK_STREAM_ERROR(stream);
+  return INT2NUM(len);
+}
+
+static VALUE fcgi_stream_print(int argc, VALUE *argv, VALUE out)
+{
+  int i;
+  VALUE line;
+
+  /* if no argument given, print `$_' */
+  if (argc == 0) {
+    argc = 1;
+    line = rb_lastline_get();
+    argv = &line;
+  }
+  for (i=0; i<argc; i++) {
+    if (!NIL_P(rb_output_fs) && i>0) {
+      fcgi_stream_write(out, rb_output_fs);
+    }
+    switch (TYPE(argv[i])) {
+    case T_NIL:
+      fcgi_stream_write(out, rb_str_new2("nil"));
+      break;
+    default:
+      fcgi_stream_write(out, argv[i]);
+      break;
+    }
+  }
+  if (!NIL_P(rb_output_rs)) {
+    fcgi_stream_write(out, rb_output_rs);
+  }
+
+  return Qnil;
+}
+
+static VALUE fcgi_stream_printf(int argc, VALUE *argv, VALUE out)
+{
+  fcgi_stream_write(out, rb_f_sprintf(argc, argv));
+  return Qnil;
+}
+
+static VALUE fcgi_stream_puts _((int, VALUE*, VALUE));
+
+static VALUE fcgi_stream_puts_ary(VALUE ary, VALUE out, int recur)
+{
+  VALUE tmp;
+  int i;
+
+  if (recur) {
+    tmp = rb_str_new2("[...]");
+    fcgi_stream_puts(1, &tmp, out);
+    return Qnil;
+  }
+
+  for (i=0; i<RARRAY_LEN(ary); i++) {
+    tmp = RARRAY_PTR(ary)[i];
+    fcgi_stream_puts(1, &tmp, out);
+  }
+  return Qnil;
+}
+
+static VALUE fcgi_stream_puts(int argc, VALUE *argv, VALUE out)
+{
+  int i;
+  VALUE line;
+
+  /* if no argument given, print newline. */
+  if (argc == 0) {
+    fcgi_stream_write(out, rb_default_rs);
+    return Qnil;
+  }
+  for (i=0; i<argc; i++) {
+    switch (TYPE(argv[i])) {
+    case T_NIL:
+      line = rb_str_new2("nil");
+      break;
+    case T_ARRAY:
+      rb_exec_recursive(fcgi_stream_puts_ary, argv[i], out);
+      continue;
+    default:
+      line = argv[i];
+      break;
+    }
+    line = rb_obj_as_string(line);
+    fcgi_stream_write(out, line);
+    if (RSTRING_PTR(line)[RSTRING_LEN(line)-1] != '\n') {
+      fcgi_stream_write(out, rb_default_rs);
+    }
+  }
+
+  return Qnil;
+}
+
+static VALUE fcgi_stream_addstr(VALUE out, VALUE str)
+{
+  fcgi_stream_write(out, str);
+  return out;
+}
+
+static VALUE fcgi_stream_flush(VALUE self)
+{
+  FCGX_Stream *stream;
+
+  Data_Get_Stream(self, stream);
+  if (FCGX_FFlush(stream) == EOF)
+    CHECK_STREAM_ERROR(stream);
+  return Qnil;
+}
+
+static VALUE fcgi_stream_getc(VALUE self)
+{
+  FCGX_Stream *stream;
+  int c;
+
+  Data_Get_Stream(self, stream);
+  if ((c = FCGX_GetChar(stream)) == EOF) {
+    CHECK_STREAM_ERROR(stream);
+    return Qnil;
+  }
+  else {
+    return INT2NUM(c);
+  }
+}
+
+static VALUE fcgi_stream_ungetc(VALUE self, VALUE ch)
+{
+  FCGX_Stream *stream;
+  int c;
+
+  if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
+    rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
+  }
+  Data_Get_Stream(self, stream);
+  c = FCGX_UnGetChar(NUM2INT(ch), stream);
+  CHECK_STREAM_ERROR(stream);
+  return INT2NUM(c);
+}
+
+static VALUE fcgi_stream_gets(VALUE self) {
+  FCGX_Stream *stream;
+  char buff[BUFSIZ];
+  VALUE str = rb_str_new(0,0);
+  OBJ_TAINT(str);
+
+  if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
+    rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
+  }
+
+  Data_Get_Stream(self, stream);
+
+  for (;;) {
+    if (FCGX_GetLine(buff, BUFSIZ, stream) == NULL) {
+      CHECK_STREAM_ERROR(stream);
+      break;
+    }
+    rb_str_cat(str, buff, strlen(buff));
+    if (strchr(buff, '\n')) break;
+  }
+  if (RSTRING_LEN(str) > 0)
+    return str;
+  else
+    return Qnil;
+}
+
+static VALUE fcgi_stream_read(int argc, VALUE *argv, VALUE self)
+{
+  VALUE num,str;
+  FCGX_Stream *stream;
+  char *buff;
+  int n;
+
+  if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
+    rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
+  }
+
+  Data_Get_Stream(self, stream);
+
+  if (argc==0) {
+    buff = ALLOC_N(char, 16384);
+    n = FCGX_GetStr(buff, 16384, stream);
+    CHECK_STREAM_ERROR(stream);
+    if (n == 0) {
+      free(buff);
+      return  Qnil;
+    }
+    str = rb_str_new(buff, n);
+    OBJ_TAINT(str);
+
+    while(!FCGX_HasSeenEOF(stream)) {
+      n = FCGX_GetStr(buff, 16384, stream);
+      CHECK_STREAM_ERROR(stream);
+      if (n > 0) {
+        rb_str_cat(str, buff, n);
+      } else {
+        free(buff);
+        return Qnil;
+      }
+    }
+    free(buff);
+    return str;
+  }
+
+  num = argv[0];
+  n = NUM2INT(num);
+
+  buff = ALLOC_N(char, n);
+  n = FCGX_GetStr(buff, n, stream);
+  CHECK_STREAM_ERROR(stream);
+  if (n > 0) {
+    str = rb_str_new(buff, n);
+    OBJ_TAINT(str);
+    free(buff);
+    return str;
+  }
+  else {
+    free(buff);
+    return Qnil;
+  }
+}
+
+static VALUE fcgi_stream_eof(VALUE self)
+{
+  FCGX_Stream *stream;
+
+  if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
+    rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
+  }
+  Data_Get_Stream(self, stream);
+  return FCGX_HasSeenEOF(stream) ? Qtrue : Qfalse;
+}
+
+static VALUE fcgi_stream_close(VALUE self)
+{
+  FCGX_Stream *stream;
+
+  if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
+    rb_raise(rb_eSecurityError, "Insecure: can't close");
+  }
+  Data_Get_Stream(self, stream);
+  if (FCGX_FClose(stream) == EOF)
+    CHECK_STREAM_ERROR(stream);
+  return Qnil;
+}
+
+static VALUE fcgi_stream_closed(VALUE self)
+{
+  FCGX_Stream *stream;
+
+  Data_Get_Stream(self, stream);
+  return stream->isClosed ? Qtrue : Qfalse;
+}
+
+static VALUE fcgi_stream_binmode(VALUE self)
+{
+  if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
+    rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
+  }
+  return self;
+}
+
+static VALUE fcgi_stream_isatty(VALUE self)
+{
+  if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
+    rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
+  }
+  return Qfalse;
+}
+
+static VALUE fcgi_stream_sync(VALUE self)
+{
+  if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
+    rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
+  }
+  return Qfalse;
+}
+
+static VALUE fcgi_stream_setsync(VALUE self,VALUE sync)
+{
+  if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
+    rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
+  }
+  return Qfalse;
+}
+
+
+
+void Init_fcgi() {
+
+  FCGX_Init();
+
+  cFCGI = rb_define_class("FCGI", rb_cObject);
+  eFCGIError =rb_define_class_under(cFCGI, "Error", rb_eStandardError);
+  rb_define_singleton_method(cFCGI, "accept", fcgi_s_accept, 0);
+  rb_define_singleton_method(cFCGI, "each", fcgi_s_each, 0);
+  rb_define_singleton_method(cFCGI, "each_request", fcgi_s_each, 0);
+  rb_define_singleton_method(cFCGI, "is_cgi?", fcgi_s_iscgi, 0);
+  rb_define_method(cFCGI, "in",  fcgi_in, 0);
+  rb_define_method(cFCGI, "out", fcgi_out, 0);
+  rb_define_method(cFCGI, "err", fcgi_err, 0);
+  rb_define_method(cFCGI, "env", fcgi_env, 0);
+  rb_define_method(cFCGI, "finish", fcgi_finish, 0);
+
+  cFCGIStream = rb_define_class_under(cFCGI, "Stream", rb_cObject);
+  eFCGIStreamError =rb_define_class_under(cFCGIStream, "Error", rb_eStandardError);
+  eFCGIStreamUnsupportedVersionError =
+    rb_define_class_under(cFCGIStream, "UnsupportedVersionError",
+                          eFCGIStreamError);
+  eFCGIStreamProtocolError = rb_define_class_under(cFCGIStream, "ProtocolError",
+                                                   eFCGIStreamError);
+  eFCGIStreamParamsError = rb_define_class_under(cFCGIStream, "ParamsError",
+                                                 eFCGIStreamError);
+  eFCGIStreamCallSeqError = rb_define_class_under(cFCGIStream, "CallSeqError",
+                                                  eFCGIStreamError);
+  rb_undef_method(CLASS_OF(cFCGIStream), "new");
+  rb_define_method(cFCGIStream, "putc", fcgi_stream_putc, 1);
+  rb_define_method(cFCGIStream, "write", fcgi_stream_write, 1);
+  rb_define_method(cFCGIStream, "print", fcgi_stream_print, -1);
+  rb_define_method(cFCGIStream, "printf", fcgi_stream_printf, -1);
+  rb_define_method(cFCGIStream, "puts", fcgi_stream_puts, -1);
+  rb_define_method(cFCGIStream, "<<", fcgi_stream_addstr, 1);
+  rb_define_method(cFCGIStream, "flush", fcgi_stream_flush, 0);
+  rb_define_method(cFCGIStream, "getc", fcgi_stream_getc, 0);
+  rb_define_method(cFCGIStream, "ungetc", fcgi_stream_ungetc, 1);
+  rb_define_method(cFCGIStream, "gets", fcgi_stream_gets, 0);
+  rb_define_method(cFCGIStream, "read", fcgi_stream_read, -1);
+  rb_define_method(cFCGIStream, "eof", fcgi_stream_eof, 0);
+  rb_define_method(cFCGIStream, "eof?", fcgi_stream_eof, 0);
+  rb_define_method(cFCGIStream, "close", fcgi_stream_close, 0);
+  rb_define_method(cFCGIStream, "closed?", fcgi_stream_closed, 0);
+  rb_define_method(cFCGIStream, "binmode", fcgi_stream_binmode, 0);
+  rb_define_method(cFCGIStream, "isatty", fcgi_stream_isatty, 0);
+  rb_define_method(cFCGIStream, "tty?", fcgi_stream_isatty, 0);
+  rb_define_method(cFCGIStream, "sync", fcgi_stream_sync, 0);
+  rb_define_method(cFCGIStream, "sync=", fcgi_stream_setsync, 1);
+}
diff --git a/fcgi.gemspec b/fcgi.gemspec
new file mode 100644
index 0000000..994b6f6
--- /dev/null
+++ b/fcgi.gemspec
@@ -0,0 +1,34 @@
+Gem::Specification.new do |s|
+  s.name = %q{fcgi}
+  s.version = "0.9.2.1"
+  s.license = "MIT"
+
+  s.authors = [%q{mva}]
+  s.date = %q{2013-09-30}
+  s.description = %q{FastCGI is a language independent, scalable, open extension to CGI that provides high performance without the limitations of server specific APIs. This version aims to be compatible with both 1.8.x and 1.9.x versions of Ruby, and also will be ported to 2.0.x.}
+  s.email = %q{mva at mva.name}
+  s.extensions = [%q{ext/fcgi/extconf.rb}]
+  s.extra_rdoc_files = [
+    "LICENSE",
+    "README.rdoc",
+    "README.signals"
+  ]
+  s.rdoc_options = ["--charset=UTF-8"]
+  s.files = [
+    "VERSION",
+    "ext/fcgi/MANIFEST",
+    "ext/fcgi/Makefile",
+    "ext/fcgi/extconf.rb",
+    "ext/fcgi/fcgi.c",
+    "lib/fcgi.rb",
+    "fcgi.gemspec"
+  ]
+  s.test_files = [
+    "test/helper.rb",
+    "test/test_fcgi.rb"
+  ]
+  s.homepage = %q{http://github.com/alphallc/ruby-fcgi-ng}
+  s.require_paths = [%q{lib}]
+  s.summary = %q{FastCGI library for Ruby.}
+end
+
diff --git a/lib/fcgi.rb b/lib/fcgi.rb
new file mode 100644
index 0000000..106fcb3
--- /dev/null
+++ b/lib/fcgi.rb
@@ -0,0 +1,640 @@
+=begin
+
+fcgi.rb 0.9.2 - fcgi.so compatible pure-ruby FastCGI library
+
+fastcgi.rb Copyright (C) 2001 Eli Green
+fcgi.rb    Copyright (C) 2002-2003 MoonWolf <moonwolf at moonwolf.com>
+fcgi.rb    Copyright (C) 2004 Minero Aoki
+fcgi.rb    Copyright (C) 2011 saks and Julik Tarkhanov
+fcgi.rb    Copyright (C) 2012-2013 mva
+
+=end
+trap('SIGTERM') { exit }
+trap('SIGPIPE','IGNORE')
+
+begin
+  raise LoadError if ENV["USE_PURE_RUBY_FCGI"]
+  require "fcgi.so"
+rescue LoadError # Load the pure ruby version instead
+  # At this point we do have STDERR so put it to some good use
+  $stderr.puts "Your FCGI gem does not contain the FCGI shared library, running pure ruby instead"
+
+  require 'socket'
+  require 'stringio'
+
+  class FCGI
+
+    def self.is_cgi?
+      begin
+        s = Socket.for_fd($stdin.fileno)
+        s.getpeername
+        false
+      rescue Errno::ENOTCONN
+        false
+      rescue Errno::ENOTSOCK, Errno::EINVAL
+        true
+      end
+    end
+
+    def self.each(&block)
+      f = default_connection()
+      Server.new(f).each_request(&block)
+    ensure
+      f.close if f
+    end
+
+    def self.each_request(&block)
+      f = default_connection()
+      Server.new(f).each_request(&block)
+    ensure
+      f.close if f
+    end
+
+    def self.default_connection
+      ::Socket.for_fd($stdin.fileno)
+    end
+
+
+
+    ProtocolVersion = 1
+
+    # Record types
+    FCGI_BEGIN_REQUEST = 1
+    FCGI_ABORT_REQUEST = 2
+    FCGI_END_REQUEST = 3
+    FCGI_PARAMS = 4
+    FCGI_STDIN = 5
+    FCGI_STDOUT = 6
+    FCGI_STDERR = 7
+    FCGI_DATA = 8
+    FCGI_GET_VALUES = 9
+    FCGI_GET_VALUES_RESULT = 10
+    FCGI_UNKNOWN_TYPE = 11
+    FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
+
+    FCGI_NULL_REQUEST_ID = 0
+
+    # FCGI_BEGIN_REQUSET.role
+    FCGI_RESPONDER = 1
+    FCGI_AUTHORIZER = 2
+    FCGI_FILTER = 3
+
+    # FCGI_BEGIN_REQUEST.flags
+    FCGI_KEEP_CONN = 1
+
+    # FCGI_END_REQUEST.protocolStatus
+    FCGI_REQUEST_COMPLETE = 0
+    FCGI_CANT_MPX_CONN = 1
+    FCGI_OVERLOADED = 2
+    FCGI_UNKNOWN_ROLE = 3
+
+
+    class Server
+
+      def initialize(server)
+        @server = server
+        @buffers = {}
+        @default_parameters = {
+          "FCGI_MAX_CONNS" => 1,
+          "FCGI_MAX_REQS"  => 1,
+          "FCGI_MPX_CONNS" => true
+        }
+      end
+
+      def each_request(&block)
+        graceful = false
+        trap("SIGUSR1") { graceful = true }
+        while true
+          begin
+            session(&block)
+          rescue Errno::EPIPE, EOFError
+            # HTTP request is canceled by the remote user
+          end
+          exit 0 if graceful
+        end
+      end
+
+      def session
+        sock, addr = *@server.accept
+        return unless sock
+        fsock = FastCGISocket.new(sock)
+        req = next_request(fsock)
+        yield req
+        respond_to req, fsock, FCGI_REQUEST_COMPLETE
+      ensure
+        sock.close if sock and not sock.closed?
+      end
+
+      private
+
+      def next_request(sock)
+        while rec = sock.read_record
+          if rec.management_record?
+            case rec.type
+            when FCGI_GET_VALUES
+              sock.send_record handle_GET_VALUES(rec)
+            else
+              sock.send_record UnknownTypeRecord.new(rec.request_id, rec.type)
+            end
+          else
+            case rec.type
+            when FCGI_BEGIN_REQUEST
+              @buffers[rec.request_id] = RecordBuffer.new(rec)
+            when FCGI_ABORT_REQUEST
+              raise "got ABORT_REQUEST"   # FIXME
+            else
+              buf = @buffers[rec.request_id]   or next # inactive request
+              buf.push rec
+              if buf.ready?
+                @buffers.delete rec.request_id
+                return buf.new_request
+              end
+            end
+          end
+        end
+        raise "must not happen: FCGI socket unexpected EOF"
+      end
+
+      def handle_GET_VALUES(rec)
+        h = {}
+        rec.values.each_key do |name|
+          h[name] = @default_parameters[name]
+        end
+        ValuesRecord.new(FCGI_GET_VALUES_RESULT, rec.request_id, h)
+      end
+
+      def respond_to(req, sock, status)
+        split_data(FCGI_STDOUT, req.id, req.out) do |rec|
+          sock.send_record rec
+        end
+        split_data(FCGI_STDERR, req.id, req.err) do |rec|
+          sock.send_record rec
+        end if req.err.length > 0
+        sock.send_record EndRequestRecord.new(req.id, 0, status)
+      end
+
+      DATA_UNIT = 16384
+
+      def split_data(type, id, f)
+        unless f.length == 0
+          f.rewind
+          while s = f.read(DATA_UNIT)
+            yield GenericDataRecord.new(type, id, s)
+          end
+        end
+        yield GenericDataRecord.new(type, id, '')
+      end
+
+    end
+
+
+    class FastCGISocket
+      def initialize(sock)
+        @socket = sock
+      end
+
+      def read_record
+        header = @socket.read(Record::HEADER_LENGTH) or return nil
+        return nil unless header.size == Record::HEADER_LENGTH
+        version, type, reqid, clen, padlen, reserved = *Record.parse_header(header)
+        Record.class_for(type).parse(reqid, read_record_body(clen, padlen))
+      end
+
+      def read_record_body(clen, padlen)
+        buf = ''
+        while buf.length < clen
+          buf << @socket.read([1024, clen - buf.length].min)
+        end
+        @socket.read padlen if padlen
+        buf
+      end
+      private :read_record_body
+
+      def send_record(rec)
+        @socket.write rec.serialize
+        @socket.flush
+      end
+    end
+
+
+    class RecordBuffer
+      def initialize(rec)
+        @begin_request = rec
+        @envs = []
+        @stdins = []
+        @datas = []
+      end
+
+      def push(rec)
+        case rec
+        when ParamsRecord
+          @envs.push rec
+        when StdinDataRecord
+          @stdins.push rec
+        when DataRecord
+          @datas.push rec
+        else
+          raise "got unknown record: #{rec.class}"
+        end
+      end
+
+      def ready?
+        case @begin_request.role
+        when FCGI_RESPONDER
+          completed?(@envs) and
+          completed?(@stdins)
+        when FCGI_AUTHORIZER
+          completed?(@envs)
+        when FCGI_FILTER
+          completed?(@envs) and
+          completed?(@stdins) and
+          completed?(@datas)
+        else
+          raise "unknown role: #{@begin_request.role}"
+        end
+      end
+
+      def completed?(records)
+        records.last and records.last.empty?
+      end
+      private :completed?
+
+      def new_request
+        Request.new(@begin_request.request_id, env(), stdin(), nil, nil, data())
+      end
+
+      def env
+        h = {}
+        @envs.each {|rec| h.update rec.values }
+        h
+      end
+
+      def stdin
+        StringIO.new(@stdins.inject('') {|buf, rec| buf << rec.flagment })
+      end
+
+      def data
+        StringIO.new(@datas.inject('') {|buf, rec| buf << rec.flagment })
+      end
+    end
+
+
+    class Request
+      def initialize(id, env, stdin, stdout = nil, stderr = nil, data = nil)
+        @id = id
+        @env = env
+        @in = stdin
+        @out = stdout || StringIO.new
+        @err = stderr || StringIO.new
+        @data = data || StringIO.new
+      end
+
+      attr_reader :id
+      attr_reader :env
+      attr_reader :in
+      attr_reader :out
+      attr_reader :err
+      attr_reader :data
+
+      def finish   # for backword compatibility
+      end
+    end
+
+
+    class Record
+      # uint8_t  protocol_version;
+      # uint8_t  record_type;
+      # uint16_t request_id;     (big endian)
+      # uint16_t content_length; (big endian)
+      # uint8_t  padding_length;
+      # uint8_t  reserved;
+      HEADER_FORMAT = 'CCnnCC'
+      HEADER_LENGTH = 8
+
+      def self.parse_header(buf)
+        return *buf.unpack(HEADER_FORMAT)
+      end
+
+      def self.class_for(type)
+        RECORD_CLASS[type]
+      end
+
+      def initialize(type, reqid)
+        @type = type
+        @request_id = reqid
+      end
+
+      def version
+        ::FCGI::ProtocolVersion
+      end
+
+      attr_reader :type
+      attr_reader :request_id
+
+      def management_record?
+        @request_id == FCGI_NULL_REQUEST_ID
+      end
+
+      def serialize
+        body = make_body()
+        padlen = body.length % 8
+        header = make_header(body.length, padlen)
+        header + body + "\000" * padlen
+      end
+
+      private
+
+      def make_header(clen, padlen)
+        [version(), @type, @request_id, clen, padlen, 0].pack(HEADER_FORMAT)
+      end
+    end
+
+    class BeginRequestRecord < Record
+      # uint16_t role; (big endian)
+      # uint8_t  flags;
+      # uint8_t  reserved[5];
+      BODY_FORMAT = 'nCC5'
+
+      def BeginRequestRecord.parse(id, body)
+        role, flags, *reserved = *body.unpack(BODY_FORMAT)
+        new(id, role, flags)
+      end
+
+      def initialize(id, role, flags)
+        super FCGI_BEGIN_REQUEST, id
+        @role = role
+        @flags = flags
+      end
+
+      attr_reader :role
+      attr_reader :flags
+
+      def make_body
+        [@role, @flags, 0, 0, 0, 0, 0].pack(BODY_FORMAT)
+      end
+    end
+
+    class AbortRequestRecord < Record
+      def AbortRequestRecord.parse(id, body)
+        new(id)
+      end
+
+      def initialize(id)
+        super FCGI_ABORT_REQUEST, id
+      end
+    end
+
+    class EndRequestRecord < Record
+      # uint32_t appStatus; (big endian)
+      # uint8_t  protocolStatus;
+      # uint8_t  reserved[3];
+      BODY_FORMAT = 'NCC3'
+
+      def self.parse(id, body)
+        appstatus, protostatus, *reserved = *body.unpack(BODY_FORMAT)
+        new(id, appstatus, protostatus)
+      end
+
+      def initialize(id, appstatus, protostatus)
+        super FCGI_END_REQUEST, id
+        @application_status = appstatus
+        @protocol_status = protostatus
+      end
+
+      attr_reader :application_status
+      attr_reader :protocol_status
+
+      private
+
+      def make_body
+        [@application_status, @protocol_status, 0, 0, 0].pack(BODY_FORMAT)
+      end
+    end
+
+    class UnknownTypeRecord < Record
+      # uint8_t type;
+      # uint8_t reserved[7];
+      BODY_FORMAT = 'CC7'
+
+      def self.parse(id, body)
+        type, *reserved = *body.unpack(BODY_FORMAT)
+        new(id, type)
+      end
+
+      def initialize(id, t)
+        super FCGI_UNKNOWN_TYPE, id
+        @unknown_type = t
+      end
+
+      attr_reader :unknown_type
+
+      private
+
+      def make_body
+        [@unknown_type, 0, 0, 0, 0, 0, 0, 0].pack(BODY_FORMAT)
+      end
+    end
+
+    class ValuesRecord < Record
+      def self.parse(id, body)
+        new(id, parse_values(body))
+      end
+
+      def self.parse_values(buf)
+        result = {}
+        until buf.empty?
+          name, value = *read_pair(buf)
+          result[name] = value
+        end
+        result
+      end
+
+      def self.read_pair(buf)
+        nlen = read_length(buf)
+        vlen = read_length(buf)
+        [buf.slice!(0, nlen), buf.slice!(0, vlen)]
+      end
+
+
+      if "".respond_to?(:bytes) # Ruby 1.9 string semantics
+        def self.read_length(buf)
+          if buf[0].bytes.first >> 7 == 0
+            buf.slice!(0,1)[0].bytes.first
+          else
+            buf.slice!(0,4).unpack('N')[0] & ((1<<31) - 1)
+          end
+        end
+      else # Ruby 1.8 string
+        def self.read_length(buf)
+          if buf[0] >> 7 == 0
+            buf.slice!(0,1)[0].bytes.first
+          else
+            buf.slice!(0,4).unpack('N')[0] & ((1<<31) - 1)
+          end
+        end
+      end
+
+      def initialize(type, id, values)
+        super type, id
+        @values = values
+      end
+
+      attr_reader :values
+
+      private
+
+      def make_body
+        buf = ''
+        @values.each do |name, value|
+          buf << serialize_length(name.length)
+          buf << serialize_length(value.length)
+          buf << name
+          buf << value
+        end
+        buf
+      end
+
+      def serialize_length(len)
+        if len < 0x80
+        then len.chr
+        else [len | (1<<31)].pack('N')
+        end
+      end
+    end
+
+    class GetValuesRecord < ValuesRecord
+      def initialize(id, values)
+        super FCGI_GET_VALUES, id, values
+      end
+    end
+
+    class ParamsRecord < ValuesRecord
+      def initialize(id, values)
+        super FCGI_PARAMS, id, values
+      end
+
+      def empty?
+        @values.empty?
+      end
+    end
+
+    class GenericDataRecord < Record
+      def self.parse(id, body)
+        new(id, body)
+      end
+
+      def initialize(type, id, flagment)
+        super type, id
+        @flagment = flagment
+      end
+
+      attr_reader :flagment
+
+      def empty?
+        @flagment.empty?
+      end
+
+      private
+
+      def make_body
+        if @flagment.respond_to? 'force_encoding' then
+          return @flagment.dup.force_encoding('BINARY')
+        else
+          return @flagment
+        end
+      end
+    end
+
+    class StdinDataRecord < GenericDataRecord
+      def initialize(id, flagment)
+        super FCGI_STDIN, id, flagment
+      end
+    end
+
+    class StdoutDataRecord < GenericDataRecord
+      def initialize(id, flagment)
+        super FCGI_STDOUT, id, flagment
+      end
+    end
+
+    class DataRecord < GenericDataRecord
+      def initialize(id, flagment)
+        super FCGI_DATA, id, flagment
+      end
+    end
+
+    class Record   # redefine
+      RECORD_CLASS = {
+        FCGI_GET_VALUES    => GetValuesRecord,
+
+        FCGI_BEGIN_REQUEST => BeginRequestRecord,
+        FCGI_ABORT_REQUEST => AbortRequestRecord,
+        FCGI_PARAMS        => ParamsRecord,
+        FCGI_STDIN         => StdinDataRecord,
+        FCGI_DATA          => DataRecord,
+        FCGI_STDOUT        => StdoutDataRecord,
+        FCGI_END_REQUEST   => EndRequestRecord
+      }
+    end
+
+  end # FCGI class
+end # begin
+
+# There is no C version of 'each_cgi'
+# Note: for ruby-1.6.8 at least, the constants CGI_PARAMS/CGI_COOKIES
+# are defined within module 'CGI', even if you have subclassed it
+
+class FCGI
+  def self.each_cgi(*args)
+    require 'cgi'
+
+    eval(<<-EOS,TOPLEVEL_BINDING)
+    class CGI
+      public :env_table
+      def self.remove_params
+        if (const_defined?(:CGI_PARAMS))
+          remove_const(:CGI_PARAMS)
+          remove_const(:CGI_COOKIES)
+        end
+      end
+    end # ::CGI class
+
+    class FCGI
+      class CGI < ::CGI
+        def initialize(request, *args)
+          ::CGI.remove_params
+          @request = request
+          super(*args)
+          @args = *args
+        end
+        def args
+          @args
+        end
+        def env_table
+          @request.env
+        end
+        def stdinput
+          @request.in
+        end
+        def stdoutput
+          @request.out
+        end
+      end # FCGI::CGI class
+    end # FCGI class
+    EOS
+
+    if FCGI::is_cgi?
+      yield ::CGI.new(*args)
+    else
+      exit_requested = false
+      FCGI::each do |request|
+
+        $stdout, $stderr = request.out, request.err
+
+        yield CGI.new(request, *args)
+
+        request.finish
+      end
+    end
+  end
+end
diff --git a/metadata.yml b/metadata.yml
new file mode 100644
index 0000000..8c81294
--- /dev/null
+++ b/metadata.yml
@@ -0,0 +1,65 @@
+--- !ruby/object:Gem::Specification
+name: fcgi
+version: !ruby/object:Gem::Version
+  version: 0.9.2.1
+platform: ruby
+authors:
+- mva
+autorequire: 
+bindir: bin
+cert_chain: []
+date: 2013-09-30 00:00:00.000000000 Z
+dependencies: []
+description: FastCGI is a language independent, scalable, open extension to CGI that
+  provides high performance without the limitations of server specific APIs. This
+  version aims to be compatible with both 1.8.x and 1.9.x versions of Ruby, and also
+  will be ported to 2.0.x.
+email: mva at mva.name
+executables: []
+extensions:
+- ext/fcgi/extconf.rb
+extra_rdoc_files:
+- LICENSE
+- README.rdoc
+- README.signals
+files:
+- VERSION
+- ext/fcgi/MANIFEST
+- ext/fcgi/Makefile
+- ext/fcgi/extconf.rb
+- ext/fcgi/fcgi.c
+- lib/fcgi.rb
+- fcgi.gemspec
+- LICENSE
+- README.rdoc
+- README.signals
+- test/helper.rb
+- test/test_fcgi.rb
+homepage: http://github.com/alphallc/ruby-fcgi-ng
+licenses:
+- MIT
+metadata: {}
+post_install_message: 
+rdoc_options:
+- --charset=UTF-8
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+  requirements:
+  - - '>='
+    - !ruby/object:Gem::Version
+      version: '0'
+required_rubygems_version: !ruby/object:Gem::Requirement
+  requirements:
+  - - '>='
+    - !ruby/object:Gem::Version
+      version: '0'
+requirements: []
+rubyforge_project: 
+rubygems_version: 2.0.3
+signing_key: 
+specification_version: 4
+summary: FastCGI library for Ruby.
+test_files:
+- test/helper.rb
+- test/test_fcgi.rb
diff --git a/test/helper.rb b/test/helper.rb
new file mode 100644
index 0000000..541ed2c
--- /dev/null
+++ b/test/helper.rb
@@ -0,0 +1,9 @@
+require 'rubygems'
+require 'test/unit'
+
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
+require 'fcgi'
+
+class Test::Unit::TestCase
+end
diff --git a/test/test_fcgi.rb b/test/test_fcgi.rb
new file mode 100644
index 0000000..7d577ea
--- /dev/null
+++ b/test/test_fcgi.rb
@@ -0,0 +1,7 @@
+require 'helper'
+
+class TestFcgi < Test::Unit::TestCase
+  def test_something_for_real
+    flunk "hey buddy, you should probably rename this file and start testing for real"
+  end
+end

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/ruby-fcgi.git



More information about the Pkg-ruby-extras-commits mailing list