пятница, 20 января 2012 г.

MS Office 365 vs Google Docs

Попробовал новый продукт Майкрософт — Офис 365. Конкурент гугльдоков. Если кратко: к практическому применению не пригоден.

Проблема с ним одна, но очень большая: онлайн-редактор не поддерживает мало-мальски сложных документов. Причем, вместо того, чтобы проигнорировать неподдерживаемую фичу, он просто предлагает открыть документ в десктопном офисе. И в этом случае никакой групповой работы не будет. Вот и всё. Желали люди править документ одновременно и ушли ни с чем.

Из прочих замечаний: интерфейс портала аляповатый, найти нужное сложно. Обновление при групповом редактировании тормозит. Из приятных сторон – интерфейс редакторов похож на десктопный офис.

четверг, 22 декабря 2011 г.

Google 2-Phase Authentication & Google Authenticator & Time Zone & Android Phone Root

По горячим следам.

Итак, есть у гугла такая крутая фича, как двухэтапное подтверждение: кроме пароля, нужно ещё вводить одноразовый код подтверждения, который можно получить на телефон через SMS, сгенерировать на телефоне специальной программой Google Authenticator или взять из заранее распечатанного списка. И всё бы хорошо, но код генерируемый Authenticator’ом привязан ко времени. И вот не так давно правительства России, Украины и Беларуси ввели в странах декретное время. Соответственно, телефоны, не имея обновленной информации начали неправильно определять часовой пояс и, как результат, коды, генерируемые Authenticator’ом работать перестали.

Как же обновить на телефоне базу часовых поясов? Ну, способы есть разные, но на данный момент проще всего воспользоваться программой TimeZone Fixer. Одна только проблема — эта программа требует root-доступа к телефону. Что представляет определенную проблему.

Не буду томить, для телефона HTC Wildfire с Android 2.2.1 рабочий алгоритм оказался таким:

  1. Ставим новый загрузчик . Можно использовать программы с ресурсов http://revolutionary.io/ или http://unrevoked.com/.  Последняя у меня отработала с ошибкой на последнем этапе, но это мелочи. В процессе работы системе нужно будет подсунуть подходящие драйверы. Я их сделал из Android SDK Google USB Drivers, отредактировав файл android_winusb.inf – я добавил в обе секции инсталляции строки
    ; HTC Wildfire
    %CompositeAdbInterface%     = USB_Install, USB\VID_0BB4&PID_0C8B&MI_01
    ;HTC Incredible
    %SingleAdbInterface%        = USB_Install, USB\VID_0BB4&PID_0C9E
    %CompositeAdbInterface%     = USB_Install, USB\VID_0BB4&PID_0C9E&MI_01
    %SingleBootLoaderInterface% = USB_Install, USB\VID_0BB4&PID_0C94
    ;HTC Supersonic
    %SingleAdbInterface%        = USB_Install, USB\VID_0BB4&PID_0C8D
    %CompositeAdbInterface%     = USB_Install, USB\VID_0BB4&PID_0C8D&MI_01
    Сами идентификаторы, понятное дело, посмотрел в Диспетчере Устройств.
  2. Теперь, собственно, Root. Нужно установить пакет http://androidsu.com/superuser/. Забросить его в корень флешки, выключить телефон, включить, удерживая кнопку уменьшения громкости, выбрать пункт RECOVERY, затем выбрать инсталлирование zip-архива.
  3. Ну а теперь можно запускать TimeZone Fixer и радоваться жизни.

суббота, 20 августа 2011 г.

dotNet Sources

Кстати о исходных текстах платформы. Ведь без них никуда. Доступ к ним можно получить следующими путями:

  1. Скачать и распаковать архив Shared Source Common Language Infrastructure 2.0 Release. Кстати, и на гитхабе кто-то его выложил.
  2. Подключить отладочные файлы в студии. Это много где описано. Неудобно, т.к. исходники доступны не все и то только при отладке.
  3. Скачать исходники с майкрософта напрямую — .NET Mass Downloader
    Использовать так NetMassDownloader.exe -d C:\Windows\Microsoft.NET\Framework\v2.0.50727 -output symbols

WaitHandle Resources

Занимаясь многопоточным программированием на платформе dotNet, нельзя не обратить внимание на совершенно ужасные примеры кода в изобилии встречающиеся в сети. Особенно бросается в глаза нежелание программистов освобождать ресурсы. И в первую очередь достается таким потомкам класса WaitHandle как Mutex и ManualResetEvent. Разработчики майкрософта не пожелали сделать публично доступный Dispose(), из чего большинство делает однозначный вывод — нечего там освобождать. Не повторяйте эту ошибку, вернуть ресурсы системе нужно. Другие же начинают извращаться различными способами, например, закрывая SafeWaitHandle. Не повторяйте эту ошибку, освобождение своих ресурсов — внутреннее дело класса.

