Dacă ați intrat odată google.com, atunci ați folosit un server proxy. Aproape toate serviciile oferite de companiile mari din tehnologie sunt conectate prin acest tip de servere, care elimină grija gestionării infrastructurii la nivel de rețea și datacenter. Două dintre cele mai interesante proiecte de software-defined networking sunt Pingora1 și Katran2. Primul este un set de librării sau mai bine spus framework, în Rust, destinat integrării de servicii de rețea cu un focus pe protocolul HTTP/S. Katran în schimb este o librărie în C++, destinată forwarding-ului și serverelor de load-balancing la nivelul de transport - TCP/UDP. Poate fi folosit ca un program în kernel (BPF3) și folosește XDP4 pentru performanță.
Tipuri de proxy servers
Sunt două tipuri de servere proxy, respectiv forward proxy și reverse proxy.
Forward proxy: un server care rulează local sau pe rețeaua locală și prin care se face conexiunea către unele domenii, porturi sau protocoale. Poate fi folosit pentru a filtra traficul de pe internet, sau a genera rapoarte legate de trafic.
Reverse proxy: un server care deservește un alt server, de exemplu un load balancer care servește trafic pe un singur domeniu dar în spate se conectează la alte servere prin rotație pentru a reduce încărcarea și a menține serviciile disponibile.
Un local (forward) proxy în limbajul C#
Pentru acest tutorial voi folosi editorul JetBrains Rider, dar puteți folosi orice editor. Desigur primul pas este să creez proiectul, pe care îl voi numi mscproxy:
Vom face un proxy pentru protocolul HTTP, așadar vom folosi un HttpListener(3). În Program.cs adăugăm următoarele linii:
Serverul nostru va asculta pe adresa locală (127.0.0.1) pe portul 7777. Este important ca portul ales să fie mai mare de 1024 pentrua evita zona privilegiată și conflicte cu alte servicii. Putem compila acest program (desigur nu uitați să adăugați acoladele care lispesc din screenshot) și mesajul se va afișa în consolă:
Listening on port 7777 ...
Procesarea request-ului
Serverul nostru va primi o cerere de la un client (browser) pe care mai apoi o va procesa și va returna răspunsul, o pagină web sau un mesaj de eroare. Vom avea nevoie să procesăm mai multe cereri în paralel, de aceea apelăm la fire de execuție (threads):
Clasa ProxyThreadRelay
implementează procesarea request-ului în contextul pasat de la listener
și va fi rulată într-un fir de execuție separat de programul (serverul) nostru care ascultă pentru cereri noi. Aceasta este resposabilă de transmiterea request-ului către server și are două metode processRequest()
și asyncResponseCallBack()
:
Salvăm contextul ca proprietate, iar în processRequest contruim un HttpWebRequest din url-ul specific contextului. Re-setăm niște parametrii (keepAlive și UserAgent) și la final lansăm cererea cu pasarea metodei care va procesa răspunsul.
asyncResponseCallBack, după cum îi spune și prefixul este o metodă asincronă, care va fi chemată atunci când vine un răspuns de la serverul care primește cererea înaintată de proxy:
Aici citim răspunsul, îl transformăm într-un byte array și îl copiem în responseStream
care va fi trimis către clientul serverului nostru proxy. Aici am pus și un comentariu, unde putem modifica răspunsul în cazul în care acesta este text/html
.
RequestState
Această clasă este un container pentru webRequest
și context
, și ne ajută să pasăm un obiect unic între cele două metode de mai sus de la processRequest la asyncResponseCallBack:
Conectarea cu proxy
Prima mențiune ar fi că proxy-ul nostru este doat HTTP, așadar nu ne vom putea conecta la servere care forțează conexiuni prin HTTPS. Vom porni aplicația și serverul nostru, care va asculta pe portul 7777, apoi folosim utilitarul Invoke-WebRequest
din Powershell pentru a ne conecta prin proxy la www.example.com:
Iar în consola IDE-ului vor apărea mesajele de log: