Yazılım

Yazılım işleri

Daha önce SOAP kullanan web hizmetleri hakkında şu yazıyı yazmıştım.
Aradan vakit geçip, Logipik‘i bir web hizmeti haline getirirken, bu kez REST kullanmayı düşündüm ve “hazır kütüphane olarak ne bulabilirim?” diye araştırırken Ersin Güvenç‘in yazdığı easyREST’e denk geldim.
EasyREST, oldukça düzgün kotarılmış ve gereken her şeyi PEAR kütüphanelerinden bir araya topladığından, “stand-alone” çalışabilen bir REST kütüphanesi. Benim indirdiğim sürüme dahil edilmiş olan PEAR paketleri biraz eski olduğundan, PHP 5.3.x üstünde “#” ile yorumlarda notice döndürüyor ama artık o kadar kusur kadı kızında da olur.
Elbette ben EasyREST’i kendi gereksinimlerim doğrultusunda bir miktar değiştirmek zorunda kaldım, ancak REST’e başlangıç noktası olarak çok doğru bir adres olduğunu söyleyebilir ve tüm PHP programcılarına da gönül rahatlığıyla öneririm.
Ersin Güvenç’e, bu kütüphane için çok teşekkür ederim.

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: ,

Annemin Ubuntu’su

Gnu/Linux’la bir geçmişi olan herkesin kafasından “GNU/Linux harikulade, açık kaynak. Üstelik çoğunlukla da hem işletim sisteminin kendi, hem de uygulamaların ezici çoğunluğu bedava. Pekiyi neden yaygın değil?” sorusu mutlaka geçmiştir.
Herkesin bu soruya kendince bir yanıtı vardır. Bana göre de yanıt eğitim ve alışkanlıklarda yatıyor.

Geçtiğimiz hafta içinde bir gün annem (1980 Darbesi olduğunda üniversitede bir kaç yıllık hocaydı. Varın yaşını siz çıkarın) yanında, içinde dizüstü bilgisayarının olduğu bir çantayla çıkageldi. Derdi, kullandığı Windows’un “Lisansınız sahte. Sizi gidi üçkağıtçı sizi!” yollu bağırıyor olmasıydı. Oysa lisansı sahte falan değildi. Büyük olasılıkla yaşadığı Ankara yakınındaki kasabada, herkese “yolunacak kaz” gözüyle bakan bir “bilgisayarcının” gazabına uğrayarak lisanslı özgün Windows CD’sini kaptırmış, bunun yerine eline bir kopya Windows CD’si tutuşturulmuş ve yollanmıştı. Windows destek hattından da kısa zamanda bir sonuç çıkamayacağı anlaşılınca da bilgisayarını kaptığı gibi bize getirmişti. Biz durumu anlayıp, Micro~ Destek Hattı’nı arayıp tatlı tatlı(!) konuşmayı düşünürken, annemin gözü bizim makinelerimizde koşan Ubuntu’ya takıldı.
“Linux bu mu?” diye sordu.
“Budur!” dedik.
“Tamam”, dedi, “Bana da bundan kurun!”
“Aman anne!”, dedim, “sarsmasın Linux seni?”
“Ne olacak! Öğrenirim!” dedi.

Sonuç olarak, annemin bilgisayarına Ubuntu 10.04 kurduk ve masaüstünü de Ubuntu’ya çabuk adapte olacağı şekilde düzenledik. Yukarıda annemin Ubuntu’sunun masaüstü ekran görüntüsü var. (Büyük halini kaçırmayın!)
Eve gittikten sonra bir kere (evet yalnız bir kere) Türkcell Vınn ile İnternet’e bağlanmayı beceremediği için aradı; hepsi o kadar.

Benim annem artık Ubuntu kullanıyor ve çok memnun. Ya sizin anneniz?

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: ,

Tor Projesi

Son birkaç yıldır ülkemizde hemen herkes domain bazlı engellemenin üstesinden gelmeyi öğrendi. Daha yakından ilgililer IP bazlı engellemenin nasıl geçilebileceğini de biliyorlar; anlatacaklarım ilk kez duyanlara:

Tor Projesi Nedir?
Web sitesinden alıntı:
“Tor kendinizi trafik analizine karşı koruyabilmenize yardımcı olan bir yazılım projesidir. Trafik analizi kişisel özgürlüğü ve gizliliği, gizli ticari eylemleri ve ilişkileri ve devlet güvenliğini tehdit eden bir çeşit ağ denetimidir. Tor iletişiminizi dünyanın her tarafından gönüllüler tarafından işletilen dağıtılmış bir ağ üzerinden sağlayarak sizi korur: birilerinin sizin Internet bağlantınızı izleyerek hangi siteleri gezdiğinizi öğrenmesini engeller, ayrıca girdiğiniz sitelerin sizin fiziksel yerinizi öğrenmesini de engeller. Tor, Internet tarayıcıları, anında mesajlaşma istemcileri, uzaktan erişim ve TCP protokolünü kullanan diğer uygulamalar dahil mevcut uygulamalarınızın bir çoğu ile çalışır. ”

Kendileri daha uygun biçimde söylemişlerdir ancak “trafik analizi”, “sizin internette yağtığınız şeylerle” o ya da bu saikle ilgilenmektir.

İnternette özgür olmak, engellenmemek, hepsinden daha basiti, çok beğendiğiniz bir sanatçının bir klibini izlemek istediğinizde can sıkıcı engellerle karşılaşmak istemiyorsanız Tor Projesi sizin içindir.

Aşaşığa Tor kullanarak Ubuntu üstünde Youtube’dan bir Enrico Macias-Ajda Pekkan klibini (herhangi biri ya da bir kurum, sizi teoride Youtube IP adresine ulaştırmak istemese dahi) nasıl izleyebileceğimizi görecdeğiz:

İlk olarak root haklarıyla gedit’i açıyoruz:

1
sudo gedit

