---
title: 'HTTP Sunucusu'
source: 'https://academia.sh/tr/kurslar/nodejs/http-sunucusu'
course: 'Node.js Çalışma Zamanı'
language: tr
updated: '2026-08-17T18:09:51+00:00'
license: 'CC BY-SA 4.0'
---

# HTTP Sunucusu

İstek ve yanıt nesnelerinin akış karşılığı, yol ve yöntem üzerinden dağıtım, gövde okumada boyut sınırı, uzunluk bildirimi ile parçalı aktarım arasındaki seçim.

İnternet Nasıl Çalışır kursu bir HTTP isteğini satır satır çözümledi: istek satırı,
başlıklar, boş satır, gövde. Orada bu metni okuyan taraf bir örnek programdı ve
protokolün ayrıştırılması gösterilmemişti.

Bu ders o işi yerleşik modüle devreder ve ölçüm toplayıcıyı bir hizmete dönüştürür.
Anahtar gözlem şudur: istek nesnesi okunabilir, yanıt nesnesi yazılabilir bir akıştır.
Akışlar dersinde kurulan her şey burada doğrudan geçerlidir.

## Özet Birikimini Ayrı Modüle Almak

Hizmet kodunu yazmadan önce, kayıt doğrulama ve özet birikimi ayrı bir modüle
alınmalıdır. Ağdan gelen veriyle dosyadan gelen veri aynı yoldan geçmeli ve bu yol
sunucudan bağımsız sınanabilmelidir.

```js
// ozetleyici.mjs
export function satirdanKayit(satir) {
  const kayit = JSON.parse(satir);
  if (typeof kayit.dugum !== 'string' || typeof kayit.metrik !== 'string') {
    throw new TypeError('dugum ve metrik dizgi olmali');
  }
  if (!Number.isFinite(kayit.deger)) {
    throw new TypeError('deger sonlu bir sayi olmali');
  }
  return kayit;
}

export class Ozetleyici {
  #kovalar = new Map();

  ekle({ dugum, metrik, deger }) {
    const anahtar = `${dugum}/${metrik}`;
    const kova = this.#kovalar.get(anahtar) ?? { sayi: 0, toplam: 0, enBuyuk: -Infinity };
    kova.sayi += 1;
    kova.toplam += deger;
    kova.enBuyuk = Math.max(kova.enBuyuk, deger);
    this.#kovalar.set(anahtar, kova);
  }

  ozet() {
    return [...this.#kovalar].map(([anahtar, { sayi, toplam, enBuyuk }]) => ({
      anahtar,
      sayi,
      ortalama: Number((toplam / sayi).toFixed(2)),
      enBuyuk,
    }));
  }
}
```

Doğrulama fonksiyonu yalnızca ayrıştırmakla kalmaz, alan türlerini de sınar. Ağdan gelen
veriye güvenilmez: `deger` alanı dizgi geldiğinde toplama işlemi sessizce birleştirmeye
döner ve özet anlamsızlaşır.

## Sunucu ve İşleyici

`node:http` modülündeki `createServer`, her istek için çağrılan bir işleyici alır.
İşleyicinin iki argümanı vardır ve her ikisi de akıştır.

```js
// hizmet.mjs
import { createServer } from 'node:http';
import { createReadStream } from 'node:fs';
import { createInterface } from 'node:readline';
import { pipeline } from 'node:stream/promises';
import { Ozetleyici, satirdanKayit } from './ozetleyici.mjs';

const ozetleyici = new Ozetleyici();

// Acilista dosyayi akisla okuyup kovalari doldur
async function baslangicVerisiniYukle(dosya) {
  const satirlar = createInterface({
    input: createReadStream(dosya),
    crlfDelay: Infinity,
  });
  let sayac = 0;
  for await (const satir of satirlar) {
    if (satir.trim() === '') continue;
    ozetleyici.ekle(satirdanKayit(satir));
    sayac += 1;
  }
  return sayac;
}

async function govdeyiOku(istek, enCokBayt = 64 * 1024) {
  const parcalar = [];
  let boyut = 0;
  for await (const parca of istek) {          // istek okunabilir bir akistir
    boyut += parca.length;
    if (boyut > enCokBayt) {
      istek.destroy();
      throw Object.assign(new Error('govde cok buyuk'), { durum: 413 });
    }
    parcalar.push(parca);
  }
  return Buffer.concat(parcalar).toString('utf8');
}

function jsonYaz(yanit, durum, deger) {
  const govde = JSON.stringify(deger) + '\n';
  yanit.writeHead(durum, {
    'content-type': 'application/json; charset=utf-8',
    'content-length': Buffer.byteLength(govde),
  });
  yanit.end(govde);
}

const sunucu = createServer(async (istek, yanit) => {
  const adres = new URL(istek.url, `http://${istek.headers.host}`);
  try {
    if (istek.method === 'GET' && adres.pathname === '/ozet') {
      jsonYaz(yanit, 200, ozetleyici.ozet());
      return;
    }
    if (istek.method === 'GET' && adres.pathname === '/ham') {
      yanit.writeHead(200, { 'content-type': 'application/x-ndjson; charset=utf-8' });
      await pipeline(createReadStream('olcumler.ndjson'), yanit);   // govde akisla yazilir
      return;
    }
    if (istek.method === 'POST' && adres.pathname === '/olcum') {
      const govde = await govdeyiOku(istek);
      let sayac = 0;
      for (const satir of govde.split('\n')) {
        if (satir.trim() === '') continue;
        ozetleyici.ekle(satirdanKayit(satir));
        sayac += 1;
      }
      jsonYaz(yanit, 201, { alinan: sayac });
      return;
    }
    jsonYaz(yanit, 404, { hata: 'bulunamadi' });
  } catch (hata) {
    jsonYaz(yanit, hata.durum ?? 400, { hata: hata.message });
  }
});

