Berapa banyak kode C ++ perlu ditulis untuk mengurai header HTTP Otorisasi menggunakan easy_parser dari RESTinio?


Kami terus mengembangkan server HTTP RESTinio yang bebas dan terbuka yang tertanam dalam aplikasi C ++ . Templat C ++ secara aktif digunakan dalam implementasi RESTinio, yang kami diskusikan secara rutin di sini ( contoh terkini ).


Salah satu poin aplikasi untuk C ++ template magic adalah easy_parser, sebuah implementasi kecil dari parser rekursif ke bawah berdasarkan PEG . Easy_parser ditambahkan ke RESTinio tahun lalu untuk menyederhanakan pekerjaan dengan header HTTP.


Kami sudah berbicara sedikit tentang easy_parser di artikel sebelumnya . Dan hari ini saya ingin menunjukkan bagaimana easy_parser digunakan dalam pengembangan RESTinio. Dengan mem-parsing isi header HTTP Otorisasi. Mari kita coba, untuk berbicara, untuk melihat ke dalam nyali RESTinio.


Otorisasi Tata Bahasa


Struktur header Otorisasi didefinisikan dalam RFC7235 sebagai berikut:


authorization-field = "Authorization" ":" OWS credentials

credentials = auth-scheme [ 1*SP ( token68 / [ #auth-param ] ) ]

auth-scheme = token

auth-param = token BWS "=" BWS ( token / quoted-string )

token68 = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="

Itu nilai header Otorisasi harus mencakup nama yang diperlukan dari skema otentikasi dan parameter opsional untuk skema ini.


, token68 ( , base64), "=".


Authorization easy_parser?


?


easy_parser , , .


, , :


struct authorization_value_t
{
   enum class value_form_t { token, quoted_string };

   struct param_value_t
   {
      std::string value;
      value_form_t form;
   };

   struct param_t
   {
      std::string name;
      param_value_t value;
   };

   using param_container_t = std::vector< param_t >;

   struct token68_t
   {
      std::string value;
   };

   using auth_param_t = variant_t< token68_t, param_container_t >;

   std::string auth_scheme;
   auth_param_t auth_param;
};

authorization_value_t : auth_scheme auth_params. auth_paramstoken68_t, , param_t.


Authorization


producer


, , . ( ) , , :


auto make_parser()
{
   ... //   ,    ,  .

   return produce< authorization_value_t >(
         token_p() >> to_lower() >> &authorization_value_t::auth_scheme,
         maybe(
            repeat( 1, N, space() ),
            produce< auth_param_t >(
                  alternatives( token68_seq, params_seq )
            ) >> &authorization_value_t::auth_param
         )
   );
}

make_parser -, authorization_value_t. produce, . produce , C++ DSL.


:


  • token ( RFC7230). token- auth_scheme authorization_value_t;
  • , , , maybe;
  • ;
  • token68, . , auth_param authorization_value_t auth_param_t.

- . , , easy_parser DSL.


, - , , token68_seq params_seq . , ;)


token68_seq?


token68_seq — , make_parser :


auto token68_seq = sequence(
      token68_p() >> as_result(),
      not_clause( any_symbol_p() >> skip() ) );

, token68_seq — . token68 , , .


, , token68 . PEG, not-predicate. Not-pedicate , , . , not-predicate, .


, RFC7617 , token68, . , token68 . , .


token68_seq , token68_p . :


struct is_token68_char_predicate_t
   : protected hfp_impl::is_alphanum_predicate_t
{
   using base_type_t = hfp_impl::is_alphanum_predicate_t;

   bool operator()( const char actual ) const noexcept
   {
      return base_type_t::operator()(actual)
            || '-' == actual
            || '.' == actual
            || '_' == actual
            || '~' == actual
            || '+' == actual
            || '/' == actual
            ;
   }
};

inline auto token68_symbol_p()
{
   return restinio::easy_parser::impl::symbol_producer_template_t<
         is_token68_char_predicate_t >{};
}

inline auto token68_p()
{
   return produce< token68_t >(
         produce< std::string >(
            repeat( 1, N, token68_symbol_p() >> to_container() ),
            repeat( 0, N, symbol_p('=') >> to_container() )
         ) >> &token68_t::value
      );
}

, token68_p. is_token68_char_predicate_t token68_symbol_p — . , easy_parser , - , : digit_p, space_p, hexdigit_p, alpha_symbol_p, alphanum_symbol_p .. , , token68, . is_token68_char_predicate_t token68_symbol_p .


token68_p token68 -- : token68, =. .


params_seq?


, params_seq. , , make_parser, :


auto make_parser()
{
   auto token_to_v = []( std::string v ) -> param_value_t {
      return { std::move(v), value_form_t::token };
   };
   auto qstring_to_v = []( std::string v ) -> param_value_t {
      return { std::move(v), value_form_t::quoted_string };
   };

   auto token68_seq = sequence(
         token68_p() >> as_result(),
         not_clause( any_symbol_p() >> skip() ) );

   //    name=value   .
   auto params_seq = maybe_empty_comma_separated_list_p< param_container_t >(
         produce< param_t >(
            token_p() >> to_lower() >> &param_t::name,
            ows(),
            symbol('='),
            ows(),
            produce< param_value_t >(
               alternatives(
                  token_p() >> convert( token_to_v ) >> as_result(),
                  quoted_string_p() >> convert( qstring_to_v )
                        >> as_result()
               )
            ) >> &param_t::value
         )
      ) >> as_result();

   return produce< authorization_value_t >(
         token_p() >> to_lower() >> &authorization_value_t::auth_scheme,
         maybe(
            repeat( 1, N, space() ),
            produce< auth_param_t >(
                  alternatives( token68_seq, params_seq )
            ) >> &authorization_value_t::auth_param
         )
   );
}

, lambda- token_to_v qstring_to_v. , std::string param_value_t, : token quoted-string.


- , name=value std::string, param_value_t, std::string .


params_seq:


auto params_seq = maybe_empty_comma_separated_list_p< param_container_t >(
      ...
   ) >> as_result();

params_seq , param_container_t. , .


maybe_empty_comma_separated_list_p easy_parser- , RFC HTTP #auth-param, # :


[ ( "," / auth-param )  *( OWS "," [ OWS auth-param ] ) ]

params_seq , . auth-param. , -, :


produce< param_t >(
   token_p() >> to_lower() >> &param_t::name,
   ows(),
   symbol('='),
   ows(),
   produce< param_value_t >(
      alternatives(
         token_p() >> convert( token_to_v ) >> as_result(),
         quoted_string_p() >> convert( qstring_to_v )
               >> as_result()
      )
   ) >> &param_t::value
)

, param_t, :


  • token, param_t::name;
  • ( ows OWS (.. optional whitespace) RFC7230);
  • =;
  • ;
  • (.. param_value_t), : token, quoted-string. token, , token_to_v ( ) . quoted-string, , qstring_to_v;
  • param_value_t param_t::value.

, , .



" ?" .



, , , , RESTinio , . , , RESTinio.


, . , RESTinio, , , C++ . , .


easy_parser RESTinio , RESTinio HTTP-.


, - , - . ( , ), - .


, - ( bison, coco/r, ragel .) . RESTinio header-only ( libhttp-parser). - header-only . , : , - RESTinio, , - ( , RESTinio)? ragel coco/r? , .


, C++ DSL. , RESTinio - Boost- - PEGTL , .. . , (Boost.Spirit PEGTL) , . , show stopper-. easy_parser.


C HTTP- RESTinio easy_parser-. .


...


, easy_parser, . . , a) RFC, b) , , , c) d) . , , - 4-6 , easy_parser- — 15-20 .


, easy_parser- RESTinio , - RESTinio .


RESTinio


Jawaban kedua untuk pertanyaan "Mengapa dan apa artikel ini ditulis?" hanya sepele dan jelas: Saya ingin sekali lagi mengingat bahwa proyek ini hidup dan berkembang. Anda dapat mengambil, mencoba, dan kemudian membagikan perasaan Anda kepada kami: apa yang Anda sukai, apa yang tidak Anda sukai, apa yang hilang, apa yang tidak dilakukan seperti yang Anda inginkan ... Kami mendengarkan semua komentar dan saran yang membangun (saya pikir beberapa pembaca akan dapat mengonfirmasi ini )


All Articles