ve akabinde Tor reposunu en alta ekliyoruz:
[code]/etc/apt/sources.list[/code]
Daha sonra sırasıyla gereken gpg anahtarını ekliyor ve apt veritabanını güncelliyoruz (herhangi bir komutta terslik yaparsa:

1
sudo !!

yazın, root şifresini girdikten sonra önceki komutu root haklarıyla çalıştıracaktır.
[code]
gpg --keyserver keys.gnupg.net --recv 886DDD89
[/code]
[code]
gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | sudo apt-key add -
[/code]
[code]
apt-get update
apt-get install tor tor-geoipdb polipo
[/code]

Artık Tor sisteminizde hazır. Bir ufak ayar kaldı:

Şu adresten Polipo ayar dosyasını alıyoruz ve yine biraz önce yaptığımız gibi root haklarıyla çalışan gedit’e
[code]/etc/polipo/config[/code] adıyla kaydedip Polipo’yu yeniden başlatıyoruz:
[code]
sudo /etc/init.d/polipo restart
[/code]

Tor artık hazır.

Son olarak Firefox’ta bizim için Tor’U istediğimiz zaman devreye alıp, istemediğimiz zaman devre dışı bırakacak olan “Tor Button” eklentisini kuruyoruz ve Firefox’u yeniden başlatıyoruz. Bu işlemden sonra sağ alt tarafta “Tor Devredışı” yazısını göreceksiniz. Bu yazının üstünü tıklatığınızda ise bu yazı “Tor etkin” şeklinde değişecek.

Ben Enrico Macias’ı seviyorum, siz istediğinizi dinleyebilirsiniz: Adres kutusuna http://youtube.com yazın ve hepsi bu kadar!

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Son günlerdeki Youtube’a erişimin IP tabanlı olarak engellenmesine dair endişe verici gelişmeler beni, uzun süredir yazmak istediğim bir yazıyı iş-güç arasına sıkıştırmaya itti.
Bu yazı iki bölümden oluşuyor:
1- Günümüz Türkiye’sinde internet özelinde vatandaş-devlet dengesi ne durumdadır?
2- Vatandaş yasal olarak özgürlüğünü nasıl savunur?
Yalnız “ben Youtube’da klip izlemek istiyorum hoca; ben keyfime göre takılabildikten sonra çakmışım gerisine!” diyenler yazının birinci bölümünü atlayıp doğrudan ikinci bölüme geçebilirler. Birinci bölüm düşünsel, ikinci bölüm tekniktir ve Tor Project ile kişinin, kendi girmek istediği siteleri yine kendinin nasıl belirleyebileceğini anlatır.
Geri kalanlar için ilk bölüm:
Günümüz Türkiye’sinde internet özelinde vatandaş-devlet dengesi ne durumdadır?

Konuyla ilgili herkes biliyor ancak anımsatmakta yarar var: 5651 sayılı bir yasa var ve internet ülkemizde bu yasa uyarınca “düzenleniyor”. Bazı internet sitelerinin yasaklanması uygulaması, bu yasa hükümleri uyarınca vücut buluyor.

“İnternet özgür ortam”. Bu ifade artık klişe. Burada “özgürlük nedir?” sorusunu kabaca “başkasının özgürlükleriyle sınırlı şey” şeklinde genelgeçer yanıtlamak isterim. Gerçekleştirdiğiniz eylemin bana, öbürüne ya da diğerine herhangi bir zararı, diğerinin özgürlüğünü kısıtlayıcı bir yanı yoksa, o eylemde özgür olmalısınızdır. Eski Yunan’dan beri bu kavram “demokrasi” kavramı ile beraber anılır olmuşsa insanların bin yıllardır edindikleri deneyimlerinden çıkardıkları bir şeyler var demektir.

Hukukçu olmamakla beraber, hukukçuların yorumları ve güncel uygulamalardan çıkarabildiklerim ışığında:
- Bu yasa devletin bir kurumuna “kafasına göre” ve herhangi bir mahkeme kararı aranmasına gerek olmaksızın, “yasaklanma kriterlerine uygun gördüğü” sitelere sizin-benim erişimimizi engelleyebilme hakkı veriyor. Türkçesi, bu kurumda çalışan, maaşları sizin-benim verdiğimiz vergilerle ödenen “uzmanlar”, şu ya da bu sitenin “bizim için uygun olmadığına” karar verip, mezkur yasaya dayanarak yine bizim erişimimizi engelleyebiliyor. (Aşağıda yazacaklarım doğrudan herhangi bir kurum ya da kişiyi hedef almıyor; genel saptamalar niteliğinde okunmalı)
Bu durumun gerçeklenmesini bir sürü porno siteye erişimin engellenmesi şeklinde kendini gösteren uygulamalarda görebiliyoruz. Üstelik iş “porno” olunca kimse elini taşın altına koyup “kardeşim, sana ne!” demek istemiyor. Buyurunuz, işte ben diyorum: Otuzbeş yaşında adamım. İster porno izlerim, ister masturbasyon yaparım ve bunu size soracak değilim. Keyfimin bileceği iştir. Ben gelip size “gece hanımınla şöyle yapma, böyle yap!” diyor muyum? Demiyorum çünkü haddime düşmemiştir. Benim özel hayatımda yaptıklarımın sınırını belirlemek de, başkalarının sınırlarına herhangi bir tecavüz söz konusu olmadığı durumda, kimsenin haddine düşmez. Bu durum, işçinin patronuna “Şu filmleri izleyebilirsin, bunu izleyemezsin!” demesine benzer ve uygar bir sistemde kabul edilmesi mümkün değildir. Devletin, bireyin hizmetinde olduğunu söyleyen herhangi bir sistem için görünen budur.

Yasa koyucu bu durumu göz önüne aldığı için “erişmeye” cezai yaptırım uygulamıyor, ancak “erişim sağlayıcısına” yönelik yaptırımlar öngörüyor.

Bir parantez: (Öte taraftan “genel ahlaka mugayir” kavramı var ki, işte bu kavram içine girebilecek herhangi şeyi bulundurmak da bildiğim kadarıyla suç teşkil edebilir. Herhangi bir hukuk metninin içinde “genel ahlak” türünden öznel ifadeler gördüğüm vakit tüylerim diken diken oluyor. Neye göre yargılayacaksın? Kimin ahlakı “genel”? Bu ayrıca hukukçular ve düşünürler tarafından incelenmesi gereken bir kavram.)

- Diğer nokta “youtube” gibi popüler video paylaşım sitelerinin başına gelenler:
Bu noktada yasaya dayalı bir mahkeme kararı var. Mahkeme eldeki yasaya göre elbette en adil kararı verecektir. Buna göre, Atatürk’e hakaret eden site engellenmeli. Kanımca, bırakın bize hür bir memleket hediye etmiş Atatürk’ü, herhangi sıradan kişiye hakaret, yukarıdaki “başkalarının özgürlüğü” sınırlarını ihlal etmektir ve elbette yaptırımı olmalıdır. Ancak bu yaptırım mutlaka ve mutlaka “hakareti edene” uygulanmalıdır; dünyadaki geri kalan insanlara değil. Gücünüz yetiyorsa gidip bu tür içeriği web sitesinden kaldırtırsınız. Gücünüzün yetmediği durumlarda, hıncınızı gücünüzün yettiklerinden çıkarmak adil değildir.
Tüm dünya bu hakaretleri sınırsızca izleyebilirken, ülkemizdeki insanları, bir takım yabancıların yükledikleri birkaç görüntü yüzünden cezalandırmaya çalışmak, en masum benzetmeyle “katili serbest bırakıp, cinayeti görenlere ’siz bunu görmeyin, yasaktır!’” demektir.

- Son nokta yasanın uygulanabilirliği:
Yasalar gerçekçi olmalıdır. Youtube ülkemizde zaten iki yıldan uzun süredir yasaktı ancak bu yasak ancak İnternet servis sağlayıcılarının alan adı sunucularının yüklerinin hafiflemesine yaradı, başka da bir işe yaramadı. Yürürlükteki yasa sayesinde 12 yaşındaki genç arkadaşlarımız bile “nameserver nedir?” , “DNS protokolü nasıl çalışır?” sorularına yanıt veren alanlarda küçümsenmeyecek deneyim sahibi oldu.
Youtube’a IP bazlı erişim kısıtlaması içeren güncel durumdan sonra da gençlerin, yazının ikinci bölümünde anlatacağım uygulamalar hakkında uzmanlaşmaları da sağlanacağı aşikar olduğundan, yasama organı üyeleri, şapkalarını önlerine koyup aşağıdaki sorular hakkında tekrar düşünmelidir:
1- “Özgürlük” nedir?
2- “Yasak” nedir?
3- “Üstüne oy verdiğim konu hakkında ne derece bilgi sahibiyim?”
4- “Olumlu oy verdiğim bu yasa beni tarih önünde ne duruma sokacak? İnsanlar adımı kimle beraber anacak?”

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler:

PHP-Pcntl süreçleri çatallamak (~fork) için gerçekten önemli bir eklenti, ancak ne yazık ki PECL paketi yok.
Hazır bir deb paketinin el altında bulunmasının faydalı olacağını düşündüm. Buradan indirebilirsiniz.

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: , ,

İki günlük bir aradan sonra serinin yeni bölümüyle(!) karşınızdayım.
Bu yazının ana konusu pcntl destekli, daemon olarak çalışan Azra Web Server. Değişiklik olsun diye önce benchmarklara bakalım. Aşağıdaki tabloda açıkça görülebildiği gibi Azra ağır yük altında bile Apache’den çok iyi, Nginx’ten biraz daha iyi.

X Ortalama (ms) real user sys
Apache 0.00938119351864 0m2.247s 0m0.068s 0m0.028s
Nginx 0,00461062550545 0m4.443s 0m0.152s 0m0.036s
Azra 0,00429985105991 0m1.992s 0m0.112s 0m0.024s

Benchmark yöntemi:
Apache, Nginx ve Azra kullanarak aşağıdaki kodu 400 kere çağırıyoruz.
test.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

$start = microtime(1);

for($n = 0; $n <= 10000; $n++)
{
    echo $n;
}
$end = microtime(1);
$elapsed = $end - $start;
echo "<br/>vakit(ms): $elapsed";
$fileHandle = fopen('serverBenchmark.txt' , 'a');
fputs($fileHandle , $elapsed . "\n");
fclose($fileHandle);

ve test için kullandığımız python kodu (benchmark.py):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import urllib2
from threading import Thread

class testSuite:
    def testConnection(self , url , thread = 0):
        page = urllib2.urlopen(url)
        data = page.read()
        page.close()
        print("Thread:%d" % thread)
           
    def simpleConnection(self , url , tourNum):
        for i in range(tourNum):
            self.testConnection(url)
       
    def threadedConnection(self , url , tourNum , threadNum):
        for i in range(tourNum):
            myThread = i % threadNum
            t = Thread(target=self.testConnection , args=(url , myThread))
            t.start()

myTestSuite = testSuite()

myTestSuite.threadedConnection('http://localhost:8114/test.php' , 400 , 10) #azra
#myTestSuite.threadedConnection('http://localhost/webserver/test.php' , 400 , 10) #apache
#myTestSuite.threadedConnection('http://localhost/test.php' , 400 , 10) #nginx

Gelişmeler:
Artık Azra, Ubuntu altında daemon olarak çalışabiliyor.
/etc/init.d/azra

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#! /bin/sh
### BEGIN INIT INFO
# Provides:          azra
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start and stop azra web server
# Description:       Start and stop azra web server
### END INIT INFO

# Author: Can Ince <[EMAIL PROTECTED]>

# Do NOT "set -e"

PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="AZRA WEB SERVER"
NAME=Azra
DAEMON=/var/www/webserver/azra.php
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
PHP_CONFIG_FILE=/etc/php5/cgi/php.ini

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions

# If the daemon is not enabled, give the user a warning and then exit,
# unless we are stopping the daemon
if [ "$START" != "yes" -a "$1" != "stop" ]; then
        log_warning_msg "To enable $NAME, edit /etc/default/$NAME and set START=yes"
        exit 0
fi

# Process configuration
export PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS
DAEMON_ARGS="-q -b $FCGI_HOST:$FCGI_PORT -c $PHP_CONFIG_FILE"

do_start()
{
        # Return
        #   0 if daemon has been started
        #   1 if daemon was already running
        #   2 if daemon could not be started
        start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
                || return 1
        start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
                --background --make-pidfile --chuid $EXEC_AS_USER www-data $DAEMON -- \
                $DAEMON_ARGS \
                || return 2
}

do_stop()
{
        # Return
        #   0 if daemon has been stopped
        #   1 if daemon was already stopped
        #   2 if daemon could not be stopped
        #   other if a failure occurred
    killall azra.php
        start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE > /dev/null # --name $DAEMON
        RETVAL="$?"
        [ "$RETVAL" = 2 ] && return 2
        # Wait for children to finish too if this is a daemon that forks
        # and if the daemon is only ever run from this initscript.
        # If the above conditions are not satisfied then add some other code
        # that waits for the process to drop all resources that could be
        # needed by services started subsequently.  A last resort is to
        # sleep for some time.
        start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
        [ "$?" = 2 ] && return 2
        # Many daemons don't delete their pidfiles when they exit.
        rm -f $PIDFILE
        return "$RETVAL"
}
case "$1" in
  start)
        [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
        do_start
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
  stop)
        [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
        do_stop
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
  restart|force-reload)
        log_daemon_msg "Restarting $DESC" "$NAME"
        do_stop
        case "$?" in
          0|1)
                do_start
                case "$?" in
                        0) log_end_msg 0 ;;
                        1) log_end_msg 1 ;; # Old process is still running
                        *) log_end_msg 1 ;; # Failed to start
                esac
                ;;
          *)
                # Failed to stop
                log_end_msg 1
                ;;
        esac
        ;;
  *)
        echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
        exit 3
        ;;
