WEB SUNUCUSU NEDİR? BASİT BİR WEB SUNUCUSU NASIL YAPILIR?

Cüneyt
8 min readJun 19, 2022

--

Web, son 20 yılda toplumu çok fazla değiştirdi. Ama aslında temeli çok az değişti. Çoğu sistem hala Tim Berners Lee’nin 25 sene önce belirlediği kurallar üzerine kurulmuştur. Çoğu web server hala daha aynı tipte mesajları aynı yol ile kullanmaktadır

Web’deki neredeyse her program Internet Protocol (IP) diye adlandırılan bir iletişim ailesi üzerinde çalışmaktadır. Bu protokol (TCP/IP) bilgisayarlar arasında dosya okuma ve yazmayı sağlamaktadır.

IP

IP üzerinden iletişim kuran programlar soketler aracılığı ile haberleşmektedir. Her soket, örneğin bir telefonun telefon görüşmesinin bir ucu olması gibi, noktadan noktaya iletişim kanalının bir ucudur. Bir soket bir bilgisayarı tanımlayan bir IP adresi ve o makinede bulunan bir port’tan oluşmaktadır. IP adresi örneğin 174.136.14.108 gibi 8-bit adresten oluşmaktadır. Domain Name System ise (DNS) IP adresinin sembolik isimler (aosabook.org) ile eşleştirilerek daha okunabilir ve kalıcı hale getirilmesini sağlamaktadır.

PORT

Port numarası ise 0–65535 arasında bir sayı olup makinedeki soketi tanımlamaktadır. (Eğer IP adresi bir firmanın telefon numarası gibi düşünülürse port numarası da dahili numara gibi düşünülebilir. ) 0–1023 kadar olan port’lar işletim sistemi tarafından kullanılmaktadır. Bunların dışında kalan port’lar başka işler ve başkaları tarafından kullanılabilir.

HTTP

Hypertext Transfer Protocol (HTTP) internet protokolleri arasında (IP) veri alışverişi yapmayı sağlamaktadır. HTTP temelde çok basit çalışmaktadır. İstemci bir soket bağlantısının üzerinden ne istediğini belirterek bir istek yollamaktadır ve sunucu da bu isteğe karşılık olarak bir cevap dönmektedir. Burada dönülen cevap bellekten kopyalanmış bir dosya, bir program tarafından dinamik olarak üretilmiş bir veri veya bu ikisinin birleşiminden oluşturulmuş bir cevap olabilir.

HTTP İsteği akışı

Bir HTTP isteği ile ilgili en önemli şey aslında bunun sadece metinden (text) oluşmasıdır. Bu metnin anlaşılabilmesi için metnin belirli bölümlerden oluşması gerekmektedir.

HTTP isteği

Bir HTTP metodu bir bilgi almak istiyorsa GET veya bir veri/dosya göndermek istıyorsa POST olmaktadır. URL istemcinin ne istediğini belirtmektedir, genellikle sunucudaki bir dosya olmaktadır örneğin /research/experiments.html fakat burada dikkat edilmesi gereken husus bununla ne yapacağına karar vermek tamamen sunucu’ya kalmıştır. HTTP versiyonları HTTP/1.0 veya HTTP/1.1 olmaktadır.

HTTP header’ları key/value değerlerinden oluşmaktadır. Örneğin;

Accept: text/html
Accept-Language: en, fr
If-Modified-Since: 16-May-2005

Buradaki key değerleri herhangi bir sayıda olabilir. Bu bir isteğin çeşitli içerik türlerini kabul edeceğini belirtmeye olanak sağlamaktadır.

Son olarak isteklerin body’si bu istekle ilişkili olan ve gönderilmesi gereken ekstra verilerin konumlandırıldığı kısımdır. Bu kısım web formlardan veri gönderilmesinde, dosya yüklenmesinde vb. kullanılmaktadır. Son header ve body kısmının başladığı yerde boş bir satır olması gerekmektedir. Bu satır istek metni içerisindeki header’ların bittiğini göstermektedir.

