12-bo‘lim
HTTP asoslari
So'rov va javob tuzilishi, usullar, holat kodlari, sarlavhalar, Content-Length ning roli va xom soket ustida HTTP.
Ushbu bo‘lim mundarijasi
HTTP - bugungi internetda eng ko'p ishlatiladigan ilova protokoli. Uning eng ajablanarli tomoni - u oddiy matn.
Shuning uchun HTTP so'rovini qo'lda yozib, oddiy soket orqali yuborish mumkin. Aynan shuni qilamiz.
So'rov tuzilishi #
SOROV = (
"GET /darslik/tarmoqlar HTTP/1.1\r\n"
"Host: edu.softromeda.com\r\n"
"User-Agent: qollanma/1.0\r\n"
"Accept: text/html\r\n"
"Accept-Language: uz\r\n"
"\r\n"
)
print("so'rov uzunligi:", len(SOROV), "bayt")
print()
qatorlar = SOROV.split("\r\n")
usul, yol, versiya = qatorlar[0].split(" ")
print("so'rov qatori:")
print(" usul :", usul)
print(" yo'l :", yol)
print(" versiya:", versiya)
print()
print("sarlavhalar:")
for q in qatorlar[1:]:
if q == "":
break
nom, qiymat = q.split(": ", 1)
print(f" {nom:<16} {qiymat}")
tana = SOROV.split("\r\n\r\n", 1)[1]
print()
print("tana uzunligi:", len(tana), "- GET da odatda tana bo'lmaydi")
so'rov uzunligi: 127 bayt
so'rov qatori:
usul : GET
yo'l : /darslik/tarmoqlar
versiya: HTTP/1.1
sarlavhalar:
Host edu.softromeda.com
User-Agent qollanma/1.0
Accept text/html
Accept-Language uz
tana uzunligi: 0 - GET da odatda tana bo'lmaydi
\r\n - \n emasHTTP standarti qator oxiri sifatida \r\n ni talab qiladi
(carriage return + line feed).
Faqat \n yozsangiz, ko'p serverlar baribir tushunadi - lekin
ba'zilari tushunmaydi va so'rovingiz sababsiz rad etiladi.
Shuning uchun HTTP yozayotganda har doim \r\n ishlating.
Host sarlavhasi nima uchun majburiy?Bitta IP manzilda yuzlab sayt joylashishi mumkin. Server qaysi sayt so'ralayotganini qayerdan biladi?
Javob: Host sarlavhasidan. HTTP/1.1 da u majburiy -
usiz server 400 Bad Request qaytaradi.
Bu virtual xosting ning asosi. HTTP/1.0 da bunday sarlavha yo'q edi va har sayt uchun alohida IP kerak bo'lardi.
Xom soket ustida HTTP #
Endi haqiqiy so'rov yuboramiz. Hech qanday HTTP kutubxonasi yo'q - faqat soket:
import socket
import threading
PORT = 9600
SAHIFA = b"<h1>Salom, tarmoq!</h1>"
# Tinglash ASOSIY oqimda ochiladi - shunda mijoz ulanganda
# port allaqachon band bo'ladi (poyga holati bo'lmaydi)
tinglovchi = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tinglovchi.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tinglovchi.bind(("127.0.0.1", PORT))
tinglovchi.listen(1)
def server():
ulanish, _ = tinglovchi.accept()
sorov = ulanish.recv(4096).decode()
birinchi = sorov.split("\r\n")[0]
print("server so'rov qatorini oldi:", birinchi)
javob = (
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
f"Content-Length: {len(SAHIFA)}\r\n"
"Connection: close\r\n"
"\r\n"
).encode() + SAHIFA
ulanish.sendall(javob)
ulanish.close()
oqim = threading.Thread(target=server, daemon=True)
oqim.start()
mijoz = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mijoz.settimeout(5)
mijoz.connect(("127.0.0.1", PORT))
mijoz.sendall(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
javob = b""
while True:
bolak = mijoz.recv(1024)
if not bolak:
break
javob += bolak
mijoz.close()
oqim.join()
tinglovchi.close()
print()
print("--- xom javob")
for q in javob.decode().split("\r\n"):
print(" " + (q if q else "(bo'sh qator - sarlavhalar tugadi)"))
server so'rov qatorini oldi: GET / HTTP/1.1
--- xom javob
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 23
Connection: close
(bo'sh qator - sarlavhalar tugadi)
<h1>Salom, tarmoq!</h1>
Mana shu - brauzeringiz har sahifa ochganda qiladigan ishning o'zi. Faqat brauzer bundan keyin HTML ni chizadi.
bind va listen nima uchun asosiy oqimda?Serverni butunlay alohida oqimga joylashtirish tabiiy tuyuladi. Lekin unda poyga holati (race condition) paydo bo'ladi:
| Oqim | Nima qiladi |
|---|---|
| Asosiy | Oqimni ishga tushiradi va darhol connect qiladi |
| Server | Hali bind va listen ga ulgurmagan |
Natijada connect ConnectionRefusedError bilan yiqiladi -
lekin har safar emas. Uch urinishdan bittasida.
Aynan shunday xatolar eng yomoni: sinovda o'tib ketadi, keyin tasodifan yiqiladi.
Yechim ikki xil bo'lishi mumkin:
| Usul | Izoh |
|---|---|
Bu yerdagidek: bind va listen asosiy oqimda | Eng sodda |
threading.Event bilan tayyorligini kutish | Murakkabroq holatlarda |
Ikkinchisini 1-bo'limdagi birinchi misolda ko'rgansiz.
curl ning -v bayrog'i xom so'rov va javobni ko'rsatadi:
curl -v https://edu.softromeda.com/
> bilan boshlangan qatorlar - siz yuborgan so'rov,
< bilan boshlanganlari - server javobi.
Faqat sarlavhalar kerak bo'lsa:
curl -I https://edu.softromeda.com/
Javobni o'qish #
Javob tuzilishi so'rovnikiga juda o'xshash - farqi birinchi qatorda:
XOM = (
b"HTTP/1.1 301 Moved Permanently\r\n"
b"Location: https://edu.softromeda.com/darslik/tarmoqlar\r\n"
b"Content-Type: text/html; charset=utf-8\r\n"
b"Content-Length: 28\r\n"
b"\r\n"
b"<a>Yangi manzilga o'ting</a>"
)
def javobni_och(xom):
sarlavha_qismi, tana = xom.split(b"\r\n\r\n", 1)
qatorlar = sarlavha_qismi.decode().split("\r\n")
versiya, kod, izoh = qatorlar[0].split(" ", 2)
sarlavhalar = {}
for q in qatorlar[1:]:
nom, qiymat = q.split(": ", 1)
sarlavhalar[nom.lower()] = qiymat
return int(kod), izoh, sarlavhalar, tana
kod, izoh, sarlavhalar, tana = javobni_och(XOM)
SINFLAR = {
1: "ma'lumot", 2: "muvaffaqiyat", 3: "yo'naltirish",
4: "mijoz xatosi", 5: "server xatosi",
}
print("holat kodi :", kod, izoh)
print("sinfi :", SINFLAR[kod // 100])
print()
for nom, qiymat in sarlavhalar.items():
print(f" {nom:<16} {qiymat}")
print()
print("e'lon qilingan uzunlik:", sarlavhalar["content-length"])
print("haqiqiy tana uzunligi :", len(tana))
print("mos keldimi:", int(sarlavhalar["content-length"]) == len(tana))
holat kodi : 301 Moved Permanently
sinfi : yo'naltirish
location https://edu.softromeda.com/darslik/tarmoqlar
content-type text/html; charset=utf-8
content-length 28
e'lon qilingan uzunlik: 28
haqiqiy tana uzunligi : 28
mos keldimi: True
E'tibor bering: sarlavha nomlari kichik harfga o'tkazildi. HTTP da sarlavha nomlari katta-kichikligiga sezgir emas, shuning uchun ularni bir xil ko'rinishga keltirish odat.
Content-Length xato bo'lsa - ulanish buziladi8-bo'limda ko'rgandik: TCP - bayt oqimi, xabar chegarasi yo'q.
HTTP bu muammoni Content-Length bilan hal qiladi.
Agar u noto'g'ri bo'lsa:
| Xato | Oqibati |
|---|---|
| Haqiqiydan kichik | Ortiqcha baytlar keyingi javobning boshi deb o'qiladi |
| Haqiqiydan katta | Mijoz kelmaydigan baytlarni kutib qotib qoladi |
Birinchi holat ayniqsa xavfli: ulanish qayta ishlatilsa (HTTP/1.1 da bu odatiy hol), hujumchi shu yo'l bilan boshqa foydalanuvchining javobiga o'z mazmunini qo'shib qo'yishi mumkin.
Bu so'rovni ajratish (request smuggling) deb ataladi.
Shuning uchun uzunlikni qo'lda hisoblamang - har doim
len() dan oling.
Usullar #
| Usul | Nima qiladi | Xavfsizmi | Takrorlansa |
|---|---|---|---|
GET | O'qiydi | Ha | Zarar yo'q |
HEAD | Faqat sarlavhalarni o'qiydi | Ha | Zarar yo'q |
POST | Yangi narsa yaratadi | Yo'q | Takrorlanadi |
PUT | To'liq almashtiradi | Yo'q | Zarar yo'q |
PATCH | Qisman o'zgartiradi | Yo'q | Bog'liq |
DELETE | O'chiradi | Yo'q | Zarar yo'q |
Ikkita muhim tushuncha bor:
- Xavfsiz (safe) - hech narsani o'zgartirmaydi.
- Takrorlansa zarar yo'q (idempotent) - bir marta ham, o'n marta ham natija bir xil.
POST ikkalasi ham emas. Aynan shuning uchun brauzer sahifani
yangilashda "formani qayta yuborasizmi?" deb so'raydi - u
ikkinchi buyurtma yaratib yuborishi mumkin.
Holat kodlari #
| Sinf | Ma'nosi | Eng ko'p uchraydiganlari |
|---|---|---|
1xx | Ma'lumot | 101 Switching Protocols |
2xx | Muvaffaqiyat | 200 OK, 201 Created, 204 No Content |
3xx | Yo'naltirish | 301, 302, 304 Not Modified |
4xx | Mijoz xatosi | 400, 401, 403, 404, 429 |
5xx | Server xatosi | 500, 502, 503, 504 |
| Juftlik | Farqi |
|---|---|
401 va 403 | 401 - "kimligingizni bilmayman", 403 - "bilaman, lekin ruxsat yo'q" |
301 va 302 | 301 - doimiy (brauzer keshlaydi), 302 - vaqtinchalik |
500 va 502 | 500 - ilova qulagan, 502 - proksi orqadagi serverdan javob ololmadi |
404 va 410 | 404 - topilmadi, 410 - bor edi, o'chirilgan |
301 bilan ehtiyot bo'ling: brauzer uni uzoq muddat keshlaydi
va xato yo'naltirishni orqaga qaytarish qiyin bo'ladi. Shubha
bo'lsa, avval 302 dan boshlang.
- So'rov tahlili misolini ishga tushiring.
- Unga
Accept-Encoding: gzipsarlavhasini qo'shing. \r\nni\nga almashtiring - tahlil qanday buzildi?- Xom soket misolini ishga tushiring.
- Serverni
404 Not Foundqaytaradigan qiling. - So'ralgan yo'lga qarab ikki xil javob bering.
- Javobni tahlil qilish misolini ishga tushiring.
Content-Lengthni ataylab 10 ga o'zgartiring va natijani izohlang.curl -vbilan biror saytga murojaat qiling va sarlavhalarni o'qing.401va403farqini o'z so'zlaringiz bilan yozing.
Xulosa #
- HTTP - matnli protokol, so'rovni qo'lda yozish mumkin.
- So'rov: so'rov qatori, sarlavhalar, bo'sh qator, tana.
- Javob: holat qatori, sarlavhalar, bo'sh qator, tana.
- Qator oxiri
\r\nbo'lishi kerak. Hostsarlavhasi HTTP/1.1 da majburiy - virtual xosting shunga tayanadi.- Sarlavha nomlari katta-kichiklikka sezgir emas.
Content-LengthTCP oqimida xabar chegarasini belgilaydi.- Noto'g'ri
Content-Lengthso'rovni ajratish zaifligiga olib keladi. GETvaHEAD- xavfsiz;POSTtakrorlansa yangi yozuv yaratadi.- Holat kodlari sinflari:
2xxmuvaffaqiyat,3xxyo'naltirish,4xxmijoz,5xxserver.
Keyingi bo'limda HTTP ning yangi versiyalarini va ular qanday muammolarni yechganini ko'ramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.