esac

… ve /etc/defaults/Azra:

1
2
START=yes
VERBOSE=yes

Azra multi-process olarak çalışabiliyor.
Ana kod (azra.php)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#! /usr/bin/php-cgi
<?php
/**
 * Azra Web Server
 *
 * Web Server coded in pure PHP
 *
 * @package     Azra Web Server
 * @author      Can Ince
 * @copyright           Copyright (c) 2010, Logikit / Can Ince.
 * @version             0.0.2
 * @license     http://www.opensource.org/licenses/mit-license.php
 * @link        http://can.logikit.net
 */


set_time_limit(0);

interface server
{
    public function setWebRoot($path);
    public function setPort($port);
    public function serve();
}

final class webserver implements server
{
    public $socket;
    private $_port , $_webroot , $_queryString , $_webRootWithoutSlash, $_input , $_file , $_verbose , $_remoteAddr , $_remotePort;
    public function __construct()
    {
        $this->_verbose = 0;
        $_SERVER = array();
        $_COOKIE = array();
    }
   
    public function setPort($port)
    {
        $this->_port = $port;
    }
   
    public function verbose()
    {
        $this->_verbose = 1;
    }
   
    public function setWebRoot($path)
    {
        $this->_webroot = $path . '/';
        $this->_webRootWithoutSlash = $path;
    }
   
    private function _mimeHandler($input)
    {
            $fileinfo = pathinfo($input);
            switch ($fileinfo['extension'])
            {
                case 'png':
                    $mime = "text/png";  
                    break;
               
                case 'jpg':
                case 'jpeg':
                    $mime = "text/jpeg";  
                    break;
               
                case 'gif':
                    $mime = "text/gif";  
                    break;
               
                case 'css':
                    $mime = "text/css";  
                    break;
                   
                case 'swf':
                    $mime = "application/x-shockwave-flash";
                    break;
             
                default:
                    $mime = "text/html; charset=UTF-8";
            }
        return $mime;
    }
   
    private function _handleCode($contents)
    {
         //eval() edilecek kodun başında <?php ya da <? varsa çakar. halledelim.
        $contents = trim($contents);
        if(substr($contents , 0 , 5) == '<?php')
        {
            $contents = substr($contents , 5);
        }
        elseif(substr($contents , 0 , 2) == '<?')
        {
            $contents = substr($contents , 2);
        }
       
        return $contents;
    }
   
    private function _parseGet($str)
    {
        $_GET = array();
        $array = explode('&' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_GET[$itemArray[0]] = $itemArray[1];
        }
        unset($_GET['hyperTextServer_php']); //bunu $_GET'e enjekte ediyor. neden, bilmiyorum...
    }
   
    private function _parsePost($str)
    {
        $_POST = array();
        $array = explode('&' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_POST[$itemArray[0]] = $itemArray[1];
        }
    }
   
    private function _parseCookie($str)
    {
       
        $array = explode('; ' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_COOKIE[$itemArray[0]] = $itemArray[1];
        }
    }
   
