суббота, 3 апреля 2010 г.

IE & Chrome Frame Serious Bug

Наткнулся на крайне неприятную ошибку. Если установлен Chrome Frame, то загруженная в IE страница инициализируется дважды! Казалось бы — и ладно, но попалась такая страница, в которой был скрипт с относительным путем для перенаправления — document.location.href = “dir1/dir2/page.html”. Скрипт выполнился дважды и, соответственно, результирующий абсолютный адрес адрес стал /dir1/dir2/dir1/dir2/page.html. По этому адресу естественно ничего нет, имеем 404.

Всё свежее. IE8 — 8.0.7600.16385, ChromeFrame BHO — 5.0.366.0. Страница и заголовки ответа сервера не содержат кода, инициализирующего Chrome Frame. Т.е. сам факт инсталляции нарушил работу стороннего сайта.

Страница для тестирования — http://www.ad.by/test/chrome-frame-redirect.html. Если ChromeFrame установлен и включен, тестовая страница будет перенаправлять на несуществующий адрес.

Как лечить — понятно. Пользователь может отключить надстройку ChromeFrame BHO в “Сервис –> Надстройки”. А программисту следует использовать абсолютные адреса для перенаправлений.

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

Client-side XSLT

Этот пост посвящен вопросам:

  1. Кто может делать XSLT на клиенте?
  2. Какие способы есть для этого?
  3. Какова полнота реализации XSLT/XPATH современными браузерами?
  4. Что с поддержкой node-set в частности и EXSLT в общем?
  5. Какие есть сопутствующие проблемы и как их решать?

С первым вопросом всё просто. По большому счету, XSLT 1.0 сейчас умеют все, кроме мелких мобильных устройств. XSLT 2.0 не умеет никто.

Способов всего существует три:

  1. Самый универсальный способ – это включить в заголовок xml-файла ссылку на xsl. Например <?xml-stylesheet type="text/xsl" href="/html-page.xslt"?>. Это работает везде, но не всегда. Например не работает в IE при отключенных ActiveX. Также не работает в Opera, если отдаваемый xml имеет формат Atom или RSS.
  2. Второй способ менее универсален — можно запускать трансформацию из JS. Этот способ имеет очень серьезные ограничения для браузеров на основе WebKit — в этом случае в них не будет работать функция document() и директива xsl:import. Разработчики про эту ошибку знают, но исправлять не спешат. 
    Ну и про отключенный ActiveX для IE не забываем — XSLT-процессоры от майкрософт реализованы как ActiveX.
  3. И, наконец, буквально на днях узнал третий способ. Сам ещё не проверил. Способ работает только для IE и даже, как утверждается, при отключенном ActiveX!
    IE имеет очень интересную технологию внедрения xml-данных в html через тег xml. И кое-кто выяснил, что к этому xml можно подключать и xsl. И даже оформил это дело как плагин для jQuery.

Полнота реализации. В общих чертах – реализовано на 99%. Т.е. работает почти всё, но ошибки портят кайф. Кое-кто частично поддерживает даже EXSLT. Но, чтобы функция node-set заработала в IE, приходится применять хак.

Проблемы. Их реально много, перечислю лишь некоторые:

  1. Критичен Content-Type для отдаваемых xml- и xsl-файлов. Например Firefox ожидает xsl как text/xml, а не text/xsl.
  2. IE иногда намертво кэширует xsl-файлы. Помогает только перезапуск браузера.
  3. IE кэширует Content-Type! Нельзя с одного uri раздавать контент и как html и как xml. Помогает только перезагрузка страницы.
  4. Индексация контента поисковиками — в данный момент индексация xml-документов в поисковиках работает очень нестабильно. Так что пока выход только один – клоакинг. Звучит страшно, но и гугл и яндекс для данного случая клоакинг неофициально разрешают. Всё работает.