Content-Length sunucuya body içerisinde ne kadar byte’tan oluşan bir veri okuyacağını belirten bir header’dır.

HTTP cevapları da HTTP istekleri gibi standart formata sahiptir.

HTTP İsteği Cevabı

Burada version, header ve body HTTP isteğinde olduğu gibi aynı anlamı taşımaktadır.

Status Code, istek tamamlandığında isteğin sunucu tarafında işlendiğine cevabın ne olduğuna yönelik durumu gösteren bir sayıdır. Örneğin 200 her şeyin düzgün çalıştığını, 404 hedefin bulunmadığını ve diğer durum kodları da farklı anlamlar taşıyan cevapları belirtmektedir. Burada status phrase sayı ile gösterilen durumun okunabilir olarak belirtildiği bir kısımdır. (”200 → OK”, “404 → NOT FOUND”).

Bu bölümde HTTP ile alakalı olarak bilinmesi gereken 2 önemli durum daha vardır.

Bunların birincisi stateless’dır. HTTP isteklerinde sunucu art arda yapılan HTTP istekleri arasındaki ilişkiyi bilmemektedir. Eğer bir uygulama bir şeyi veya bir kullanıcıyı takip etmek istiyorsa bunu kendi başına yapmalıdır. Bunun en genel kullanımı ise sunucunun istemciye, istemcinin de sunucuya gönderdiği bir karakter dizisini çerezler’i (cookie) kullanarak yapılmaktadır. Eğer bir kullanıcı birden fazla isteği ilgilendirebilecek bir aksiyonda bulunacağı zaman, yani bu isteğinin sonuçlarının diğer isteklerinin etkileneceği gibi durumlarda sunucu bir cookie oluşturur ve bunu bir veritabanında depolayarak kullanıcıya göndermektedir. Herhangi bir anda oluşuturulan ve depolanmış olan bu cookie ile gelen istek ile hangi kullanıcının ne yaptığı takip edilebilir ve HTTP istek cevapları da buna göre dönülebilmektedir.

HTTP hakkında bilinmesi gereken ikinci durum ise daha fazla bilgi sağlayabilmek için URL’lerin parametreler ile desteklenebilmesidir.

Örneğin bir arama motoru yapılıyorsa arama yapılan terimlerin sunucuya belirtilmesi gerekmektedir. Bu URL’deki yola eklenebilir fakat bunu yapabilmenin daha doğru yöntemi URL’e parametre olarak eklenmesiyle olacaktır. URL içerisinde parametre eklenmesi “&” ile ayrılmış “key=value” ikililerinden önce “?” ekleyerek yapılmaktadır. Örneğin **http://www.google.ca?q=Python**

URL’i, Google’a “Python” değeri ile ilgili sayfaların getirilmesi için bir istek yapılmasını sağlamaktadır. Burada key q ve bunun değeri ise Python olmaktadır.

Birden fazla parametreli ve uzun bir sorguya örnek olarak http://www.google.ca/search?q=Python&client=Firefox URL’i verilebilir. Bu URL aracılılıyla yapılan istekte ise Google’a aranması istenen değerin yanında tarayıcı olarak Firefox’un kullanıldığı belirtilmiştir. Bunun yanında istenilen sayıda parametre eklenebilmektedir.

URL parametrelerinde verilen örneklerden anlaşılacağı üzere “&” ve “?” karakterleri özel karakterlerdir. Bu karakterlerin URL içerisinde kullanılması gerektiği durumlar ortaya çıkabilir. Bunun için URL encodig standartları “%” karakterinden sonra gelecek 2 basamaklı kodu, karşılığında gelecek karakter ile değiştirmektedir. Örneğin “grade = A+” (boşluklar ile birlikte) gibi bir arama yapılması için http://www.google.ca/search?q=grade+%3D+A%2B URL kullanılmaktadır.