    private function _parseRequest($str)
    {
       
        $lines = explode("\n" , $str);
        $rawData = explode(' ' , $lines[0]);
        $_SERVER['REQUEST_METHOD'] = $rawData[0];
        if($_SERVER['REQUEST_METHOD'] == 'POST') $this->_parsePost(end($lines));
        $_SERVER['PHP_SELF'] = $this->_webRootWithoutSlash . $rawData[1];
        $_SERVER['HTTP_HOST'] = substr($lines[1] , 6);
        $_SERVER['HTTP_USER_AGENT'] = substr($lines[2] , 12);
        $_SERVER['HTTP_ACCEPT'] = substr($lines[3] , 8);
        $_SERVER['HTTP_ACCEPT_LANGUAGE'] = substr($lines[4] , 17);
        $_SERVER['HTTP_ACCEPT_ENCODING'] = substr($lines[5] , 17);
        $_SERVER['HTTP_ACCEPT_CHARSET'] = substr($lines[6] , 16);
        $_SERVER['HTTP_KEEP_ALIVE'] = substr($lines[7] , 12);
        $_SERVER['HTTP_CONNECTION'] = substr($lines[8] , 12);
        if(strstr($lines[9] , 'Referer'))
        {
            $_SERVER['HTTP_REFERER'] = substr($lines[9] , 9);
            $_SERVER['HTTP_COOKIE'] = substr($lines[10] , 8);
        }
        else $_SERVER['HTTP_COOKIE'] = substr($lines[9] , 8);
       
        if(!empty($_SERVER['HTTP_COOKIE'])) $this->_parseCookie($_SERVER['HTTP_COOKIE']);
       
        $_SERVER['SERVER_SIGNATURE'] = ' <address>Azra/0.0.1 Server at ' . $_SERVER['HTTP_HOST'] . ' Port ' . $this->_port . '</address>';
        $_SERVER['SERVER_SOFTWARE'] = 'Azra/0.0.1';
        $_SERVER['DOCUMENT_ROOT'] = $this->_webroot;
        $_SERVER['SCRIPT_FILENAME'] = $this->_webRootWithoutSlash . str_replace('./' , '/' , $this->_file);
        $_SERVER['SCRIPT_NAME'] = $this->_file;
        $_SERVER['REQUEST_TIME'] = time();
        $_SERVER['REMOTE_ADDR'] = $this->_remoteAddr;
        $_SERVER['REMOTE_PORT'] = $this->_remotePort;
        if(!empty($this->_queryString)) $_SERVER['QUERY_STRING'] = $this->_queryString;
    }
   
    private function _handle404()
    {
        return "<title>UHU 404</title><h2>UHU 404</h2>Iın-ıın-ıın-ıın-cav-cav-cav-cav-oooooo - oooooo - zört - zört - zört -zört (araba alarmı efekti) Çok afedersin müdürüm ama yok öyle bir dosya.";
    }
   
    public function openSocket()
    {
        $this->socket = @socket_create_listen($this->_port);
        if (!$this->socket)
        {
            echo "Soket olamiyor!\n";
            exit;
        }
        $this->_loadConfigHandler();
    }
   
    private function _loadCOnfigHandler()
    {
        $directoryConfigCandidate = $this->_webroot . '/.ConfigHandler.php';
       
        if ($directoryConfigCandidate && is_readable($directoryConfigCandidate))
        {
            require ($directoryConfigCandidate);
        }      
    }
   
    public function serve()
    {

        while (1) //daire-i ilanihaye!
        {
            $client = socket_accept($this->socket);
            socket_getpeername($client , $remoteAddr , $remotePort);
            $this->_remoteAddr = $remoteAddr;
            $this->_remotePort = $remotePort;
            $input = $this->_input = trim(socket_read ($client, 4096));
           
            if($this->_verbose == 1) echo $input;
           
            $input = explode(" " , $input);
            $input = $input[1];

            if(class_exists('ConfigHandler'))
            {
                $configHandler = new ConfigHandler($input , $this->_webroot);
 
                //konvansiyonumuza göre rewrite işi $configHandler'ın url özelliğinde bulunmalı
                if($configHandler->url != NULL) $input = realpath($configHandler->url);
            }
             
            $mime = $this->_mimeHandler($input);
               
            if ($input == "" || $input == '/')
            {
                $input = "/index.html";
            }
            $input = ".$input";
            $input = str_replace('//' , '/' , $input);
            $inputArray = explode('?' , $input);
            $this->_file = $inputArray[0];
           
            $input = $this->_webroot . $this->_file;
            if (file_exists($input) && is_readable($input))
            {
                if($this->_verbose == 1) echo "Dosya: $input\n";
                 
                $contents = file_get_contents($input);
                if(strstr($input , '.php'))
                {
                    if(!empty($inputArray[1])) $this->_queryString = $inputArray[1];
                   
                    $this->_parseGet($this->_queryString);
                    $this->_parseRequest($this->_input);
                   
                    $_REQUEST = array_merge($_GET , $_POST , $_COOKIE);
                   
                    $contents = $this->_handleCode($contents);
                   
                    //PHP çalıştıktan sonra sonucu echo etmesini değil, $contents stringine atmasını istiyoruz.
                    ob_start();
                    eval($contents);
                    //gidip $contents'in üstüne yazmak doğru alışkanlık değil ama yeni değişken register etmek istemiyorum
                    $contents = ob_get_contents();
                    ob_end_clean();
                }
                $output = "HTTP/1.0 200 OK\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: $mime\r\n\r\n$contents";
            }
            else
            {
                $contents = $this->_handle404();
                $output = "HTTP/1.0 404 OBJECT NOT FOUND\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n$contents";
            }
            socket_write($client, $output);
            socket_close ($client);
        }
        socket_close ($this->socket);
    }
   
    public function start()
    {
        $pids = array();

        $this->openSocket();
        for($i = 0; $i < 10; $i++)
        {
            $pids[$i] = pcntl_fork();
           
                if(!$pids[$i])
                {
                    // child process
                    $this->serve();
                    exit();
                }
        }
       
        for($i = 0; $i < 10; $i++)
        {
            pcntl_waitpid($pids[$i], $status, WUNTRACED);
        }
    }
}

$server = new webserver();
$server->setWebRoot('/var/www/act');
$server->verbose(); //ayrıntıları stdout'a echo et.
$server->setPort(8115);
$server->start();

Ara-sonuçlar:
- Bir kaç sorun hala devam ediyor.
– Session’lar düzgün halledilemiyor.
– include’larda sorun yaşanıyor zira dirname(__FILE__) Azra’nın çalıştığı dizini döndürüyor.
- log işine çözüm bulmak lazım.
– header() kullanarak 302 yollanırken path düzgün alınamıyor.
Bu sorunlar da halledildikten sonra şaka olarak başlayan denememiz oldukça ilginç hale gelecek ve bu benchmarkları korumayı başarabilirsem 0.0.3 sürümüne Launchpad yolları görünüyor.

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: , ,

Ubuntu’da PHP-PCNTL Desteği

Ubuntu üstünde PHP kullanıyoruz. Günün birince pcntl desteğine ihtiyacımız oluyor ve tüm PHP’yi yeniden derlemek istemiyoruz. Nasıl yapıyoruz? Şu şekilde:
- Önce bir dizin oluşturuyoruz ve o dizine geçiyoruz. Nerede oluşturduğumuz önemli değil

1
2
mkdir php
cd php

Sistemimizde PHP-dev paketi yoksa kuruyoruz:

1
sudo apt-get install php5-dev

- Daha sonra php source paketini indiriyoruz:

1
apt-get source php5

..ve pcntl dizinine geçiyoruz:

1
cd php5-[php sürümü]/ext/pcntl

Akabinde bildiğimiz PHP modül derleme işi:

1
2
3
phpize
./configure
make

Son olarak derlediğimiz paylaşılmış nesneyi ilgili yere kopyalıyoruz

1
2
cp modules/pcntl.so /usr/lib/php5/phpSürümTarihi/
echo "extension=pcntl.so" > /etc/php5/conf.d/pcntl.ini

Sunucuyu yeniden başlatıp phpinfo() çektiğimizde pcntl’i görüp mutlu oluyoruz.

Tahmin edebileceğiniz gibi, gelecek bölüm: “Bir Web Sunucusu Kodlama Denemesi – 4 (Pcntl destekli Azra)
:-)

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: ,

Heyecan devam ediyor:-)

Bu bölümde:

- Bir önceki sürümde eksik kalan $_SERVER['REMOTE_ADDR'] ve $_SERVER['REMOTE_PORT'] halloldu.

- .ConfigHandler.php isimli dosyayı kullanarak Apache’de .htaccess ile çözülen işler saf PHP ile çözülür hale geldi. Bir rewrite örneğini yazının devamında bulabilirsiniz.

- Benchmark sevenler: Bu bölümde istemediğiniz kadar benchmark var;-) Ayrıca, benchmark işleri küçük bir Python kodu yardımıyla otomatik hale getirildi.

