window.location - не всегда оно работает как надо
Если раньше мы долго ругались на проблемы IE, то сейчас на FF и Chrome - ничего особо не поменялось.
На этот раз отличились механизмы работы с хешом в window.location.
Кейс: мы хотим перенаправить пользователя с помощью js на другую страницу.
Что может пойти не так?
Например у нас есть ссылка вида /foo. И логично предположить, что в современном мире мы хотим сохранить хеш (это нужно для angularjs например) чтобы на новой странице отработал нужный функционал.
Что мы делаем
window.location.pathname = '/foo'
Работает.
Но стоит только в ссылку нечаянно попасть хешу #, как поведение браузеров сразу резко меняется.
window.location.pathname='/foo#bar'
Chrome среагирует правильно и отправит нас по ссылке /foo%23bar, а Firefox - нет. У него будет ссылка /foo#bar.
Ниже иллюстрации.
Как видим FF с задачей не справился. И не перекодировал # автоматом.
Баг этот давний - аж 2009го года.
Поэтому внимательно следите за редиректами если у вас есть спецсимволы в урле.
Проще говоря: pathname в FF вседет себя так же, как и href (за мелким исключением вроде сохранения хеша).
Исходники на посмотреть тут.
Ajax и заголовок location: особенности
Все как обычно: в коде ajax-приложения нужно обрабатывать хидер location, который отдает сервер в некоторых случаях.
Это может потребоваться где угодно. И сейчас для нас важен результат (чтобы оно работало).
Как мы все прекрасно знаем хидеры в jquery можно получить через метод getResponseHeader объекта jqXHR (для других библиотек и голого js можно заглянуть в мануалы).
Казалось бы:
$.get('some_url', function(data, status, request) {
console.log(request.getResponseHeader('location'))
})
Но не все так просто.
Если мы отдаем хидер стандартно для php.
header('location: to_url');
То видим. А что видим? А видим, что браузер взял на себя переход по редиректу. Почему? Потому что ответы с кодом 301 и 302 прозрачно обрабатываются самим браузером и в ответе придет уже конечная страница.
Но как только ответ сервера будет 200, так сразу все становится хорошо. Этот код заставляет браузеры забыть об обработке хидера location и позволяет прочитать его на стороне js.
header('location: to_url', true, 200);
В примере можно наглядно посмотреть, как происходит обработка. Достаточно его запустить внутри встроенного сервера php.
Python3: feedparser unicode error
Есть расширение universal feed parser и есть у него одна очень неприятная бага: если установлено расширение chardet, то парсинг лент в юникоде ломается.
$ ./rss.py Traceback (most recent call last):
File "./rss.py", line 11, in <module>
feed = feedparser.parse(rss_response.text)
File "/usr/lib/python3.3/site-packages/feedparser.py", line 3966, in parse
data, result['encoding'], error = convert_to_utf8(http_headers, data)
File "/usr/lib/python3.3/site-packages/feedparser.py", line 3768, in convert_to_utf8
chardet_encoding = str(chardet.detect(data)['encoding'] or '', 'ascii', 'ignore')
TypeError: decoding str is not supported
Неприятно. Можно удалить chardet, но тогда другие расширения, которые от него зависят будут удалены. В том числе и requests.
Значит надо чинить. Но поскольку баг пофикшен только в транке, а текущая стейбл 5.1.3, то надо обновляться из транка.
$ sudo python3-pip install git+https://code.google.com/p/feedparser/ --upgrade
Или накатить патч руками.
Cookies: с ними нужно быть внимательным
Однажды на проекте случлось странное с функционалом. Выясняя причину наткнулся на очень много кода, который работает с кукисами. Но вот только работает код неправильно.
Вообще чем кукиса характеризуется:
- имя
- значение
- путь
- время истечения
- домен
- секурность
Здесь и далее нас будет интересовать домен, поскольку именно он является причиной множества странный и часто трудноуловимых багов.
Возьмем пример https://github.com/RussianPenguin/blogSamples/blob/master/cookies.php и запустим его на локальном сервере пхп (примем за аксиому то, что у нас домен localhost.localdomain резолвится на локалхост).
Теперь зайдем в браузер по нашему скрипту
Видно, что скрипт выставляет две кукисы для одного и того же домена.
Стандарт предписывает, что кукиса с точкой перед именем домена и без точки доступна в пределах всех поддоменов, а точка оставлена лишь для совместимости с более ранними браузерами.
Казалось бы, мы ставим кукисы на одном и том же домене. Кстати, вот код:
// Конфигурация (домен на котором запускаем)
$domain = 'localhost.localdomain';
$cookieName = 'test';
setcookie($cookieName, 'empty host', 3600+time(), '/');
setcookie($cookieName, 'host: ' . $domain, 3600+time(), '/', $domain);
тут я оставил конфигурацию домена руками так как запуск сервера идет на нестандартном порту.
Так вот. Думалось, что браузер должен поставить одну печеньку и в ней должно быть значение, которое передано последним.
Но нет. Если хидер установки кукиса содержит домен, то кукису будет присвоено имя домена с точкой в начале. А если имени домена не указывать, то кукис будет поставлен на текущий домен без точки в начале.
И когда мы попытаемся считать на стороне клиента (или сервера) наш поставленый кукис, то какой из них вернется - это уже немного рандом (хотя нет, все зависит от того, в какой последовательности печеньки ставились).
И то же самое происходит на клиенте: если мы при установки кукиса указываем домен, то браузер поставит кукис на домен с точкой в начале, а если не укажем, то без точки.
Попробуем прописать в тестовом скрипте какое-то значение для печеньки, но не указывать домен. А потом нажмем кнопку “поставить кукис”.
Что мы видим? А видим мы то, что браузер считал значение для текущего домена (первое) и перезаписал его новым. Которое мы выбрали. А второй кукис до перезагрузки остался нетронутым. Круто. А значит нажмем “прочитать печеньку” и увидим наше новое значение.
Однако если мы так думаем, то ошибаемся.
Даже FireCookie что-то показывает - это не значит, что значение печенья поменялось.
Огнелись ничего не поставил.
Но как только мы домен укажем при установке
Так сразу все работает как надо - устанавливается значение и при нажатии на кнопку “прочитать” мы его получаем.
Так вот. Это я о том, что в проекте нельзя смешивать подходы установки мучных изделий. Это приводит к тому, что много-много времени можно угробить в поисках ошибки, которой нет.
Учтите, что если вы заходите повторить этот эксперимент, то на доменах верхнего уровня подобное не работает. Т.е. если мы будем использовать localhost, то подобное поведение не воспроизведется.
Intel Galileo IDE и Linux
Если мы запускаем первый раз IDE для Intel Galileo, то можно увидать очень интересную картину в консоли
$ arduino-1.5.3-Intel.1.0.4/arduino
Board arduino:edison:izmir_ec doesn't define a 'build.board' preference. Auto-set to: EDISON_IZMIR_EC
Board arduino:x86:izmir_fd doesn't define a 'build.board' preference. Auto-set to: X86_IZMIR_FD
Board arduino:x86:izmir_fg doesn't define a 'build.board' preference. Auto-set to: X86_IZMIR_FG
Experimental: JNI_OnLoad called.
Stable Library
=========================================
Native lib Version = RXTX-2.1-7
Java lib Version = RXTX-2.1-7
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
check_group_uucp(): error testing lock file creation Error details:Отказано в доступеcheck_lock_status: No permission to create lock file.
please see: How can I use Lock Files with rxtx? in INSTALL
Ага. А решается все очень просто.
- Наш пользователь должен быть в группе lock
- Для папки /run/lock (/var/lock в некоторых дистрибах) должны стоять права
root:lock (776)