Soket açmak HTTP isteği yapmak, cevapları anlaşılabilir bir şekilde formatlamak zahmetli ve vakit almaktadır. Bunun için çoğu geliştirici bunlar için kütüphaneler kullanmaktadır. Örneğin Python urllib2 adındaki bir kütüphane ile birlikte gelmektedir.

status code: 200
content length: 61
<html>
<body>
<p>Test page.</p>
</body>
</html>

request.get sunucuya bir HTTP GET isteği yollamaktadır ve sunucu da buna cevap olarak bir obje dönmektedir. Bu obje içerisindeki status_code cevabın durumunu belirtmektedir. content_length ise cevabın ne kadar byte’tan oluştuğunu belirtmektedir ve text (örnekte bir HTML sayfası) ise asıl cevabı belirtmektedir.

Merhaba Web

1- Sunucuya bağlanıp bir HTTP isteği yollanması

2- İsteği ayrıştırmak

3- İsteğin anlamlandırılması

4- Verilecek cevabın oluşturulması

5- Verinin HTML olarak formatlanması

6- Geri yollanması

1., 2. ve 6. adımlar farklı uygulamalarda da değişmeyecek adımlardır. Pyhton (3.7.4) kendi içerisinde http.server bulunmaktadır. Bunun içerisinde BaseHTTPRequestHandler kullanılarak aşağıdaki gibi bir basit örnek oluşturulabilir.

BaseHTTPRequestHandler sınıfı gelen HTTP isteğini ayrıştırıp hangi metot olduğuna karar verebiliyor. Eğer GET metoduysa do_GET metodunu çağırmaktadır. Yukarıdaki örnekte oluşturulmuş olan RequestHandler sınıfı bu metodu override ederek dinamik olarak bir basit bir html sayfasını (Page) cevap olarak dönmektedir. Cevap verilirken Content-Type header’ı ile istemciye cevabın bir HTML verisi olarak yorumlanması belirtilmektedir.

Kendi makinemizde çalıştıran bu sunucuya tarayıcı üzerinden istek atıp DevTools’tan Network tabını açıp isteği incelediğimizde cevabın şu şekilde olduğu görülmektedir.

HTTP/1.0 200 OK 
Server: BaseHTTP/0.6 Python/3.7.4
Date: Sat, 18 Jun 2022 19:50:30 GMT
Content-Type: text/html
Content-Length: 73

Değerlerin Görüntülenmesi

Web sunucusunu HTTP isteğindeki değerleri geri dönecek şekilde düzenlenip görüntülenmesi için sunucuya bazı kısımlar eklenmiştir.

Cevap olarak dönülecek değer ise yine aslında bir HTML şablonu içeren string bir değerdir.

HTML şablonundaki alanların doldurulmasını sağlayan metot ise;

Web sunucumuza farklı bir URL ile istek yolladığımızda karşımıza çıkan sayfa;

Sunucu tarafında /something.html diye bir sayfamız olmamasına rağmen 404 (not found) gibi bir hata alınmamasının nedeni sunucunun bir programdan ibaret olması ve istemciden bir istek gönderildiğinde sunucu tarafından istenilen cevabın dönülebileceği anlaşılmaktadır.

Statik Sayfaların Gösterilmesi

Sayfaların istek atıldığında oluşturulmasının yerine diskte bulunan sayfanın sunulması şeklindedir.

Bu metot sunucudaki dizin içerisindeki veya altındaki dosyaların erişimine izin vermektedir. Eğer yok ise veya bir dosya değil ise bir hata dönecektir. Eğer bir yol ile eşleşiyor ise handle_file metodu çağırılarak dizindeki dosya okunup cevaba ekleniyor olacaktır.

Dizinde bulunmayan bir yol için erişim istendiğinde farklı hata şablonları oluşturulabilir.

Şu durumda bir yol için istek atıldığında bulunmayan sayfa için bir özel hata şablonu karşımıza çıkar fakat isteğimizin durumu hala 200 yani OK durumdadır. Tarayıcının da bunun bir hata olduğunu anlaması için handle_error ve send_content metotları şu şekilde güncellenebilir.