Bir önceki yazımda bir web sunucusunda bulunması gereken temel özelliklerin çoğuna sahip, PHP çalıştıran  bir web sunucusunu, yine PHP kullanarak hayata geçirebilmiş ve adını da Azra Erhat’ın anısına “Azra” koymuştum. Yukarıda gördüğünüz logo için ise, bir bakışta anlayabileceğiniz gibi hiç uğraşmadım, çünkü uğraşsam da daha iyisini yapamayacağımı biliyorum! Hayırsever bir arkadaş düzgün bir logo yapabilirse seve seve kullanırım. Şimdiki logo basitçe Göktürk alfabesi kullanarak “Azra” stringinden ibaret.

Bu sürümde yeni ne var?

- Artık supergloballer ($_POST, $_GET, $_REQUEST, $_COOKIE, $_SERVER) tam olarak destekleniyor.

- .ConfigHandler.php diye bir dosya icat ettim. Azra bu dosya var mı diye bakıyor, varsa içeri alıyor ve ConfigHandler sınıfını çağırıyor.  Aşağıdaki dosyada da görebileceğiniz gibi, örneğin “url rewrite” işi çocuk oyuncağı.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class ConfigHandler
{
    public $url = NULL;
    public function __construct($input)
    {
        $this->azraRewrite($input);
    }
   
    public function azraRewrite($input)
    {
        $candidate = getcwd() . $input;
        if(!is_file($candidate) && !is_dir($candidate)) $this->url = '/router.php?' . $input;
        else $this->url = $input;
    }
}

- Web sunucumuzun yeni sürümü ise şu şekilde:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
<?php
/**
 * Azra Web Server
 *
 * Web Server coded in pure PHP
 *
 * @package     Azra Web Server
 * @author      Can Ince
 * @copyright           Copyright (c) 2010, Logikit / Can Ince.
 * @version             0.0.2
 * @license     http://www.opensource.org/licenses/mit-license.php
 * @link        http://can.logikit.net
 */


set_time_limit(0);

interface server
{
    public function setWebRoot($path);
    public function setPort($port);
    public function serve();
}

final class webserver implements server
{
    private $_port , $_webroot , $_queryString , $_webRootWithoutSlash, $_input , $_file , $_verbose , $_remoteAddr , $_remotePort;
    public function __construct()
    {
        $this->_verbose = 0;
    }
   
    public function setPort($port)
    {
        $this->_port = $port;
    }
   
    public function verbose()
    {
        $this->_verbose = 1;
    }
   
    public function setWebRoot($path)
    {
        $this->_webroot = $path . '/';
        $this->_webRootWithoutSlash = $path;
    }
   
    private function _mimeHandler($input)
    {
            $fileinfo = pathinfo($input);
            switch ($fileinfo['extension'])
            {
                case 'png':
                    $mime = "text/png";  
                    break;
               
                case 'jpg':
                case 'jpeg':
                    $mime = "text/jpeg";  
                    break;
               
                case 'gif':
                    $mime = "text/gif";  
                    break;
               
                case 'css':
                    $mime = "text/css";  
                    break;
                   
                case 'swf':
                    $mime = "application/x-shockwave-flash";
                    break;
             
                default:
                    $mime = "text/html; charset=UTF-8";
            }
        return $mime;
    }
   
    private function _handleCode($contents)
    {
         //eval() edilecek kodun başında <?php ya da <? varsa çakar. halledelim.
        $contents = trim($contents);
        if(substr($contents , 0 , 5) == '<?php')
        {
            $contents = substr($contents , 5);
        }
        elseif(substr($contents , 0 , 2) == '<?')
        {
            $contents = substr($contents , 2);
        }
       
        return $contents;
    }
   
    private function _parseGet($str)
    {
        $_GET = array();
        $array = explode('&' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_GET[$itemArray[0]] = $itemArray[1];
        }
        unset($_GET['hyperTextServer_php']); //bunu $_GET'e enjekte ediyor. neden, bilmiyorum...
    }
   
    private function _parsePost($str)
    {
        $_POST = array();
        $array = explode('&' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_POST[$itemArray[0]] = $itemArray[1];
        }
    }
   
    private function _parseCookie($str)
    {
        $_COOKIE = array();
        $array = explode('; ' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_COOKIE[$itemArray[0]] = $itemArray[1];
        }
    }
   
    private function _parseRequest($str)
    {
        $_SERVER = array();
        $lines = explode("\n" , $str);
        $rawData = explode(' ' , $lines[0]);
        $_SERVER['REQUEST_METHOD'] = $rawData[0];
        if($_SERVER['REQUEST_METHOD'] == 'POST') $this->_parsePost(end($lines));
        $_SERVER['PHP_SELF'] = $this->_webRootWithoutSlash . $rawData[1];
        $_SERVER['HTTP_HOST'] = substr($lines[1] , 6);
        $_SERVER['HTTP_USER_AGENT'] = substr($lines[2] , 12);
        $_SERVER['HTTP_ACCEPT'] = substr($lines[3] , 8);
        $_SERVER['HTTP_ACCEPT_LANGUAGE'] = substr($lines[4] , 17);
        $_SERVER['HTTP_ACCEPT_ENCODING'] = substr($lines[5] , 17);
        $_SERVER['HTTP_ACCEPT_CHARSET'] = substr($lines[6] , 16);
        $_SERVER['HTTP_KEEP_ALIVE'] = substr($lines[7] , 12);
        $_SERVER['HTTP_CONNECTION'] = substr($lines[8] , 12);
        if(strstr($lines[9] , 'Referer'))
        {
            $_SERVER['HTTP_REFERER'] = substr($lines[9] , 9);
            $_SERVER['HTTP_COOKIE'] = substr($lines[10] , 8);
        }
        else $_SERVER['HTTP_COOKIE'] = substr($lines[9] , 8);
       
        if(!empty($_SERVER['HTTP_COOKIE'])) $this->_parseCookie($_SERVER['HTTP_COOKIE']);
       
        $_SERVER['SERVER_SIGNATURE'] = ' <address>Azra/0.0.1 Server at ' . $_SERVER['HTTP_HOST'] . ' Port ' . $this->_port . '</address>';
        $_SERVER['SERVER_SOFTWARE'] = 'Azra/0.0.1';
        $_SERVER['DOCUMENT_ROOT'] = $this->_webroot;
        $_SERVER['SCRIPT_FILENAME'] = $this->_webRootWithoutSlash . str_replace('./' , '/' , $this->_file);
        $_SERVER['REQUEST_TIME'] = time();
        $_SERVER['REMOTE_ADDR'] = $this->_remoteAddr;
        $_SERVER['REMOTE_PORT'] = $this->_remotePort;
        if(!empty($this->_queryString)) $_SERVER['QUERY_STRING'] = $this->_queryString;
    }
   
    private function _handle404()
    {
        return "<title>UHU 404</title><h2>UHU 404</h2>Iın-ıın-ıın-ıın-cav-cav-cav-cav-oooooo - oooooo - zört - zört - zört -zört (araba alarmı efekti) Çok afedersin müdürüm ama yok öyle bir dosya.";
    }
   