const yuklenen = await baslangicVerisiniYukle('olcumler.ndjson');
sunucu.listen(8791, '127.0.0.1', () => {
  const { address, port } = sunucu.address();
  console.log(`${yuklenen} kayit yuklendi; dinleniyor: http://${address}:${port}`);
});
```

```sh
node hizmet.mjs
```

```
12 kayit yuklendi; dinleniyor: http://127.0.0.1:8791
```

Örnekteki 8791 sayısı bir seçimdir; makinenizde kullanılan bir bağlantı noktası varsa
başka bir sayı yazın. Sonraki derste bu değer yapılandırmadan okunacak.

`listen` çağrısının ikinci argümanı olan `127.0.0.1`, sunucunun yalnızca geri döngü
arayüzünü dinlemesini sağlar; bu adres İnternet Nasıl Çalışır kursunda tanıtılmıştı.
Argüman verilmediğinde sunucu bütün arayüzlerden erişilebilir olur — geliştirme
sırasında istenmeyen bir açıklıktır.

## İsteği Ayrıştırmak

İstek nesnesi, protokolün ayrıştırılmış hâlini alan olarak sunar. `istek.method`
istek satırının ilk alanı, `istek.url` ikinci alanı, `istek.headers` ise küçük harfe
çevrilmiş başlık adlarından oluşan bir nesnedir.

`istek.url` yalnızca yol ve sorgu bölümünü taşır; alan adı `host` başlığındadır.
Bu ayrım, İnternet Nasıl Çalışır kursunda ada dayalı sanal barındırmanın dayanağı
olarak açıklanmıştı. İki parçayı birleştirip `URL` nesnesi kurmak, yol ile sorguyu
elle ayırmaktan daha güvenlidir: kaçış çözme ve normalleştirme kuralları nesnenin
içindedir.

Gövde, alanlarda bulunmaz — akıştır. Bunun nedeni, gövdenin keyfi büyüklükte
olabilmesidir. Yukarıdaki `govdeyiOku`, `for await` ile parçaları toplarken bir
sınır da uygular. Sınır zorunludur: sınırsız gövde okuyan bir uç, tek bir istekle
sürecin belleğini tüketebilir.

## Yanıtı Yazmak

Yanıt nesnesi yazılabilir bir akıştır. `writeHead` durum kodunu ve başlıkları
belirler; `end` gövdeyi yazıp yanıtı kapatır.

```sh
curl -s -i http://127.0.0.1:8791/ozet
```

```
HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
content-length: 420
Date: Mon, 27 Jul 2026 04:54:03 GMT
Connection: keep-alive
Keep-Alive: timeout=5

[{"anahtar":"kenar-01/sicaklik","sayi":3,"ortalama":21.87,"enBuyuk":22.3},{"anahtar":"kenar-01/nem","sayi":2,"ortalama":47.6,"enBuyuk":48},{"anahtar":"kenar-02/sicaklik","sayi":3,"ortalama":20.23,"enBuyuk":20.7},{"anahtar":"kenar-02/nem","sayi":1,"ortalama":52.5,"enBuyuk":52.5},{"anahtar":"kenar-03/sicaklik","sayi":2,"ortalama":24.35,"enBuyuk":24.6},{"anahtar":"kenar-03/nem","sayi":1,"ortalama":41.3,"enBuyuk":41.3}]
```

`Date` başlığındaki değer isteğin anına göre değişir; `Connection` ve `Keep-Alive`
başlıkları da istemcinin pazarlığına bağlıdır. Sabit olan, işleyicinin yazdığı iki
başlık ile gövdedir.

`content-length` başlığı burada elle hesaplandı. Karakter sayısı değil bayt sayısı
yazılmalıdır; arabellekler dersindeki ayrım tam olarak bu yüzden önemliydi.

Uzunluğu önceden bilmediğiniz yanıtlarda bu başlık yazılmaz. Gövde akışla
yazıldığında çalışma zamanı parçalı aktarıma geçer:

```sh
curl -s -i http://127.0.0.1:8791/ham | head -8
```

```
HTTP/1.1 200 OK
content-type: application/x-ndjson; charset=utf-8
Date: Mon, 27 Jul 2026 04:54:03 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked

{"zaman":"2024-02-07T09:12:44Z","dugum":"kenar-01","metrik":"sicaklik","deger":21.4}
```

`Transfer-Encoding: chunked` başlığı, gövdenin her parçasının önüne kendi uzunluğunun
yazıldığını bildirir; alıcı sonu bu yolla anlar. İnternet Nasıl Çalışır kursunda
"uzunluk önceden bilinmiyorsa gövde parçalar hâlinde gönderilir" diye anlatılan
düzenek budur.

Seçim şöyle yapılır: yanıt küçük ve bellekte hazırsa uzunluk bildirmek istemciye
ilerleme bilgisi verir; yanıt büyük veya üretilirken akıyorsa parçalı aktarım, belleği
yanıt boyunca meşgul etmemeyi sağlar. `pipeline` kullanılması, geri basıncın
uygulanmasını da güvence altına alır: istemci yavaşsa dosya okuması da yavaşlar.

## Veri Göndermek ve Hatalar

Ölçüm göndermek gövdeli bir istekle yapılır:

```sh
curl -s -X POST --data-binary '{"dugum":"kenar-01","metrik":"sicaklik","deger":25.9}' -w 'durum: %{http_code}\n' http://127.0.0.1:8791/olcum
```

```
{"alinan":1}
durum: 201
```

201 kodu, İnternet Nasıl Çalışır kursundaki sınıflandırmaya göre "yeni kaynak
oluşturuldu" anlamına gelir; 200'den ayrı seçilmesi isteğin etkisini bildirir.

Bozuk gövde farklı bir sınıfa düşer:

```sh
curl -s -X POST --data-binary 'bozuk' -w 'durum: %{http_code}\n' http://127.0.0.1:8791/olcum
```

```
{"hata":"Unexpected token 'b', \"bozuk\" is not valid JSON"}
durum: 400
```

```sh
curl -s -w 'durum: %{http_code}\n' http://127.0.0.1:8791/yok
```

```
{"hata":"bulunamadi"}
durum: 404
```

400 sınıfı isteği gönderenin hatasını, 500 sınıfı sunucunun hatasını bildirir. Bu
ayrımı doğru yapmak yalnızca nezaket değil, işletim gereğidir: 500 oranı bir arıza
göstergesidir, 400 oranı ise istemci davranışının göstergesi. İkisi karışırsa
gösterge anlamını yitirir.

Yukarıdaki işleyicideki hata yakalayıcının bir eksiği vardır: her yakalanan hatayı
400 olarak bildiriyor, oysa özetleme sırasında oluşan bir programcı hatası 500
olmalıdır. Bu ayrım Hata Yönetimi Stratejisi dersinde düzeltilecek.

Bir uyarı: işleyici eşzamansız olduğu için, içinde oluşan ve yakalanmayan bir hata
`createServer` tarafından görülmez. `try`/`catch` bloğunun bütün gövdeyi sarması bu
yüzden gereklidir — sarmazsa yanıt hiç yazılmaz ve istemci zaman aşımına uğrar.

## Özet

- İstek nesnesi okunabilir, yanıt nesnesi yazılabilir akıştır; akışlar dersindeki
  kuralların tümü burada geçerlidir.
- İstek satırı ve başlıklar ayrıştırılmış alanlar olarak sunulur; `istek.url` yalnızca
  yol ve sorguyu taşır, alan adı `host` başlığındadır.
- Gövde akış olduğundan boyutu sınırlanmalıdır; sınırsız okuma tek istekle belleği
  tüketebilir.
- Uzunluk bildiren yanıtta bayt sayısı yazılır; uzunluk bilinmiyorsa çalışma zamanı
  parçalı aktarıma geçer ve akışla yazma geri basıncı korur.
- Durum kodu sınıfı, hatanın kaynağını bildirir; istemci ve sunucu hatalarının
  karışması işletim göstergelerini anlamsızlaştırır.

## Sonraki Adım

Hizmet tek bir süreçte çalışıyor ve bütün işi tek iş parçacığında yapıyor. Ölçüm
verisi üzerinde pahalı bir hesap gerektiğinde — örneğin uzun bir dönemin yeniden
özetlenmesi — bu tek iş parçacığı bütün istekleri bekletir. Sonraki ders sürecin
dışına iş çıkarmanın ilk yolunu ele alır: alt süreç başlatmak, onunla akışlar
üzerinden konuşmak ve dış komutları güvenli çağırmak.
