Go Down

Topic: [completo]parser HTTP quasi REST aka "webServer" (Read 1 time) previous topic - next topic

lestofante

Feb 01, 2014, 01:32 am Last Edit: Feb 01, 2014, 01:38 am by lesto Reason: 1
Questa libreria NON è un webserver completo, ma solo una sua parte, il PARSER

Che cosa fa il PARSER?
Esso legge la richiesta del client e ne estrae i dati a poi necessari.

Quando un browser effettua una richiesta, essa può essere una GET, POST, DELETE, PUT ed altre meno usate.
In realtà è solo negli ultimi anni che si usano ampiamente PUT e DELETE, fino ad ora GET e POST facevano da padroni, infatti visto che è il webserver che interpreta la richiesta come vuole, l'uso di una o di un altra è una pura convenzione, nota come REST

In parole povere?
la GET serve per RICHIEDERE uno o più dati (l'eventuale dato o filtri da applicare sulla serie dati si passano via query string)
la POST serve per AGGIORNARE un dato (il riferimento al dato va nella query string, il dato nel corpo)
la PUT serve per CREARE un dato (il riferimento al dato va nella query string, il dato nel corpo)
la DELETE serve per ELIMINARE un dato (il riferimento al dato va nella query string)

Quindi cosa fa la libreria?
semplice, si occupa di estrarre i dati (metodo, uri, lista di chiavi->valore e gli eventuali dati) dalla richiesta che vi passa il vostro client. E non solo; lo fa SENZA dover tenere in memoria TUTTA la richiesta, una cosa che invece vedo fare spesso e che porta inevitabilmente ad esaurire la RAM con catastrofici effetti. In effetti questa libreria usa solo lo spazio per i dati, il carattere attilamente letto e pochi byte per le variabili di gestione del tutto.

Cosa sono query string e corpo dati?
Prima di tutto non confondiamo query string con le query SQL che sono ben altra cosa, anche se spesso si incontrano :)

esaminiamo una GET "pulita"
Code: [Select]

GET /index.html?user=asd HTTP/1.1
Host: www.example.com



GET è il metodo
/index.html è la URI, ovvero il file richiesto
user=asd è la query string, che ci dice che la variabile user è asd, puoi aggiungere altre variabili usando &
HTTP/1.1 è il PROTOCOLLO utilizzato, con arduino si implementa l'HTTP/1.0
Host: www.example.com è un header, di solito sono moltidi più ed a noi danno solo fastidio

la delete funziona allo stesso modo.
Se guardate molti siti, anche questo, sono piene di questi dati. (per esempio, la sezione software è la "board" 84.0, cambiando questo numerino vedete un pò dove finite!)
Ovviamente avrete anche visto quei siti cheriempiono la barran di numeri strani e arcani, il che è un pò brutto da vedere, ma potrebbe anche contenere dati sensibili; ecco che entrano in gioco POST e PUT con illoro corpo dati


esaminiamo ora una POST "pulita"
Code: [Select]

POST /index.html?user=asd HTTP/1.1
Host: www.example.com

dato1=una lunga sfilza di dati


notare lo spazio tra la fine degli header e l'inizio del corpo dati

il corpo dati non sarà visibile nella barra degli indirizzi (che però mostrerà come al solito l'uri completo di query string), in oltre il corpo dati NON ha alcunelimitazioni che ha l'URI, permettendo di usalro per inviare dati anche binari (non leggibili) senza problemi di codifica.

la PUT funziona allo stesso modo.



esempio di una GET vera:
Code: [Select]
GET / HTTP/1.1[CRLF]
Host: www.google.it[CRLF]
Connection: close[CRLF]
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9) Gecko/2008052906 Firefox/3.0[CRLF]
Accept-Encoding: gzip[CRLF]
Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7[CRLF]
Cache-Control: no-cache[CRLF]
Accept-Language: de,en;q=0.7,en-us;q=0.3[CRLF]
Referer: http://web-sniffer.net/[CRLF]
[CRLF]

[CRLF] equivale ad \n+\r, ovvero un invio

