Quelle quantité de code C ++ doit être écrite pour analyser l'en-tête HTTP d'autorisation à l'aide d'easy_parser de RESTinio?


Nous continuons à développer le serveur HTTP RESTinio gratuit et ouvert intégré aux applications C ++ . Les modèles C ++ sont activement utilisés dans l'implémentation de RESTinio, dont nous discutons régulièrement ici ( exemple récent ).


L'un des points d'application de la magie des modèles C ++ était easy_parser, une petite implémentation d'un analyseur récursif descendant basé sur PEG . Easy_parser a été ajouté à RESTinio l'année dernière afin de simplifier le travail avec les en-têtes HTTP.


Nous avons déjà parlé un peu d'easy_parser dans un article précédent . Et aujourd'hui, je veux montrer comment easy_parser est utilisé dans le développement de RESTinio. En analysant le contenu de l'en-tête HTTP d'autorisation. Essayons, pour ainsi dire, d'examiner les tripes de RESTinio.


Autorisation de grammaire


La structure d'en-tête d'autorisation est définie dans la RFC7235 comme suit:


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 / "-" / "." / "_" / "~" / "+" / "/" ) *"="

Ceux. la valeur de l'en-tête d'autorisation doit inclure le nom requis du schéma d'authentification et des paramètres facultatifs pour ce schéma.


, 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


La deuxième réponse à la question "Pourquoi et sur quoi cet article a-t-il été écrit?" juste trivial et évident: je voudrais rappeler une fois de plus que le projet vit et se développe. Vous pouvez prendre, essayer, puis partager vos sentiments avec nous: ce que vous avez aimé, ce que vous n'avez pas aimé, ce qui manquait, ce qui n'a pas été fait comme vous le souhaitez ... Nous écoutons tous les commentaires et suggestions constructifs (je pense que certains lecteurs pourront le confirmer )


All Articles