Итог. Применять можно, но только в тех случаях, когда вы уверены, что данный браузер переварит ваш шаблон. Т.е. или фильтруем по User-Agent, или выполняем пробное преобразование и, если оно удачно, далее отдаем xml и xsl.

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

XSLT/XPATH Tips & Tricks

Это пост-памятка про различные нюансы XSLT/XPATH. Ничего секретного, просто компиляция из разных источников. Примеры использовал в основном чужие.

  1. Поиск предка лучше реализовывать не через ../../../ а через ancestor::foo[1] — такой подход будет работать и в случае, если искомый элемент изменил своё положение.
  2. По возможности, не следует пользоваться конструкцией // так как её использование ведет к потере производительности. Процессор вынужден будет просмотреть всё дерево документа целиком.
  3. По возможности, не следует пользоваться конструкцией .// так она часто употребляется неверно. Пример – требуется найти первого потомка с именем foo в текущем элементе. Ответ .//foo[1] неверен. Запись .//foo является короткой формой self::node()/descendant-or-self::node()/child::foo соответственно .//foo[1] означает “все потомки с именем foo, каждый из которых первый у своего родителя”. Т.е. возможен возврат более одного элемента. Правильный ответ — descendant::foo[1]. В большинстве случаев именно это и требуется. Другое возможное решение проблемы получения лишних узлов — всегда использовать скобки если в xpath-выражении есть хоть один слеш и оно применяется вне конструкции <xsl:value-of select="" />. Т.е. второй правильный ответ — (.//foo)[1]. Конструкция <xsl:value-of select="" /> вызывает функцию string, которая возвращает текстовое представление только первого узла из отобранных.
  4. Существует красивый способ применения оси self. Например following-sibling::*[1][name() = 'foo'] можно заменить на following-sibling::*[1]/self::foo. Т.е. убираем предикат.
  5. Достаточно распространена ошибка с непониманием работы оператора !=. Особенно при попытке эмулировать ветку else для xsl:if. Это не отрицание, это условие отбора всех узлов для которых не выполняется некоторое условие. А узлов ведь может и не быть. Отрицание корректно задается функций not().
  6. Также многие не понимают оператор =. Это не только проверка на равенство, но и проверка на наличие в списке. Хороший пример: person[@name = $vip/person/@name] позволяет найти людей, у которых имя встречается в списке $vip (а не равно всему списку сразу, как кажется).
  7. Как проверить, что два узла есть один и тот же узел. Первый вариант — count($foo | $bar) = 1. Второй вариант — generate-id($foo) = generate-id($bar).
  8. Как элегантно писать шаблоны с внутренними условиями без громоздкого xsl:choose. Говорящий пример:
    <xsl:template match="Brands" />

    <xsl:template match="Brands[Brand]">
      <h2>Производители</h2>
      <ul>
        <xsl:apply-templates select="Brand" />
      </ul>
    </xsl:template>

    <xsl:template match="Brands[count(Brand) > 10]">
      <h2>Много производителей</h2>
      <!-- Выводим бренды в три колонки -->
    </xsl:template>
  9. Как посчитать среднее. <xsl:variable name="avg" select="sum(/list/item) div count(/list/item)"/>
  10. Как найти минимум/максимум — отсортировать по возрастанию/убыванию и взять первую.
    <xsl:variable name="min">
       <xsl:for-each select="/list/item">
          <xsl:sort data-type="number" order="ascending"/>
          <xsl:if test="position() = 1"><xsl:value-of select="." /></xsl:if>
       </xsl:for-each>
    </xsl:variable>
  11. current() в предикате позволяет вернуться к контексту текущего узла. То есть вместо того, чтобы создавать переменную:
    <xsl:template match="navigation">
       <xsl:variable name="status" select="@status" />
       <xsl:value-of select="/captions/caption[@status = $status]" />
    </xsl:template>
    гораздо удобнее писать
    <xsl:template match="navigation">
       <xsl:value-of select="/captions/caption[@status = current()/@status]" />
    </xsl:template>
  12. Вместо того чтобы писать несколько xsl:value-of подряд, можно использовать concat(). Например <xsl:value-of select="concat(@Name, ' ', @Surname)" />
  13. Нужно помнить, что всегда выполняется следующий порядок:
    preceding-sibling[last()]
    ...
    preceding-sibling[2]
    preceding-sibling[1]
    current item
    following-sibling[1]
    following-sibling[2]
    ...
    following-sibling[last()]
    Но! position() после <xsl:sort> возвращает позицию в отсортированном дереве.
  14. Также нужно помнить что call-template, в отличие от apply-templates, не меняет контекстный (текущий) узел.
  15. При написании тестов узлов важно не забывать о богатстве возможностей предикатов. Вот хороший пример. <xsl:template match="news[region/person[starts-with(., 'A')]]" />. Или вот — <xsl:apply-template select="comment[@status = 'real'][position() &lt;= 3]" />