esempio di una POST vera:
Code: [Select]
POST / HTTP/1.1[CRLF]
Host: www.google.it[CRLF]
Connection: close[CRLF]
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9) Gecko/2008052906 Firefox/3.0[CRLF]
Accept-Encoding: gzip[CRLF]
Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7[CRLF]
Cache-Control: no-cache[CRLF]
Accept-Language: de,en;q=0.7,en-us;q=0.3[CRLF]
Referer: http://web-sniffer.net/[CRLF]
Content-type: application/x-www-form-urlencoded[CRLF]
Content-length: 12[CRLF]
[CRLF]
dati di test[CRLF]
[CRLF]


notare che esiste un header che ci dice quanti byte seguiranno, Content-length: 12, ho deciso di ignorarlo per semplicità
Guida per principianti http://playground.arduino.cc/Italiano/newbie
Unoffical Telegram group https://t.me/genuino

toreg


lestofante

no, nulla, ma ci sono io :P

se sei interessato, basandosi sulle Stream forse si riesce a miglireare il codice
Guida per principianti http://playground.arduino.cc/Italiano/newbie
Unoffical Telegram group https://t.me/genuino

toreg

Grazie x la disponibilità!!
Sto riprendendo a lavorare su un mio progettino, mi ero fermato proprio alla fase del parsing di una richiesta inviata da una pagina web.
La confusione è tanta! Ad esempio non capisco come una get viene gestita e/o memorizzata per poi essere letta con,ad esempio, client.read.
Quest ultimo comando,se nn sbaglio,legge byte byte la get inviata dal client, e dopo essere letta che fine fà? Se la leggo byte x byte, come fà il comando client.find("led") a trovare una "parola"  in una richiesta inviata,appunto,byte x byte?
:-)

lestofante

il sistema è che i dati arrivano su di un piccolo buffer, che la read legge ed il bite viene cancellato.

La find funziona un modo che in modo semplicistico:

se cerchi "led", scorre il flussi non appena trova una "l" controlla che le due successive (senza eliminarle) siano "ed". questo è un pò semplicistico, vedi il BUG che segnalai a suo tempo (https://github.com/arduino/Arduino/issues/2591)
Guida per principianti http://playground.arduino.cc/Italiano/newbie
Unoffical Telegram group https://t.me/genuino

toreg

ok...domanda:
per ricevere comandi da un get, esempio GET / ?Stop?21,22 HTTP/1.1
dove tra i punti ? ? invio il tipo di comando, e 21 e 22 sono i pin di riferimento del comando,
conviene memorizzare la get su un array di char o su una Stringa?
grazie

toreg

faccio un passo indietro...
faccio il parser direttamente dai dati che mi passa il "client", esempio:
<if (client.available()) {   
        Serial.println(F("3 client raggiungibile"));

        /* esempi di get
         GET /   HTTP/1.1
         GET /?index HTTP/1.1
         GET /?S?21,22 HTTP/1.1
        */
       
        client.find("?");
        char type = client.read();
        Serial.println(type);
        if ( type == 'a' )  {
          int pintap1 = client.parseInt();
          apritap (pintap1, client);
        }
        if ( type == 'c' )  {
          int pintap1 = client.parseInt();
          chiuditap (pintap1, client);
        }
        if ( type == 's' )  {
          int pintap1 = client.parseInt();
          int pintap2 = client.parseInt();
          stoptap (pintap1, pintap2, client);
        }
        else if ( type == 'i' ){
          Serial.println(F("invio html"));

che ne dici? è abbastanza efficiente?

lestofante

assicurati di cosa succede se fai parseInt() e NON hai un numero.
Guida per principianti http://playground.arduino.cc/Italiano/newbie
Unoffical Telegram group https://t.me/genuino

toreg

Ho provato con un paio di lettere e mi assegna sempre il valore 1 alla variabile, quindi mi cambia lo stato del pin 1.
Con £, mi assegna 2, con é mi assegna 3!!

La get dovrebbe essere inviata da una pag web realizzata sempre da me, quindi dovrebbero arrivare get senza errori!

Quale potrebbe essere il problema??

lestofante

non capisco che richiesta stai facendo e di che variabile stai parlando
Guida per principianti http://playground.arduino.cc/Italiano/newbie
Unoffical Telegram group https://t.me/genuino

PaoloP


toreg

int pintap1 = client.parseInt();

Lesto...mi hai chiesto : assicurati di cosa succede se fai parseInt() e NON hai un numero.

la variabile in questo caso è pintap1.

Ho fatto delle prove e se non ho un numero, ma ad esempio lettere, il parseint si comporta nel modo che ho descritto nel precedente post!!

Go Up