¿Cuánto código C ++ se debe escribir para analizar el encabezado HTTP de autorización con easy_parser de RESTinio?


Continuamos desarrollando el servidor RESTinio HTTP gratuito y abierto integrado en aplicaciones C ++ . Las plantillas de C ++ se utilizan activamente en la implementación de RESTinio, que discutimos regularmente aquí ( ejemplo reciente ).


Uno de los puntos de aplicación para la magia de plantillas de C ++ fue easy_parser, una pequeña implementación de un analizador recursivo hacia abajo basado en PEG . Easy_parser se agregó a RESTinio el año pasado para simplificar el trabajo con encabezados HTTP.


Ya hablamos un poco sobre easy_parser en un artículo anterior . Y hoy quiero mostrar cómo easy_parser se utiliza en el desarrollo de RESTinio. Analizando el contenido del encabezado HTTP de autorización. Intentemos, por así decirlo, analizar las entrañas de RESTinio.


Autorización de gramática


La estructura del encabezado de autorización se define en RFC7235 de la siguiente manera:


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

Aquellos. El valor del encabezado de autorización debe incluir el nombre requerido del esquema de autenticación y los parámetros opcionales para este esquema.


, 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 segunda respuesta a la pregunta "¿Por qué y sobre qué se escribió este artículo?" simplemente trivial y obvio: me gustaría recordar una vez más que el proyecto vive y se desarrolla. Puede tomarlo, probarlo y luego compartir sus sentimientos con nosotros: lo que le gustó, lo que no le gustó, lo que faltaba, lo que no se hizo de la manera que le gustaría ... Escuchamos todos los comentarios constructivos y sugerencias (creo que algunos lectores podrán confirmar esto )


All Articles