Prathit Bondre написал интересный документ XSLT – Efficient Programming Techniques. Есть перевод. В этом документе рассмотрены весьма полезные приемы:

  1. Подключение внешних файлов с данными. Через DOCTYPE ENTITY, через функцию document(). Я сам применяю первый способ для подключения мелких файлов, нужно только помнить, что на эти файлы распространяется ряд неприятных ограничений. Например, при подключении js-скрипта в файле не должно быть символа &.
  2. Пересечение, разность и дизъюнкция множеств.
  3. Нерекурсивные циклы.
  4. Условная выборка в xpath.
  5. Классическая группировка по Мюнху.
  6. Библиотека готовых функций http://xsltsl.sourceforge.net/. Это не EXSLT.

Подключение ENTITY также интересно рассмотрено у Сергея Чикуенка. Он применяет их для эмуляции переменных в тестах узлов шаблонов.

Ну и на закуску. Как получить кросс-процессорную функцию node-set. Использую этот метод давно, отлично работает.

понедельник, 14 декабря 2009 г.

D-LINK DSL-2500U/BRU/D & PPTP

ADSL-провайдер, услугами которого я сейчас пользуюсь, использует VPN-туннель через PPTP. На практике это проявляется в том, что невозможно получить интернет просто включив модем. Нужно ещё создать подключение на компьютере и включать его, когда модем соединится. Раздражает страшно. Техподдержка прикидывается валенком, мол, мелочь, “пропишите ярлык в автозагрузку” или уверяют, что ради заботы о клиентах, ведь “это так удобно, трафик лишний не набежит”. Они, видимо, не подозревают, что компьютеры последние лет пять не перезагружаются месяцами, а просто впадают в гибернацию. А гибернация рвёт VPN-туннель. Да и байтики экономить ни к чему, хотя доступных безлимитных тарифов в Беларуси и нет, но есть доступные тарифы с большим количеством трафика. Кроме того, техподдержка прописывает статические маршруты на свои подсети прямо на компьютере. Совсем обленились, а в модеме такая функция зачем? И dns-сервера прописывают ещё и в ярлыке соединения. В общем устал я от этого.

Проблему можно решить используя ADSL-модем с внутренней поддержкой PPTP-соединений. Из закромов был извлечен D-LINK DSL-2500U/BRU/D. Путем долгой борьбы модем таки удалось отконфигурировать и запустить с поддержкой PPTP.

