Server web asincron
Obiectivele temei
Enunț
Să se implementeze un server web care să folosească operații avansate de intrare/ieșire:
operații asincrone pe fișiere;
operații non-blocante pe sockeți;
zero-copying;
multiplexarea operațiilor I/O.
Pentru implementare se va folosi API-ul de operații I/O avansate specific sistemelui de operare Linux:
Serverul web va utiliza API-ul modern de multiplexare pentru a aștepta conexiuni din partea clienților: epoll (Linux). Pe conexiunile realizate se vor recepționa cereri din partea clienților și apoi se vor distribui răspunsurile către aceștia.
Clienții și serverul vor comunica folosind protocolul HTTP. Pentru parsarea cererilor HTTP din partea clienților recomandăm folosirea acestui parser HTTP, disponibil și în cadrul directorului de resurse ale temei. Va trebui să folosiți un callback pentru obținerea căii către resursa locală solicitată de client. Tot în directorul de resurse, găsiți un exemplu simplificat de folosire a parser-ului.
Serverul implementează o funcționalitate limitată a protocolului HTTP, aceea de a transmite fișiere clienților. Serverul va furniza fișiere din directorul AWS_DOCUMENT_ROOT
, definit în cadrul antetului temei. Fișiere se găsesc doar în subdirectoarele AWS_DOCUMENT_ROOT/static/
, respectiv AWS_DOCUMENT_ROOT/dynamic/
, iar request path-uri corespunzătoare vor fi, de exemplu, AWS_DOCUMENT_ROOT/static/test.dat
, respectiv AWS_DOCUMENT_ROOT/dynamic/test.dat
. Prelucrarea fișierelor va fi următoarea:
Fișierele din directorul
AWS_DOCUMENT_ROOT/static/
sunt fișiere statice care vor fi transmise clienților folosind
API de zero-copying (
sendfile/
TransmitFile).
Fișierele din directorul
AWS_DOCUMENT_ROOT/dynamic/
sunt fișiere pentru care se presupune că este necesară o fază de post-procesare din partea serverului. Aceste fișiere vor fi citite de pe disc folosind
API asincron și apoi vor fi transmise către clienți. Transmiterea va folosi sockeți non-blocanți (Linux).
Pentru
request path-uri nevalide se va transmite un mesaj
HTTP 404.
După transmiterea unui fișier, conform protocolului HTTP, conexiunea este închisă.
Precizări/recomandări pentru implementare
Implementarea temei presupune existența unei mașini de stări pentru fiecare conexiune, pe care să o interogați și actualizați periodic pe măsura desfășurării transferului.
Definițiile macro-urilor și structurilor utile se găsesc în
header-ul temei.
Răspunsurile HTTP vor avea codul 200 pentru fișiere existente și 404 pentru fișiere inexistente.
Un răspuns valid este format din antetul HTTP, conținând directivele aferente, două newline-uri (\r\n\r\n
), urmat de conținutul efectiv (fișierul).
-
Puteți folosi directive de request predefinite, precum Date
, Last-Modified
etc.
Portul pe care serverul web ascultă pentru conexiuni este definit în cadrul
header-ului temei ca macro:
AWS_LISTEN_PORT
.
Directorul rădăcină raportat la care se caută resursele/fișierele este definit în cadrul
header-ului temei ca macro:
AWS_DOCUMENT_ROOT
.
Resurse utile
Depanare/troubleshooting
Pentru depanarea serverului, recomandăm folosirea
wget și a
netcat.
Pentru netcat, dacă doriți să comunicați folosind HTTP, va trebui să transmiteți o cerere specifică (spre exemplu
GET /path/to/resource HTTP/1.0
). Exemplu de folosire:
$ nc localhost 8888
GET / HTTP/1.0
Sau:
echo -ne "GET / HTTP/1.0\r\n\r\n" | nc -q 2 localhost 8888
Exemple de utilizare găsiți în
teste.
Precizări
Atât citirea cât și scrierea peste sockeți se realizează doar la notificarea dată de
API-ul specific sistemului de operare, folosind
epoll.
Tot folosind
epoll se va aștepta notificarea încheierii operațiilor asincrone pe fișiere.
-
API-ul epoll este apelat prin intermediul wrapper-elor din cadrul header-ului
w_epoll.h.
Scrierea pe sockeți, atât în cazul fișierelor statice, cât și în cazul fișierelor dinamice, se realizează non-blocant: sockeții sunt marcați ca non-blocanți, iar la un apel de scriere se scrie cât permite buffer-ul socketului. La următoarea notificare din partea
API-ului specific se va realiza o nouă scriere și așa mai departe.
Pentru configurarea unui socket ca non-blocant puteți folosi
fcntl (flag-ul
O_NONBLOCK
).
Pentru implementarea operațiilor I/O, folosiți funcțiile din familia
io_setup.
Pentru utilizarea funcțiilor va trebui să realizați link-area cu
biblioteca libaio și includerea header-ului
<libaio.h>
.
Recomandăm folosirea unei variabile de tipul
io_context_t
și a unui descriptor
eventfd pentru fiecare conexiune.
Pentru încheierea operațiilor asincrone, folosiți
io_getevents.
-
Transmiterea de fișiere statice se realizează folosind
sendfile.
Tema se va rezolva folosind doar funcții POSIX. Se pot folosi de asemenea și funcțiile de formatare din familia printf
, funcțiile de alocare de memorie malloc
, free
și funcțiile de lucru cu șiruri de caractere (strcat
, strdup
etc.)
Pentru partea de I/O se vor folosi doar funcții POSIX și funcții pentru operații asincrone. De exemplu, funcțiile fopen
, fread
, fwrite
, fclose
nu trebuie folosite; în locul acestora folosiți open
, io_setup
, io_submit
, close
.
Scrierea și citirea pe sockeți se vor face doar prin funcțiile send()
și recv()
. Nu este permisă folosirea funcțiilor read()
și write()
pentru lucrul cu sockeți.
Testare
Corectarea temelor se va realiza automat cu ajutorul unor suite de teste publice:
Indicații despre utilizarea suitei de teste se găsesc în fișierul README
din cadrul arhivei.
-
În urma compilării temei trebuie să rezulte un executabil denumit aws
(Linux).
Suita de teste conține un set de teste. Trecerea unui test conduce la obținerea punctajului aferent acestuia.
În urma rulării testelor, se va acorda, în mod automat, un punctaj total. Punctajul total maxim este de 90 de puncte, pentru o temă care trece toate testele. La acest punctaj se adaugă 10 puncte care reprezintă aprecierea temei de către asistentul care o corectează.
Cele 100 de puncte corespund la 10 puncte din cadrul notei finale.
Pot exista penalizări în caz de întârzieri sau pentru neajunsuri de implementare sau de stil.
Pe lângă penalizările precizate în cadrul
listei de depunctări, se vor avea în vedere următoarele elemente:
-2p Linux – fișierele statice nu sunt transmise prin mecanismul de zero-copy
-0.5p Linux – alocare statică a unui vector de conexiuni
-4p Linux – nu se folosesc doar send
și recv
pentru operațiile cu sockeți
-2p Linux – folosirea de operații blocante pe sockeți în locul operațiilor non-blocante
-1p Linux – recv/send/sendfile sunt apelate într-o buclă și nu pe baza evenimentelor semnalizate de epoll
-2p Linux – operațiile I/O asincrone pe fișiere nu sunt așteptate “integrat” folosind eventfd
și epoll
-1p Linux – alte implementări nevalide la nivelul sistemului asincron de comunicație
O temă care nu folosește operații I/O asincrone pe fișiere: io_*
(din libaio
) pe Linux va pierde punctajul pentru testele cu fișiere dinamice (testele 26-35)
Testul 0 din cadrul checker-ului temei verifică automat coding style-ul surselor voastre. Ca referință este folosit
stilul de coding din kernelul Linux. Acest test nu oferă sau scade puncte, dar poate fi folosit orientativ de către echipa de corectare pentru a penaliza problemele grave de coding style. Important este să vă definiți un stil și să-l folosiți consecvent. Pentru mai multe informații despre un cod de calitate citiți
pagina de recomandări.
Resurse de suport
Resursele temei se găsesc în repo-ul
temei de pe GitHub.
FAQ
Suport, întrebări și clarificări
Pentru întrebări sau nelămuriri legate de temă folosiți forumul temei.
Orice intrebare pe forum
trebuie să conțină o descriere cât mai clară a eventualei probleme. Întrebări de forma: “Nu merge X. De ce?” fără o descriere mai amănunțită vor primi un răspuns mai greu. Înainte să postați o întrebare pe forum citiți și celelalte întrebări(dacă există) pentru a vedea dacă întrebarea voastră a fost deja adresată sub o altă formă(în cazul în care răspunsul din partea echipei vine mai greu este mai rapid să căutați voi deja printre întrebările existente).
ATENȚIE să nu postați imagini cu părți din soluția voastră pe forumul pus la dispoziție sau orice alt canal public de comunicație. Dacă veți face acest lucru, vă asumați răspunderea dacă veți primi copiat pe temă.