    public function serve()
    {
        $socket = @socket_create_listen($this->_port);
        if (!$socket)
        {
            echo "Soket olamiyor!\n";
            exit;
        }
   
        while (1) //daire-i ilanihaye!
        {
            $client = socket_accept($socket);
            socket_getpeername($client , $remoteAddr , $remotePort);
            $this->_remoteAddr = $remoteAddr;
            $this->_remotePort = $remotePort;
            $input = $this->_input = trim(socket_read ($client, 4096));
           
            if($this->_verbose == 1) echo $input;
           

           

               
            $input = explode(" " , $input);
            $input = $input[1];
           
            $dirName = dirname(__FILE__);
     
            $directoryConfigCandidate = $dirName . '/.ConfigHandler.php';
           
            if ($directoryConfigCandidate && is_readable($directoryConfigCandidate))
            {
                require_once ($directoryConfigCandidate);
                if(class_exists('ConfigHandler'))
                {
                    $configHandler = new ConfigHandler($input);
                    //konvansiyonumuza göre rewrite işi $configHandler'ın url özelliğinde bulunmalı
                    if($configHandler->url != NULL) $input = $configHandler->url;
                }
            }
     
            $mime = $this->_mimeHandler($input);
               
            if ($input == "" || $input == '/')
            {
                $input = "/index.html";
            }
            $input = ".$input";
            $inputArray = explode('?' , $input);
            $this->_file = $inputArray[0];
           
            $input = $this->_webroot . $this->_file;
            if (file_exists($input) && is_readable($input))
            {
                if($this->_verbose == 1) echo "Dosya: $input\n";
                 
                $contents = file_get_contents($input);
                if(strstr($input , '.php'))
                {
                    $this->_queryString = $inputArray[1];
                   
                    $this->_parseGet($this->_queryString);
                    $this->_parseRequest($this->_input);
                   
                    $_REQUEST = array_merge($_GET , $_POST , $_COOKIE);
                   
                    $contents = $this->_handleCode($contents);
                   
                    //PHP çalıştıktan sonra sonucu echo etmesini değil, $contents stringine atmasını istiyoruz.
                    ob_start();
                    eval($contents);
                    //gidip $contents'in üstüne yazmak doğru alışkanlık değil ama yeni değişken register etmek istemiyorum
                    $contents = ob_get_contents();
                    ob_end_clean();
                }
                $output = "HTTP/1.0 200 OK\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: $mime\r\n\r\n$contents";
            }
            else
            {
                $contents = $this->_handle404();
                $output = "HTTP/1.0 404 OBJECT NOT FOUND\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n$contents";
            }
            socket_write($client, $output);
            socket_close ($client);
        }
        socket_close ($socket);
    }
}
$server = new webserver();
$server->setWebRoot('/var/www/webserver');
//$server->verbose(); //ayrıntıları stdout'a echo et.
$server->setPort(8008);
$server->serve();

konsoldan

1
php-cgi azra.php

şeklinde çağırmak suretiyle çalıştırıyoruz.

- Benchmarklar:

Benchmark işleri için aşağıdaki test.php dosyasını kullandım. Göreceğiniz gibi bir for döngüsüne girip 10001′e kadar saymaktan ve çalışma süresini bir metin dosyasına yeni satır olarak yazmaktan başka bir iş yapmıyor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

$start = microtime(1);

for($n = 0; $n <= 10000; $n++)
{
    //bir şey yapma. yalnızca say...
}
$end = microtime(1);
$elapsed = $end - $start;
echo "<br/>vakit(ms): $elapsed";
$fileHandle = fopen('serverBenchmark.txt' , 'a');
fputs($fileHandle , $elapsed . "\n");
fclose($fileHandle);

Testleri otomatik hale getirebilmek için de, en kolayıma Python geldiği için kısa ve basit bir kod yazdım:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import urllib2
from threading import Thread

class testSuite:
    def testConnection(self , url , thread = 0):
        page = urllib2.urlopen(url)
        data = page.read()
        page.close()
        print("Thread:%d" % thread)
           
    def simpleConnection(self , url , tourNum):
        for i in range(tourNum):
            self.testConnection(url)
       
    def threadedConnection(self , url , tourNum , threadNum):
        for i in range(tourNum):
            myThread = i % threadNum
            t = Thread(target=self.testConnection , args=(url , myThread))
            t.start()

myTestSuite = testSuite()
#myTestSuite.simpleConnection('http://localhost:8008/test.php' , 1000)
#myTestSuite.simpleConnection('http://localhost/webserver/test.php' , 1000)
myTestSuite.threadedConnection('http://localhost:8008/test.php' , 1000 , 10)

Bu kodu ‘benchmark.py’ adıyla kaydedip konsoldan

1
time python benchmark.py

ile çağırıyoruz ve böylece ne kadar sürede çalıştığını ayrıntılarıyla okuyabiliyoruz. Benim apache için webroot’um /var/www. Azra için webroot’um ise /var/www/webserver. localhost:8008′den Azra’ya, localhost:80 (varsayılan port) adresinden Apache’ye ulaşabiliyoruz.

Bunların yanında, bir tür “pseudo-multi-processing” elde edebilmek için aşağıdaki PHP kodunu kullandım:

1
2
3
4
5
6
7
8
9
10
<?php
set_time_limit(0);
include ("thread.php");

$t2 = new Thread("azra.php");
while ($t2->isActive())
{
    echo $t2->listen();
}
$t2->close();

Bu kodu da konsoldan

1
php-cgi server.php

ile çağırıyoruz. Göreceğiniz gibi server.php, yeni bir PID ile bildiğimiz, sevdiğimiz azra.php’yi çağırıyor.

Kullandığımız üçüncü parti PHP sınıfı (thread.php) ise aşağıda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<?php

/**
 * original version can be found at http://www.alternateinterior.com/2007/05/multi-threading-strategies-in-php.html
 * This version is slightly modified to run with PHP 5 - Can Ince
 */

class Thread
{
    var $pref ; // process reference
    var $pipes; // stdio
    var $buffer; // output buffer
    public function __construct($file)
    {
        $this->pref = 0;
        $this->buffer = "";
        $this->pipes = (array)NULL;
       
        $descriptor = array (0 => array ("pipe", "r"), 1 => array ("pipe", "w"), 2 => array ("pipe", "w"));
        $this->pref = proc_open ("php $file ", $descriptor, $this->pipes);
        stream_set_blocking ($this->pipes[1], 0);
    }

   
    public function isActive()
    {
        $this->buffer .= $this->listen();
        $f = stream_get_meta_data ($this->pipes[1]);
        return !$f["eof"];
    }
   
    public function close()
    {
        $r = proc_close ($this->pref);
        $this->pref = NULL;
        return $r;
    }
   
    public function tell($thought)
    {
        fwrite ($this->pipes[0], $thought);
    }
   
    public function listen()
    {
        $buffer = $this->buffer;
        $this->buffer = "";
        while ($r = fgets ($this->pipes[1], 1024))
        {
            $buffer .= $r;
        }
        return $buffer;
    }
   
    public function getError()
    {
        $buffer = "";
        while ($r = fgets ($this->pipes[2], 1024))
        {
            $buffer .= $r;
        }
        return $buffer;
    }
}

Benim lokal sistemimde benchmarklar aşağıdaki gibi çıktı:

—-Azra——–

0.00114736318588 – 10 farklı thread – 100 tur
real    0m0.334s
user    0m0.056s
sys    0m0.020s

0.0180547189713 – 10 farklı thread – 1000 tur
real    0m2.982s
user    0m0.192s
sys    0m0.056s

0.00165226125717 – 100 farklı thread – 1000 tur
real    0m5.251s
user    0m0.196s
sys    0m0.056s

0.00191986322403 – 10 farklı thread – 10000 tur
real    0m38.442s
user    0m1.640s
sys    0m0.608s

0.00110848665237 – tek thread – 100 tur
real    0m0.354s
user    0m0.092s
sys    0m0.068s

0.0111782860756 – tek thread – 1000 tur
real    0m3.421s
user    0m0.744s
sys    0m0.424s

0.00116171479225 – tek thread – 1000 tur (pseudo-multiprocess destekli)
real    0m4.293s
user    0m0.792s
sys    0m0.384s

0.00114714858532 – tek thread – 10000 tur
real    0m37.138s
user    0m8.209s
sys    0m4.280s

—-Apache——-

0.00138350725174 – 10 farklı thread – 100 tur
real    0m0.360s
user    0m0.068s
sys    0m0.028s

0.0136778688431 – 10 farklı thread – 1000 tur
real    0m3.524s
user    0m0.176s
sys    0m0.060s

0.00142541861534 – 100 farklı thread – 1000 tur
real    0m4.030s
user    0m0.144s
sys    0m0.040s