Алгоритм такой:

  1. Модем придется перепрошить. Нужна прошивка RU_1.50_02162009 и никакая другая, т.к. в этой уже отключили шифрование для PPTP (провайдер не дает соединиться с шифрованием), а во всех последующих прошивках PPTP нет совсем.
  2. После перепрошивки модем нужно сбросить аппаратно. Сзади есть кнопка сброса, её нужно зажать на секунд 10 и включить питание. После этого модем действительно сбросит все настройки в заводские. Как ни странно, но аналогичная кнопка в веб-интерфейсе модема таким действием не обладает.
  3. Теперь нужно настроить WAN. Заходим через веб-интерфейс. Advanced Setup > WAN. Тут никаких отклонений от того, что настраивают люди из техподдержки. Ровно так и прописываем, в конце нажимаем Save/Reboot.
  4. А теперь нужно прописать статическую маршрутизацию. Advanced Setup > Routing > Static Route. Берем за образец командный файл от техподдержки, только в качестве Gateway прописываем ip-адрес гейта по умолчанию из предыдущего шага.
  5. И только теперь заходим в Advanced Setup > PPTP. Там пишем Enable: да, Tunnel Name: vpn.adsl.by, PPTP Server IP Address: vpn.adsl.by, User Name и Password берете из пямятки от провайдера, Authentication Method: MSCHAPv2, Default Route: да. Обращаю внимание на то, что вместо ip-адреса можно сразу имя сервера вписать. Нажимаем Save/Apply.
  6. Теперь идем в Management > Save/Reboot. После перезагрузки идем в Device Info > WAN и смотрим на PPTP Info — там через минуту появится информация о PPTP-соединении. Всё. Больше рекомендую ничего не трогать. Ну разве что SNMP отключить.

Итоги борьбы:

  1. Включил модем — есть интернет. Без всяких дополнительных телодвижений. Это удобно.
  2. К модему можно подсоединить любой компьютер, ничего конфигурировать не нужно. Это удобно.
  3. Упала скорость скачивания! Линия включена как 8Мбит. Реальная скорость была 6.6Мбит. После переноса PPTP на модем стало 3.6Мбит! А скорость закачки как была 0.6Мбит, так и осталась. Модем-то тормозной, оказывается. То-то я думаю, почему это D-LINK убрал PPTP-из прошивки.
  4. Вот так. Приходится выбирать. Или скорость или отсутствие геморроя. Видимо придется купить нормальную технику, Linksys или Cisco.
  5. Update по предварительным результатам эксплуатации: при организации VPN-туннелей поверх модемного PPTP возникают глюки с потерей пакетов, соединение часто рвется, в общем неприятно. Вдвойне понятно почему PPTP удален из современных прошивок.

Несмотря на то, что технически у Белинфонета всё сделано исключительно бездарно с точки зрения клиента, это сейчас единственная контора дающая и нормальную скорость и 30ГБ трафика одновременно за 50$. У остальных такого нет при схожих ценах.

четверг, 22 октября 2009 г.

DNS, Hosting, Panel, Redirect, Email for Free

