Source

diveintopython3-it / servizi-web-http.html

Full commit
   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
<!DOCTYPE html>
<meta charset=utf-8>
<title>Servizi web HTTP - Immersione in Python 3</title>
<!--[if IE]><script src=j/html5.js></script><![endif]-->
<link rel=stylesheet href=dip3.css>
<style>
body{counter-reset:h1 14}
mark{display:inline}
</style>
<link rel=stylesheet media='only screen and (max-device-width: 480px)' href=mobile.css>
<link rel=stylesheet media=print href=print.css>
<meta name=viewport content='initial-scale=1.0'>
<form action=http://www.google.com/cse><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8>&nbsp;<input type=search name=q size=25 placeholder="powered by Google&trade;">&nbsp;<input type=submit name=root value=Search></div></form>
<p>Voi siete qui: <a href=index.html>Inizio</a> <span class=u>&#8227;</span> <a href=indice.html#servizi-web-http>Immersione in Python 3</a> <span class=u>&#8227;</span>
<p id=level>Livello di difficoltà: <span class=u title=avanzato>&#x2666;&#x2666;&#x2666;&#x2666;&#x2662;</span>
<h1>Servizi web HTTP</h1>
<blockquote class=q>
<p><span class=u>&#x275D;</span> Non c&#8217;è cuscino più morbido di una coscienza tranquilla. <span class=u>&#x275E;</span><br>&mdash; Charlotte Bront&euml;
</blockquote>
<p id=toc>&nbsp;
<h2 id=divingin>Immersione!</h2>
<p class=f>Relativamente alla descrizione della natura dei servizi web <abbr>HTTP</abbr>, non occorrono più di 15 parole: lo scambio di dati con server remoti utilizzando nient&#8217;altro che le operazioni di <abbr>HTTP</abbr>. Se volete ottenere dati dal server, usate <abbr>HTTP</abbr> <code>GET</code>; se volete inviare nuovi dati al server, usate <abbr>HTTP</abbr> <code>POST</code>. Alcune delle <abbr>API</abbr> più avanzate per i servizi web <abbr>HTTP</abbr> permettono anche di creare, modificare e cancellare dati utilizzando <abbr>HTTP</abbr> <code>PUT</code> e <abbr>HTTP</abbr> <code>DELETE</code>. In altre parole, i &#8220;verbi&#8221; definiti dal protocollo <abbr>HTTP</abbr> (<code>GET</code>, <code>POST</code>, <code>PUT</code> e <code>DELETE</code>) possono corrispondere direttamente a operazioni a livello di applicazione per recuperare, creare, modificare e cancellare dati.

<p>Il vantaggio principale di questo approccio è la semplicità, e la sua semplicità si è dimostrata popolare. I dati&nbsp;&mdash;&nbsp;tipicamente in formato <a href=xml.html><abbr>XML</abbr></a> o <a href=serializzare-oggetti-python.html#json><abbr>JSON</abbr></a>&nbsp;&mdash;&nbsp;possono essere assemblati e memorizzati staticamente oppure generati dinamicamente da un programma lato server, e tutti i linguaggi di programmazione più importanti (compreso Python, naturalmente!) includono una libreria <abbr>HTTP</abbr> per scaricarli. Anche le attività di debug vengono facilitate: dato che ogni risorsa in un servizio web <abbr>HTTP</abbr> ha un indirizzo unico (sotto forma di <abbr>URL</abbr>), potete caricarla nel vostro browser web e vederne immediatamente i dati grezzi.

<p>Esempi di servizi web <abbr>HTTP</abbr>:
<ul>
<li>le <a href=http://code.google.com/apis/gdata/><abbr>API</abbr> di Google Data</a> vi permettono di interagire con un&#8217;ampia varietà di servizi forniti da Google, compresi <a href=http://www.blogger.com/>Blogger</a> e <a href=http://www.youtube.com/>YouTube</a>;
<li>i <a href=http://www.flickr.com/services/api/>Servizi Flickr</a> vi permettono di caricare e scaricare foto da <a href=http://www.flickr.com/>Flickr</a>;
<li>la <a href=http://apiwiki.twitter.com/><abbr>API</abbr> di Twitter</a> vi permette di pubblicare aggiornamenti di stato su <a href=http://twitter.com/>Twitter</a>;
<li><a href='http://www.programmableweb.com/apis/directory/1?sort=mashups'>&hellip;e molti altri</a>.
</ul>

<p>Python 3 viene distribuito con due diverse librerie per interagire con i servizi web <abbr>HTTP</abbr>:

<ul>
<li><a href=http://docs.python.org/3.1/library/http.client.html><code>http.client</code></a> è una libreria di basso livello che implementa la <a href=http://www.w3.org/Protocols/rfc2616/rfc2616.html><abbr>RFC</abbr> 2616</a>, cioè il protocollo <abbr>HTTP</abbr>;
<li><a href=http://docs.python.org/3.1/library/urllib.request.html><code>urllib.request</code></a> è un livello di astrazione costruito sulla base di <code>http.client</code>. Fornisce una <abbr>API</abbr> standard per accedere sia ai server <abbr>HTTP</abbr> sia ai server <abbr>FTP</abbr>, segue automaticamente le redirezioni <abbr>HTTP</abbr> e gestisce alcune forme comuni di autenticazione <abbr>HTTP</abbr>.
</ul>

<p>Quindi quale delle due dovreste usare? Nessuna. Invece, dovreste usare <a href=http://code.google.com/p/httplib2/><code>httplib2</code></a>, una libreria open source di terze parti che implementa <abbr>HTTP</abbr> in maniera più completa rispetto ad <code>http.client</code> ma fornisce astrazioni migliori rispetto a <code>urllib.request</code>.

<p>Per comprendere perché <code>httplib2</code> è la scelta giusta dovete prima conoscere <abbr>HTTP</abbr>.

<p class=a>&#x2042;

<h2 id=http-features>Le caratteristiche di HTTP</h2>

<p>Ci sono cinque importanti caratteristiche che tutti i client <abbr>HTTP</abbr> dovrebbero supportare.

<h3 id=caching>La cache</h3>

<p>La cosa più importante da capire per un qualsiasi tipo di servizio web è che l&#8217;accesso alla rete è incredibilmente costoso. Non nel senso di &#8220;euro e centesimi&#8221; (sebbene la banda non sia gratis). Voglio dire che ci vuole un tempo estremamente lungo per aprire una connessione, spedire una richiesta e ricevere una risposta da un server remoto. Anche sulla connessione a banda larga più veloce, la <i>latenza</i> (il tempo che ci vuole per spedire una richiesta e cominciare a ricevere i dati in una risposta) può ancora essere più grande di quanto prevedete. Un router si comporta in modo strano, un pacchetto viene scartato, un proxy intermedio è sotto attacco&nbsp;&mdash;&nbsp;non c&#8217;è <a href=http://isc.sans.org/>mai un momento di calma</a> sulla rete Internet pubblica, e potrebbe non esserci nulla che voi possiate fare.

<aside><code>Cache-Control: max-age</code> significa: &#8220;Non scocciarmi fino alla prossima settimana.&#8221;</aside>

<p><abbr>HTTP</abbr> è progettato con l&#8217;uso della cache in mente. Esiste un&#8217;intera categoria di dispositivi (chiamati &#8220;proxy di cache&#8221;) il cui solo lavoro è sedere in mezzo tra voi e il resto del mondo e minimizzare l&#8217;accesso alla rete. La vostra azienda o il vostro <abbr>ISP</abbr> quasi certamente gestisce alcuni proxy di cache, anche se voi non ve ne rendete conto. Questi dispositivi funzionano perché l&#8217;uso della cache è integrato nel protocollo <abbr>HTTP</abbr>.

<p>Ecco un esempio concreto di come funziona la cache. Visitate <a href=http://diveintomark.org/><code>diveintomark.org</code></a> nel vostro browser. Quella pagina include un&#8217;immagine di sfondo, <a href=http://wearehugh.com/m.jpg><code>wearehugh.com/m.jpg</code></a>. Quando il vostro browser scarica quell&#8217;immagine, il server include le seguenti intestazioni <abbr>HTTP</abbr>:

<pre class=nd><code>HTTP/1.1 200 OK
Date: Sun, 31 May 2009 17:14:04 GMT
Server: Apache
Last-Modified: Fri, 22 Aug 2008 04:28:16 GMT
ETag: "3075-ddc8d800"
Accept-Ranges: bytes
Content-Length: 12405
<mark>Cache-Control: max-age=31536000, public</mark>
<mark>Expires: Mon, 31 May 2010 17:14:04 GMT</mark>
Connection: close
Content-Type: image/jpeg</code></pre>

<p>Le intestazioni <code>Cache-Control</code> ed <code>Expires</code> dicono al vostro browser (e a qualsiasi proxy di cache tra voi e il server) che questa immagine può essere tenuta in cache per un anno. <em>Un anno!</em> E se nel prossimo anno visitate un&#8217;altra pagina che include un collegamento a questa immagine, il vostro browser caricherà l&#8217;immagine dalla propria cache <em>senza generare alcun tipo di attività di rete</em>.

<p>Ma aspettate, le cose migliorano. Diciamo che per qualche motivo il vostro browser cancella l&#8217;immagine dalla vostra cache locale. Magari ha finito lo spazio su disco, magari voi avete pulito la cache manualmente. Ma le intestazioni <abbr>HTTP</abbr> dicevano che questi dati potevano essere memorizzati in cache da proxy di cache pubblici. (Tecnicamente, la cosa importante è quello che le intestazioni <em>non</em> dicono: l&#8217;intestazione <code>Cache-Control</code> non contiene la parola chiave <code>private</code>, quindi questi dati sono memorizzabili in cache per default.) I proxy di cache sono progettati per avere tonnellate di spazio di memorizzazione, probabilmente molto più di quanto abbiate allocato per il vostro browser locale.

<p>Se la vostra azienda o il vostro <abbr>ISP</abbr> gestisce un proxy di cache, il proxy potrebbe ancora avere l&#8217;immagine nella propria cache. Quando visitate <code>diveintomark.org</code> un&#8217;altra volta, il vostro browser cercherà l&#8217;immagine nella sua cache locale ma non la troverà, quindi effettuerà una richiesta di rete per cercare di scaricarla dal server remoto. Ma se il proxy di cache ha ancora una copia dell&#8217;immagine, intercetterà quella richiesta e preleverà l&#8217;immagine dalla <em>propria</em> cache. Questo significa che la vostra richiesta non raggiungerà mai il server remoto; in effetti, non lascerà mai la rete della vostra azienda. Questo rende lo scaricamento più veloce (meno salti sui nodi della rete) e fa risparmiare denaro alla vostra azienda (meno dati che vengono scaricati dal mondo esterno).

<p>L&#8217;uso della cache in <abbr>HTTP</abbr> funziona solo quando tutti fanno la loro parte. Da un lato, i server devono spedire le intestazioni corrette nelle loro risposte. Dall&#8217;altro lato, i client devono comprendere e rispettare quelle intestazioni prima di richiedere due volte gli stessi dati. I proxy nel mezzo non sono una panacea; possono essere tanto intelligenti solo quanto i server e i client permettono loro di essere.

<p>Le librerie <abbr>HTTP</abbr> incluse in Python non supportano l&#8217;uso della cache, ma <code>httplib2</code> lo fa.

<h3 id=last-modified>Il controllo della modifica più recente</h3>

<p>Alcuni dati non cambiano mai, mentre altri dati cambiano continuamente. Nel mezzo, c&#8217;è un vasto assortimento di dati che <em>potrebbero</em> essere cambiati ma non lo hanno fatto. Il feed di CNN.com viene aggiornato ogni pochi minuti, ma il feed del mio weblog potrebbe non cambiare per diversi giorni o intere settimane. In quest&#8217;ultimo caso, non voglio dire ai client di mantenere in cache il mio feed per intere settimane, perché quando scrivo effettivamente qualcosa i miei lettori potrebbero non vederla per settimane (perché stanno rispettando le mie intestazioni di cache che dicono &#8220;non preoccuparti di controllare questo feed per settimane&#8221;). D&#8217;altra parte, non voglio che i client scarichino il mio intero feed una volta ogni ora se non è cambiato!

<aside><code>304 Not Modified</code> significa: &#8220;Stessa merda, altro giorno.&#8221;</aside>

<p><abbr>HTTP</abbr> fornisce una soluzione anche a questo problema. Quando richiedete un dato per la prima volta, il server può spedire indietro un&#8217;intestazione <code>Last-Modified</code>. Questa è esattamente ciò che sembra: la data in cui i dati sono stati modificati. Quell&#8217;immagine di sfondo riferita da <code>diveintomark.org</code> includeva un&#8217;intestazione <code>Last-Modified</code>.