0.00147548000813 – 10 farklı thread – 10000 tur
real    0m46.358s
user    0m1.476s
sys    0m0.496s

0.00153880357742 – tek thread – 100 tur
real    0m0.499s
user    0m0.140s
sys    0m0.068s

0.0135918140411 – tek thread – 1000 tur
real    0m3.824s
user    0m0.972s
sys    0m0.504s

0.0135918140411 – tek thread – 10000 tur
real    1m7.090s
user    0m9.149s
sys    0m4.984s

Benchmarklara hızlıca göz attığımızda fark edebileceğimiz gibi, Azra multi-threaded isteklerde yavaş kalıyor. Bu durumu yeni Ubuntu çıktıktan sonra, PHP’yi pcntl ile derleyerek aşabilmeyi umuyorum. Diğer benchmarkların hepsinde Azra, Apache’ye fark atıyor.

Yeni bölümde yeni maceralarla görüşmek üzere:)

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: , ,

Önceki yazımda basit bir web sunucusu kodlama denemesini anlatmıştım.

Bugün biraz daha vakit bulabildim ve hem yapay bir tür multi-processing ile denemeler yaptım, hem de superglobal değişkenleri işledim.

Başarısız taraftan başlayayım: Şu adreste bulunan PHP sınıfını -biraz tozunu alarak- kullandım zira yeni Ubuntu’ya bir hafta kala pcntl desteği ile yeniden PHP derlemekle uğraşmak istemedim. Ubuntu’yu yükselttikten sonra bu konuya döneceğim. Yukarıda bahsi geçen sınıf ile thread oluşturmak ise sistem kaynakları açısından korkunç pahalı sonuçlar ortaya çıkardı.

Başarılı tarafa gelince: Tüm supergloballeri ($_GET, $_POST, $_COOKIE, $_SERVER, $_REQUEST) $_SERVER['REMOTE_ADDR'] dışında başarıyla ayrıştırdım. Böylece ufak-tefek bir-iki eksik dışında gayet başarıyla çalışan, PHP üstünde yazılmış ve native olarak PHP destekleyen bir web sunucumuz oldu.

Elbet artık bu sunucuya bir de isim bulmak gerekti. Altan Tanrıverdi Hypatia‘yı önerdi. Bir bilim kadınının adını vermek fikri hoşuma gitmekle birlikte daha “bizden” bir ismi tercih ettim. Tanrıverdi’nin “bilimkadını adı” önerisi artı “bizden isim” arayışı, beni Azra Erhat‘ın anısına götürdü. Kodun kulağına ezanını okudum, vaftiz ettim ve adını böylece “Azra” koydum.

Kodun yeni hali şöyle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
<?php
/**
 * Azra Web Server
 *
 * Web Server coded in pure PHP
 *
 * @package     Azra Web Server
 * @author      Can Ince
 * @copyright           Copyright (c) 2010, Logikit / Can Ince.
 * @license     http://www.opensource.org/licenses/mit-license.php
 * @link        http://can.logikit.net
 */


interface server
{
    public function setWebRoot($path);
    public function setPort($port);
    public function serve();
}

final class webserver implements server
{
    private $_port , $_webroot , $_queryString , $_webRootWithoutSlash, $_input , $_file , $_verbose;
    public function __construct()
    {
        $this->_verbose = 0;
    }
   
    public function setPort($port)
    {
        $this->_port = $port;
    }
   
    public function verbose()
    {
        $this->_verbose = 1;
    }
   
    public function setWebRoot($path)
    {
        $this->_webroot = $path . '/';
        $this->_webRootWithoutSlash = $path;
    }
   
    private function _mimeHandler($input)
    {
            $fileinfo = pathinfo($input);
            switch ($fileinfo['extension'])
            {
                  case 'png':
                      $mime = "text/png";  
                      break;
                 
                  case 'jpg':
                  case 'jpeg':
                      $mime = "text/jpeg";  
                      break;
                 
                  case 'gif':
                      $mime = "text/gif";  
                      break;
                 
                  case 'css':
                      $mime = "text/css";  
                      break;
             
                  default:
                      $mime = "text/html; charset=UTF-8";
            }
        return $mime;
    }
   
    private function _handleCode($contents)
    {
         //eval() edilecek kodun başında <?php ya da <? varsa çakar. halledelim.
        $contents = trim($contents);
        if(substr($contents , 0 , 5) == '<?php')
        {
            $contents = substr($contents , 5);
        }
        elseif(substr($contents , 0 , 2) == '<?')
        {
            $contents = substr($contents , 2);
        }
       
        return $contents;
    }
   
    private function _parseGet($str)
    {
        $_GET = array();
        $array = explode('&' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_GET[$itemArray[0]] = $itemArray[1];
        }
        unset($_GET['hyperTextServer_php']); //bunu $_GET'e enjekte ediyor. neden, bilmiyorum...
    }
   
    private function _parsePost($str)
    {
        $_POST = array();
        $array = explode('&' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_POST[$itemArray[0]] = $itemArray[1];
        }
    }
   
    private function _parseCookie($str)
    {
        $_COOKIE = array();
        $array = explode('; ' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_COOKIE[$itemArray[0]] = $itemArray[1];
        }
    }
   
    private function _parseRequest($str)
    {
        $_SERVER = array();
        $lines = explode("\n" , $str);
        $rawData = explode(' ' , $lines[0]);
        $_SERVER['REQUEST_METHOD'] = $rawData[0];
        if($_SERVER['REQUEST_METHOD'] == 'POST') $this->_parsePost(end($lines));
        $_SERVER['PHP_SELF'] = $this->_webRootWithoutSlash . $rawData[1];
        $_SERVER['HTTP_HOST'] = substr($lines[1] , 6);
        $_SERVER['HTTP_USER_AGENT'] = substr($lines[2] , 12);
        $_SERVER['HTTP_ACCEPT'] = substr($lines[3] , 8);
        $_SERVER['HTTP_ACCEPT_LANGUAGE'] = substr($lines[4] , 17);
        $_SERVER['HTTP_ACCEPT_ENCODING'] = substr($lines[5] , 17);
        $_SERVER['HTTP_ACCEPT_CHARSET'] = substr($lines[6] , 16);
        $_SERVER['HTTP_KEEP_ALIVE'] = substr($lines[7] , 12);
        $_SERVER['HTTP_CONNECTION'] = substr($lines[8] , 12);
        if(strstr($lines[9] , 'Referer'))
        {
            $_SERVER['HTTP_REFERER'] = substr($lines[9] , 9);
            $_SERVER['HTTP_COOKIE'] = substr($lines[10] , 8);
        }
        else $_SERVER['HTTP_COOKIE'] = substr($lines[9] , 8);
       
        if(!empty($_SERVER['HTTP_COOKIE'])) $this->_parseCookie($_SERVER['HTTP_COOKIE']);
       
        $_SERVER['SERVER_SIGNATURE'] = ' <address>Azra/0.0.1 Server at ' . $_SERVER['HTTP_HOST'] . ' Port ' . $this->_port . '</address>';
        $_SERVER['SERVER_SOFTWARE'] = 'Azra/0.0.1';
        $_SERVER['DOCUMENT_ROOT'] = $this->_webroot;
        $_SERVER['SCRIPT_FILENAME'] = $this->_webRootWithoutSlash . str_replace('./' , '/' , $this->_file);
        $_SERVER['REQUEST_TIME'] = time();
        if(!empty($this->_queryString)) $_SERVER['QUERY_STRING'] = $this->_queryString;
    }
   