Сегодня расскажу как получить быстрый и при этом полностью бесплатный в обслуживании сайт. Ноль рублей, ноль копеек в год. Но нужно быть программистом, хотя бы начинающим.

  1. Имя. Нужно купить доменное имя второго уровня ) Ну да, немного обманул ) На имени сэкономить, наверное, не удастся. Но бывают акции у разных регистраторов. Так что вероятность получить бесплатное доменное имя всё-таки есть. У меня один раз получилось. Для работы домена нужно сообщить регистратору, какие DNS-сервера будут обслуживать записи о вашем домене. Сразу же при регистрации уведомляем своего регистратора, чтобы прописал сервера freedns1.registrar-servers.com, freedns2.registrar-servers.com и freedns3.registrar-servers.com в качестве наших первичных/вторичных DNS-серверов. Через несколько часов/дней эта информация разойдется по миру и ваш будущий домен станет доступен всем.
  2. DNS-сервера. Регистраторы обычно дают такой сервис за деньги, предоставляя свои DNS-сервера. На этот трюк покупаться не стоит. А стоит найти того кто предоставляет сервис FreeDNS. Таких мест много, но все они, по разному ущербны. Кроме одного - namecheap.com! Именно его сервера мы использовали на предыдущем шаге. Итак, идем, регистрируемся, потом заходим в пункт Free DNS Hosting и добавляем свой домен.
  3. Панель управления. К доменному имени большая часть регистраторов дает готовую панель управления DNS-записями. Тем, кому как и мне, не повезло, namecheap.com дает свою. Заходим в Free DNS –> Your Hosted Domains, щелкаем по своему, добавленному на предыдущем шаге, домену и приступаем к редактированию DNS-записей. Это будет долго и нудно, но стоит того. Кстати, на этой странице, возможно будет кнопка “подтвердить владение”, так вот её нужно нажать, и namecheap отправит на почтовый адрес администратора (который вы указали при регистрации домена) специальную ссылку, пройдете по ссылке, и namecheap будет считать вас владельцем вашего домена.
  4. Почта. Без почты никуда ) Идем на Google Apps For Domain и регистрируем там свой домен как администратор. Кстати, там же и купить можно, но не пробовал. Регистрируемся как некоммерческий пользователь, а не триальный на 30 дней, как это предлагается по умолчанию! Бесплатная ссылка запрятана где-то сбоку. Перейти с коммерческого на бесплатный потом нельзя! Гугл предложит подтвердить владение доменом и сообщит код. Идем в панель и прописываем там специальную запись: в первую колонку строчку с кодом от гугла, во вторую колонку “ghs.google.com.” (без кавычек и точкой!) в третьей выбираем “CNAME (alias)”. Сохраняем. Возвращаемся на гугл и нажимаем на кнопку проверки. Гугл проверит и поверит, что этот аккаунт действительно от администратора домена. В аккаунте включаем SSL, включаем почту, создаем нужные почтовые ящики. У почты прописываем Web Address какой нравится. У меня, например, это mail.ad.by. Через этот адрес будете заходить в свою почту. Потом идем в панель управления DNS и прописываем там строку “mail”, “ghs.google.com.”, “ghs.google.com.”. Кроме того, чтобы никто не рассылал спам от вашего имени можно прописать SPF-запись - строку “@” “v=spf1 +a include:aspmx.googlemail.com +mx -all” “TXT Record”. Это не всё. Самое главное, выбрать в панели пункт “User (Mail Server's Host Name Required)” и прописать туда семь строк. Если строк не хватает, сохраняетесь, после этого на странице появятся ещё поля для ввода. Всё, почта работает. Идем на адрес mail.вашдомен.com или что вы там указывали, и наслаждаемся нормальной почтой.
  5. Уведомления о новой почте/GoogleTalk/Jabber. Ставим GoogleTalk и он будет уведомлять нас новых письмах. Идем в гугловую панель, включаем сервис Chat, качаем и ставим GoogleTalk. Идем в namecheap-панель и долго и нудно прописываем там 15 строчек в разделе SRV SETTINGS. Параметры можно посмотреть много где. Основные 10 строк описаны у гугла. Остальные 5 строк нужны для тех кто собирается использовать сторонний jabber-клиент. Это самая нудная часть.
  6. Хостинг. Нужно же сайт к домену привинтить. Идем на Google App Engine, регистрируемся (понадобится мобильный телефон – на него придет SMS с кодом подтверждения, причем в Беларусь не отправляют), регистрируем новое приложение. Писать можно на языках Python и Java. Но программировать не обязательно, если сайт статический. Качаем выбранный SDK, разбираемся и программируем сайт, или гуглим и качаем уже готовый. Заливаем код на App Engine. Привязывает его к имени www.вашсайт.ком. Потом идем в гугловую панель своего домена и подключаем сайт как сервис. Теперь идем в namecheap-панель и в двух верхних строках пишем следующее. Первая строка - “http://www.вашдомен.ком/?” (вопросительный знак важен) и “URL Redirect (301)”. Вторая строка “ghs.google.com.” и “CNAME (Alias)”. Сохраняем. Всё. Теперь при заходе на ваш сайт пользователи будут видеть чего вы там напрограммировали.
  7. DynamicDNS. Если кому-то нужно, то имейте виду, что namecheap дает и такую возможность.

Всё. Имеем полноценный сайт с почтой. Быстрый, бесплатный. Если он статический, так ему ещё и DDOS не страшен – гугл раздает статику через свой CDN.