Итак, заглянув в публично доступные исходники платформы и документацию, выяснено три способа правильной работы:

  1. using (var gate = new ManualResetEvent(false)) { … }
    Да, Dispose() не доступен публично, он доступен как метод интерфейса IDisposable и нормально отрабатывает через
    using.

  2. gate.Close();
    Да, Dispose() не доступен публично, но его функционал доступен через публичный метод Close(). Достаточно вызвать его в нужный момент.

  3. Ничего не делать, если объект должен дожить до конца работы приложения.
    Да, при выгрузке домена приложения хендл будет закрыт принудительно, ресурсы будут возвращены системе.

четверг, 11 августа 2011 г.

HTTP/1.0 Internals

Это просто пост-памятка, чтоб навести порядок в знаниях. Итак, что интересного есть внутри протокола HTTP/1.0 для разработчика веб-приложений? Как всё работает по стандарту и на практике?

Методы

HTTP/1.0 содержит следующие методы: GET (получение данных), POST (создание), PUT (создание или изменение), DELETE (удаление), HEAD, LINK и UNLINK.

LINK и UNLINK — задуманы для установления связи между ресурсами, не используются на практике и интереса не представляют.

HEAD отличается от GET только тем, что не содержит тело ответа и не поддерживает условный запрос (If-Modified-Since). На практике используется только чтобы удостовериться, что ресурс существует. GET и HEAD кэшируются.

Что интересно — ни от одного из методов не требуется идемпотентности, однако есть интересная оговорка, что только POST может создать ресурс (сервер в этом случае должен вернуть код состояния 201, а в тексте сообщения описать, что он создал). В описании метода также PUT впрочем, также сказано, что он тоже может создать ресурс, если данных достаточно.

PUT отличается от POST только смыслом, вкладываемым в имя ресурса: для POST это адрес какого-то обработчика данных, а для PUT – это именно идентификатор ресурса. POST не кэшируется. Про кеширование PUT и DELETE ничего не сказано, но логично предположить, что оно также невозможно.

Нужно отметить, что на практике далеко не всегда можно послать запросы PUT и DELETE. Поэтому их часто заменяют на POST, а дополнительную информацию об операцию производить передают косвенно. Например, для эмуляции DELETE можно просто оставить пустым тело запроса. А Google в своих протоколах использует поле X-HTTP-Method-Override для переопределения POST на PUT или DELETE.

Коды состояния

