понедельник, 22 марта 2010 г.

Puri и utf-8

Всё началось с того, что я обнаружил, что restas-directory-publisher не может обрабатывать URL, содержащие #\+. Исследование показало, что используемая функция hunchentoot:url-decode, по совершенно неведомой мне причине, заменяет #\+ на #\Space (так нужно делать при декодировании форм, но не url). Чуть присмотревшись к коду функций hunchentoot:url-decode и hunchentoot:url-encode я вообще перестал понимать, что именно они делают и какому rfc это соответствуют, но, в большинстве случаев, результат приемлем. При этом, hunchentoot:url-decode вообще не очень хорошо реализована и я сначала попробовал переписать её как-нибудь более вменяемо. После чего вдруг вообще мои приложения отказались обрабатывать url, содержащие кириллицу. Что меня ввело в ступор, поскольку я был уверен в своём коде. Начав копать дальше вдруг обнаружил, что замечательная библиотека Puri жутко коверкает подобные url, ибо просто игнорирует тот факт, что для кодирования символов используется кодировка utf-8. Примечательным моментом при этом было то, что совместное использование Puri и hunchentoot:url-decode давало обычно приемлемый результат. А поскольку я обычно использовал их в связке, то долго не обращал внимания на эту проблему. Ну и тот факт, что всё работало, вселял в меня уверенность, что этим библиотекам можно доверять. Как оказалось, это классический случай, когда минус на минус давал плюс и приемлемые результаты получались только из-за довольно топорного и прямого метода реализации hunchentoot:url-decode: стоило переписать эту функции в чуть более интлектуальном виде, так сразу полезли проблемы. Итого, функциям hunchentoot:url-decode и hunchentoot:url-encode вообще нельзя доверять, а Puri содержит в себе серьёзную проблему. Самая большая моя проблема в том, что Puri лежит в основе используемого мною подхода, а более детальный анализ показал, что возможность декодирования url за пределами Puri вызывает весьма серьёзные проблемы (в случае работы с нетривиальными url). Мемного поразмыслив решил, что иного пути, как патчить Puri просто нет. В общем, это замечательная библиотека, вот только о существовании utf-8 не подозревает. Исправить код оказалось не очень сложно, патчи отослал разработчику. Но как-то многовато патчей я отослал за последние полторы недели и толи английский мой совсем плох, то со мной что не так, но только отвечать мне не спешат. Да и по прошлому опыту помню, что на некоторые патчи бывает приходит ответ спустя несколько недель. Так что пока решил разместить форк Puri с моими исправлениями на github.

10 комментариев:

  1. Наличие индусокода в лисповых проектах не может не печалить

    ОтветитьУдалить
  2. А вы много в своих проектах закладываетесь, например, на то, что буквы могут быть иероглифами, печатаемые справа налево, снизу вверх? ;)

    Ну или когда на C/C++ пишите, много ли вы обращаете внимания на невыровненный доступ, LSB/MSB, etc?

    7-битные американцы - проблема не американцев.

    ОтветитьУдалить
  3. а причем тут индусокод?
    Просто малое комьюнити, что влечет соответствующие проблемы. Не мейнстрим, так сказать. Или нужно пользоваться коммерческими реализациями. К сожалению, вот так:(

    ОтветитьУдалить
  4. @13-49
    Правильно, проблема не в конкретной библиотеке, а в малом количестве пользователей, малом количестве багрепортов и мало количестве патчей, ну и имеющей место безответственности. CL, как я недавно уже говорил, это не меч-кладенец, и нельзя надеяться, что один человек разработает очень качественное решение (такое бывает, но редко). По моим наблюдениям, самый качественный код (именно код) в SLIME, просто потому, что это самый востребованный проект на CL, но у него другие проблемы (о коих я писал раньше)...

    По факту, в мире CL ты должен быть готов много пилить, по крайней мере на данном этапе.

    ОтветитьУдалить
  5. > Или нужно пользоваться коммерческими
    > реализациями.

    Наивный, если говорить о Puri, то это портабельная версия http://github.com/franzinc/uri, которая отрыта, но написана коммерческим поставщиком, я специально посмотрел, там те же самые проблемы.

    Это вообще большой миф, что коммерческие решения обладают более высоким качеством. Это верно для некоторых конкретных решений, но не распространяется автоматически на всё.

    ОтветитьУдалить
  6. >Это вообще большой миф, что коммерческие решения обладают более высоким качеством.

    Да я с этим и не спорю. коммерческий софт != качественный софт. Те же GNU emacs, XEmacs или eclipse - примеры хороших открытых программ. Или slime. Но в мире CL, возможно, на данный момент лучше пользоваться коммерческими реализациями (если возможно по деньгам). Я, конечно, за развитие открытых библиотек и реализаций CL, но пока низкая популярность и комьюнити. Может быть со временем допилится.
    Я тоже не люблю коммерческий софт в софтостроении, поскольку он, как правило, не дает гибкости в управлении кодом и т.д.

    ОтветитьУдалить
  7. А замена #\+ на #\Space не на уровне reader идет?

    ОтветитьУдалить
  8. > А замена #\+ на #\Space не на уровне reader идет?
    Нет, конечно, это же просто строки, в hunchentoot:url-decode есть такой код:
    (case char
    ((#\+) #\Space)
    (otherwise char))

    ОтветитьУдалить
  9. Что-то не совсем корректно он юникод сейчас понимает (ставил версию из твоего оверлея gentoo-lisp):

    (puri::decode-escaped-encoding "/%D0%98%20" "")
    "/И%98"

    А должно быть "/И "

    Из restas-а он как-то по-другому вызывается, там это приводит к сигнализированию ошибки babel-ем (при попытке декодировать %98 как utf8)

    ОтветитьУдалить
  10. > (puri::decode-escaped-encoding "/%D0%98%20" "")

    Просто неправильный вызов, decode-escaped-encoding имеет следующие параметры (string escape reserved-chars), где escape означает надо ли декодировать, а reserved-chars это символы, которые не надо декодировать, при чём, в формате битового массива. Попробуй (puri::decode-escaped-encoding "/%D0%98%20" t nil)

    > Из restas-а он как-то по-другому вызывается

    Хм, где? Я изменил puri в оверлее и вскрыл этим массу проблем, ибо я часто раньше использовал hunchentoot:url-decode для url, полученных с помощью puri, что как я теперь понял является полной глупостью. Сейчас я изменил genurl в restas, который теперь использует puri для корректной генерации url, но эта версия пока только в git. Также надо разбираться отдельно и с другими моими пакетами, особенно большие проблемы с restas-wiki. Мне потребуется некоторые время, что бы всё это разгрести.

    Но зато мне теперь открылся "истинный путь" для работы с url :)

    ОтветитьУдалить