Dizinlerin Listelenmesi

Bir sonraki adımımız olarak, URL’deki yol bir dosya yerine bir dizin olduğunda, web sunucusuna bir dizinin içeriğinin bir listesini görüntülemesi öğretilebilir. Hatta bir adım daha ileri gidebilir ve bir index.html dosyasının görüntülenmesi için o dizine bakmasını ve yalnızca o dosya yoksa dizinin içeriğinin bir listesini göstermesi sağlanabilir. Buradaki tüm durumları do_GET metodunun içerisinde yapmak karmaşıl olabilir. Bu yüzden bu durumlar için 3 farklı sınıf eklenebilir. Her sınıfın içerisinde act ve test adında 2 farklı metot bulunmaktadır. test metodu bu isteğin yapılabileceği durumu gösterirken act metodu ise o işlemi gerçekleştirmektedir.

Sonrasında bu sınıf içerisindeki metotlar RequestHandler sınıfımıza en üst seviyede eklenmektedir.

Sonrasında sunulan dizin altında bir index.html dosyası var ise onu sunmaktadır, yok ise oradaki dizinlerin listelenmesini sağlayan sınıf diğer eklenen sınıflar gibi eklenmektedir.

index.html dosyasının olduğu durumda eklenen sınıf:

index.html dosyasının bulunmadığı durumda ise o dizin altındaki dosyalar listelenmektedir.

Bu durumda sunucuda 5 farklı durumun kontrolu yapılmış olmaktadır.

CGI Protokolü

Çoğu kişi çoğu zaman doğal olarak yeni fonksiyonlar ekleyebilmek için web sunucularını düzenlemek isteyebilmektedir. Bu ihtiyaca yönelik sunucular herhangi bir isteğin harici bir programın çalıştırılabilmesi için Common Gateway Interface adıyla bir mekanizmayı sağlamaktadır.

Örneğin sunucu tarafından bir HTML sayfası içerisinde yerel zamanın gösterilmesi istenildiğinde bu kendi başına çalışan bir program sayesinde yapılabilir.

Bu programın çalıştırılabilmesi için de sunucuya bir case handler daha eklenmektedir.

Test kısmı oldukça basit. Sonunda .py ile biten bir dosya var ise bunun çalıştırılması bir metot ile sağlanabilir.

Bu şekilde bir dosyanın çalıştırılabilmesini sağlamak kesinlikle güvenli değildir. Eğer bir kullanıcı sunucuda çalıştırılacak python dosyasının yolunu biliyorsa o programı çalıştırabiliyor olacaktır. Bu bir kenara bırakıldığında amaç aslında çok karışık değildir:

1- Programı bir subprocess’te çalıştırılması

2- Subprocess’in standart çıktısının yakalanması

3- İstemciye geri gönderilmesi

CGI protokolü bundan çok daha zengindir. Özellikle, sunucunun çalıştırılan programa ilettiği URL’deki parametrelere izin verir ancak bu ayrıntılar sistemin genel mimarisini etkilemez.

Request Handler sınıfının başlangıçta handle_file adında içerikle alakalı bir metodu bulunmaktadır. Bunun yanında list_dir ve run_cgi adında 2 farklı metot daha eklenmiştir.

Sunucu altındaki bir zamanı gösteren dosya çalıştırıldığında:

Son durumda eklenen case handler’lar şu şekildedir:

Buradan da anlaşılacağı üzere sunuculara farklı fonksiyonlar CGI kullanılarak veya farklı durumları ekleyerek (case handler’lar) kazandırılabilmektedir. Burada kullanılan BaseHTTPRequestHandler sınıfı soket bağlantıları ve isteklerin ayrıştırılması gibi daha düşük seviyedeki detaylar ile uğraştırmamaktadır.

Kaynak: http://aosabook.org/en/500L/a-simple-web-server.html

Kaynak Kodları: https://github.com/cuneytdev/simple-web-server

--

--