Good progress on FCGI. I'm able to reach the STDIN state. master
authorEbersold <aebersol@n3150.home>
Fri, 22 Jul 2022 19:44:51 +0000 (21:44 +0200)
committerEbersold <aebersol@n3150.home>
Fri, 22 Jul 2022 19:44:51 +0000 (21:44 +0200)
aeb/fcgi/fcgi.h
aeb/fcgi/fcgi_endian.h [new file with mode: 0644]
aeb/fcgi/fcgi_handler.h
aeb/fcgi/fcgi_request.h [new file with mode: 0644]
aeb/net/basic_socket_acceptor.h
tests/fcgi_helloworld.cpp

index 54adef679c3b0f35e92dd76099cda9d99a515f93..f5a01aad2ff4ec2d44ca9a4351e9c41214ed55e2 100644 (file)
@@ -1,6 +1,11 @@
 #ifndef __FCGI_H__
 #define __FCGI_H__
 
+#include <aeb/fcgi/fcgi_endian.h>
+
+#include <aeb/fcgi/fcgi_request.h>
+#include <aeb/fcgi/fcgi_handler.h>
+
 namespace aeb {
 namespace fcgi {
 
@@ -9,22 +14,22 @@ namespace fcgi {
  *
  */
 template <typename Descriptor,typename InternetProtocol,typename RequestHandler>
-class acceptor : public basic_socket_acceptor<Descriptor,InternetProtocol>
+class acceptor : public aeb::net::basic_socket_acceptor<Descriptor,InternetProtocol>
 {
   public:
     typedef Descriptor socket_type;
-    acceptor(const ip::basic_endpoint<InternetProtocol> &e) :
-           basic_socket_acceptor<Descriptor,InternetProtcol>(e)
+    acceptor(const aeb::net::ip::basic_endpoint<InternetProtocol> &e) :
+         aeb::net::basic_socket_acceptor<Descriptor,InternetProtocol>(e)
    {
       int val = 1;
-      m_socket.set_option(SO_REUSEADDR,val);
+      aeb::net::basic_socket_acceptor<Descriptor,InternetProtocol>::m_socket.set_option(SO_REUSEADDR,val);
    };
     // Acceptor main entry
     void on_accept()
     {
-      endpoint_type tmp;
+      typename InternetProtocol::endpoint_type tmp;
       int len = tmp.size();
-      detail::socket_type s;
+      socket_type s;
       aeb::sys::error ec;
       
       s = aeb::net::accept<socklen_t>(this->m_descriptor,tmp.data(),&len,ec);
@@ -32,12 +37,12 @@ class acceptor : public basic_socket_acceptor<Descriptor,InternetProtocol>
       
       if (s != aeb::net::detail::invalid_socket)
       {
-          m_r = RequestHandler::ptr(new RequestHandler(s)) ;
+          m_r = typename RequestHandler::ptr(new RequestHandler(s)) ;
           m_r->add_ref(); //because request_handler will kill himself 
       }   
     };
   protected:
-    RequestHandler::ptr m_r;    
+    typename RequestHandler::ptr m_r;
 };
 
 
diff --git a/aeb/fcgi/fcgi_endian.h b/aeb/fcgi/fcgi_endian.h
new file mode 100644 (file)
index 0000000..6af0793
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef __FCGI_ENDIAN_H__
+#define __FCGI_ENDIAN_H__
+
+namespace aeb {
+namespace fcgi {
+
+template <typename T>
+class endianBase
+{
+  protected:
+    static T swap(const T &_val);
+};
+
+template <>
+uint16_t endianBase<uint16_t>::swap(const uint16_t &_val)
+{
+  return ( (_val & 0xFF00) >> 8) | ( (_val & 0x00FF) <<8 );
+}
+
+template <>
+uint32_t endianBase<uint32_t>::swap(const uint32_t &_val)
+{
+  return ( (_val & 0xFF000000) >> 24) 
+         | ( (_val & 0x00FF0000) >>  8 )
+         | ( (_val & 0x0000FF00) <<  8)
+         | ( (_val & 0x000000FF) << 24 );
+}
+
+/**
+ * @brief
+ */
+template <typename T>
+class bigEndian : public endianBase<T>
+{
+  protected:
+    T m_Data;
+
+  protected:
+    static T doSwap(const T &_b) { return endianBase<T>::swap(_b); };
+  public:
+    bigEndian() = default;
+
+    // Build from platform
+    bigEndian(const T& _val) : m_Data(doSwap(_val)) {}
+
+    // Store out
+    operator T() const { return doSwap(m_Data); }
+
+    friend std::ostream &operator <<(std::ostream &out,const bigEndian b) {out<<T(b) ; return out;}
+};
+
+}; // End fcgi
+}; // End aeb
+
+#endif
index 79b14564ad0ffbf73a6de9f98048e0cfe523c522..21fece69407fe5fc2d81a70b136e681f2e12c7d1 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __FCGI_H__
-#define __FCGI_H__
+#ifndef __FCGI_HANDLER_H__
+#define __FCGI_HANDLER_H__
 
 namespace aeb {
 namespace fcgi {
@@ -13,49 +13,152 @@ namespace fcgi {
  * on top of which we will have the Request handler. :w
  *
  */
-
-class handler : public event_handler<detail::socket_type,ip::tcp>
+template <typename Descriptor,typename InternetProtocol>
+class handler : 
+    public aeb::net::event_handler<Descriptor
+                                ,InternetProtocol >
 {
   private:
       int m_ref;
+
+      const int BUFFER_SIZE = 4096;
   public: 
     
-    typedef basic_socket_stream<ip::tcp>        Stream;
+    typedef aeb::net::basic_socket_stream<InternetProtocol>        Stream;
     /// aeb::ssl::stream<ip::tcp> ? or detail::socket_type
-    typedef basic_socket_stream<ip::tcp>        SSLStream;
+    typedef aeb::net::basic_socket_stream<InternetProtocol>        SSLStream;
     
-    typedef aeb::intrusive_ptr<ws_handler>      ptr;
+    typedef aeb::intrusive_ptr<handler>      ptr;
+    typedef aeb::net::event_handler<Descriptor
+                                ,InternetProtocol > Parent;
+    typedef aeb::net::io_dispatcher<Descriptor,InternetProtocol> io_service;
 
+    typedef aeb::fcgi::request<Descriptor,InternetProtocol> Request;
     inline void add_ref() {m_ref++;};
     
     inline void release() {
       if (--m_ref == 0) delete this;
     };
-    bool available () const;   
+    bool available () const;
  
   public:
-    handler(detail::socket_type s);
+    handler(aeb::net::detail::socket_type s) : Parent(s) ,m_socket(s)
+    {
+      std::cout<<"handler::handler ok create fcgi handler with socket ="<<s<<std::endl;
+      io_service *io = aeb::singleton<io_service>::get_instance();
+      
+      io->register_handler(0,this);
+    };
   
-    virtual ~handler() ;
+    virtual ~handler() 
+    {
+      std::cout<<"handler::~handler"<<std::endl;
+      io_service *io = aeb::singleton<io_service>::get_instance();
+      io->unregister_handler(0,this);
+      m_socket.close();
+    }
+    ;
     
-    virtual void onClose(unsigned short _code) ;
+    virtual void onClose(unsigned short _code) 
+    { std::cout<<"handler::onClose "<<_code<<std::endl; } ;
 
-    void on_write() ;
+    void on_write()
+    { std::cout<<"handler::on_write "<<std::endl; } ;
     
-    void on_read() ;
-    
-    void on_accept();
+    void on_read()
+    {
+        aeb::fcgi::Header _header;
+        int res=m_socket.receive( reinterpret_cast<char *>(&_header)
+                                , sizeof(aeb::fcgi::Header));
+        if ( res== 0)
+        {
+          io_service *io = aeb::singleton<io_service>::get_instance();
+          io->unregister_handler(0,this);
+          release();
+        } else if (res == sizeof(aeb::fcgi::Header))
+        {
+          process_header(_header,_header.m_Length) ;
+        } else {
+          std::cout<<"on_read ("<<res<<") not a Header"<<std::endl;
+        }
+    }
+    ;
+    // Not sure this is called
+    void on_accept()
+    { std::cout<<"handler::on_accept "<<std::endl; } ;
 
     void io_register();
     
     void io_unregister();
     // reuse handler with a new socket
     void reuse(aeb::net::detail::socket_type s);
-  
+  private:
+    //
+    void process_header(aeb::fcgi::Header &_h,uint16_t length)
+    {
+      char s[BUFFER_SIZE];
+      memset(s,0x00,BUFFER_SIZE);
+      std::cout<<"p_header Type:"<<(int)_h.m_Type<<" Id:"<<_h.m_Id<<" length :"<<length<<std::endl;
+      switch (_h.m_Type)
+      {
+        case aeb::fcgi::RecordType::BEGIN_REQ :
+        {
+          aeb::fcgi::BeginRequest _req;
+          int res=m_socket.receive( reinterpret_cast<char *>(&_req)
+                                  , length>sizeof(_req)?sizeof(_req):length);
+          std::cout<<"process_header begin("<<res<<") Role: "<<_req.m_Role<<std::endl;
+        }
+        break;
+        case aeb::fcgi::RecordType::ABORT_REQ  :
+        break;
+        case aeb::fcgi::RecordType::END_REQ    :
+        break;
+        case aeb::fcgi::RecordType::PARAMS     :
+        {
+          Request _req(_h);
+          if (length >0)
+            _req.process_parameters(m_socket,length);
+          int res=m_socket.receive(s,_h.m_PaddingLength);
+        }
+        break;
+        case aeb::fcgi::RecordType::IN         :
+        {
+          int res=m_socket.receive(s,length>BUFFER_SIZE?BUFFER_SIZE:length);
+          std::cout<<"process_header IN ("<<res<<"):"<<s<<std::endl;
+        }
+        break;
+        case aeb::fcgi::RecordType::OUT        :
+        break;
+        case aeb::fcgi::RecordType::ERR        :
+        {
+          int res=m_socket.receive(s,length>BUFFER_SIZE?BUFFER_SIZE:length);
+          std::cout<<"process_header ERR ("<<res<<"):"<<s<<std::endl;
+        }
+        break;
+        case aeb::fcgi::RecordType::DATA       :
+        {
+          int res=m_socket.receive(s,length>BUFFER_SIZE?BUFFER_SIZE:length);
+          std::cout<<"process_header data("<<res<<"):"<<s<<std::endl;
+        }
+        break;
+        case aeb::fcgi::RecordType::GET_VALUES :
+        break;
+        case aeb::fcgi::RecordType::GET_RESULT :
+        break;
+        default:
+        {
+          int res=m_socket.receive(s,length>BUFFER_SIZE?BUFFER_SIZE:length);
+          std::cout<<"process_header Unkown ("<<res<<","<<(int)_h.m_Type<<"):"<<s<<std::endl;
+        }
+        break;
+
+      };
+    }
   protected:
-    handler(const ws_handler &p) ;
+    handler(const handler &p) ;
     // Should own an endpoint 
-    long                                m_max_frame_size;
+    long   m_max_frame_size;
+    Stream m_socket;
 };
 
 /**
@@ -63,7 +166,8 @@ vim:et:sw=2:ts=2
  */
 
 
-#endif /*WS_HANDLER_H*/
 }
 }
 
+#endif /*WS_HANDLER_H*/
+
diff --git a/aeb/fcgi/fcgi_request.h b/aeb/fcgi/fcgi_request.h
new file mode 100644 (file)
index 0000000..caf8794
--- /dev/null
@@ -0,0 +1,200 @@
+#ifndef __FCGI_REQUEST_H__
+#define __FCGI_REQUEST_H__
+
+namespace aeb {
+namespace fcgi {
+
+  //! Fastcgi Id
+  typedef uint16_t Id;
+  
+  enum class RecordType : uint8_t
+  {
+    BEGIN_REQ  = 1
+   ,ABORT_REQ  = 2
+   ,END_REQ    = 3
+   ,PARAMS     = 4
+   ,IN         = 5
+   ,OUT        = 6
+   ,ERR        = 7
+   ,DATA       = 8
+   ,GET_VALUES = 9
+   ,GET_RESULT = 10
+   ,UNKNOWN    = 11
+  };
+
+  enum class Role : uint16_t
+  {
+    RESPONDER  = 1
+   ,AUTORIZER  = 2
+   ,FILTER     = 3
+  };
+
+  enum class RequestStatus : uint8_t
+  {
+    REQUEST_COMPLETE = 0
+   ,CANT_MPX_CONN    = 1
+   ,OVERLOADED       = 2
+   ,UNKNOWN_ROLE     = 3
+  };
+
+#pragma pack(push,1)
+
+  struct Header
+  {
+    //! Factgi version
+    uint8_t    m_Version;
+    //! Record type
+    RecordType m_Type;
+    //! Request Id
+    bigEndian<uint16_t>  m_Id;
+    //! Request length
+    bigEndian<uint16_t>   m_Length;
+    //! Padding length
+    uint8_t    m_PaddingLength;
+    //! Reserved
+    uint8_t    m_Reserved;
+  };
+
+  //
+  //Helper bodies 
+  //
+
+  /**
+   *
+   */
+  struct BeginRequest 
+  {
+    //! 
+    //int     m_KeepConn; // Should be always 1
+    //! Role
+    bigEndian<uint16_t>    m_Role;
+    //! Flags
+    uint8_t m_Flags;
+    //! reserved
+    uint8_t m_Reserved[5];
+  };
+
+  /**
+   *
+   */
+  struct UnknownType
+  {
+    RecordType m_Type;
+    uint8_t    m_Reserved[7];
+  };
+  /**
+   * Named Value Pairs
+   */
+  struct NameValuePairHeader11
+  {
+    uint8_t nameLength;
+    uint8_t valueLength;
+  };
+
+  struct NameValuePairHeader14
+  {
+    uint8_t nameLength;
+    bigEndian<uint32_t> valueLength;
+  };
+
+  struct NameValuePairHeader41
+  {
+    bigEndian<uint32_t> nameLength;
+    uint8_t              valueLength;
+  };
+
+  struct NameValuePairHeader44
+  {
+    bigEndian<uint32_t> nameLength;
+    bigEndian<uint32_t> valueLength;
+  };
+#pragma pack(pop)
+  /**
+   *
+   */
+  struct EndRequet
+  {
+    //! Return application request status
+    int32_t m_ApplicationStatus;
+    //! Return Fastcgi protocol status
+    RequestStatus m_ProtocolStatus;
+    //! Reserverd
+    uint8_t m_Reserved[3];
+  };
+
+  /**
+   * @brief
+   */
+  template <typename Descriptor,typename InternetProtocol>
+  class request 
+  {
+    private:
+      enum class State : uint32_t {
+        ST_Init
+        , ST_Params
+        , ST_StdIn
+        , ST_StdOut
+        , ST_Error     
+      } ;    
+    private:
+      /// Reference counter...
+      int   m_ref;
+      /// 
+      State m_State;
+    public: 
+    ///
+    typedef aeb::net::basic_socket_stream<InternetProtocol>        Stream;
+    ///
+    typedef aeb::intrusive_ptr<request>      ptr;
+    request(aeb::fcgi::Header &_h) : m_State(State::ST_Init) , m_Id(_h.m_Id)
+    {
+    }
+    /**
+     * @brief read all parameters
+     */
+    void process_parameters(Stream &_s,uint16_t _length)
+    {
+      char _Buffer[4096];
+      int res = 0;
+      uint16_t _totalRead = 0;
+      NameValuePairHeader11 *_h11 = reinterpret_cast<NameValuePairHeader11 *>(_Buffer);
+      NameValuePairHeader14 *_h14 = reinterpret_cast<NameValuePairHeader14 *>(_Buffer);
+      do {
+        res = _s.receive(_Buffer,2);
+        _totalRead+=res;
+        if ( (_h11->nameLength < 127)  && (_h11->valueLength < 127))
+        {
+            res = _s.receive(_Buffer+2,_h11->nameLength + _h11->valueLength);
+            _totalRead+=res;
+            std::string name(_Buffer+2,_h11->nameLength);
+            std::string value(_Buffer+2+_h11->nameLength,_h11->valueLength);
+            std::cout<<"Got Parameter ("<<(int)_h11->nameLength<<","<<(int)_h11->valueLength<<")"<<name<<" "<<value<<std::endl;
+        } else
+        if ( (_h11->nameLength > 127)  && (_h11->valueLength < 127))
+        {
+            std::cout<<"not yet 1"<<std::endl;
+        } else
+        if ( (_h11->nameLength < 127)  && (_h11->valueLength > 127))
+        {
+            std::cout<<"not yet enter nameLength="<<(int)_h14->nameLength<<" vl="<<(int)_h11->valueLength<<std::endl;
+            res = _s.receive(_Buffer+2,3);
+            _totalRead+=res;
+            // Read name and value
+            res = _s.receive(_Buffer+5,_h11->nameLength + _h14->valueLength & 0x7FFFFFFF);
+            _totalRead+=res;
+            std::string name(_Buffer+5,_h11->nameLength);
+            std::string value(_Buffer+5+_h11->nameLength,_h14->valueLength & 0x7FFFFFFF);
+            std::cout<<"Got Parameter "<<name<<" "<<value<<std::endl;
+        }
+      } while  ( _totalRead != _length);
+      //res = _s.receive(_Buffer,2);
+      //std::cout<<"Final Read "<<res<<std::endl;
+    }
+    private:
+      uint16_t m_Id;
+  };
+
+}; // end fcgi
+}; // end aeb
+#endif
index afa19df162d379ab9f06627571f36adad36893f1..d8899d12c2ce5bad4b1a549521d11c7dcfdf36d6 100644 (file)
@@ -55,6 +55,8 @@ class basic_socket_acceptor :
       LOG_AEB_DEBUG_FMT("~basic_socket_acceptor::listen socket %d max_s=%d",this->m_descriptor,max_s);\r
       return result;\r
     }\r
+  protected:\r
+    inline socket_type &get_socket() {return m_socket;}  \r
   protected:\r
     endpoint_type m_endpoint;\r
     socket_type   m_socket;      \r
index 78b0d534000aa7fc6f4efabf816cd46b3945d6b0..8be9d5dc5e299c67b46dc20b0a2ce90d9f8b9e13 100644 (file)
 #endif
 
 #include <aeb/fcgi/fcgi.h>
+#include <aeb/fcgi/fcgi_request.h>
 #include <aeb/fcgi/fcgi_handler.h>
 
 using namespace aeb::net;
+
 typedef aeb::net::io_dispatcher<aeb::net::detail::socket_type,ip::tcp> io_service;
 typedef aeb::net::basic_socket<ip::tcp> tcp_socket;
 
+typedef aeb::fcgi::handler<aeb::net::detail::socket_type,ip::tcp > fcgi_handler;
 /**
  * @brief main entry point. A simple example to use
  * the fcgi implementation. Will use this test
@@ -35,11 +38,13 @@ int main(int argc,char **argv)
 
   ip::basic_endpoint<ip::tcp> ep(ip::tcp::v4(),10224);
 
-  aeb::fcgi::acceptor a(ep);
+  aeb::fcgi::acceptor<aeb::net::detail::socket_type
+                     ,ip::tcp
+                     ,fcgi_handler > a(ep);
 
   a.listen();
 
-:  io_service *io = aeb::singleton<io_service>::get_instance();
+  io_service *io = aeb::singleton<io_service>::get_instance();
 
   io->register_handler(2,&a);