RUBY   22
socket for fd
Guest on 7th March 2023 01:32:34 PM


  1. require 'socket'
  2. require 'stringio'
  3.  
  4. class FCGI
  5.  
  6.   def FCGI.cgi?
  7.     begin
  8.       Socket.for_fd($stdin.fileno).getpeername
  9.       return false
  10.     rescue Errno::ENOTCONN
  11.       return false
  12.     rescue Errno::ENOTSOCK, Errno::EINVAL
  13.       return true
  14.     end
  15.   end
  16.  
  17.   def FCGI.each_request(&block)
  18.     Server.new(::Socket.for_fd($stdin.fileno)).each_request(&block)
  19.   end
  20.  
  21.   class << FCGI
  22.     alias each each_request
  23.     alias is_cgi? cgi?   # obsolete
  24.   end
  25.  
  26.   def FCGI.each_cgi_request(&block)
  27.     if cgi?
  28.       yield Request.new(FCGI_NULL_REQUEST_ID, ENV, $stdin, $stdout, $stderr)
  29.     else
  30.       each_request(&block)
  31.     end
  32.   end
  33.  
  34.  
  35.   ProtocolVersion = 1
  36.  
  37.   # Record types
  38.   FCGI_BEGIN_REQUEST = 1
  39.   FCGI_ABORT_REQUEST = 2
  40.   FCGI_END_REQUEST = 3
  41.   FCGI_PARAMS = 4
  42.   FCGI_STDIN = 5
  43.   FCGI_STDOUT = 6
  44.   FCGI_STDERR = 7
  45.   FCGI_DATA = 8
  46.   FCGI_GET_VALUES = 9
  47.   FCGI_GET_VALUES_RESULT = 10
  48.   FCGI_UNKNOWN_TYPE = 11
  49.   FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
  50.  
  51.   FCGI_NULL_REQUEST_ID = 0
  52.  
  53.   # FCGI_BEGIN_REQUSET.role
  54.   FCGI_RESPONDER = 1
  55.   FCGI_AUTHORIZER = 2
  56.   FCGI_FILTER = 3
  57.  
  58.   # FCGI_BEGIN_REQUEST.flags
  59.   FCGI_KEEP_CONN = 1
  60.  
  61.   # FCGI_END_REQUEST.protocolStatus
  62.   FCGI_REQUEST_COMPLETE = 0
  63.   FCGI_CANT_MPX_CONN = 1
  64.   FCGI_OVERLOADED = 2
  65.   FCGI_UNKNOWN_ROLE = 3
  66.  
  67.  
  68.   class Server
  69.  
  70.     def initialize(server)
  71.       @server = server
  72.       @default_parameters = {
  73.         "FCGI_MAX_CONNS" => 1,
  74.         "FCGI_MAX_REQS"  => 1,
  75.         "FCGI_MPX_CONNS" => true
  76.       }
  77.     end
  78.  
  79.     def each_request(&block)
  80.       while true
  81.         begin
  82.           sock, addr = *@server.accept
  83.           break unless sock
  84.           handle_socket FastCGISocket.new(sock), &block
  85.         ensure
  86.           sock.close if sock
  87.         end
  88.       end
  89.     end
  90.  
  91.     private
  92.  
  93.     def handle_socket(sock)
  94.       buffers = {}
  95.       while rec = sock.read_record
  96.         if rec.management_record?
  97.           case rec.type
  98.           when FCGI_GET_VALUES
  99.             sock.send_record handle_GET_VALUES(rec)
  100.           else
  101.             sock.send_record UnknownTypeRecord.new(rec.request_id, rec.type)
  102.           end
  103.         else
  104.           case rec.type
  105.           when FCGI_BEGIN_REQUEST
  106.             buffers[rec.request_id] = RecordBuffer.new(rec)
  107.           when FCGI_ABORT_REQUEST
  108.             raise "got ABORT_REQUEST"   # FIXME
  109.           else
  110.             buf = buffers[rec.request_id]   or next # inactive request
  111.             buf.push rec
  112.             if buf.ready?
  113.               buffers.delete rec.request_id
  114.               req = buf.new_request
  115.               yield req
  116.               respond_to req, sock, FCGI_REQUEST_COMPLETE
  117.               return
  118.             end
  119.           end
  120.         end
  121.       end
  122.     end
  123.  
  124.     def handle_GET_VALUES(rec)
  125.       h = {}
  126.       rec.values.each_key do |name|
  127.         h[name] = @default_parameters[name]
  128.       end
  129.       ValuesRecord.new(FCGI_GET_VALUES_RESULT, rec.request_id, h)
  130.     end
  131.  
  132.     def respond_to(req, sock, status)
  133.       split_stream(FCGI_STDOUT, req.id, req.out) do |rec|
  134.         sock.send_record rec
  135.       end
  136.       split_stream(FCGI_STDERR, req.id, req.err) do |rec|
  137.         sock.send_record rec
  138.       end if req.err.length > 0
  139.       sock.send_record EndRequestRecord.new(req.id, 0, status)
  140.     end
  141.  
  142.     DATA_UNIT = 16384
  143.  
  144.     def split_stream(type, id, f)
  145.       unless f.length == 0
  146.         f.rewind
  147.         while s = f.read(DATA_UNIT)
  148.           yield GenericDataRecord.new(type, id, s)
  149.         end
  150.       end
  151.       yield GenericDataRecord.new(type, id, '')
  152.     end
  153.  
  154.   end
  155.  
  156.  
  157.   class FastCGISocket
  158.     def initialize(sock)
  159.       @socket = sock
  160.     end
  161.  
  162.     def read_record
  163.       header = @socket.read(Record::HEADER_LENGTH) or return nil
  164.       return nil unless header.size == Record::HEADER_LENGTH
  165.       version, type, reqid, clen, padlen, reserved = *Record.parse_header(header)
  166.       Record.class_for(type).parse(reqid, read_record_body(clen, padlen))
  167.     end
  168.  
  169.     def read_record_body(clen, padlen)
  170.       buf = ''
  171.       while buf.length < clen
  172.         buf << @socket.read([1024, clen - buf.length].min)
  173.       end
  174.       @socket.read padlen if padlen
  175.       buf
  176.     end
  177.     private :read_record_body
  178.  
  179.     def send_record(rec)
  180.       @socket.write rec.serialize
  181.       @socket.flush
  182.     end
  183.   end
  184.  
  185.  
  186.   class RecordBuffer
  187.     def initialize(rec)
  188.       @begin_request = rec
  189.       @envs = []
  190.       @stdins = []
  191.       @datas = []
  192.     end
  193.  
  194.     def push(rec)
  195.       case rec
  196.       when ParamsRecord
  197.         @envs.push rec
  198.       when StdinDataRecord
  199.         @stdins.push rec
  200.       when DataRecord
  201.         @datas.push rec
  202.       else
  203.         raise "got unknown record: #{rec.class}"
  204.       end
  205.     end
  206.  
  207.     def ready?
  208.       case @begin_request.role
  209.       when FCGI_RESPONDER
  210.         completed?(@envs) and
  211.         completed?(@stdins)
  212.       when FCGI_AUTHORIZER
  213.         completed?(@envs)
  214.       when FCGI_FILTER
  215.         completed?(@envs) and
  216.         completed?(@stdins) and
  217.         completed?(@datas)
  218.       else
  219.         raise "unknown role: #{@begin_request.role}"
  220.       end
  221.     end
  222.  
  223.     def completed?(records)
  224.       records.last and records.last.empty?
  225.     end
  226.     private :completed?
  227.  
  228.     def new_request
  229.       Request.new(@begin_request.request_id, env(), stdin(), nil, nil, data())
  230.     end
  231.  
  232.     def env
  233.       h = {}
  234.       @envs.each {|rec| h.update rec.values }
  235.       h
  236.     end
  237.  
  238.     def stdin
  239.       StringIO.new(@stdins.inject('') {|buf, rec| buf << rec.flagment })
  240.     end
  241.  
  242.     def data
  243.       StringIO.new(@datas.inject('') {|buf, rec| buf << rec.flagment })
  244.     end
  245.   end
  246.  
  247.  
  248.   class Request
  249.     def initialize(id, env, stdin, stdout = nil, stderr = nil, data = nil)
  250.       @id = id
  251.       @env = env
  252.       @in = stdin
  253.       @out = stdout || StringIO.new
  254.       @err = stderr || StringIO.new
  255.       @data = data || StringIO.new
  256.     end
  257.  
  258.     attr_reader :id
  259.     attr_reader :env
  260.     attr_reader :in
  261.     attr_reader :out
  262.     attr_reader :err
  263.     attr_reader :data
  264.  
  265.     def finish   # for backword compatibility
  266.     end
  267.   end
  268.  
  269.  
  270.   class Record
  271.     # uint8_t  protocol_version;
  272.     # uint8_t  record_type;
  273.     # uint16_t request_id;     (big endian)
  274.     # uint16_t content_length; (big endian)
  275.     # uint8_t  padding_length;
  276.     # uint8_t  reserved;
  277.     HEADER_FORMAT = 'CCnnCC'
  278.     HEADER_LENGTH = 8
  279.  
  280.     def Record.parse_header(buf)
  281.       return *buf.unpack(HEADER_FORMAT)
  282.     end
  283.  
  284.     def Record.class_for(type)
  285.       RECORD_CLASS[type]
  286.     end
  287.  
  288.     def initialize(type, reqid)
  289.       @type = type
  290.       @request_id = reqid
  291.     end
  292.  
  293.     def version
  294.       ::FCGI::ProtocolVersion
  295.     end
  296.  
  297.     attr_reader :type
  298.     attr_reader :request_id
  299.  
  300.     def management_record?
  301.       @request_id == FCGI_NULL_REQUEST_ID
  302.     end
  303.  
  304.     def serialize
  305.       body = make_body()
  306.       padlen = body.length % 8
  307.       header = make_header(body.length, padlen)
  308.       header + body + "\000" * padlen
  309.     end
  310.  
  311.     private
  312.  
  313.     def make_header(clen, padlen)
  314.       [version(), @type, @request_id, clen, padlen, 0].pack(HEADER_FORMAT)
  315.     end
  316.   end
  317.  
  318.   class BeginRequestRecord < Record
  319.     # uint16_t role; (big endian)
  320.     # uint8_t  flags;
  321.     # uint8_t  reserved[5];
  322.     BODY_FORMAT = 'nCC5'
  323.  
  324.     def BeginRequestRecord.parse(id, body)
  325.       role, flags, *reserved = *body.unpack(BODY_FORMAT)
  326.       new(id, role, flags)
  327.     end
  328.  
  329.     def initialize(id, role, flags)
  330.       super FCGI_BEGIN_REQUEST, id
  331.       @role = role
  332.       @flags = flags
  333.     end
  334.  
  335.     attr_reader :role
  336.     attr_reader :flags
  337.   end
  338.  
  339.   class AbortRequestRecord < Record
  340.     def AbortRequestRecord.parse(id, body)
  341.       new(id)
  342.     end
  343.  
  344.     def initialize(id)
  345.       super FCGI_ABORT_REQUEST, id
  346.     end
  347.   end
  348.  
  349.   class EndRequestRecord < Record
  350.     # uint32_t appStatus; (big endian)
  351.     # uint8_t  protocolStatus;
  352.     # uint8_t  reserved[3];
  353.     BODY_FORMAT = 'NCC3'
  354.  
  355.     def EndRequestRecord.parse(id, body)
  356.       appstatus, protostatus, *reserved = *body.unpack(BODY_FORMAT)
  357.       new(id, appstatus, protostatus)
  358.     end
  359.  
  360.     def initialize(id, appstatus, protostatus)
  361.       super FCGI_END_REQUEST, id
  362.       @application_status = appstatus
  363.       @protocol_status = protostatus
  364.     end
  365.  
  366.     attr_reader :application_status
  367.     attr_reader :protocol_status
  368.  
  369.     private
  370.  
  371.     def make_body
  372.       [@application_status, @protocol_status, 0, 0, 0].pack(BODY_FORMAT)
  373.     end
  374.   end
  375.  
  376.   class UnknownTypeRecord < Record
  377.     # uint8_t type;
  378.     # uint8_t reserved[7];
  379.     BODY_FORMAT = 'CC7'
  380.  
  381.     def UnknownTypeRecord.parse(id, body)
  382.       type, *reserved = *body.unpack(BODY_FORMAT)
  383.       new(id, type)
  384.     end
  385.  
  386.     def initialize(id, t)
  387.       super FCGI_UNKNOWN_TYPE, id
  388.       @unknown_type = t
  389.     end
  390.  
  391.     attr_reader :unknown_type
  392.  
  393.     private
  394.  
  395.     def make_body
  396.       [@unknown_type, 0, 0, 0, 0, 0, 0, 0].pack(BODY_FORMAT)
  397.     end
  398.   end
  399.  
  400.   class ValuesRecord < Record
  401.     def ValuesRecord.parse(id, body)
  402.       new(id, parse_values(body))
  403.     end
  404.  
  405.     class << self
  406.       private
  407.  
  408.       def parse_values(buf)
  409.         result = {}
  410.         until buf.empty?
  411.           name, value = *read_pair(buf)
  412.           result[name] = value
  413.         end
  414.         result
  415.       end
  416.  
  417.       def read_pair(buf)
  418.         nlen = read_length(buf)
  419.         vlen = read_length(buf)
  420.         return buf.slice!(0, nlen), buf.slice!(0, vlen)
  421.       end
  422.  
  423.       def read_length(buf)
  424.         if buf[0] >> 7 == 0
  425.         then buf.slice!(0,1)[0]
  426.         else buf.slice!(0,4).unpack('N')[0] & ((1<<31) - 1)
  427.         end
  428.       end
  429.     end
  430.  
  431.     def initialize(type, id, values)
  432.       super type, id
  433.       @values = values
  434.     end
  435.  
  436.     attr_reader :values
  437.  
  438.     private
  439.  
  440.     def make_body
  441.       buf = ''
  442.       @values.each do |name, value|
  443.         buf << serialize_length(name.length)
  444.         buf << serialize_length(value.length)
  445.         buf << name
  446.         buf << value
  447.       end
  448.       buf
  449.     end
  450.  
  451.     def serialize_length(len)
  452.       if len < 0x80
  453.       then len.chr
  454.       else [len | (1<<31)].pack('N')
  455.       end
  456.     end
  457.   end
  458.  
  459.   class GetValuesRecord < ValuesRecord
  460.     def initialize(id, values)
  461.       super FCGI_GET_VALUES, id, values
  462.     end
  463.   end
  464.  
  465.   class ParamsRecord < ValuesRecord
  466.     def initialize(id, values)
  467.       super FCGI_PARAMS, id, values
  468.     end
  469.  
  470.     def empty?
  471.       @values.empty?
  472.     end
  473.   end
  474.  
  475.   class GenericDataRecord < Record
  476.     def GenericDataRecord.parse(id, body)
  477.       new(id, body)
  478.     end
  479.  
  480.     def initialize(type, id, flagment)
  481.       super type, id
  482.       @flagment = flagment
  483.     end
  484.  
  485.     attr_reader :flagment
  486.  
  487.     def empty?
  488.       @flagment.empty?
  489.     end
  490.  
  491.     private
  492.  
  493.     def make_body
  494.       @flagment
  495.     end
  496.   end
  497.  
  498.   class StdinDataRecord < GenericDataRecord
  499.     def initialize(id, flagment)
  500.       super FCGI_STDIN, id, flagment
  501.     end
  502.   end
  503.  
  504.   class DataRecord < GenericDataRecord
  505.     def initialize(id, flagment)
  506.       super FCGI_DATA, id, flagment
  507.     end
  508.   end
  509.  
  510.   class Record   # redefine
  511.     RECORD_CLASS = {
  512.       FCGI_GET_VALUES    => GetValuesRecord,
  513.  
  514.       FCGI_BEGIN_REQUEST => BeginRequestRecord,
  515.       FCGI_ABORT_REQUEST => AbortRequestRecord,
  516.       FCGI_PARAMS        => ParamsRecord,
  517.       FCGI_STDIN         => StdinDataRecord,
  518.       FCGI_DATA          => DataRecord
  519.     }
  520.   end
  521.  
  522. end

Raw Paste

Login or Register to edit or fork this paste. It's free.