cURL путаница кодировки URL - PullRequest
1 голос
/ 13 апреля 2019

Я пытаюсь закодировать запрос. Запрос идет следующим образом:

https://www.overpass-api.de/api/interpreter?data=area["name"="Nicaragua"]["admin_level"="2"]->.boundaryarea;(node["type"="route"]["route"="bus"](area.boundaryarea);way["type"="route"]["route"="bus"](area.boundaryarea);>;relation["type"="route"]["route"="bus"](area.boundaryarea);>>;);out meta;

Как видите, у вас много специальных символов. Если я дам этот URL для curl, я не буду обрабатывать его из-за некоторых символов. Поэтому я решил закодировать URL с помощью моего собственного метода и метода curl. Вот пример кода для кодирования с помощью curl:

std::string d = ...;
   CURL *curl = curl_easy_init();
if(curl) {
  char *output = curl_easy_escape(curl, d.c_str(), d.length());
  if(output) {
    printf("Encoded: %s\n", output);
    curl_free(output);
  }
}

Будет закодировать весь запрос, в результате чего-то вроде

https%3A%2F%2Fwww.overpass-api.de%2Fapi%2Finterpreter%3Fdata%3D ...

Если я тогда попытаюсь дать ему свернуться, чтобы обработать его, он выдаст и скажет, что не может разрешить хост, что имеет смысл для меня. Поэтому я решил проверить, что делает chrome при его кодировании - благодаря инструментам dev. И вот как это выглядит:

https://www.overpass-api.de/api/interpreter?data=area[%22name%22=%22Nicaragua%22][%22admin_level%22=%222%22]-%3E.boundaryarea;(node[%22type%22=%22route%22][%22route%22=%22bus%22](area.boundaryarea);way[%22type%22=%22route%22][%22route%22=%22bus%22](area.boundaryarea);%3E;relation[%22type%22=%22route%22][%22route%22=%22bus%22](area.boundaryarea);%3E%3E;);out%20meta;

И если я дам это завитку как есть - он обработает его правильно.

Почему некоторые символы кодируются, а не остальные? и почему керл принимает это таким образом?

РЕДАКТИРОВАТЬ: и что более важно, как я могу повторить это в моем коде?

Ответы [ 2 ]

1 голос
/ 13 апреля 2019

Вы должны экранировать части URI.Взгляните на функции JavaScript encodeURI () и encode URIComponent () , это путь.

Я использую следующую функцию, которая имитирует кодировку JavaScript encodeURIComponent, чтобы кодировать отдельные части

std::string encodeURIComponent(std::string const&value)
{
    std::ostringstream oss;
    oss << std::hex;
    for(auto c : value){
      int uc = static_cast<unsigned char>(c);
      if(((0x30 <= uc) && (uc <= 0x39)) || ((0x41 <= uc) && (uc <= 0x5A)) || ((0x61 <= uc) && (uc <= 0x7A))){
        oss << c;
        continue;
      }
      switch(c){
      case '-': oss << c; break;
      case '_': oss << c; break;
      case '.': oss << c; break;
      case '!': oss << c; break;
      case '~': oss << c; break;
      case '*': oss << c; break;
      case '\'': oss << c; break;
      case '(': oss << c; break;
      case ')': oss << c; break;
      default:
          oss << std::uppercase << '%' << std::setw(2) << uc << std::nouppercase;
          break;
      }
    }
    return oss.str();
}
0 голосов
/ 13 апреля 2019

Не экранируйте URL-адрес весь как одну строку. Избегайте только отдельных частей, которые на самом деле нужно экранировать, например параметров запроса. Но даже тогда, в парах name=value, экранируйте name и value отдельно при необходимости, в противном случае разделитель = в паре name=value и разделитель & между парами будут экранированы, что ты не хочешь случиться.

Попробуйте что-то еще подобное:

std::string query_encode(const std::string &s)
{
    std::string ret;

    // curl_easy_escape() escapes way more than it needs to in
    // a URL Query component! Which is not TECHNICALLY wrong, but
    // it won't produce the output you are expecting...
    /*
    char *output = curl_easy_escape(curl, s.c_str(), s.length());
    if (output) {
        ret = output;
        curl_free(output);
    }
    */

    #define IS_BETWEEN(ch, low, high) (ch >= low && ch <= high)
    #define IS_ALPHA(ch) (IS_BETWEEN(ch, 'A', 'Z') || IS_BETWEEN(ch, 'a', 'z'))
    #define IS_DIGIT(ch) IS_BETWEEN(ch, '0', '9')
    #define IS_HEXDIG(ch) (IS_DIGIT(ch) || IS_BETWEEN(ch, 'A', 'F') || IS_BETWEEN(ch, 'a', 'f'))

    for(size_t i = 0; i < s.size();)
    {
        char ch = s[i++];

        if (IS_ALPHA(ch) || IS_DIGIT(ch))
        {
            ret += ch;
        }
        else if ((ch == '%') && IS_HEXDIG(s[i+0]) && IS_HEXDIG(s[i+1]))
        {
            ret += s.substr(i-1, 3);
            i += 2;
        }
        else
        {
            switch (ch)
            {
                case '-':
                case '.':
                case '_':
                case '~':
                case '!':
                case '$':
                case '&':
                case '\'':
                case '(':
                case ')':
                case '*':
                case '+':
                case ',':
                case ';':
                case '=':
                case ':':
                case '@':
                case '/':
                case '?':
                case '[':
                case ']':
                    ret += ch;
                    break;

                default:
                {
                    static const char hex[] = "0123456789ABCDEF";
                    char pct[] = "%  ";
                    pct[1] = hex[(ch >> 4) & 0xF];
                    pct[2] = hex[ch & 0xF];
                    ret.append(pct, 3);
                    break;
                }
            }
        }
    }

    return ret;
}

std::string d = "https://www.overpass-api.de/api/interpreter?data=" + query_encode("area[\"name\"=\"Nicaragua\"][\"admin_level\"=\"2\"]->.boundaryarea;(node[\"type\"=\"route\"][\"route\"=\"bus\"](area.boundaryarea);way[\"type\"=\"route\"][\"route\"=\"bus\"](area.boundaryarea);>;relation[\"type\"=\"route\"][\"route\"=\"bus\"](area.boundaryarea);>>;);out meta;");

std::cout << "Encoded: " + d + "\n";

Демоверсия

Выход:

https://www.overpass-api.de/api/interpreter?data=area[%22name%22=%22Nicaragua%22][%22admin_level%22=%222%22]-%3E.boundaryarea;(node[%22type%22=%22route%22][%22route%22=%22bus%22](area.boundaryarea);way[%22type%22=%22route%22][%22route%22=%22bus%22](area.boundaryarea);%3E;relation[%22type%22=%22route%22][%22route%22=%22bus%22](area.boundaryarea);%3E%3E;);out%20meta;

Почему некоторые символы кодируются, а не остальные?

Правила охватываются RFC 3986 , в частности Разделом 2 «Символы» и его подразделами 2.1 - 2.5. Компонент Query охватывается Разделом 3.4 .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...