    public function serve()
    {
        $socket = @socket_create_listen($this->_port);
        if (!$socket)
        {
          echo "Soket olamiyor!\n";
          exit;
        }

        while (1) //daire-i ilanihaye!
        {
          $client = socket_accept($socket);

          $input = $this->_input = trim(socket_read ($client, 4096));

          if($this->_verbose == 1) echo $input;
         
          $input = explode(" " , $input);
          $input = $input[1];

          $mime = $this->_mimeHandler($input);
         
          if ($input == "" || $input == '/')
          {
            $input = "/index.html";
          }
          $input = ".$input";
          $inputArray = explode('?' , $input);
          $this->_file = $inputArray[0];
          $input = $this->_webroot . $this->_file;


          if (file_exists($input) && is_readable($input))
          {
            if($this->_verbose == 1) echo "Dosya: $input\n";
           
            $contents = file_get_contents($input);
            if(strstr($input , '.php'))
            {
                $this->_queryString = $inputArray[1];
               
                $this->_parseGet($this->_queryString);
                $this->_parseRequest($this->_input);
               
                $_REQUEST = array_merge($_GET , $_POST , $_COOKIE);
               
                $contents = $this->_handleCode($contents);
               
                //PHP çalıştıktan sonra sonucu echo etmesini değil, $contents stringine atmasını istiyoruz.
                ob_start();
                eval($contents);
                //gidip $contents'in üstüne yazmak doğru alışkanlık değil ama yeni değişken register etmek istemiyorum
                $contents = ob_get_contents();
                ob_end_clean();
            }
            $output = "HTTP/1.0 200 OK\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: $mime\r\n\r\n$contents";
          }
          else
          {
            $contents = "<title>UHU 404</title><h2>UHU 404</h2>Iın-ıın-ıın-ıın-cav-cav-cav-cav-oooooo - oooooo - zört - zört - zört -zört (araba alarmı efekti) Çok afedersin müdürüm ama yok öyle bir dosya.";
            $output = "HTTP/1.0 404 OBJECT NOT FOUND\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n$contents";
          }
          socket_write($client, $output);
          socket_close ($client);
        }
        socket_close ($socket);
    }
}
$server = new webserver();
$server->setWebRoot('/var/www/webserver');
//$server->verbose(); //ayrıntıları stdout'a echo et.
$server->setPort(8008);
$server->serve();

…ve benchmarklar:

Önceki yazıdaki benchmarkla aynı şartlarda ve aynı php dosyasını kullanarak 100 çalıştırma ortalaması

apache: 0.012167468071

azra: 0.00674695968628

Temiz şekilde iki kat hızlı.

socket_getpeername() de düzgün çalışabilir ve peer adresini alabilirsem $_SERVER['REMOTE_ADDR'] de dolacak ve hiç sorun kalmayacak.

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: ,

Önyargılar Üzerine

Bu yazı bir süredir kafamın içinde bir yerlerde yazılmayı bekliyordu; bugünü bekliyormuş.

Öncelikle yazılarını -genel olarak yaklaşım açısını- fevkalade bulduğum, beğendiğim Altan Tanrıverdi’den bir alıntı yapmak isterim:
‘Doğrusu da budur. Diğer taraftan “senin yerinde olsaydım” ibaresi ile başlayan cevaplar da yanlış sayılmaz ki birazdan ben de öyle yapacağım.’
Yazının tamamı da oldukça ilginç ve şuradan okunabilir.

Tanrıverdi yazısında, “deneyimli bir programcıdan çaylağa nasihatler” kabilinden  -doğru yorumlayabildiğim ölçüde- diğer unsurların yanında, satır aralarında önyargılardan kaçınmak gerektiğinden de dem vuruyor.

Ben de fikirlerimi bu çerçeveye oturtmak bu buradan yola çıkarak düşüncelerimi geliştirmek isterim:

“Çok bilen çok önyargı sahibi olandır.” genel olarak doğru bir önerme olarak kabul görebilir, ta ki “düzgün fikir sahibi olmak” ile “önyargı sahibi olmak” ayrıştırılabilsin.

Son günlerde heyecanlı gençleri sıkça görüyorum: “Hoca, iyi diyorsun da, falancayı filanca şekilde yapsak daha iyi sonuç alamaz mıydık?”

…ve sıkça yanıtlıyorum: “Ben benchmark severim. Görelim!”

Doğal olarak işbu genç arkadaşlarımızın bir çoğu “Görürüz!” diyor da arkasını -adı üstünde “gençlik” hezeyanının sonucu- bir türlü getiremiyor.

Oysa ben ne söylüyorum? “Disiplinli düşünebilme yetisi en büyük yetenektir.”

“Disiplinli düşünme” ya da “düşünceyi disiplin altına alma” derken bir yöntemler bütününden söz ediyorum; önyargılardan değil.

Devlet dairesinde memur olacak kişiler yazının bundan sonrasını okumayabilirler, ancak gelecek için kendinden bir şeyler bekleyenler okumaya devam etmeli.

- Eğitim sistemimiz ne yazık ki analist yetiştirmek üstüne değil, “vasat adam” yetiştirmek üstüne kurulu. Bu sistemden “ukala papağanlar yetiştirmek” durumundan ötesini -ne yazıktır- elde edemiyoruz. Çocuk pırıl pırıl, harika bir sistem çözümleyicisi olacak, biz ona “hayır!” diyoruz, “önce üniversite bilmemne hadisesini kazan!” ve beynini böylece -o vakte kadar elimizden kurtarabilmişse bile- düdüklüyoruz, dimağının ırzına geçiyoruz. Ondan sonra da oradan mezun olunca adama (bakın artık “adam” oldu) “haydi, sistemlerimiz, kıçımız başımız sana emanet!” diyoruz. (Tabii işin böyle olduğunu bildiğimiz için demiyoruz ama sistem buna yönelik)

- İşbu (artık) “adam” mektebi bitirdikten sonra elimize düşüyor ve dünyanın gerçekte kaç bucak olduğunu biraz olsun görmeye başlıyor: Zaten düdüklenmiş beyniyle (özellikle yazılım sektöründen söz ediyorsak) okulda hoca olmaktan gayrısını başaramamış hocaların elinde, beyni çift kat düdüklenmiş adamdan söz ediyoruz. Söz ediyoruz ve bu adamların sektörde “insan kaynağı” diye ortalıkta dolaştığını da böylece söylüyoruz.

- “Kendini geliştirmek istiyor musun? Unut!” diyoruz, “okulda ne öğrendiysen unut, çünkü hepsi ya önyargılı, ya yanlış, ya eksik ya da düpedüz palavra! Yeni doğmuşsun gibi yap. Aç bak, dokümantasyon orada. En başından oku, hepsini oku. Sonra gel bize anlat ‘ruby fastcgi ile şöyle çalışırmış’, ‘python aslında ne kadar şahaneymiş, php bilmemneymiş’. ama önce oku! Edindiğin bilgiyi sınıflandırmak için, sana uygun gelebilecek bir yöntem geliştir. Bunları yapmıyorsan, ancak devlet dairesinde memur olursun. Çocukların da devlet dairesinde memur olur.”

Bunları söylerken devlet memuru olma durumunu aşağılıyor muyuz? Hayır. Haddimize düşmez. Öte taraftan, bir yazılımcının devlet memuru ya da okulda hoca olmak için hakikaten çok rasyonel gerekçeleri olmalı. Sizin kişisel olarak verebileceklerinizin çok çok sınırlı olduğu bir ekosistemde isterseniz allame-i cihan olun, hiç ama hiç bir şeyi değiştiremezsiniz. Oysa, bir yazılımcı “bir şeyleri daha doğru yapmak” için vardır; öyle yazılımcıdır.

Önceki gün yazdığı kod hakkında (istediği kadar iyi çalışıyor olsun) şüpheye düşmeyen adama dahi artık yazılımcı değil “kod ağası” denir.

Öğrendiklerinizin, uzun süredir eski öğrendiklerinizin yerini almaması durumunu gözlemliyorsanız (yaşınız kaç olursa olsun) kod yazmaktan elinizi-ayağınızı-eteğinizi çekmenin vakti gelmiş demektir.

Yok, aksi durumda diretiyor ve üstelik ukalalık yapmaya kalkıyorsanız, yeri burası değildir!

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: ,

« Older entries