<pre class=nd><code>HTTP/1.1 200 OK
Date: Sun, 31 May 2009 17:14:04 GMT
Server: Apache
<mark>Last-Modified: Fri, 22 Aug 2008 04:28:16 GMT</mark>
ETag: "3075-ddc8d800"
Accept-Ranges: bytes
Content-Length: 12405
Cache-Control: max-age=31536000, public
Expires: Mon, 31 May 2010 17:14:04 GMT
Connection: close
Content-Type: image/jpeg
</code></pre>

<p>Quando richiedete gli stessi dati una seconda (o terza, o quarta) volta, potete spedire insieme alla vostra richiesta un&#8217;intestazione <code>If-Modified-Since</code> contenente la data che avete ottenuto dal server l&#8217;ultima volta. Se i dati sono cambiati da allora, il server ignora l&#8217;intestazione <code>If-Modified-Since</code> e vi restituisce semplicemente i nuovi dati con un codice di stato <code>200</code>. Ma se i dati <em>non</em> sono cambiati, il server rimanda indietro lo speciale codice di stato <abbr>HTTP</abbr> <code>304</code> per indicare che &#8220;questi dati non sono cambiati dall&#8217;ultima volta che li avete richiesti&#8221;. Potete verificare questo comportamento dalla riga di comando usando <a href=http://curl.haxx.se/>curl</a>:

<pre class='nd screen'>
<samp class=p>you@localhost:~$ </samp><kbd>curl -I <mark>-H "If-Modified-Since: Fri, 22 Aug 2008 04:28:16 GMT"</mark> http://wearehugh.com/m.jpg</kbd>
<samp>HTTP/1.1 304 Not Modified
Date: Sun, 31 May 2009 18:04:39 GMT
Server: Apache
Connection: close
ETag: "3075-ddc8d800"
Expires: Mon, 31 May 2010 18:04:39 GMT
Cache-Control: max-age=31536000, public</samp></pre>