HTTP/1.0 описывает следующие коды состояния:

  • 200 (OK) — стандартный ответ, означает что операция успешно завершена, тело ответа содержит подробности;
  • 201 (Создан) — возвращается в ответ на POST и PUT, означает, что ресурс создан, а тело ответа содержит подробности, например, адрес созданного ресурса;
  • 202 (Принято) — означает, что запрос принят к рассмотрению, но еще не выполнен. Логично предположить, что возвращается в ответ на POST, PUT, DELETE. Тело ответа содержит подробности, например, где смотреть прогресс исполнения;
  • 204 (Нет содержимого) — тоже самое, что и 200, но для случая когда тела ответа нет. Агент пользователя, получив такой ответ не меняет текущий вид документа;
  • 300 (Множественный выбор) — не используется;
  • 301 (Постоянно перенесен), 302 (Временно перемещен) — запрос будет повторен с новым, присланным адресом. Новый адрес ресурса передается как поле Location в заголовке ответа. Коды можно возвращать в ответ на GET и не следует возвращать в ответ на POST, PUT, DELETE, т.к. в этом случае агент пользователя должен запросить подтверждения пользователя на повтор операции. Тело ответа должно содержать пояснения;
  • 304 (Не модифицирован) — можно вернуть как ответ на условный GET. Означает, что сообщение не изменилось и можно использовать ранее кэшированное. А вот поля ответа расцениваются как новая информация. Тело ответа должно отсутствовать;
  • 400 (Испорченный запрос), 502 (Ошибка шлюза) — низкоуровневые ошибки http-сервера;
  • 401 (Не санкционировано) — означает, что выполнение запроса требует аутентификации. Причем не любой, а именно стандартной, описанной в стандарте протокола. Поскольку такие случаи редки, это делает данный код неупотребимым;
  • 403 (Запрещено) — означает, что доступ к ресурсу или метод запрещены. Также может использоваться вместо 404 если нельзя сообщать пользователю информацию об отсутствии ресурса;
  • 404 (Не найден) — означает, что ресурс с таким адресом не найден;
  • 500 (Внутренняя ошибка сервера) — именно это и означает;
  • 501 (Не реализовано) — именно это и означает;
  • 503 (Сервис недоступен) — применяется при плановом отключении функционала сервера. Следует возвращать поле Retry-After для информирования агента, когда ему повторить запрос.

    поля запросов

    Pragma — используется только со значением no-cache. В этом случае использование кэшированных данных запрещается, соответственно прокси должен передать запрос далее, и клиент сможет получить гарантированно свежий ответ. Теоретически сервер тоже может посылать такое поле, но смысл такого действия в протоколе не определен и перекладывается на получателя.

    Authorization — применяется для при аутентификации пользователя, ничем не интересно.

    From — предназначен для отсылки почтового адреса пользователя, но только если пользователь разрешит. OMG. Сложно сказать, что  творилось в голове разработчиков стандарта, когда они это сочиняли.

    If-Modified-Since — очень интересное поле. Применяется только с GET и его наличие превращает запрос в “условный”. Алгоритм работы: если дата в поле валидна, ресурс с запрошенного времени не изменялся и код результата определенно будет 200, то вместо 200 следует вернуть 304. Таким образом экономим трафик или обновляем кэш.

    Referer — в этом поле агент может указать адрес, где агент узнал адрес запрашиваемого ресурса. Пересылается далеко не всегда, и часто режется прокси.

    User-Agent — в этом поле клиент указывает информацию о себе. Может и не соответствовать действительности.

    Accept — в этом поле агент указывает ответ какого MIME-типа он желал бы получить. Обработка этого поля — это забота веб-сервера, что конечному разработчику обычно не интересно. Достаточно знать, что сервер может отдавать ответ блоками, т.н. chunks.

    Accept-Charset — в этом поле агент указывает желаемую кодировку ответа. Поскольку практика давно показала, что кроме UTF-8 ничего применять нельзя, то особого интереса поле не представляет.

    Accept-Encoding — в этом поле агент указывает возможную компрессию ответа. Поскольку компрессией контента обычно занимается отдельный сервер, и делает он это автоматически, то особого интереса поле не представляет.

    Accept-Language — желаемый язык контента в ответе.

    поля ответов

    Date — дата запроса/ответа. Теоретически клиент тоже может послать такое поле, но смысл? А вот серверу всегда следует посылать такое поле, т.к. эта дата активно используется при кешировании и более того, если этого не делать, то клиент назначит эту дату самостоятельно.

    Location — указывается для ответов 300/301/302 и означает новый адрес запрашиваемого ресурса.

    Server — поле в котором сервер может указать информацию о себе. Интересно, но абсолютно бесполезно.

    WWW-Authenticate — применяется для при аутентификации пользователя, ничем не интересно.

    Allow — в этом заголовке сервер может перечислить методы, которые он поддерживает для указанного адреса. Почему-то нельзя возвращать это поле при POST-запросах. WTF. OMG. На практике, впрочем, всё равно не применяется.

    Content-Encoding — если ответ сжат, то в этом поле указан метод сжатия;

    Content-Length — это поле обязательно заполняется сервером если в ответе присутствует тело ответа. Если равно нулю, то считается, что тело ответа есть, но пустое.

    Content-Type — содержит MIME-тип ответа и кодировку. Если кодировка не указана, то считается что она ISO-8859-1.

    Expires — содержит дату, после которой ответ сервера считается протухшим. Точнее, если дата поля Expires меньше или равна дате поля Date, то клиент должен прекратить использование кэшированных данных. Ноль и невалидная дата также должны рассматриваться агентом как указание на незамедлительное протухание кэша. Действие поля не распространяется на использование кнопки “Назад” в браузере, так что обычно в этом случае возьмутся данные из кэша.

    Last-Modified — дата последнего изменения передаваемого документа. Не может быть больше значения поля Date. В стандарте сказано, что если у получателя есть копия сообщения более старого, чем указывает заголовок Last-Modified, то можно считать эту копию протухшей. Поскольку новое сообщение и так затрет старое в кэше, то очевидно, это имеет смысл только для ответов без тела сообщения: HEAD, 204, 304. Что в свою очередь, если подумать, практического смысла не несет. Такое вот загадочное поле.

    Content-Language — язык ответа.

    Retry-After — поле только для кода 503. Информирует агента об интервале повторения запроса. На практике игнорируется мозиллой.

    Title — заголовок/название ответа. Теоретически может пригодиться при передаче простых текстовых файлов. На практике похоже что браузеры этот заголовок вообще не понимают для любых типов возвращаемого контента.

    URI — может содержать несколько адресов, по которым может быть идентифицирован и может быть доступен запрошенный ресурс.

    ОБЩИЕ поля

    Link — для методов LINK и UNLINK содержит информацию о связываемом ресурсе.

    MIME-Version — в стандарте сказано, что его следует игнорировать. О как.

    Выводы

    Легко заметить, что стандарт местами весьма противоречив и исключительно бестолково написан. В следующей версии, HTTP/1.1, порядка немного добавили. С практической точки зрения, для разработчика веб-сервисов представляют интерес только методы GET, POST, PUT, DELETE и поля Pragma, Date, Expires, If-Modified-Since, Referer, User-Agent, Location, Content-Type, Retry-After. Не так уж и много. С некоторым удивлением осознал бесполезность поля Last-Modified.

  •