<p>Perché questo è un miglioramento? Perché quando il server restituisce un <code>304</code>, <em>non rispedisce i dati</em>. Tutto quello che ottenete è il codice di stato. Anche dopo che la copia nella vostra cache è scaduta, il controllo della modifica più recente vi assicura che non scaricherete gli stessi dati due volte se non sono cambiati. (Come bonus aggiuntivo, questa risposta <code>304</code> include anche le intestazioni di cache. I proxy manterranno una copia dei dati anche dopo che sono ufficialmente &#8220;scaduti&#8221;, nella speranza che i dati non siano <em>realmente</em> cambiati e che la prossima richiesta ottenga una risposta con un codice di stato <code>304</code> e informazioni di cache aggiornate.)

<p>Le librerie <abbr>HTTP</abbr> incluse in Python non supportano il controllo della modifica più recente, ma <code>httplib2</code> lo fa.

<h3 id=etags>Il controllo degli ETag</h3>

<p>Gli ETag sono un modo alternativo per effettuare il <a href=#last-modified>controllo della modifica più recente</a>. Con gli ETag, il server spedisce un codice hash in un&#8217;intestazione <code>ETag</code> insieme ai dati che avete richiesto. (Decidere come determinare esattamente questo hash è interamente compito del server. L&#8217;unico requisito è che l&#8217;hash cambi quando i dati cambiano.) Quell&#8217;immagine di sfondo riferita da <code>diveintomark.org</code> aveva un&#8217;intestazione <code>ETag</code>.

<aside><code>ETag</code> significa: &#8220;Nulla di nuovo sotto il sole.&#8221;</aside>

<pre class=nd><code>HTTP/1.1 200 OK
Date: Sun, 31 May 2009 17:14:04 GMT
Server: Apache
Last-Modified: Fri, 22 Aug 2008 04:28:16 GMT
<mark>ETag: "3075-ddc8d800"</mark>
Accept-Ranges: bytes
Content-Length: 12405
Cache-Control: max-age=31536000, public
Expires: Mon, 31 May 2010 17:14:04 GMT
Connection: close
Content-Type: image/jpeg
</code></pre>

<p>La seconda volta che richiedete gli stessi dati, includete il valore di ETag in un&#8217;intestazione <code>If-None-Match</code> della vostra richiesta. Se i dati non sono cambiati, il server vi restituirà un codice di stato <code>304</code>. Come con il controllo sulla data della modifica più recente, il server invia <em>solo</em> il codice di stato <code>304</code>, evitando di spedirvi gli stessi dati una seconda volta. Includendo il valore di ETag nella vostra seconda richiesta state dicendo al server che non c&#8217;è alcun bisogno di rispedire gli stessi dati se quei dati corrispondono ancora a questo hash, dato che <a href=#caching>avete ancora i dati dall&#8217;ultima volta</a>.

<p>Utilizzando ancora <kbd>curl</kbd>:

<pre class='nd screen'>
<a><samp class=p>you@localhost:~$ </samp><kbd>curl -I <mark>-H "If-None-Match: \"3075-ddc8d800\""</mark> http://wearehugh.com/m.jpg</kbd>  <span class=u>&#x2460;</span></a>
<samp>HTTP/1.1 304 Not Modified
Date: Sun, 31 May 2009 18:04:39 GMT
Server: Apache
Connection: close
ETag: "3075-ddc8d800"
Expires: Mon, 31 May 2010 18:04:39 GMT
Cache-Control: max-age=31536000, public</samp></pre>
<ol>
<li>Gli ETag vengono comunemente racchiusi tra virgolette, ma <em>le virgolette sono parte del valore</em>. Questo significa che dovete rimandare le virgolette al server nell&#8217;intestazione <code>If-None-Match</code>.
</ol>

<p>Le librerie <abbr>HTTP</abbr> incluse in Python non supportano gli ETag, ma <code>httplib2</code> lo fa.

<h3 id=compression>La compressione</h3>

<p>Quando parlate di servizi web <abbr>HTTP</abbr>, state quasi certamente parlando di spostare dati basati su testo avanti e indietro attraverso la rete. Forse i dati sono in formato <abbr>XML</abbr>, forse sono in formato <abbr>JSON</abbr>, forse sono solo <a href=stringhe.html#boring-stuff title='il testo semplice non esiste'>testo semplice</a>. A prescindere dal formato, il testo si comprime bene. Il feed di esempio nel <a href=xml.html>capitolo su  XML</a> è di 3070 byte, ma sarebbe di 941 byte dopo una compressione effettuata tramite gzip, esattamente il 30% della dimensione originale!

<p><abbr>HTTP</abbr> supporta <a href=http://www.iana.org/assignments/http-parameters>diversi algoritmi di compressione</a>. I due tipi più comuni sono <a href=http://www.ietf.org/rfc/rfc1952.txt>gzip</a> e <a href=http://www.ietf.org/rfc/rfc1951.txt>deflate</a>. Quando richiedete una risorsa via <abbr>HTTP</abbr>, potete chiedere al server di mandarvela in formato compresso includendo nella vostra richiesta un&#8217;intestazione <code>Accept-encoding</code> che elenchi quali algoritmi di compressione supportate. Se il server supporta uno qualsiasi degli stessi algoritmi, vi risponderà inviando i dati compressi (insieme a un&#8217;intestazione <code>Content-encoding</code> che vi dice quale algoritmo ha usato). Poi sta a voi decomprimere i dati.

<blockquote class=note>
<p><span class=u>&#x261E;</span>Suggerimento importante per gli sviluppatori lato server: assicuratevi che la versione compressa di una risorsa abbia un <a href=#etags>Etag</a> differente rispetto alla versione non compressa. Altrimenti, i proxy di cache si confonderanno e potrebbero servire la versione compressa a client che non sono in grado di gestirla. Leggete la discussione sul <a href="https://issues.apache.org/bugzilla/show_bug.cgi?id=39727">bug 39727 di Apache</a> per maggiori dettagli su questo sottile problema.
</blockquote>

<p>Le librerie <abbr>HTTP</abbr> incluse in Python non supportano la compressione, ma <code>httplib2</code> lo fa.

<h3 id=redirects>Le redirezioni</h3>

<p><a href=http://www.w3.org/Provider/Style/URI>Gli <abbr>URI</abbr> fighi non cambiano</a>, ma molti <abbr>URI</abbr> sono seriamente sfigati. I siti web vengono riorganizzati, le pagine vengono spostate a nuovi indirizzi, persino i servizi web possono venire riorganizzati. Un feed pubblicato all&#8217;indirizzo <code>http://example.com/index.xml</code> potrebbe venire spostato all&#8217;indirizzo <code>http://example.com/xml/atom.xml</code>. Oppure un intero dominio potrebbe spostarsi, man mano che un&#8217;organizzazione si espande e viene ristrutturata: <code>http://www.example.com/index.xml</code> diventa <code>http://server-farm-1.example.com/index.xml</code>.

<aside><code>Location</code> significa: &#8220;Guarda là!&#8221;</aside>

<p>Ogni volta che richiedete un qualsiasi tipo di risorsa da un server <abbr>HTTP</abbr>, il server include un codice di stato nella propria risposta. Il codice di stato <code>200</code> significa &#8220;è tutto normale, ecco la pagina che avete chiesto&#8221;. Il codice di stato <code>404</code> significa &#8220;pagina non trovata&#8221;. (Avete probabilmente visto errori 404 navigando sul web.) I codici di stato che cominciano con 3 indicano una qualche forma di redirezione.

<p><abbr>HTTP</abbr> ha molti modi diversi per dire che una risorsa si è spostata. Le due tecniche più comuni sono i codici di stato <code>302</code> e <code>301</code>. Il codice di stato <code>302</code> indica una <i>redirezione temporanea</i>: significa &#8220;oops, quella risorsa è stata temporaneamente spostata qui&#8221; (e poi vi dà l&#8217;indirizzo temporaneo in un&#8217;intestazione <code>Location</code>). Il codice di stato <code>301</code> indica una <i>redirezione permanente</i>: significa &#8220;oops, quella risorsa è stata permanentemente spostata&#8221; (e poi vi dà il nuovo indirizzo in un&#8217;intestazione <code>Location</code>). Se ottenete un codice di stato <code>302</code> e un nuovo indirizzo, la specifica <abbr>HTTP</abbr> dice che dovreste usare il nuovo indirizzo per ottenere quello che avete chiesto, ma la prossima volta che volete accedere alla stessa risorsa dovreste riprovare il vecchio indirizzo. Se invece ottenete un codice di stato <code>301</code> e un nuovo indirizzo, siete tenuti a utilizzare il nuovo indirizzo da quel momento in poi.

<p>Il modulo <code>urllib.request</code> &#8220;segue&#8221; automaticamente le redirezioni quando riceve i codici di stato appropriati da un server <abbr>HTTP</abbr>, ma non vi dice di averlo fatto. Finirete per ottenere i dati che avete chiesto, ma non saprete mai che la libreria sottostante vi ha &#8220;aiutato&#8221; a seguire una redirezione. Così continuerete a tempestare di richieste il vecchio indirizzo, e ogni volta il modulo <code>urllib.request</code> vi &#8220;aiuterà&#8221; a seguire la redirezione. In altre parole, il modulo tratta le redirezioni permanenti allo stesso modo delle redirezioni temporanee. Questo significa due viaggi invece di uno, che è male per il server è male per voi.

<p><code>httplib2</code> gestisce le redirezioni permanenti per voi. Non solo vi dirà che c&#8217;è stata una redirezione permanente, ma terrà traccia di tali redirezioni localmente e riscriverà automaticamente gli <abbr>URL</abbr> rediretti prima di spedire loro una richiesta.

<p class=a>&#x2042;

<h2 id=dont-try-this-at-home>Come non prelevare dati via HTTP</h2>

<p>Diciamo che volete scaricare una risorsa via <abbr>HTTP</abbr>, per esempio <a href=xml.html>un feed Atom</a>. Essendo un feed, non vi limiterete a scaricare la risorsa una sola volta, ma vorrete scaricarla più e più volte. (La maggior parte dei lettori di feed controllano ogni ora se ci sono stati dei cambiamenti.) Facciamolo prima in modo veloce ma grossolano, poi vediamo come potete farlo meglio.
<pre class='nd screen'>
<samp class=p>>>> </samp><kbd class=pp>import urllib.request</kbd>
<samp class=p>>>> </samp><kbd class=pp>a_url = 'http://diveintopython3.org/examples/feed.xml'</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>data = urllib.request.urlopen(a_url).read()</kbd>  <span class=u>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>type(data)</kbd>                                   <span class=u>&#x2461;</span></a>
<samp class=pp>&lt;class 'bytes'></samp>
<samp class=p>>>> </samp><kbd class=pp>print(data)</kbd>
<samp class=pp>&lt;?xml version='1.0' encoding='utf-8'?>
&lt;feed xmlns='http://www.w3.org/2005/Atom' xml:lang='it'>
  &lt;title>dive into mark&lt;/title>
  &lt;subtitle>attualmente tra una dipendenza e l'altra&lt;/subtitle>
  &lt;id>tag:diveintomark.org,2001-07-29:/&lt;/id>
  &lt;updated>2009-03-27T21:56:07Z&lt;/updated>
  &lt;link rel='alternate' type='text/html' href='http://diveintomark.org/'/>
  &hellip;
</samp></pre>
<ol>
<li>Scaricare qualsiasi cosa via <abbr>HTTP</abbr> è incredibilmente facile in Python; in effetti, vi ci vuole solo una riga di codice. Il modulo <code>urllib.request</code> ha una comoda funzione <code>urlopen()</code> che prende l&#8217;indirizzo della pagina che volete e restituisce un oggetto simile a un file che potete semplicemente leggere con il metodo <code>read()</code> per ottenere il contenuto completo della pagina. Non potrebbe proprio essere più facile.
<li>Il metodo <code>urlopen().read()</code> restituisce sempre un <a href=stringhe.html#byte-arrays>oggetto <code>bytes</code>, non una stringa</a>. Ricordatevi che i byte sono byte e che i caratteri sono un&#8217;astrazione. I server <abbr>HTTP</abbr> non si occupano di astrazioni. Se richiedete una risorsa, ottenete byte. Se volete una stringa, dovrete <a href=http://feedparser.org/docs/character-encoding.html>determinare la codifica di carattere</a> dei byte ed effettuare esplicitamente la conversione.
</ol>

<p>Quindi cosa c&#8217;è di sbagliato in questo modo di operare? Per un impiego rapido e occasionale durante il collaudo o lo sviluppo, non c&#8217;è niente di sbagliato. Lo faccio sempre. Volevo i contenuti del feed e ho ottenuto i contenuti del feed. La stessa tecnica funziona per tutte le pagine web. Ma una volta che cominciate a pensare in termini di un servizio web che volete accedere regolarmente (<i>e.g.</i> richiedendo questo feed una volta ogni ora), allora lo state facendo in maniera inefficiente e grossolana.

<p class=a>&#x2042;

<h2 id=whats-on-the-wire>Cosa viene trasmesso attraverso la rete?</h2>

<p>Per vedere perché questo modo di prelevare dati è inefficiente e grossolano, attiviamo le caratteristiche di debug della libreria <abbr>HTTP</abbr> inclusa in Python e vediamo cosa viene trasmesso &#8220;sul filo&#8221; (cioè attraverso la rete).

<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>from http.client import HTTPConnection</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>HTTPConnection.debuglevel = 1</kbd>                                       <span class=u>&#x2460;</span></a>
<samp class=p>>>> </samp><kbd class=pp>from urllib.request import urlopen</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>response = urlopen('http://diveintopython3.org/examples/feed.xml')</kbd>  <span class=u>&#x2461;</span></a>
<samp><a>send: b'GET /examples/feed.xml HTTP/1.1                                 <span class=u>&#x2462;</span></a>
<a>Host: diveintopython3.org                                               <span class=u>&#x2463;</span></a>
<a>Accept-Encoding: identity                                               <span class=u>&#x2464;</span></a>
<a>User-Agent: Python-urllib/3.1'                                          <span class=u>&#x2465;</span></a>
Connection: close
reply: 'HTTP/1.1 200 OK'
&hellip;ulteriori informazioni di debug omesse&hellip;</samp></pre>
<ol>
<li>Come avevo menzionato all&#8217;inizio del capitolo, <code>urllib.request</code> si basa su un&#8217;altra libreria standard inclusa in Python, <code>http.client</code>. Di solito non avete bisogno di toccare <code>http.client</code> direttamente. (Il modulo <code>urllib.request</code> la importa automaticamente.) Ma qui noi la importiamo in modo da poter attivare il flag di debug sulla classe <code>HTTPConnection</code> che <code>urllib.request</code> usa per connettersi a un server <abbr>HTTP</abbr>.
<li>Ora che il flag di debug è impostato, le informazioni sulla richiesta e sulla risposta <abbr>HTTP</abbr> sono stampate in tempo reale. Come potete vedere, quando richiedete il feed Atom il modulo <code>urllib.request</code> invia cinque righe al server.
<li>La prima riga specifica il verbo <abbr>HTTP</abbr> che state usando e il percorso della risorsa (senza il nome del dominio).
<li>La seconda riga specifica il nome del dominio da dove stiamo richiedendo questo feed.
<li>La terza riga specifica gli algoritmi di compressione che il client supporta. Come avevo menzionato in precedenza, <a href=#compression><code>urllib.request</code> non supporta la compressione</a> di default.
<li>La quarta riga specifica il nome della libreria che sta effettuando la richiesta. Per default, questo nome è <code>Python-urllib</code> più un numero di versione. Sia <code>urllib.request</code> che <code>httplib2</code> permettono di cambiare il nome dell&#8217;applicazione usata semplicemente aggiungendo un&#8217;intestazione <code>User-Agent</code> alla richiesta (che sostituirà il valore predefinito).
</ol>

<aside>Stiamo scaricando 3070 byte mentre ne avremmo potuti scaricare 941.</aside>

<p>Ora diamo un&#8217;occhiata ai dati che il server ha inviato nella sua risposta.

<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>print(response.headers.as_string())</kbd>        <span class=u>&#x2460;</span></a>
<samp><a>Date: Sun, 31 May 2009 19:23:06 GMT            <span class=u>&#x2461;</span></a>
Server: Apache
<a>Last-Modified: Sun, 31 May 2009 06:39:55 GMT   <span class=u>&#x2462;</span></a>
<a>ETag: "bfe-93d9c4c0"                           <span class=u>&#x2463;</span></a>
Accept-Ranges: bytes
<a>Content-Length: 3070                           <span class=u>&#x2464;</span></a>
<a>Cache-Control: max-age=86400                   <span class=u>&#x2465;</span></a>
Expires: Mon, 01 Jun 2009 19:23:06 GMT
Vary: Accept-Encoding
Connection: close
Content-Type: application/xml</samp>
<a><samp class=p>>>> </samp><kbd class=pp>data = response.read()</kbd>                     <span class=u>&#x2466;</span></a>
<samp class=p>>>> </samp><kbd class=pp>len(data)</kbd>
<samp class=pp>3070</samp></pre>
<ol>
<li>L&#8217;oggetto <var>response</var> restituito dalla funzione <code>urllib.request.urlopen()</code> contiene tutte le intestazioni <abbr>HTTP</abbr> che il server ha inviato nella risposta. Contiene anche i metodi per scaricare i dati effettivi, a cui arriveremo tra un minuto.
<li>Il server vi dice quando si è occupato della vostra richiesta.
<li>Questa risposta include un&#8217;intestazione <a href=#last-modified><code>Last-Modified</code></a>.
<li>Questa risposta include un&#8217;intestazione <a href=#etags><code>ETag</code></a>.
<li>La dimensione dei dati è 3070 byte. Notate quello che <em>manca</em> qui: un&#8217;intestazione <code>Content-encoding</code>. La vostra richiesta ha dichiarato di accettare solo dati non compressi (<code>Accept-encoding: identity</code>) e, come c&#8217;era da aspettarsi, questa risposta contiene dati non compressi.
<li>Questa risposta include intestazioni di cache che affermano che questo feed può essere tenuto in memoria per 24 ore (86400 secondi).
<li>E infine scarichiamo i dati effettivi invocando <code>response.read()</code>. Come potete vedere dal risultato della funzione <code>len()</code>, questa operazione scarica tutti i 3070 byte in una volta sola.
</ol>

<p>Come potete vedere, questo codice è già inefficiente: ha richiesto (e ricevuto) dati non compressi. So per certo che questo server supporta la <a href=#compression>compressione tramite gzip</a>, ma la compressione <abbr>HTTP</abbr> è opzionale. Non l&#8217;abbiamo richiesta e quindi non l&#8217;abbiamo ottenuta. Questo significa che stiamo scaricando 3070 byte mentre ne avremmo potuti scaricare solo 941. Cattivo cane, niente biscotto.

<p>Ma aspettate, le cose peggiorano! Per vedere quanto questo codice sia davvero inefficiente, richiediamo lo stesso feed una seconda volta.

<pre class='nd screen'>
# continua dall'<a href=#whats-on-the-wire>esempio precedente</a>
<samp class=p>>>> </samp><kbd class=pp>response2 = urlopen('http://diveintopython3.org/examples/feed.xml')</kbd>
<samp>send: b'GET /examples/feed.xml HTTP/1.1
Host: diveintopython3.org
Accept-Encoding: identity
User-Agent: Python-urllib/3.1'
Connection: close
reply: 'HTTP/1.1 200 OK'
&hellip;ulteriori informazioni di debug omesse&hellip;</samp></pre>

<p>Notate qualcosa di peculiare in questa richiesta? Non è cambiata! &Egrave; esattamente uguale alla prima richiesta. Nessun segno di <a href=#last-modified>intestazioni <code>If-Modified-Since</code></a>. Nessun segno di <a href=#etags>intestazioni <code>If-None-Match</code></a>. Nessun rispetto per le intestazioni di cache. Ancora nessuna compressione.

<p>E cosa succede quando effettuate due volte la stessa richiesta? Ottenete due volte la stessa risposta.

<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>print(response2.headers.as_string())</kbd>     <span class=u>&#x2460;</span></a>
<samp>Date: Mon, 01 Jun 2009 03:58:00 GMT
Server: Apache
Last-Modified: Sun, 31 May 2009 22:51:11 GMT
ETag: "bfe-255ef5c0"
Accept-Ranges: bytes
Content-Length: 3070
Cache-Control: max-age=86400
Expires: Tue, 02 Jun 2009 03:58:00 GMT
Vary: Accept-Encoding
Connection: close
Content-Type: application/xml</samp>
<samp class=p>>>> </samp><kbd class=pp>data2 = response2.read()</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>len(data2)</kbd>                               <span class=u>&#x2461;</span></a>
<samp class=pp>3070</samp>
<a><samp class=p>>>> </samp><kbd class=pp>data2 == data</kbd>                            <span class=u>&#x2462;</span></a>
<samp class=pp>True</samp></pre>
<ol>
<li>Il server sta ancora mandando la stessa schiera di intestazioni &#8220;intelligenti&#8221;: <code>Cache-Control</code> ed <code>Expires</code> per consentire l&#8217;uso della cache, <code>Last-Modified</code> ed <code>ETag</code> per abilitare il rilevamento della modifica più recente. Persino l&#8217;intestazione <code>Vary: Accept-Encoding</code> suggerisce che il server supporterebbe la compressione, se solo la chiedessimo. Ma non l&#8217;abbiamo fatto.
<li>Ancora una volta, l&#8217;operazione di prelievo di questi dati scarica tutti i 3070 byte&hellip;
<li>&hellip;esattamente gli stessi 3070 byte che avete scaricato l&#8217;ultima volta.
</ol>

<p><abbr>HTTP</abbr> è progettato per funzionare meglio di così. <code>urllib</code> parla <abbr>HTTP</abbr> come io parlo spagnolo&nbsp;&mdash;&nbsp;abbastanza bene da cavarmela improvvisando, ma non a sufficienza da tenere una conversazione. <abbr>HTTP</abbr> è una conversazione. &Egrave; ora di sostituire <code>urllib</code> con una libreria in grado di parlare <abbr>HTTP</abbr> correntemente.

<p class=a>&#x2042;

<h2 id=introducing-httplib2>Una introduzione ad <code>httplib2</code></h2>

<p>Prima di usare <code>httplib2</code>, avrete bisogno di installarla. Visitate <a href=http://code.google.com/p/httplib2/><code>code.google.com/p/httplib2/</code></a> e scaricate l&#8217;ultima versione. <code>httplib2</code> è disponibile per Python 2.x e Python 3.x. Assicuratevi di prendere la versione per Python 3, che ha un nome simile a <code>httplib2-python3-0.5.0.zip</code>.

<p>Estraete i contenuti dell&#8217;archivio, aprite una finestra di terminale e posizionatevi nella directory <code>httplib2</code> appena creata. Su Windows, aprite il menu <code>Start</code>, selezionate <code>Esegui...</code>, digitate <kbd>cmd.exe</kbd> e premete <kbd>INVIO</kbd>.

<pre class=screen>
<samp class=p>c:\Users\pilgrim\Downloads> </samp><kbd><mark>dir</mark></kbd>
<samp> Il volume nell'unità C non ha etichetta.
 Numero di serie del volume: DED5-B4F8

 Directory di c:\Users\pilgrim\Downloads

28/07/2009  12:36    &lt;DIR>          .
28/07/2009  12:36    &lt;DIR>          ..
28/07/2009  12:36    &lt;DIR>          httplib2-python3-0.5.0
28/07/2009  12:33            18,997 httplib2-python3-0.5.0.zip
               1 File        18.997 byte
               3 Directory   61.496.684.544 byte disponibili</samp>

<samp class=p>c:\Users\pilgrim\Downloads> </samp><kbd><mark>cd httplib2-python3-0.5.0</mark></kbd>
<samp class=p>c:\Users\pilgrim\Downloads\httplib2-python3-0.5.0> </samp><kbd><mark>c:\python31\python.exe setup.py install</mark></kbd>
<samp>eseguo install
eseguo build
eseguo build_py
eseguo install_lib
creo c:\python31\Lib\site-packages\httplib2
copio build\lib\httplib2\iri2uri.py -> c:\python31\Lib\site-packages\httplib2
copio build\lib\httplib2\__init__.py -> c:\python31\Lib\site-packages\httplib2
compilo c:\python31\Lib\site-packages\httplib2\iri2uri.py in iri2uri.pyc
compilo c:\python31\Lib\site-packages\httplib2\__init__.py in __init__.pyc
eseguo install_egg_info
Scrivo c:\python31\Lib\site-packages\httplib2-python3_0.5.0-py3.1.egg-info</samp></pre>

<p>Su Mac OS X, aprite l&#8217;applicazione <code>Terminal.app</code> che si trova nella vostra cartella <code>/Applications/Utilities/</code>. Su Linux, lanciate l&#8217;applicazione <code>Terminale</code>, che di solito si trova nel vostro menu <code>Applicazioni</code> sotto la voce <code>Accessori</code> o <code>Strumenti di sistema</code>.

<pre class=screen>
<samp class=p>you@localhost:~/Desktop$ </samp><kbd><mark>unzip httplib2-python3-0.5.0.zip</mark></kbd>
<samp>Archivio:  httplib2-python3-0.5.0.zip
  decomprimo: httplib2-python3-0.5.0/README
  decomprimo: httplib2-python3-0.5.0/setup.py
  decomprimo: httplib2-python3-0.5.0/PKG-INFO
  decomprimo: httplib2-python3-0.5.0/httplib2/__init__.py
  decomprimo: httplib2-python3-0.5.0/httplib2/iri2uri.py</samp>
<samp class=p>you@localhost:~/Desktop$ </samp><kbd><mark>cd httplib2-python3-0.5.0/</mark></kbd>
<samp class=p>you@localhost:~/Desktop/httplib2-python3-0.5.0$ </samp><kbd><mark>sudo python3 setup.py install</mark></kbd>
<samp>eseguo install
eseguo build
eseguo build_py
creo build
creo build/lib.linux-x86_64-3.1
creo build/lib.linux-x86_64-3.1/httplib2
copio httplib2/iri2uri.py -> build/lib.linux-x86_64-3.1/httplib2
copio httplib2/__init__.py -> build/lib.linux-x86_64-3.1/httplib2
eseguo install_lib
creo /usr/local/lib/python3.1/dist-packages/httplib2
copio build/lib.linux-x86_64-3.1/httplib2/iri2uri.py -> /usr/local/lib/python3.1/dist-packages/httplib2
copio build/lib.linux-x86_64-3.1/httplib2/__init__.py -> /usr/local/lib/python3.1/dist-packages/httplib2
compilo /usr/local/lib/python3.1/dist-packages/httplib2/iri2uri.py in iri2uri.pyc
compilo /usr/local/lib/python3.1/dist-packages/httplib2/__init__.py in __init__.pyc
eseguo install_egg_info
Scrivo /usr/local/lib/python3.1/dist-packages/httplib2-python3_0.5.0.egg-info</samp></pre>

<p>Per utilizzare <code>httplib2</code>, create un&#8217;istanza della classe <code>httplib2.Http</code>.

<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import httplib2</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>h = httplib2.Http('.cache')</kbd>                                                    <span class=u>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>response, content = h.request('http://diveintopython3.org/examples/feed.xml')</kbd>  <span class=u>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>response.status</kbd>                                                                <span class=u>&#x2462;</span></a>
<samp class=pp>200</samp>
<a><samp class=p>>>> </samp><kbd class=pp>content[:52]</kbd>                                                                   <span class=u>&#x2463;</span></a>
<samp class=pp>b"&lt;?xml version='1.0' encoding='utf-8'?>\r\n&lt;feed xmlns="</samp>
<samp class=p>>>> </samp><kbd class=pp>len(content)</kbd>
<samp class=pp>3070</samp></pre>
<ol>
<li>L&#8217;interfaccia principale al modulo <code>httplib2</code> è l&#8217;oggetto <code>Http</code>. Per ragioni che vedrete nella prossima sezione, dovreste sempre passare un nome di directory quando create un oggetto <code>Http</code>. La directory non deve per forza esistere, <code>httplib2</code> la creerà se è necessario.
<li>Una volta che avete un oggetto <code>Http</code>, recuperare dati è tanto semplice quanto invocare il metodo <code>request()</code> con l&#8217;indirizzo dei dati che volete. Questo metodo emetterà una richiesta <abbr>HTTP</abbr> <code>GET</code> per quell&#8217;<abbr>URL</abbr>. (Più avanti in questo capitolo vedrete come emettere altre richieste <abbr>HTTP</abbr>, come <code>POST</code>.)
<li>Il metodo <code>request()</code> restituisce due valori. Il primo è un oggetto <code>httplib2.Response</code>, che contiene tutte le intestazioni <abbr>HTTP</abbr> restituite dal server. Per esempio, il codice di stato <code>200</code> memorizzato nell&#8217;attributo <code>status</code> indica che la richiesta ha avuto successo.
<li>La variabile <var>content</var> contiene i dati effettivi che sono stati restituiti dal server <abbr>HTTP</abbr>. I dati vengono restituiti come un <a href=stringhe.html#byte-arrays>oggetto <code>bytes</code>, non una stringa</a>. Se li volete sotto forma di stringa dovete <a href=http://feedparser.org/docs/character-encoding.html>determinarne la codifica di carattere</a> ed effettuare da voi la conversione.
</ol>

<blockquote class=note>
<p><span class=u>&#x261E;</span>Avrete probabilmente bisogno di un solo oggetto <code>httplib2.Http</code>. Esistono valide ragioni per crearne più di uno, ma dovreste farlo solo se sapete perché ne avete bisogno. &#8220;Devo richiedere dati da due <abbr>URL</abbr> differenti&#8221; non è una ragione valida. Riutilizzate l&#8217;oggetto <code>Http</code> e invocate semplicemente il metodo <code>request()</code> due volte.
</blockquote>

<h3 id=why-bytes>Una breve digressione per spiegare perché <code>httplib2</code> restituisce byte invece di stringhe</h3>

<p>Byte. Stringhe. Che sofferenza. Perché <code>httplib2</code> non può &#8220;semplicemente&#8221; fare la conversione per voi? Be&#8217;, è complicato, perché le regole per determinare la codifica di carattere sono specifiche per il tipo di risorsa che state richiedendo. Come potrebbe fare <code>httplib2</code> a sapere che tipo di risorsa state richiedendo? Di solito, il tipo è elencato nell&#8217;intestazione <abbr>HTTP</abbr> <code>Content-Type</code>, ma questa è una caratteristica opzionale di <abbr>HTTP</abbr> e non tutti i server <abbr>HTTP</abbr> la supportano. Se quell&#8217;intestazione non è presente nella risposta <abbr>HTTP</abbr>, tocca al client indovinare. Questa operazione viene comunemente chiamata &#8220;content sniffing&#8221; (letteralmente, annusare il contenuto) e non è mai perfetta.

<p>Se sapete che tipo di risorsa vi aspettate (in questo caso, un documento <abbr>XML</abbr>), forse potreste &#8220;semplicemente&#8221; passare l&#8217;oggetto <code>bytes</code> restituito alla funzione <a href=xml.html#xml-parse><code>xml.etree.ElementTree.parse()</code></a>. Questo funzionerà purché il documento <abbr>XML</abbr> includa informazioni sulla propria codifica di carattere (come accade in questo caso), ma questa è una caratteristica opzionale e non tutti i documenti <abbr>XML</abbr> lo fanno. Se un documento <abbr>XML</abbr> non include informazioni di codifica, il client è tenuto a controllare il protocollo di trasporto a cui sono allegati i dati&nbsp;&mdash;&nbsp;cioè l&#8217;intestazione <abbr>HTTP</abbr> <code>Content-Type</code>, che può includere un parametro <code>charset</code>.

<p class=ss><a style=border:0 href=http://www.cafepress.com/feedparser><img src=http://feedparser.org/img/feedparser.jpg alt='[Maglietta &#8220;Io supporto la RFC 3023&#8221;]' width=150 height=150></a>

<p>Ma le cose vanno anche peggio di così. Ora le informazioni sulla codifica di carattere possono trovarsi in due posti: all&#8217;interno del documento <abbr>XML</abbr> e nell&#8217;intestazione <abbr>HTTP</abbr> <code>Content-Type</code>. Se l&#8217;informazione è in <em>entrambi</em> i posti, quale deve essere ritenuta valida? Secondo la <a href=http://www.ietf.org/rfc/rfc3023.txt>RFC 3023</a> (vi giuro che non me lo sto inventando), se il tipo di contenuto fornito nell&#8217;intestazione <abbr>HTTP</abbr> <code>Content-Type</code> è <code>application/xml</code>, <code>application/xml-dtd</code>, <code>application/xml-external-parsed-entity</code>, o un qualsiasi sottotipo di <code>application/xml</code> come per esempio <code>application/atom+xml</code> o <code>application/rss+xml</code> o persino <code>application/rdf+xml</code>, allora la codifica è

<ol>
<li>la codifica data nel parametro <code>charset</code> dell&#8217;intestazione <abbr>HTTP</abbr> <code>Content-Type</code>, oppure
<li>la codifica data nell&#8217;attributo <code>encoding</code> della dichiarazione <abbr>XML</abbr> all&#8217;interno del documento, oppure
<li><abbr>UTF-8</abbr>.
</ol>

<p>D&#8217;altra parte, se il tipo di contenuto dato nell&#8217;intestazione <abbr>HTTP</abbr> <code>Content-Type</code> è <code>text/xml</code>, <code>text/xml-external-parsed-entity</code>, o un sottotipo come per esempio <code>text/QualsiasiCosa+xml</code>, allora l&#8217;attributo di codifica della dichiarazione <abbr>XML</abbr> nel documento viene completamente ignorato e la codifica è

<ol>
<li>la codifica data nel parametro <code>charset</code> dell&#8217;intestazione <abbr>HTTP</abbr> <code>Content-Type</code>, oppure
<li><abbr>US-ASCII</abbr>.
</ol>

<p>E questo è solo per i documenti <abbr>XML</abbr>. Per i documenti <abbr>HTML</abbr>, i browser web hanno costruito <a type=application/pdf href=http://www.adambarth.com/papers/2009/barth-caballero-song.pdf>regole per il content sniffing</a> [<abbr>PDF</abbr>] talmente bizantine che <a href='http://www.google.com/search?q=barth+content-type+processing+model'>stiamo ancora cercando di capire come funzionano</a>.

<p>&#8220;<a href=http://code.google.com/p/httplib2/source/checkout>Le patch sono benvenute</a>.&#8221;

<h3 id=httplib2-caching>Come <code>httplib2</code> gestisce la cache</h3>

<p>Ricordate quando, nella sezione precedente, ho detto che dovreste sempre creare un oggetto <code>httplib2.Http</code> con un nome di directory? Il motivo è la cache.

<pre class=screen>
# continua dall'<a href=#introducing-httplib2>esempio precedente</a>
<a><samp class=p>>>> </samp><kbd class=pp>response2, content2 = h.request('http://diveintopython3.org/examples/feed.xml')</kbd>  <span class=u>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>response2.status</kbd>                                                                 <span class=u>&#x2461;</span></a>
<samp class=pp>200</samp>
<a><samp class=p>>>> </samp><kbd class=pp>content2[:52]</kbd>                                                                    <span class=u>&#x2462;</span></a>
<samp class=pp>b"&lt;?xml version='1.0' encoding='utf-8'?>\r\n&lt;feed xmlns="</samp>
<samp class=p>>>> </samp><kbd class=pp>len(content2)</kbd>
<samp class=pp>3070</samp></pre>
<ol>
<li>Questo non dovrebbe essere terribilmente sorprendente. &Egrave; la stessa cosa che avete fatto l&#8217;ultima volta, senonché state mettendo il risultato in due nuove variabili.
<li>Il valore del codice di stato <abbr>HTTP</abbr> memorizzato in <code>status</code> è ancora <code>200</code>, proprio come l&#8217;ultima volta.
<li>Anche il contenuto scaricato è lo stesso dell&#8217;ultima volta.
</ol>

<p>E quindi&hellip; chi se ne importa? Uscite dalla shell interattiva di Python e rilanciatela con una nuova sessione, e vi farò vedere.

<pre class=screen>
# NON continua dall'esempio precedente!
# Per favore uscite dalla shell interattiva
# e lanciatene una nuova.
<samp class=p>>>> </samp><kbd class=pp>import httplib2</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>httplib2.debuglevel = 1</kbd>                                                        <span class=u>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>h = httplib2.Http('.cache')</kbd>                                                    <span class=u>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>response, content = h.request('http://diveintopython3.org/examples/feed.xml')</kbd>  <span class=u>&#x2462;</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>len(content)</kbd>                                                                   <span class=u>&#x2463;</span></a>
<samp class=pp>3070</samp>
<a><samp class=p>>>> </samp><kbd class=pp>response.status</kbd>                                                                <span class=u>&#x2464;</span></a>
<samp class=pp>200</samp>
<a><samp class=p>>>> </samp><kbd class=pp>response.fromcache</kbd>                                                             <span class=u>&#x2465;</span></a>
<samp class=pp>True</samp></pre>
<ol>
<li>Attiviamo il debug e vediamo <a href=#whats-on-the-wire>cosa viene trasmesso attraverso la rete</a>. Questo è l&#8217;equivalente per <code>httplib2</code> dell&#8217;attivazione del debug in <code>http.client</code>. <code>httplib2</code> stamperà tutti i dati inviati al server e alcune informazioni chiave restituite dal server.
<li>Create un oggetto <code>httplib2.Http</code> con lo stesso nome di directory precedente.
<li>Richiedete lo stesso <abbr>URL</abbr> di prima. <em>Sembra che non accada nulla.</em> Più precisamente, nulla viene inviato al server e nulla viene restituito dal server. Non c&#8217;è assolutamente alcuna attività di rete.
<li>Tuttavia abbiamo &#8220;ricevuto&#8221; alcuni dati&nbsp;&mdash;&nbsp;in effetti, li abbiamo ricevuti tutti.
<li>Abbiamo anche &#8220;ricevuto&#8221; un codice di stato <abbr>HTTP</abbr> che indica che la &#8220;richiesta&#8221; ha avuto successo.
<li>Qui sta il punto: questa &#8220;risposta&#8221; è stata generata dalla cache locale di <code>httplib2</code>. Quel nome di directory che avete passato nel creare l&#8217;oggetto <code>httplib2.Http</code>&nbsp;&mdash;&nbsp;quella directory conserva la cache di <code>httplib2</code> per tutte le operazioni che ha mai effettuato.
</ol>

<aside>Cosa viene trasmesso attraverso la rete? Proprio nulla.</aside>

<blockquote class=note>
<p><span class=u>&#x261E;</span>Se volete attivare le informazioni di debug di <code>httplib2</code>, dovete impostare una costante a livello di modulo (<code>httplib2.debuglevel</code>) e poi creare un nuovo oggetto <code>httplib2.Http</code>. Se volete disattivare le informazioni di debug, dovete modificare la stessa costante a livello di modulo e poi creare un nuovo oggetto <code>httplib2.Http</code>.
</blockquote>

<p>Avete già richiesto in precedenza i dati a questo <abbr>URL</abbr>. Quella richiesta ha avuto successo (<code>status: 200</code>). Quella risposta includeva non solo i dati del feed, ma anche un insieme di <a href=#caching>intestazioni di cache</a> che dicevano a chiunque fosse in ascolto che poteva tenere in cache questa risorsa per 24 ore. (<code>Cache-Control: max-age=86400</code>, che sono 24 ore misurate in secondi). <code>httplib2</code> comprende e rispetta queste intestazioni di cache, e ha memorizzato la risposta precedente nella directory <code>.cache</code> (il cui nome avete passato quando avete creato l&#8217;oggetto <code>Http</code>). Quella informazione in cache non è ancora scaduta, quindi la seconda volta che richiedete i dati a questo <abbr>URL</abbr> <code>httplib2</code> semplicemente restituisce il risultato in cache senza nemmeno utilizzare la rete.

<p>Ho detto &#8220;semplicemente&#8221;, ma ovviamente c&#8217;è molta complessità nascosta dietro questa semplicità. <code>httplib2</code> gestisce l&#8217;uso della cache da parte di <abbr>HTTP</abbr> in maniera <em>automatica</em> e <em>predefinita</em>. Casomai per qualche ragione aveste bisogno di sapere se una risposta proveniva dalla cache, potete controllare l&#8217;attributo <code>response.fromcache</code>. Altrimenti, tutto funziona e basta.

<p id=bypass-the-cache>Ora, supponete di avere i dati nella cache, ma di voler aggirare la cache e riottenerli dal server remoto. I browser talvolta lo fanno se l&#8217;utente lo richiede esplicitamente. Per esempio, premere <kbd>F5</kbd> aggiorna la pagina corrente, ma premere <kbd>Ctrl+F5</kbd> aggira la cache e riottiene la pagina corrente dal server remoto. Potreste pensare: &#8220;Oh, cancellerò semplicemente i dati dalla mia cache locale, poi li richiederò di nuovo.&#8221; Potreste farlo, ma ricordate che potrebbero esserci più parti coinvolte anziché solo voi e il server remoto. Cosa mi dite di quei server proxy intermedi? Quelli sono completamente al di là del vostro controllo, potrebbero ancora avere quei dati nella propria cache e ve li restituiranno tranquillamente perché (per quanto li riguarda) la loro cache è ancora valida.

<p>Invece di manipolare la vostra cache locale e sperare per il meglio, dovreste usare le caratteristiche di <abbr>HTTP</abbr> per assicurarvi che la vostra richiesta raggiunga effettivamente il server remoto.

<pre class=screen>
# continua dall'esempio precedente
<samp class=p>>>> </samp><kbd class=pp>response2, content2 = h.request('http://diveintopython3.org/examples/feed.xml',</kbd>
<a><samp class=p>... </samp><kbd class=pp>    headers={'cache-control':'no-cache'})</kbd>  <span class=u>&#x2460;</span></a>
<samp><a>connect: (diveintopython3.org, 80)             <span class=u>&#x2461;</span></a>
send: b'GET /examples/feed.xml HTTP/1.1
Host: diveintopython3.org
user-agent: Python-httplib2/$Rev: 259 $
accept-encoding: deflate, gzip
cache-control: no-cache'
reply: 'HTTP/1.1 200 OK'
&hellip;ulteriori informazioni di debug omesse&hellip;</samp>
<samp class=p>>>> </samp><kbd class=pp>response2.status</kbd>
<samp class=pp>200</samp>
<a><samp class=p>>>> </samp><kbd class=pp>response2.fromcache</kbd>                        <span class=u>&#x2462;</span></a>
<samp class=pp>False</samp>
<a><samp class=p>>>> </samp><kbd class=pp>print(dict(response2.items()))</kbd>             <span class=u>&#x2463;</span></a>
<samp class=pp>{'status': '200',
 'content-length': '3070',
 'content-location': 'http://diveintopython3.org/examples/feed.xml',
 'accept-ranges': 'bytes',
 'expires': 'Wed, 03 Jun 2009 00:40:26 GMT',
 'vary': 'Accept-Encoding',
 'server': 'Apache',
 'last-modified': 'Sun, 31 May 2009 22:51:11 GMT',
 'connection': 'close',
 '-content-encoding': 'gzip',
 'etag': '"bfe-255ef5c0"',
 'cache-control': 'max-age=86400',
 'date': 'Tue, 02 Jun 2009 00:40:26 GMT',
 'content-type': 'application/xml'}</samp></pre>
<ol>
<li><code>httplib2</code> vi consente di aggiungere intestazioni <abbr>HTTP</abbr> arbitrarie a qualsiasi richiesta in uscita. Per aggirare <em>tutte</em> le cache (non solo quella sul vostro disco locale, ma anche tutti i proxy di cache tra voi e il server remoto), aggiungete un&#8217;intestazione <code>no-cache</code> nel dizionario <var>headers</var>.
<li>Ora vedete che <code>httplib2</code> inizia una richiesta di rete. <code>httplib2</code> comprende e rispetta le intestazioni di cache <em>in entrambe le direzioni</em>&nbsp;&mdash;&nbsp;come parte della risposta in arrivo <em>e come parte della richiesta in partenza</em>. Il modulo ha notato che avete aggiunto l&#8217;intestazione <code>no-cache</code>, quindi ha aggirato la propria cache locale e non ha avuto altra scelta se non quella di utilizzare la rete per richiedere i dati.
<li>Questa risposta <em>non</em> è stata generata dalla vostra cache locale. Lo sapevate, naturalmente, perché avete visto le informazioni di debug sulla richiesta in partenza. Ma è piacevole averlo programmaticamente verificato.
<li>La richiesta ha avuto successo, avete nuovamente scaricato l&#8217;intero feed dal server remoto. Naturalmente, il server ha anche spedito una serie completa di intestazioni <abbr>HTTP</abbr> insieme ai dati del feed, comprese le intestazioni di cache che <code>httplib2</code> utilizza per aggiornare la propria cache locale nella speranza di evitare l&#8217;accesso alla rete la <em>prossima</em> volta che richiedete questo feed. Ogni caratteristica della gestione della cache in <abbr>HTTP</abbr> è progettata per massimizzare l&#8217;uso della cache e minimizzare l&#8217;accesso alla rete. Anche se avete aggirato la cache questa volta, il server remoto apprezzerebbe davvero che voi manteneste in cache il risultato per la prossima volta.
</ol>

<h3 id=httplib2-etags>Come <code>httplib2</code> gestisce le intestazioni <code>Last-Modified</code> ed <code>ETag</code></h3>

<p>Le <a href=#caching>intestazioni di cache</a> <code>Cache-Control</code> ed <code>Expires</code> sono chiamate <i>indicatori di freschezza</i> e dicono alle cache in termini certi che potete evitare completamente ogni accesso alla rete fino a quando la cache scade. Questo è esattamente il comportamento che avete visto <a href=#httplib2-caching>nella sezione precedente</a>: dato un indicatore di freschezza, <code>httplib2</code> <em>non genera un singolo byte di attività di rete</em> per prelevare i dati mantenuti in cache (a meno che voi non <a href=#bypass-the-cache>aggiriate la cache</a> in maniera esplicita, naturalmente).

<p>Ma cosa succede nel caso in cui i dati <em>potrebbero</em> essere cambiati, ma non lo sono? <abbr>HTTP</abbr> definisce le intestazioni <a href=#last-modified><code>Last-Modified</code></a> ed <a href=#etags><code>Etag</code></a> proprio a questo scopo. Queste intestazioni sono chiamate <i>validatori</i>. Se la cache locale non è più fresca, un client può spedire i validatori insieme alla prossima richiesta per vedere se i dati sono effettivamente cambiati. Se i dati non sono cambiati, il server risponde con un codice di stato <code>304</code> e <em>nessun dato</em>. Quindi c&#8217;è ancora un viaggio attraverso la rete, ma finite per scaricare meno byte.

<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import httplib2</kbd>
<samp class=p>>>> </samp><kbd class=pp>httplib2.debuglevel = 1</kbd>
<samp class=p>>>> </samp><kbd class=pp>h = httplib2.Http('.cache')</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>response, content = h.request('http://diveintopython3.org/')</kbd>  <span class=u>&#x2460;</span></a>
<samp>connect: (diveintopython3.org, 80)
send: b'GET / HTTP/1.1
Host: diveintopython3.org
accept-encoding: deflate, gzip
user-agent: Python-httplib2/$Rev: 259 $'
reply: 'HTTP/1.1 200 OK'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>print(dict(response.items()))</kbd>                                 <span class=u>&#x2461;</span></a>
<samp class=pp>{'-content-encoding': 'gzip',
 'accept-ranges': 'bytes',
 'connection': 'close',
 'content-length': '6657',
 'content-location': 'http://diveintopython3.org/',
 'content-type': 'text/html',
 'date': 'Tue, 02 Jun 2009 03:26:54 GMT',
<mark> 'etag': '"7f806d-1a01-9fb97900"',</mark>
<mark> 'last-modified': 'Tue, 02 Jun 2009 02:51:48 GMT',</mark>
 'server': 'Apache',
 'status': '200',
 'vary': 'Accept-Encoding,User-Agent'}</samp>
<a><samp class=p>>>> </samp><kbd class=pp>len(content)</kbd>                                                  <span class=u>&#x2462;</span></a>
<samp class=pp>6657</samp></pre>
<ol>
<li>Invece di un feed, questa volta scaricheremo la pagina iniziale del sito, che è in <abbr>HTML</abbr>. Dato che questa è la prima volta che richiedete questa pagina, <code>httplib2</code> ha poco con cui lavorare e invia un insieme minimo di intestazioni con la richiesta.
<li>La risposta contiene una moltitudine di intestazioni <abbr>HTTP</abbr>&hellip; ma nessuna informazione di cache. Tuttavia, include entrambe le intestazioni <code>ETag</code> e <code>Last-Modified</code>.
<li>Al momento in cui ho realizzato questo esempio, la pagina era di 6657 byte. Probabilmente ora è cambiata, ma non dovete preoccuparvi di questo.
</ol>

<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>response, content = h.request('http://diveintopython3.org/')</kbd>  <span class=u>&#x2460;</span></a>
<samp>connect: (diveintopython3.org, 80)
send: b'GET / HTTP/1.1
Host: diveintopython3.org
<a>if-none-match: "7f806d-1a01-9fb97900"                             <span class=u>&#x2461;</span></a>
<a>if-modified-since: Tue, 02 Jun 2009 02:51:48 GMT                  <span class=u>&#x2462;</span></a>
accept-encoding: deflate, gzip
user-agent: Python-httplib2/$Rev: 259 $'
<a>reply: 'HTTP/1.1 304 Not Modified'                                <span class=u>&#x2463;</span></a></samp>
<a><samp class=p>>>> </samp><kbd class=pp>response.fromcache</kbd>                                            <span class=u>&#x2464;</span></a>
<samp class=pp>True</samp>
<a><samp class=p>>>> </samp><kbd class=pp>response.status</kbd>                                               <span class=u>&#x2465;</span></a>
<samp class=pp>200</samp>
<a><samp class=p>>>> </samp><kbd class=pp>response.dict['status']</kbd>                                       <span class=u>&#x2466;</span></a>
<samp class=pp>'304'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>len(content)</kbd>                                                  <span class=u>&#x2467;</span></a>
<samp class=pp>6657</samp></pre>
<ol>
<li>Avete richiesto ancora la stessa pagina con lo stesso oggetto <code>Http</code> (e la stessa cache locale).
<li><code>httplib2</code> spedisce il validatore <code>ETag</code> al server nell&#8217;intestazione <code>If-None-Match</code>.
<li><code>httplib2</code> spedisce al server anche il validatore <code>Last-Modified</code> nell&#8217;intestazione <code>If-Modified-Since</code>.
<li>Il server ha esaminato questi validatori, ha esaminato la pagina che avete richiesto e ha determinato che la pagina non è cambiata dall&#8217;ultima volta che l&#8217;avete richiesta, quindi risponde con un codice di stato <code>304</code> e <em>nessun dato</em>.
<li>Tornando al client, <code>httplib2</code> nota il codice di stato <code>304</code> e carica il contenuto della pagina dalla propria cache.
<li>Questo potrebbe confondervi un poco. In realtà ci sono <em>due</em> codici di stato&nbsp;&mdash;&nbsp;<code>304</code> (restituito dal server questa volta, che induce <code>httplib2</code> a guardare nella propria cache) e <code>200</code> (restituito dal server <em>l&#8217;ultima volta</em> e memorizzato nella cache di <code>httplib2</code> insieme ai dati della pagina). <code>response.status</code> contiene lo stato proveniente dalla cache.
<li>Se volete lo stato effettivo restituito dal server, potete ottenerlo guardando in <code>response.dict</code>, che è un dizionario delle reali intestazioni restituite dal server.
<li>Tuttavia, i dati vi vengono resi disponibili ancora nella variabile <var>content</var>. Generalmente, non avete bisogno di sapere perché una risposta è stata servita dalla cache. (Potete persino ignorare il fatto che sia stata servita dalla cache, e anche questo va bene. <code>httplib2</code> è abbastanza intelligente da lasciarvi agire in modo stupido.) Nel momento in cui il metodo <code>request()</code> restituisce il controllo al chiamante, <code>httplib2</code> ha già aggiornato la propria cache e vi ha restituito i dati.
</ol>

<h3 id=httplib2-compression>Come <code>http2lib</code> gestisce la compressione</h3>

<aside>&#8220;Facciamo musica di entrambi i generi, country E western.&#8221;</aside>

<p><abbr>HTTP</abbr> supporta <a href=#compression>diversi tipi di compressione</a>; i due tipi più comuni sono gzip e deflate. <code>httplib2</code> li supporta entrambi.

<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>response, content = h.request('http://diveintopython3.org/')</kbd>
<samp>connect: (diveintopython3.org, 80)
send: b'GET / HTTP/1.1
Host: diveintopython3.org
<a>accept-encoding: deflate, gzip                          <span class=u>&#x2460;</span></a>
user-agent: Python-httplib2/$Rev: 259 $'
reply: 'HTTP/1.1 200 OK'</samp>
<samp class=p>>>> </samp><kbd class=pp>print(dict(response.items()))</kbd>
<samp class=pp><a>{'-content-encoding': 'gzip',                           <span class=u>&#x2461;</span></a>
 'accept-ranges': 'bytes',
 'connection': 'close',
 'content-length': '6657',
 'content-location': 'http://diveintopython3.org/',
 'content-type': 'text/html',
 'date': 'Tue, 02 Jun 2009 03:26:54 GMT',
 'etag': '"7f806d-1a01-9fb97900"',
 'last-modified': 'Tue, 02 Jun 2009 02:51:48 GMT',
 'server': 'Apache',
 'status': '304',
 'vary': 'Accept-Encoding,User-Agent'}</samp></pre>
<ol>
<li>Ogni volta che <code>httplib2</code> invia una richiesta, include un&#8217;intestazione <code>Accept-Encoding</code> per dire al server che è in grado di gestire la compressione sia di tipo <code>deflate</code> che di tipo <code>gzip</code>.
<li>In questo caso, il server ha risposto con un carico utile compresso tramite gzip. Nel momento in cui il metodo <code>request()</code> termina, <code>httplib2</code> ha già decompresso il corpo della risposta e lo ha piazzato nella variabile <var>content</var>. Casomai foste curiosi di sapere se la risposta era compressa oppure no, potete controllare <var>response['-content-encoding']</var>; altrimenti, non preoccupatevi di questo.
</ol>

<h3 id=httplib2-redirects>Come <code>httplib2</code> gestisce le redirezioni</h3>

<p><abbr>HTTP</abbr> definisce <a href=#redirects>due tipi di redirezioni</a>: temporanee e permanenti. Non c&#8217;è niente di speciale da fare con le redirezioni temporanee, tranne seguirle, cosa che <code>httplib2</code> esegue automaticamente.

<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import httplib2</kbd>
<samp class=p>>>> </samp><kbd class=pp>httplib2.debuglevel = 1</kbd>
<samp class=p>>>> </samp><kbd class=pp>h = httplib2.Http('.cache')</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>response, content = h.request('http://diveintopython3.org/examples/feed-302.xml')</kbd>  <span class=u>&#x2460;</span></a>
<samp>connect: (diveintopython3.org, 80)
<a>send: b'GET /examples/feed-302.xml HTTP/1.1                                            <span class=u>&#x2461;</span></a>
Host: diveintopython3.org
accept-encoding: deflate, gzip
user-agent: Python-httplib2/$Rev: 259 $'
<a>reply: 'HTTP/1.1 302 Found'                                                            <span class=u>&#x2462;</span></a>
<a>send: b'GET /examples/feed.xml HTTP/1.1                                                <span class=u>&#x2463;</span></a>
Host: diveintopython3.org
accept-encoding: deflate, gzip
user-agent: Python-httplib2/$Rev: 259 $'
reply: 'HTTP/1.1 200 OK'</samp></pre>
<ol>
<li>Non c&#8217;è alcun feed a questo <abbr>URL</abbr>. Ho impostato il mio server in modo da emettere una redirezione temporanea verso l&#8217;indirizzo corretto.
<li>Questa è la richiesta.
<li>E questa è la risposta: <code>302 Found</code>. Qui non viene mostrata, ma questa risposta include anche un&#8217;intestazione <code>Location</code> che punta al vero <abbr>URL</abbr>.
<li><code>httplib2</code> torna immediatamente sui suoi passi e &#8220;segue&#8221; la redirezione emettendo un&#8217;altra richiesta per l&#8217;<abbr>URL</abbr> contenuto nell&#8217;intestazione <code>Location</code>: <code>http://diveintopython3.org/examples/feed.xml</code>
</ol>

<p>&#8220;Seguire&#8221; una redirezione non è niente di più di ciò che viene mostrato in questo esempio. <code>httplib2</code> invia una richiesta per l&#8217;<abbr>URL</abbr> che avete chiesto. Il server ribatte con una risposta che dice: &#8220;No, no, guarda qui invece.&#8221; <code>httplib2</code> invia un&#8217;altra richiesta per il nuovo <abbr>URL</abbr>.

<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>response</kbd>                                                          <span class=u>&#x2460;</span></a>
<samp class=pp>{'status': '200',
 'content-length': '3070',
<a> 'content-location': 'http://diveintopython3.org/examples/feed.xml',  <span class=u>&#x2461;</span></a>
 'accept-ranges': 'bytes',
 'expires': 'Thu, 04 Jun 2009 02:21:41 GMT',
 'vary': 'Accept-Encoding',
 'server': 'Apache',
 'last-modified': 'Wed, 03 Jun 2009 02:20:15 GMT',
 'connection': 'close',
<a> '-content-encoding': 'gzip',                                         <span class=u>&#x2462;</span></a>
 'etag': '"bfe-4cbbf5c0"',
<a> 'cache-control': 'max-age=86400',                                    <span class=u>&#x2463;</span></a>
 'date': 'Wed, 03 Jun 2009 02:21:41 GMT',
 'content-type': 'application/xml'}</samp></pre>
<ol>
<li>I dati memorizzati nella variabile <var>response</var> che ottenete da questa singola chiamata al metodo <code>request()</code> sono la risposta dall&#8217;<abbr>URL</abbr> finale.
<li><code>httplib2</code> aggiunge l&#8217;<abbr>URL</abbr> finale al dizionario <var>response</var>, come valore per la chiave <code>content-location</code>. Questa non è un&#8217;intestazione proveniente dal server, ma è specifica per <code>httplib2</code>.
<li>Incidentalmente, questo feed è <a href=#httplib2-compression>compresso</a>.
<li>E memorizzabile in cache. (Questo è importante, come vedrete fra un minuto.)
</ol>

<p>La risposta contenuta nella variabile <var>response</var> che avete ottenuto vi dà informazioni sull&#8217;<abbr>URL</abbr> <em>finale</em>. E se voleste informazioni sugli <abbr>URL</abbr> intermedi, quelli che alla fine vi hanno rediretto all&#8217;<abbr>URL</abbr> finale? <code>httplib2</code> vi permette di ottenere queste informazioni.

<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>response.previous</kbd>                                                     <span class=u>&#x2460;</span></a>
<samp class=pp>{'status': '302',
 'content-length': '228',
 'content-location': 'http://diveintopython3.org/examples/feed-302.xml',
 'expires': 'Thu, 04 Jun 2009 02:21:41 GMT',
 'server': 'Apache',
 'connection': 'close',
 'location': 'http://diveintopython3.org/examples/feed.xml',
 'cache-control': 'max-age=86400',
 'date': 'Wed, 03 Jun 2009 02:21:41 GMT',
 'content-type': 'text/html; charset=iso-8859-1'}</samp>
<a><samp class=p>>>> </samp><kbd class=pp>type(response)</kbd>                                                        <span class=u>&#x2461;</span></a>
<samp>&lt;class 'httplib2.Response'></samp>
<samp class=p>>>> </samp><kbd class=pp>type(response.previous)</kbd>
<samp>&lt;class 'httplib2.Response'></samp>
<a><samp class=p>>>> </samp><kbd class=pp>response.previous.previous</kbd>                                            <span class=u>&#x2462;</span></a>
<samp class=p>>>></samp></pre>
<ol>
<li>L&#8217;attributo <var>response.previous</var> mantiene un riferimento al precedente oggetto risposta che <code>httplib2</code> ha seguito per ottenere l&#8217;oggetto risposta attuale.
<li>Sia <var>response</var> che <var>response.previous</var> sono oggetti <code>httplib2.Response</code>.
<li>Questo significa che potete controllare <var>response.previous.previous</var> per seguire la catena di redirezioni facendo ulteriori passi indietro. (Scenario: un <abbr>URL</abbr> redirige verso un secondo <abbr>URL</abbr> che redirige verso un terzo <abbr>URL</abbr>. Potrebbe accadere!) In questo caso, abbiamo già raggiunto l&#8217;inizio della catena di redirezioni, quindi l&#8217;attributo vale <code>None</code>.
</ol>

<p>Cosa succede se effettuate ancora una richiesta allo stesso <abbr>URL</abbr>?

<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>response2, content2 = h.request('http://diveintopython3.org/examples/feed-302.xml')</kbd>  <span class=u>&#x2460;</span></a>
<samp>connect: (diveintopython3.org, 80)
<a>send: b'GET /examples/feed-302.xml HTTP/1.1                                              <span class=u>&#x2461;</span></a>
Host: diveintopython3.org
accept-encoding: deflate, gzip
user-agent: Python-httplib2/$Rev: 259 $'
<a>reply: 'HTTP/1.1 302 Found'                                                              <span class=u>&#x2462;</span></a></samp>
<a><samp class=p>>>> </samp><kbd class=pp>content2 == content</kbd>                                                                  <span class=u>&#x2463;</span></a>
<samp class=pp>True</samp></pre>
<ol>
<li>Stesso <abbr>URL</abbr>, stesso oggetto <code>httplib2.Http</code> (e quindi stessa cache).
<li>La risposta <code>302</code> non è stata memorizzata in cache, quindi <code>httplib2</code> invia un&#8217;altra richiesta per lo stesso <abbr>URL</abbr>.
<li>Ancora una volta, il server risponde con un <code>302</code>. Ma notate che <em>non</em> è successo: non c&#8217;è mai stata una seconda richiesta per l&#8217;<abbr>URL</abbr> finale, <code>http://diveintopython3.org/examples/feed.xml</code>. Quella risposta è stata memorizzata in cache (ricordatevi l&#8217;intestazione <code>Cache-Control</code> che avevate visto nell&#8217;esempio precedente). Una volta che <code>httplib2</code> ha ricevuto il codice <code>302 Found</code>, <em>ha controllato la propria cache prima di emettere un&#8217;altra richiesta</em>. La cache conteneva una copia fresca di <code>http://diveintopython3.org/examples/feed.xml</code>, quindi non c&#8217;era nessun bisogno di riottenerla.
<li>Nel momento in cui la sua esecuzione termina, il metodo <code>request()</code> ha già letto i dati del feed dalla cache e li ha restituiti. Naturalmente, sono gli stessi dati che avevate ricevuto l&#8217;ultima volta.
</ol>

<p>In altre parole, non dovete fare niente di speciale per le redirezioni temporanee. <code>httplib2</code> le seguirà automaticamente, e il fatto che un <abbr>URL</abbr> rediriga a un altro non ha alcun rapporto con il supporto da parte di <code>httplib2</code> per la compressione, la cache, gli <code>ETag</code>, o qualsiasi altra caratteristica di <abbr>HTTP</abbr>.

<p>Le redirezioni permanenti sono altrettanto semplici.

<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>response, content = h.request('http://diveintopython3.org/examples/feed-301.xml')</kbd>  <span class=u>&#x2460;</span></a>
<samp>connect: (diveintopython3.org, 80)
send: b'GET /examples/feed-301.xml HTTP/1.1
Host: diveintopython3.org
accept-encoding: deflate, gzip
user-agent: Python-httplib2/$Rev: 259 $'
<a>reply: 'HTTP/1.1 301 Moved Permanently'                                                <span class=u>&#x2461;</span></a></samp>
<a><samp class=p>>>> </samp><kbd class=pp>response.fromcache</kbd>                                                                 <span class=u>&#x2462;</span></a>
<samp class=pp>True</samp></pre>
<ol>
<li>Ancora una volta, in realtà questo <abbr>URL</abbr> non esiste. Ho impostato il mio server in modo da emettere una redirezione permanente verso <code>http://diveintopython3.org/examples/feed.xml</code>.
<li>Ed eccola qui: codice di stato <code>301</code>. Ma ancora, notate cosa <em>non</em> è successo: non c&#8217;è stata alcuna richiesta verso l&#8217;<abbr>URL</abbr> rediretto. Perché no? Perché è già memorizzato in cache localmente.
<li><code>httplib2</code> ha &#8220;seguito&#8221; la redirezione dritto nella propria cache.
</ol>

<p>Ma aspettate! C&#8217;è di più!

<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>response2, content2 = h.request('http://diveintopython3.org/examples/feed-301.xml')</kbd>  <span class=u>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>response2.fromcache</kbd>                                                                  <span class=u>&#x2461;</span></a>
<samp class=pp>True</samp>
<a><samp class=p>>>> </samp><kbd class=pp>content2 == content</kbd>                                                                  <span class=u>&#x2462;</span></a>
<samp class=pp>True</samp>
</pre>
<ol>
<li>Ecco una differenza tra le redirezioni temporanee e permanenti: una volta che <code>httplib2</code> segue una redirezione permanente, tutte le ulteriori richieste per quell&#8217;<abbr>URL</abbr> verranno trasparentemente riscritte per dirigersi verso l&#8217;<abbr>URL</abbr> obiettivo <em>senza utilizzare la rete per l&#8217;<abbr>URL</abbr> originale</em>. Ricordate, il debug è ancora attivo, tuttavia non risulta alcuna attività di rete.
<li>Sì, questa risposta è stata recuperata dalla cache locale.
<li>Sì, avete ottenuto l&#8217;intero feed (dalla cache).
</ol>

<p><abbr>HTTP</abbr>. Funziona.

<p class=a>&#x2042;

<h2 id=beyond-get>Oltre HTTP GET</h2>

<p>I servizi web <abbr>HTTP</abbr> non sono limitati alle richieste <code>GET</code>. E se voleste creare qualcosa di nuovo? Ogni volta che inviate un commento a una discussione su un forum, aggiornate il vostro weblog, pubblicate il vostro stato su un servizio di microblogging come <a href=http://twitter.com/>Twitter</a> o <a href=http://identi.ca/>Identi.ca</a>, state probabilmente già usando <abbr>HTTP</abbr> <code>POST</code>.

<p>Sia Twitter che Identi.ca offrono una semplice <abbr>API</abbr> basata su <abbr>HTTP</abbr> per pubblicare e aggiornare il vostro stato tramite messaggi non più lunghi di 140 caratteri. Diamo un&#8217;occhiata alla <a href=http://laconi.ca/trac/wiki/TwitterCompatibleAPI>documentazione della <abbr>API</abbr> di Identi.ca</a> per l&#8217;aggiornamento del vostro stato.

<blockquote class=pf>
<p><b>Metodo della <abbr>API</abbr> <abbr>REST</abbr> di Identi.ca: statuses/update</b><br>
Aggiorna lo stato dell&#8217;utente autenticato. Richiede il parametro <code>status</code> specificato sotto. La richiesta deve essere di tipo <code>POST</code>.
 
<dl>
<dt><abbr>URL</abbr>
<dd><code>https://identi.ca/api/statuses/update.<i><var>formato</var></i></code>
<dt>Formati
<dd><code>xml</code>, <code>json</code>, <code>rss</code>, <code>atom</code>
<dt>Metodi <abbr>HTTP</abbr>
<dd><code>POST</code>
<dt>Richiede autenticazione
<dd>vero
<dt>Parametri
<dd><code>status</code>. Obbligatorio. Il testo del vostro aggiornamento di stato. Codificatelo come <abbr>URL</abbr> se necessario.
</dl>
</blockquote>

<p>Come funziona questo metodo? Per pubblicare un nuovo messaggio su Identi.ca avete bisogno di emettere una richiesta <abbr>HTTP</abbr> <code>POST</code> verso <code>http://identi.ca/api/statuses/update.<i>formato</i></code>. (Il <var>formato</var> non è parte dell&#8217;<abbr>URL</abbr>, ma dovete sostituirlo con il formato dei dati che volete farvi restituire dal server in risposta alla vostra richiesta. Quindi, se volete una risposta in <abbr>XML</abbr> dovreste spedire la richiesta a <code>https://identi.ca/api/statuses/update.xml</code>.) La richiesta deve includere un parametro chiamato <code>status</code> che contiene il testo del vostro aggiornamento di stato. E la richiesta deve essere autenticata.

<p>Autenticata? Certamente. Per aggiornare il vostro stato su Identi.ca dovete provare chi siete. Identi.ca non è un wiki, solo voi potete aggiornare il vostro stato. Identi.ca utilizza la <a href=http://en.wikipedia.org/wiki/Basic_access_authentication>Basic Authentication di <abbr>HTTP</abbr></a> (<i>alias</i> <a href=http://www.ietf.org/rfc/rfc2617.txt>RFC 2617</a>) via <abbr>SSL</abbr> per fornire un&#8217;autenticazione sicura ma facile da usare. <code>httplib2</code> supporta la Basic Authentication sia via <abbr>SSL</abbr> che via <abbr>HTTP</abbr>, quindi questa parte è facile.

<p>Una richiesta <code>POST</code> è diversa da una richiesta <code>GET</code> perché include un <i>carico utile</i>. Il carico utile sono i dati che volete inviare al server. L&#8217;unica informazione <em>obbligatoria</em> che questo metodo della <abbr>API</abbr> richiede è <code>status</code>, e dovrebbe essere <i>codificata come <abbr>URL</abbr></i>. Questa codifica è un formato di serializzazione molto semplice che prende un insieme di coppie chiave-valore (cioè un <a href=tipi-di-dato-nativi.html#dictionaries>dizionario</a>) e lo trasforma in una stringa.

<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>from urllib.parse import urlencode</kbd>                       <span class=u>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>data = {'status': 'Aggiornamento di prova da Python 3'}</kbd>  <span class=u>&#x2461;</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>urlencode(data)</kbd>                                          <span class=u>&#x2462;</span></a>
<samp>'status=Aggiornamento+di+prova+da+Python+3'</samp></pre>
<ol>
<li>Python include una funzione di utilità per codificare come <abbr>URL</abbr> un dizionario: <code>urllib.parse.urlencode()</code>.
<li>Questo è il tipo di dizionario di cui la <abbr>API</abbr> di Identi.ca ha bisogno. Contiene una chiave <code>status</code> il cui valore è il testo di un singolo aggiornamento di stato.
<li>Questa è la stringa codificata come <abbr>URL</abbr>. Questo è il <i>carico utile</i> che verrà inviato attraverso la rete al server della <abbr>API</abbr> di Identi.ca nella vostra richiesta <abbr>HTTP</abbr> <code>POST</code>.
</ol>

<p>

<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>from urllib.parse import urlencode</kbd>
<samp class=p>>>> </samp><kbd class=pp>import httplib2</kbd>
<samp class=p>>>> </samp><kbd class=pp>httplib2.debuglevel = 1</kbd>
<samp class=p>>>> </samp><kbd class=pp>h = httplib2.Http('.cache')</kbd>
<samp class=p>>>> </samp><kbd class=pp>data = {'status': 'Aggiornamento di prova da Python 3'}</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>h.add_credentials('diveintomark', '<var>MIA_PASSWORD_SEGRETA</var>', 'identi.ca')</kbd>  <span class=u>&#x2460;</span></a>
<samp class=p>>>> </samp><kbd class=pp>resp, content = h.request('https://identi.ca/api/statuses/update.xml',</kbd>
<a><samp class=p>... </samp><kbd class=pp>    'POST',</kbd>                                                             <span class=u>&#x2461;</span></a>
<a><samp class=p>... </samp><kbd class=pp>    urlencode(data),</kbd>                                                    <span class=u>&#x2462;</span></a>
<a><samp class=p>... </samp><kbd class=pp>    headers={'Content-Type': 'application/x-www-form-urlencoded'})</kbd>      <span class=u>&#x2463;</span></a></pre>
<ol>
<li>Questo è il modo in cui <code>httplib2</code> gestisce l&#8217;autenticazione. Memorizzate il vostro nome utente e la vostra password con il metodo <code>add_credentials()</code>. Quando <code>httplib2</code> prova a emettere la richiesta, il server risponderà con un codice di stato <code>401 Unauthorized</code> ed elencherà quali metodi di autenticazione supporta (nell&#8217;intestazione <code>WWW-Authenticate</code>). <code>httplib2</code> costruirà automaticamente un&#8217;intestazione <code>Authorization</code> e richiederà nuovamente l&#8217;<abbr>URL</abbr>.
<li>Il secondo parametro è il tipo di richiesta <abbr>HTTP</abbr>, <code>POST</code> in questo caso.
<li>Il terzo parametro è il <i>carico utile</i> da spedire al server. Stiamo inviando il dizionario codificato come <abbr>URL</abbr> con il messaggio di stato.
<li>Infine, abbiamo bisogno di dire al server che il carico utile contiene dati codificati come <abbr>URL</abbr>.
</ol>

<blockquote class=note>
<p><span class=u>&#x261E;</span>Il terzo parametro del metodo <code>add_credentials()</code> è il dominio in cui le credenziali sono valide. Dovreste sempre specificarlo! Se lasciate fuori il dominio e più tardi riutilizzate l&#8217;oggetto <code>httplib2.Http</code> su un sito autenticato differente, <code>httplib2</code> potrebbe far trapelare il nome utente e la password di un sito all&#8217;altro sito.
</blockquote>

<p>Questo è quello che viene trasmesso attraverso la rete:

<pre class=screen>
# continua dall'esempio precedente
<samp>send: b'POST /api/statuses/update.xml HTTP/1.1
Host: identi.ca
Accept-Encoding: identity
Content-Length: 41
content-type: application/x-www-form-urlencoded
user-agent: Python-httplib2/$Rev: 259 $

status=Aggiornamento+di+prova+da+Python+3'
<a>reply: 'HTTP/1.1 401 Unauthorized'                        <span class=u>&#x2460;</span></a>
<a>send: b'POST /api/statuses/update.xml HTTP/1.1            <span class=u>&#x2461;</span></a>
Host: identi.ca
Accept-Encoding: identity
Content-Length: 41
content-type: application/x-www-form-urlencoded
<a>authorization: Basic HASH_SEGRETO_COSTRUITO_DA_HTTPLIB2   <span class=u>&#x2462;</span></a>
user-agent: Python-httplib2/$Rev: 259 $

status=Aggiornamento+di+prova+da+Python+3'
<a>reply: 'HTTP/1.1 200 OK'                                  <span class=u>&#x2463;</span></a></samp></pre>
<ol>
<li>Dopo la prima richiesta, il server risponde con un codice di stato <code>401 Unauthorized</code>. <code>httplib2</code> non invierà le intestazioni di autenticazione a meno che il server non le chieda esplicitamente. Questo è il modo in cui il server le chiede.
<li><code>httplib2</code> torna immediatamente sui suoi passi e richiede lo stesso <abbr>URL</abbr> una seconda volta.
<li>Questa volta include il nome utente e la password che avete aggiunto tramite il metodo <code>add_credentials()</code>.
<li>Ha funzionato!
</ol>

<p>Cos&#8217;è che il server spedisce indietro dopo una richiesta che ha successo? Questo dipende interamente dalla <abbr>API</abbr> del servzio web. In alcuni protocolli (come il <a href=http://www.ietf.org/rfc/rfc5023.txt>Protocollo di Pubblicazione Atom</a>) il server risponde con un codice di stato <code>201 Created</code> e l&#8217;ubicazione della risorsa appena creata nell&#8217;intestazione <code>Location</code>. Identi.ca restituisce un codice di stato <code>200 OK</code> e un documento <abbr>XML</abbr> contenente informazioni sulla risorsa appena creata.

<pre class=screen>
# continua dall'esempio precedente
<a><samp class=p>>>> </samp><kbd class=pp>print(content.decode('utf-8'))</kbd>                             <span class=u>&#x2460;</span></a>
<samp class=pp>&lt;?xml version="1.0" encoding="UTF-8"?>
&lt;status>
<a> &lt;text>Aggiornamento di prova da Python 3&lt;/text>               <span class=u>&#x2461;</span></a>
 &lt;truncated>false&lt;/truncated>
 &lt;created_at>Wed Jun 10 03:53:46 +0000 2009&lt;/created_at>
 &lt;in_reply_to_status_id>&lt;/in_reply_to_status_id>
 &lt;source>api&lt;/source>
<a> &lt;id>5131472&lt;/id>                                              <span class=u>&#x2462;</span></a>
 &lt;in_reply_to_user_id>&lt;/in_reply_to_user_id>
 &lt;in_reply_to_screen_name>&lt;/in_reply_to_screen_name>
 &lt;favorited>false&lt;/favorited>
 &lt;user>
  &lt;id>3212&lt;/id>
  &lt;name>Mark Pilgrim&lt;/name>
  &lt;screen_name>diveintomark&lt;/screen_name>
  &lt;location>27502, US&lt;/location>
  &lt;description>autore di libri tecnici, marito, padre&lt;/description>
  &lt;profile_image_url>http://avatar.identi.ca/3212-48-20081216000626.png&lt;/profile_image_url>
  &lt;url>http://diveintomark.org/&lt;/url>
  &lt;protected>false&lt;/protected>
  &lt;followers_count>329&lt;/followers_count>
  &lt;profile_background_color>&lt;/profile_background_color>
  &lt;profile_text_color>&lt;/profile_text_color>
  &lt;profile_link_color>&lt;/profile_link_color>
  &lt;profile_sidebar_fill_color>&lt;/profile_sidebar_fill_color>
  &lt;profile_sidebar_border_color>&lt;/profile_sidebar_border_color>
  &lt;friends_count>2&lt;/friends_count>
  &lt;created_at>Wed Jul 02 22:03:58 +0000 2008&lt;/created_at>
  &lt;favourites_count>30768&lt;/favourites_count>
  &lt;utc_offset>0&lt;/utc_offset>
  &lt;time_zone>UTC&lt;/time_zone>
  &lt;profile_background_image_url>&lt;/profile_background_image_url>
  &lt;profile_background_tile>false&lt;/profile_background_tile>
  &lt;statuses_count>122&lt;/statuses_count>
  &lt;following>false&lt;/following>
  &lt;notifications>false&lt;/notifications>
&lt;/user>
&lt;/status></samp></pre>
<ol>
<li>Ricordatevi che i dati restituiti da <code>httplib2</code> sono sempre <a href=stringhe.html#byte-arrays>byte</a>, non stringhe. Per convertirli in una stringa dovete decodificarli utilizzando la codifica di carattere corretta. I metodi della <abbr>API</abbr> di Identi.ca restituiscono sempre i risultati in <abbr>UTF-8</abbr>, quindi questa parte è facile.
<li>Ecco il testo del messaggio di stato che abbiamo appena pubblicato.
<li>Ecco l&#8217;identificatore unico per il nuovo messaggio di stato. Identi.ca lo usa per costruire un <abbr>URL</abbr> in modo da visualizzare il messaggio sul web.
</ol>

<p>Ed eccolo qui:

<p class=c><img class=fr src=i/identica-screenshot.png alt="screenshot che mostra il messaggio di stato pubblicato su Identi.ca" width=740 height=449>

<p class=a>&#x2042;

<h2 id=beyond-post>Oltre HTTP POST</h2>

<p><abbr>HTTP</abbr> non è limitato a <code>GET</code> e <code>POST</code>. Questi sono certamente i tipi più comuni di richieste, specialmente nei browser web. Ma le <abbr>API</abbr> di un servizio web possono andare oltre <code>GET</code> e <code>POST</code>, e <code>httplib2</code> è pronta.

<pre class=screen>
# continua dall'esempio precedente
<samp class=p>>>> </samp><kbd class=pp>from xml.etree import ElementTree as etree</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>tree = etree.fromstring(content)</kbd>                                          <span class=u>&#x2460;</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>status_id = tree.findtext('id')</kbd>                                           <span class=u>&#x2461;</span></a>
<samp class=p>>>> </samp><kbd class=pp>status_id</kbd>
<samp class=pp>'5131472'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>url = 'https://identi.ca/api/statuses/destroy/{0}.xml'.format(status_id)</kbd>  <span class=u>&#x2462;</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>resp, deleted_content = h.request(url, 'DELETE')</kbd>                          <span class=u>&#x2463;</span></a></pre>
<ol>
<li>Il server ha restituito un documento <abbr>XML</abbr>, giusto? Voi sapete <a href=xml.html#xml-parse>come riconoscere un documento <abbr>XML</abbr></a>.
<li>Il metodo <code>findtext()</code> trova la prima istanza dell&#8217;espressione data e ne estrae il contenuto testuale. In questo caso, stavamo giusto cercando un elemento <code>&lt;id></code>.
<li>Sulla base del contenuto testuale dell&#8217;elemento <code>&lt;id></code> possiamo costruire un <abbr>URL</abbr> per cancellare il messaggio di stato che abbiamo appena pubblicato.
<li>Per cancellare un messaggio vi basta emettere una richiesta <abbr>HTTP</abbr> <code>DELETE</code> verso quell&#8217;<abbr>URL</abbr>.
</ol>

<p>Questo è quello che viene trasmesso attraverso la rete:

<pre class=screen>
<samp><a>send: b'DELETE /api/statuses/destroy/5131472.xml HTTP/1.1      <span class=u>&#x2460;</span></a>
Host: identi.ca
Accept-Encoding: identity
user-agent: Python-httplib2/$Rev: 259 $

'
<a>reply: 'HTTP/1.1 401 Unauthorized'                             <span class=u>&#x2461;</span></a>
<a>send: b'DELETE /api/statuses/destroy/5131472.xml HTTP/1.1      <span class=u>&#x2462;</span></a>
Host: identi.ca
Accept-Encoding: identity
<a>authorization: Basic HASH_SEGRETO_COSTRUITO_DA_HTTPLIB2        <span class=u>&#x2463;</span></a>
user-agent: Python-httplib2/$Rev: 259 $

'
<a>reply: 'HTTP/1.1 200 OK'                                       <span class=u>&#x2464;</span></a></samp>
<samp class=p>>>> </samp><kbd class=pp>resp.status</kbd>
<samp class=pp>200</samp></pre>
<ol>
<li>&#8220;Cancella questo messaggio di stato.&#8221;
<li>&#8220;Mi dispiace, David, purtroppo non posso farlo.&#8221;
<li>&#8220;Non autorizzato<span class=u title='punto esclarrogativo!'>&#8253;</span> Hmmph. Cancella questo messaggio di stato, <em>per favore</em>&hellip;
<li>&hellip;e qui ci sono il mio nome utente e la mia password.&#8221;
<li>&#8220;Consideralo fatto!&#8221;
</ol>

<p>E proprio così, puff, è sparito.

<p class=c><img class=fr src=i/identica-deleted.png alt="screenshot che mostra il messaggio cancellato su Identi.ca" width=740 height=449>

<p class=a>&#x2042;

<h2 id=furtherreading>Letture di approfondimento</h2>

<p><code>httplib2</code>:

<ul>
<li>La pagina del progetto <a href=http://code.google.com/p/httplib2/><code>httplib2</code></a>
<li><a href=http://code.google.com/p/httplib2/wiki/ExamplesPython3>Ulteriori esempi di codice per <code>httplib2</code></a>
<li><a href=http://www.xml.com/pub/a/2006/02/01/doing-http-caching-right-introducing-httplib2.html>Usare la cache <abbr>HTTP</abbr> nel modo giusto: una introduzione ad <code>httplib2</code></a>
<li><a href=http://www.xml.com/pub/a/2006/03/29/httplib2-http-persistence-and-authentication.html><code>httplib2</code>: persistenza e autenticazione in <abbr>HTTP</abbr></a>
</ul>

<p>L&#8217;uso della cache in <abbr>HTTP</abbr>:

<ul>
<li><a href=http://www.mnot.net/cache_docs/>Tutorial sull&#8217;uso della cache in <abbr>HTTP</abbr></a> di Mark Nottingham
<li><a href=http://code.google.com/p/doctype/wiki/ArticleHttpCaching>Come controllare la cache con le intestazioni <abbr>HTTP</abbr></a> su Google Doctype
</ul>

<p><abbr>RFC</abbr>:

<ul>
<li><a href=http://www.ietf.org/rfc/rfc2616.txt>RFC 2616: <abbr>HTTP</abbr></a>
<li><a href=http://www.ietf.org/rfc/rfc2617.txt>RFC 2617: la Basic Authentication di <abbr>HTTP</abbr></a>
<li><a href=http://www.ietf.org/rfc/rfc1951.txt>RFC 1951: la compressione deflate</a>
<li><a href=http://www.ietf.org/rfc/rfc1952.txt>RFC 1952: la compressione gzip</a>
</ul>

<p class=v><a href=serializzare-oggetti-python.html rel=prev title='indietro a &#8220;Serializzare oggetti Python&#8221;'><span class=u>&#x261C;</span></a> <a href=caso-di-studio-convertire-chardet-verso-python-3.html rel=next title='avanti a &#8220;Caso di studio: convertire chardet verso Python 3&#8221;'><span class=u>&#x261E;</span></a>
<p class=c>&copy; 2001&ndash;10 <a href=about.html>Mark Pilgrim</a><br>
&copy; 2009&ndash;10 <a href=informazioni-sulla-traduzione.html>Giulio Piancastelli</a> per la traduzione italiana
<script src=j/jquery.js></script>
<script src=j/prettify.js></script>
<script src=j/dip3.js></script>