Requests: Header "Transfer-Encoding: chunked" disetel meskipun Content-Length disediakan yang menyebabkan body tidak benar-benar terpotong

Dibuat pada 4 Okt 2013  ·  52Komentar  ·  Sumber: psf/requests

Skrip tes

import requests
import time

def f():
    yield b"lol"
    time.sleep(2)
    yield b"man"

requests.post('http://127.0.0.1:8801/', data=f(), headers={"Content-Length": 6})

Hasil sebenarnya

Diterima di server:

$ nc -p 8801 -l
POST / HTTP/1.1
Host: 127.0.0.1:8801
User-Agent: python-requests/2.0.0 CPython/3.3.1 Linux/3.11.0-031100rc4-generic
Accept: */*
Transfer-Encoding: chunked
Content-Length: 6
Accept-Encoding: gzip, deflate, compress

lolman

Hasil yang diharapkan

Tidak mengharapkan "Transfer-Encoding: chunked" karena saya menyediakan Content-Length. Jika permintaan bersikeras melakukan penyandian transfer chunked, itu harus mengabaikan panjang konten dan benar-benar memotong konten (seperti halnya jika tidak ada header Panjang Konten yang diberikan).

Breaking API Change Bug

Komentar yang paling membantu

@timuralp saya tetap menentang menambahkan bendera untuk ini. Benar-benar tidak dapat diterima untuk memiliki implementasi HTTP/1.1 yang tidak dapat menangani penyandian transfer chunked pada tahun 2016. Sudah menjadi persyaratan spesifikasi begitu lama sehingga spesifikasi pertama yang mengharuskannya hampir cukup tua untuk memilih di Amerika Serikat: Saya tidak 't berpikir kita dapat terus memotong entitas kendur karena tidak melakukannya.

Dari sudut pandang saya, bug di sini tetap bahwa kami mungkin salah memancarkan Content-Length dan Transfer-Encoding. Tentu saja, perspektif saya tidak mengikat. ;)

Semua 52 komentar

Saya setuju, kita harus memilih salah satu dari dua opsi itu. =)

Jadi berkomentar di sini seperti yang seharusnya saya lakukan> 6 jam yang lalu:

Jika permintaan bersikeras melakukan penyandian transfer chunked, itu harus mengabaikan panjang konten dan benar-benar memotong konten (seperti halnya jika tidak ada header Panjang Konten yang diberikan).

Anda menyiratkan bahwa dalam contoh ini kami tidak benar-benar memotong data. Apakah Anda memiliki bukti nyata tentang itu? Output saja tidak cukup untuk menunjukkan hal itu.

Selanjutnya menjalankan contoh Anda di atas dalam 2.0.0 tidak berfungsi: Saya mendapatkan traceback ini:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/icordasc/virtualenv/vcr/lib/python2.7/site-packages/requests/api.py", line 88, in post
    return request('post', url, data=data, **kwargs)
  File "/Users/icordasc/virtualenv/vcr/lib/python2.7/site-packages/requests/api.py", line 44, in request
    return session.request(method=method, url=url, **kwargs)
  File "/Users/icordasc/virtualenv/vcr/lib/python2.7/site-packages/requests/sessions.py", line 357, in request
    resp = self.send(prep, **send_kwargs)
  File "/Users/icordasc/virtualenv/vcr/lib/python2.7/site-packages/requests/sessions.py", line 460, in send
    r = adapter.send(request, **kwargs)
  File "/Users/icordasc/virtualenv/vcr/lib/python2.7/site-packages/requests/adapters.py", line 319, in send
    timeout=timeout
  File "/Users/icordasc/virtualenv/vcr/lib/python2.7/site-packages/requests/packages/urllib3/connectionpool.py", line 541, in urlopen
    body=body, headers=headers)
  File "/Users/icordasc/virtualenv/vcr/lib/python2.7/site-packages/requests/packages/urllib3/connectionpool.py", line 366, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 955, in request
    self._send_request(method, url, body, headers)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 989, in _send_request
    self.endheaders(body)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 951, in endheaders
    self._send_output(message_body)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 815, in _send_output
    self.send(message_body)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 787, in send
    self.sock.sendall(data)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 224, in meth
    return getattr(self._sock,name)(*args)
TypeError: must be string or buffer, not generator

Ini bahkan bukan masalah aktual sejauh yang saya tahu. Tidak setidaknya pada 2.0.0 yang merupakan satu-satunya versi permintaan yang saat ini didukung.

Tidak mengharapkan "Transfer-Encoding: chunked" karena saya menyediakan Content-Length

Apa yang memberi Anda kesan bahwa ini akan terjadi?

Apa yang memberi saya kesan adalah fakta bahwa Permintaan tidak memotong (bahkan jika itu mengatur tajuk) setelah saya mengatur Panjang Konten.

Contoh ini bekerja dengan Python 3.3.

Mencobanya di Python 3.3 tidak meledak, di sisi lain itu memotong konten. Chunking tidak bergantung pada platform jadi saya benar-benar bingung dengan itu.

POST / HTTP/1.1
Host: 127.0.0.1:8801
Content-Length: 6
User-Agent: python-requests/1.2.3 CPython/3.3.2 Darwin/12.5.0
Transfer-Encoding: chunked
Accept: */*
Accept-Encoding: gzip, deflate, compress

3
lol
3
man
0

Kedua, saya masih ingin tahu apa yang memberi Anda kesan bahwa menyetel header Content-Length akan mencegah penyandian data yang terpotong.

@ sigmavirus24 Anda tidak menggunakan Requests 2.0.0 pada instalasi Python 3.3 Anda.

@ysangkok poin bagus. :-) Dengan 2.0 itu pasti rusak. Terlepas dari pendapat saya pasti di pengadilan kami menghapus tajuk Content-Length .

Anda mungkin benar @sigmavirus24 , kita mungkin harus menghapus header Content-Length.

Hm, saya tidak sepenuhnya yakin dengan argumen saya sendiri lagi. Saya pergi mencari diskusi lama seputar pengguna yang mengatur header Content-Length sendiri dan saya pasti mengingat percakapan IRC lama.

Saya masih berpendapat bahwa pengguna seharusnya tidak menyetel tajuk itu sendiri dan bahwa kami harus menghapusnya, namun, saya pikir masalah ini menunjukkan masalah yang jauh lebih penting, yaitu perilaku permintaan yang sangat berbeda pada dua versi yang berbeda dari Python.

Pada 2.7 (seperti yang saya tunjukkan) menyetel header Content-Length tidak mengubah cara permintaan mengunggah data. Namun, pada 3.3, @ysangkok benar bahwa pengaturannya mengirimkan semuanya secepat mungkin (masih menggunakan generator tetapi tidak mengirimkannya ke manor yang benar-benar terpotong).

Salah satu cara mudah untuk memperbaikinya adalah dengan menghapus tajuk saat menggunakan generator (atau selalu memberikan perilaku yang konsisten), kekurangannya adalah ini adalah perilaku yang tidak kompatibel.

Cara mudah lainnya adalah mematahkan konsistensi API dengan tidak selalu menggunakan penyandian transfer chunked dengan generator. @Lukasa ini pasti membutuhkan pemikiran yang lebih dalam karena saya tidak dapat menemukan percakapan lama di mana pengguna diperingatkan untuk mengatur tajuk itu sendiri.

Sejujurnya, saya tidak akan pernah berharap bahwa pengaturan tajuk akan mengubah perilaku menggunakan generator.

Ini tentu saja adalah situasi yang sangat sulit

Bleh. Aku akan merenungkan.

Juga ini bukan perubahan API yang melanggar yang saya ragu lakukan karena saya harus bertanya-tanya berapa banyak orang yang benar-benar mengandalkan perilaku ini.

Saya mengalami situasi di mana panjang generator (misalnya, file yang dialirkan dari server lain atau disk yang sangat besar pada file) diketahui. Sebagai contoh:

response = requests.get('http://example.com/mybig.iso', stream=True)
length = response.headers.get('Content-Length')

def exhaust(response):
    while True:
        out = response.raw.read(1024*1024)
        if not out:
            break
        yield out

response = requests.post('http://example.com/upload', data=exhaust(response), headers={'Content-Length': length})

Itu mungkin alasan yang sah untuk menjaga Content-Length tetap ada. Satu-satunya solusi saat ini (yang saya tahu) adalah menghabiskan generator di memori dan meneruskan byte secara langsung.

@bryanhelmig apakah server yang Anda unggah mengharuskan Anda mengirim tajuk Panjang Konten?

@bryanhelmig apakah Anda melihat komentar di permintaan tarik tertaut?

Bagaimanapun, saya tidak mengerti mengapa Content-Transfer-Encoding bukan hanya sebuah bendera. Tidak perlu menghapus tajuk apa pun (atau melakukan jenis pegangan tangan lainnya), tidak pernah ada pertanyaan tentang Panjang-Konten benar atau salah, masalah sebenarnya adalah bahwa kehadiran Setengah-Panjang Konten menonaktifkan Transfer-Konten -Encoding, yang tidak masuk akal sama sekali. Tetapi hanya membuat Permintaan mengabaikan Panjang-Konten tidak menyelesaikan masalah sebenarnya, yaitu, Permintaan menggunakan Pengkodean-Transfer Konten ketika rasanya seperti itu (kedengarannya seperti itu ketika membaca dari generator), meskipun banyak web server bahkan tidak mendukungnya.

Mengabaikan Content-Length akan membingungkan orang yang menyediakannya. Jika Anda (@sigmavirus24) bersikeras untuk tidak mengirimkannya, mengapa tidak memberikan pengecualian saja? Seperti yang Anda katakan, fungsi ini mungkin tidak digunakan secara luas.

Dalam permintaan tarik, Anda mengatakan "Kasus penggunaan dalam masalah ini hanyalah contoh dari perilaku. Tidak ada pembenaran di sana mengapa Anda melakukan apa yang Anda lakukan.". Saya tidak setuju, saya pikir kode asli dalam masalah ini adalah perilaku yang sangat normal, dan sebenarnya saya pikir streaming data POST adalah kasus penggunaan yang sangat besar, dan itu konyol jika seseorang dipaksa untuk menggunakan Content-Transfer-Encoding atau menggunakan perpustakaan tingkat rendah saat streaming/menggunakan generator.

Jadi untuk meringkas: Content-Transfer-Encoding harus menjadi flag, kombinasi parameter ilegal harus memicu pengecualian, dan flag yang disediakan pengguna harus dikirim jika memungkinkan. Dan tentu saja, seharusnya tidak mungkin untuk menonaktifkan Pengkodean Konten-Transfer-Setengah.

Berhenti berhenti berhenti.

Semua orang mengambil napas.

@ysangkok Anda dapat melakukan unggahan streaming tanpa generator dengan baik. Berikan Permintaan objek seperti file di parameter data dan itu akan berfungsi. Ya, memang tidak sesederhana menggunakan genset, tapi tidak apa-apa karena masih tidak terlalu sulit.

Sementara itu, Permintaan tidak boleh menyarankan bahwa itu adalah pemotongan data ketika tidak. Kita semua sepakat tentang itu. Pertanyaannya adalah apa yang harus kita lakukan _dalam kasus spesifik Anda_: yaitu, menyediakan generator dan Content-Length . Anda dan @sigmavirus24 secara sah tidak setuju tentang masalah ini, _yang baik-baik saja_. Namun, bisakah kita semua mengakui bahwa kedua kubu memiliki alasan rasional untuk mengharapkan posisi mereka?

@ysangkok Anda telah mengatakan bahwa "Mengabaikan Panjang Konten akan membingungkan orang yang menyediakannya." @ sigmavirus24 berpendapat bahwa mengabaikan dokumentasi yang sangat jelas ketika disediakan dengan generator akan membingungkan orang yang melakukan _that_. Anda berdua benar.

(Sebagai catatan tambahan, fakta bahwa banyak server tidak mengerti Transfer-Encoding hanyalah pernyataan liar berdasarkan tidak ada bukti yang saya lihat. Sampai ada bukti yang diberikan, saya memilih untuk mengabaikannya. )

Dengan satu atau lain cara kita harus memilih apa yang kita lakukan di sini. Ada kemungkinan bahwa keputusan yang tepat adalah mengeluarkan pengecualian ketika generator dan Content-Length disediakan. Itu layak. Itu bahkan tidak memperburuk kasus response.raw langsung daripada membungkusnya dengan dekorator ( untuk keuntungan Anda, Bryan ).

Saya secara alami cenderung untuk duduk di pagar di sini dan melemparkan pengecualian YoureACrazyPerson , tetapi saya dapat melihat mengapa Anda berdua mempercayai apa yang Anda yakini. Secara khusus, membuat keputusan berdasarkan header yang disediakan pengguna tidak tepat dan membingungkan, dan kita harus berusaha untuk tidak melakukannya. Namun, berikut ini adalah garis keras:

  1. Mengontrol Transfer-Encoding tidak akan menjadi flag. Tidak sekarang, tidak pernah. Permintaan tidak melakukan flag kasus khusus kecil seperti ini.
  2. Kita tidak bisa melakukan apa-apa.
  3. Permintaan _not_ wajib mendukung semua kasus penggunaan. Saya akan dengan senang hati membuang salah satu kasus penggunaan di bawah bus jika itu membuat API lebih baik.

apakah server yang Anda unggah mengharuskan Anda mengirim header Content-Length?

@ sigmavirus24 memang begitu. :-( 411 untuk percobaan tanpa Panjang Konten. IMO yang cukup mengganggu.

apakah Anda melihat komentar di permintaan tarik tertaut?

@ysangkok saya lakukan.

dia seharusnya hanya memberikan respons. mentah langsung daripada membungkusnya dengan dekorator

@Lukasa Sebenarnya itu adalah hasil edit asli saya, tetapi saya tidak yakin apa yang membuat kami

Terima kasih untuk semua jawaban menyeluruh semua orang. Sungguh, tidak apa-apa jika pengguna dalam situasi eksotis harus bertukar ke lib yang berbeda untuk satu dari 100 permintaan. Hidup jauh lebih mudah untuk 99 kasus lainnya.

Sungguh, tidak apa-apa jika pengguna dalam situasi eksotis harus bertukar ke lib yang berbeda untuk satu dari 100 permintaan.

Saya lebih suka Permintaan _Jadikan Hal Mudah Menjadi Mudah & Hal Sulit Menjadi Mungkin_ :)

@piotr-dobrogost Setuju, tetapi jika membuat hal yang sulit menjadi mungkin mengharuskan hal yang mudah menjadi lebih sulit, kami lebih suka membiarkan hal yang mudah menjadi mudah. =)

Beberapa hal:

Sejauh yang saya ketahui, ini adalah (dan akan terus berlanjut, sampai kita mengambil keputusan) perilaku yang tidak terdefinisi

Sebagai catatan tambahan, fakta bahwa banyak server tidak mengerti Transfer-Encoding hanyalah pernyataan liar berdasarkan tidak ada bukti yang saya lihat. Sampai ada bukti yang diberikan, saya memilih untuk mengabaikannya.

Ada perbedaan antara server yang tidak memahami Transfer-Encoding yang terpotong dan server yang tidak ingin menghormatinya. Saya menduga yang terakhir adalah kasus dalam kasus @bryanhelmig . Atau, aplikasi bisa saja ditulis oleh seseorang yang tidak mengerti atau tahu tentang Transfer-Encoding dan membutuhkan Content-Length.

Ada kemungkinan bahwa keputusan yang tepat adalah mengeluarkan pengecualian ketika generator dan Panjang Konten disediakan. Itu layak.

Pengecualian mungkin terlalu ekstrim dalam kasus ini. Kami biasanya tidak mengajukan pengecualian untuk apa pun selain URL yang tidak valid. Cara kami memproses parameter data dan file dapat menimbulkan pengecualian, tetapi kami tidak membuat kasus khusus apa pun di sana. Karena itu, kami telah berbicara tentang betapa buruknya ide ketika pengguna menentukan header Panjang Konten dan Host mereka sendiri (antara lain yang mungkin saya lupa). Karena ini bukan praktik yang secara teknis tidak valid, melainkan praktik yang kami sarankan untuk tidak dilakukan, saya menyarankan agar kami memicu peringatan dan kemudian melakukan hal yang benar dalam situasi tertentu yang terdokumentasi dengan baik.

  • Jika kami menerima header Host, kami memberikan peringatan tetapi tidak menghapusnya.
  • Jika kami menerima objek yang ukurannya dapat kami tentukan dan header Content-Length disediakan, kami harus memberikan peringatan bahwa dalam kasus seperti itu mereka tidak boleh melakukannya. Saya tidak yakin apakah kita harus mengesampingkan pengaturan mereka.
  • Jika kita menerima generator dan header Content-Length, kita harus memberikan peringatan dan menghapus header.

Menggunakan peringatan adalah cara yang lebih lembut untuk memberi tahu pengguna apa yang mereka lakukan tidak disarankan. Ini juga mencakup fakta bahwa begitu banyak pengguna kami tampaknya tidak repot-repot membaca dokumentasi dan perpustakaan menjadi semacam dokumentasi sendiri. Ini juga memberi kita kemampuan untuk mengubah perilaku di masa depan. Dan lebih baik lagi, jika pengguna ingin menonaktifkan peringatan, mereka dapat melakukannya karena python menyediakan cara untuk membungkam peringatan.

Ada perbedaan antara server yang tidak memahami Transfer-Encoding yang terpotong dan server yang tidak ingin menghormatinya. Saya menduga yang terakhir adalah kasus dalam kasus @bryanhelmig . Atau, aplikasi bisa saja ditulis oleh seseorang yang tidak mengerti atau tahu tentang Transfer-Encoding dan membutuhkan Content-Length.

Saya sebenarnya berpikir ini sangat mungkin terjadi di sebagian besar contoh saya. Kami (@zapier) mencoba mengunggah file ke lebih dari selusin API yang berbeda dan beberapa di antaranya yang memerlukan batas waktu Content-Length (tampaknya) dengan chunked Transfer-Encoding.

Sebagai catatan tambahan, fakta bahwa banyak server tidak mengerti Transfer-Encoding hanyalah pernyataan liar berdasarkan tidak ada bukti yang saya lihat. Sampai ada bukti yang diberikan, saya memilih untuk mengabaikannya.

Saya dapat menyusun rangkaian pengujian tentang bagaimana berbagai layanan merespons Content-Length/Transfer-Encoding, tetapi saya merasa bahwa meskipun API yang diterapkan salah, itu seharusnya tidak benar-benar menyarankan desain permintaan python. Lebih mudah lagi, saya hanya bisa menyebutkan nama berdasarkan pengalaman saya melawan ini selama seminggu terakhir, tetapi sekali lagi, jika itu adalah bug API/server, apa gunanya informasi seperti itu untuk permintaan python?

Saya menyarankan agar kita memicu peringatan dan kemudian melakukan hal yang benar dalam situasi tertentu yang terdokumentasi dengan baik.

Setuju dengan perilaku default, tetapi terkadang kenyataan mengalahkan "hal yang benar" (terutama ketika Anda tidak memiliki kendali atas server yang berpotensi rusak). Mungkin bagus untuk mendokumentasikan teknik untuk ditimpa (bahkan jika itu menganjurkan pengguna untuk melakukan banyak pekerjaan, seperti menulis Adaptor khusus).

Hanya untuk memperjelas: dengan "hal yang benar" saya tidak bermaksud hal RFC. (Jika itu yang Anda pikir saya maksudkan)

Saya dapat menyusun rangkaian pengujian tentang bagaimana berbagai layanan merespons Content-Length/Transfer-Encoding, tetapi saya merasa bahwa meskipun API yang diterapkan salah, itu seharusnya tidak benar-benar menyarankan desain permintaan python.

Saya setuju bahwa server yang berperilaku buruk tidak boleh menginformasikan desain kami.

Lebih mudah lagi, saya hanya bisa menyebutkan nama berdasarkan pengalaman saya melawan ini selama seminggu terakhir, tetapi sekali lagi, jika itu adalah bug API/server, apa gunanya informasi seperti itu untuk permintaan python?

Kita sudah tahu betapa rusaknya web itu. Terlepas dari itu, mengetahui apakah kita harus menghapus tajuk setelah memperingatkan pengguna atau membiarkannya di mana data itu bisa berguna.

Bleh. Ini membuatku sedih.

Oke, saya pikir rencana @sigmavirus24 adalah yang terbaik di sini, setidaknya untuk saat ini.

Beberapa hal yang muncul mengganggu saya untuk mendapatkan beberapa data mendetail kepada kalian, tetapi ini adalah kumpulan otak dari apa yang saya lihat:

Pelanggar terbesar yang pernah saya lihat adalah titik akhir API yang memerlukan unggahan multi-bagian: mereka biasanya membutuhkan Panjang Konten atau mereka panik pada Transfer-Encoding yang dipotong (saya tidak yakin yang mana). Itu tidak terjadi di mana-mana, tetapi inilah bagian yang dapat saya ambil dari sumber kami dengan grep cepat:

  1. Twitter melakukan multipart dan gagal dengan pengkodean chunked/tanpa panjang.
  2. Podio melakukan multipart dan gagal dengan penyandian chunked/tanpa panjang.
  3. Facebook melakukan multipart dan bekerja dengan penyandian chunked/tanpa panjang.
  4. Salesforce melakukan multi-bagian dan bekerja dengan penyandian terpotong/tanpa panjang.

Alasan mengapa ini segar dalam pikiran saya adalah kami telah membangun generator tubuh multi-bagian khusus yang bekerja dengan file "malas" yang juga dapat menghitung panjangnya, ini terjadi untuk memunculkan banyak masalah yang sedang kita bicarakan di sini. Saat ini kami hanya menipu dan melakukan getvalue() untuk titik akhir unggahan yang gagal. Kami mungkin akan mengunjunginya kembali suatu hari nanti.

Contoh lain lebih sulit untuk dipahami, tetapi perusahaan file (Box, Dropbox, SugarSync, dll ...) semuanya mendapatkannya dan berfungsi dengan baik dengan pengkodean chunked, jadi jangan khawatir di sana.

Semoga ini menyinari sedikit lebih banyak tentang kasus penggunaan dunia nyata kami. Saya berharap saya bisa memberi Anda lebih banyak informasi untuk mengonfirmasi tajuk mana yang biasanya menyebabkan kesalahan mana (biasanya waktu habis).

Ini seharusnya lebih mudah untuk diuji sekarang karena kami memiliki beberapa API yang dapat kami produksi ulang, yaitu Twitter.

Saya memiliki kasus penggunaan yang sama persis: mengunggah data ke server yang tidak mendukung pengkodean chunked. Panjang data diketahui sebelumnya, tetapi tidak berasal dari file (dari generator).
Saya berharap bahwa pengaturan header panjang konten akan menonaktifkan perhitungan panjang dalam permintaan, dan juga menonaktifkan penyandian transfer chunked.
Sekarang saya berhasil mengatasi masalah ini dengan menambahkan atribut 'len' tambahan ke objek generator saya, sehingga permintaan utils.super_len() mengembalikan sesuatu sehingga penyandian yang dipotong tidak dipilih oleh permintaan: ini jelek dan cemerlang.
Sebagai pengguna unix (dan seperti @piotr-dobrogost ), saya berharap programmer tahu apa yang dia lakukan, dan perpustakaan harus mematuhi: menyediakan header panjang konten adalah indikasi yang jelas bahwa penyandian chunked tidak boleh digunakan. Tetapi ini bertentangan dengan kalimat Anda di atas: "Saya tidak akan pernah berharap bahwa pengaturan tajuk akan mengubah perilaku menggunakan generator". Nah, jika didokumentasikan dengan jelas, saya tidak mengerti maksudnya. Itu tidak akan merusak API, bukan?

@netheosgithub Saya berpendapat bahwa mengubah perilaku ini benar-benar merupakan perubahan dalam API. Pertimbangkan status API saat ini, dari dokumentasi :

Permintaan juga mendukung penyandian transfer Chunked untuk permintaan keluar dan masuk. Untuk mengirim permintaan yang dikodekan dengan potongan, cukup sediakan generator (atau iterator apa pun tanpa panjang) untuk tubuh Anda.

Perhatikan bahwa dokumentasi _tidak mengatakan_ "Untuk mengirim permintaan yang dikodekan-potong, cukup sediakan generator dan jangan atur header Panjang-Konten." Ini menjadikan ini sebagai perubahan API di buku saya. Bukan sembarang perubahan API: yang buruk. Gagasan bahwa menyetel tajuk mengubah isi permintaan membuat saya merasa sangat tidak nyaman. Pertimbangkan jika seseorang meminta kami untuk permintaan serupa di mana jika mereka mengatur header Content-Type: application/json , kita harus mengkodekan JSON parameter data alih-alih mengkodekan formulir! Saya akan membuang permintaan itu dalam sekejap.

Saya pikir kita harus mencoba mengatasi akar masalahnya: mengapa orang menggunakan generator dalam situasi ini?

Saat ini, menyediakan generator dan menentukan panjang konten adalah kesalahan (ini menghasilkan permintaan http yang tidak valid), jadi kasus penggunaan ini tidak boleh digunakan oleh siapa pun. Itu sebabnya saya berpikir itu tidak akan merusak program pengguna Anda.
Mengapa generator? Data tidak selalu disediakan oleh objek seperti file. Misalnya, saya ingin melihat kemajuan unggahan dengan menghasilkan potongan data demi potongan, dll. (jika tidak, saya harus mengganti metode read())

Jika saja argumen 'permintaan HTTP tidak valid' Anda valid. Andai aku hidup di dunia itu. Namun, banyak banyak server pasti akan dengan senang hati menerima kombinasi seperti itu.

RFC 2616 menyediakan bagian yang mencerahkan ini untuk menentukan panjang pesan:

Panjang transfer pesan adalah panjang badan pesan seperti yang muncul dalam pesan; yaitu, setelah transfer-coding diterapkan. Saat badan pesan disertakan dengan pesan, panjang transfer badan tersebut ditentukan oleh salah satu dari berikut ini (dalam urutan prioritas):

  1. Pesan respons apa pun yang "HARUS TIDAK" menyertakan isi pesan (seperti respons 1xx, 204, dan 304 dan respons apa pun terhadap permintaan HEAD) selalu diakhiri oleh baris kosong pertama setelah bidang header, terlepas dari entitas- bidang header ada dalam pesan.
  2. Jika bidang header Transfer-Encoding (Transfer-Encoding) ada dan memiliki nilai selain "identitas", maka panjang transfer ditentukan dengan menggunakan "chunked" transfer-coding (Transfer Codings), kecuali pesannya diakhiri dengan menutup koneksi.
  3. Jika bidang header Content-Length (Content-Length) ada, nilai desimalnya dalam OCTET mewakili panjang entitas dan panjang transfer. Bidang header Content-Length TIDAK HARUS dikirim jika kedua panjang ini berbeda (yaitu, jika bidang header Transfer-Encoding ada). Jika pesan diterima dengan bidang header Transfer-Encoding dan bidang header Panjang Konten, yang terakhir HARUS diabaikan.
    ...
    Pesan TIDAK HARUS menyertakan bidang header Panjang Konten dan pengkodean transfer non-identitas. Jika pesan menyertakan pengkodean transfer non-identitas, Panjang Konten HARUS diabaikan.

Untuk memperjelas: kami setuju bahwa Permintaan melakukan hal yang salah. Namun, kami _tidak_ setuju bahwa menyetel header Content-Length harus menonaktifkan chunking. Itulah sedikit yang menyebabkan pertengkaran.

Saya juga harus menunjukkan bahwa server apa pun yang gagal dengan pengkodean chunked juga melanggar RFC 2616:

Semua aplikasi HTTP/1.1 HARUS dapat menerima dan memecahkan kode transfer-coding "terpotong", dan HARUS mengabaikan ekstensi ekstensi chunk yang tidak mereka pahami.

Ini bukan upaya untuk mengatakan bahwa Permintaan seharusnya tidak memperbaiki masalah ini, ini hanya untuk menunjukkan bahwa semua orang yang terlibat melanggar RFC 2616.

Saya pikir kita harus mencoba mengatasi akar masalahnya: mengapa orang menggunakan generator dalam situasi ini?

Salah satu kasus penggunaan kami adalah mengambil file yang mungkin sangat besar dengan ukuran yang diketahui dari satu layanan dan mengunggahnya ke layanan kedua, kecuali data biner hanyalah salah satu bagian dari POST multi-bagian (dengan metadata JSON sebagai yang lain) yang layanan kedua yang diperlukan.

Jadi kami membungkus pembangun multi-bagian dalam generator; satu kita bisa tahu ukuran. Sepertinya solusi yang sangat jelas dan berguna, setidaknya sampai fitur/bug permintaan ini menghentikan kami (saya yakin kami mempertahankan garpu sekarang, tetapi trik superlen mungkin membantu membatalkannya!).

Kami sekarang memiliki kasus penggunaan lain dari generator dengan lensa, tetapi saya harus menggalinya.

Bryan

Pada 18 Februari 2014, pukul 12:45, Cory Benfield [email protected] menulis:

Saya pikir kita harus mencoba mengatasi akar masalahnya: mengapa orang menggunakan generator dalam situasi ini?

@bryanhelmig Mm, ya. Saya pikir solusi yang tepat ada mulai sekarang untuk menggunakan encoder multipart streaming toolbelt permintaan .

Namun saat ini, tidak jelas bagi saya mengapa ada resistensi yang kuat untuk menggunakan objek seperti file di sini.

Dalam edisi #1895 saya mengalami masalah ini bahkan dengan objek seperti file. Permintaannya adalah PUT, dan objek seperti file kebetulan adalah sys.stdin, yang sebenarnya sangat mirip file sehingga memicu penyandian chunked seperti yang terjadi pada file biasa. Menyediakan Panjang Konten (yang saya pelajari melalui cara luar) dalam situasi ini menyebabkan HTTPAdapter.send tidak melakukan chunking seperti yang diharapkan, tetapi PreparedRequest.prepare_body masih memperhatikan bahwa objek seperti file dapat diubah dan menambahkan header Transfer-Encoding bagaimanapun. Server (Amazon S3) tersedak pada header itu meskipun permintaan akan berhasil.

@gholms Tidak. sys.stdin sangat _not_ seperti file sehingga memicu penyandian terpotong. Jika Anda memberikan objek file kepada kami, kami akan mengalirkannya, bukan memotongnya. Masalah dengan sys.stdin adalah tidak memiliki panjang, yang termasuk dalam deskripsi yang disediakan dalam dokumen seperti yang disebutkan di atas:

setiap iterator tanpa panjang

Harapan umum adalah bahwa pengguna tidak akan pernah secara eksplisit mengatur header Content-Length. Ini karena Permintaan mungkin berakhir dengan mengacaukan bagaimana badan permintaan diatur.

Akhirnya, gagasan bahwa 'orang mungkin mengharapkan' bahwa menyediakan header Panjang Konten akan menonaktifkan chunking juga tidak benar: Saya mengharapkan kita untuk menghapus header. =)

Bagaimanapun, diskusi ini telah berlangsung cukup lama. Saat dilengkapi dengan iterator tanpa panjang dan header Panjang Konten yang disediakan pengguna, kami memiliki opsi berikut:

  1. Angkat pengecualian.
  2. Hancurkan header Content-Length
  3. Jangan potong.

Salah satu dari tiga opsi ini membuat kami mematuhi RFC. Jelas bahwa setiap orang yang mengangkat masalah ini lebih memilih (3). @sigmavirus24?

Saya selalu berpendapat bahwa kami harus membuang sundulan. Dalam pengalaman saya, pengguna berpikir mereka tahu apa yang mereka lakukan tetapi jarang cukup tahu untuk benar-benar tahu apa yang mereka lakukan. Saya biasanya suka membiarkan pengguna menembak diri mereka sendiri dan kami telah melakukannya sampai sekarang dan itu tidak banyak membantu kami. Faktanya, kami memiliki kesempatan untuk menerbangkan sundulan itu sebelumnya dan secara khusus tidak melakukannya. Itu telah mengarah ke masalah ini. Mari kita bersikap logis tentang ini:

API dan dokumentasi kami selalu mempertahankan bahwa melewatkan iterable tanpa cara mengukur panjang berarti kami akan menggunakan penyandian transfer chunked. Dengan spec dalam hal ini kita harus mengabaikan header Content-Length karena beberapa server tidak mematuhinya. Itu harus diabaikan. Kami tidak melakukan kesalahan dengan mengirimkannya terlepas dari penyandiannya, server melakukan hal yang salah dengan tidak mengabaikannya. Untuk melindungi dari server buruk seperti ini, kami _harus_ menghapusnya.

Jika di 3.0.0 kami memutuskan untuk mengubah API sedemikian rupa sehingga memberikan header tidak memotong itu adalah binatang yang berbeda. Kami tidak mempertimbangkan 3.0.0 dan itulah masalah sebenarnya di sini. Apa yang bisa kita lakukan untuk mengatasi masalah ini sekarang. Yang bisa kita lakukan adalah mengikuti spek.

Saya cenderung setuju dengan Anda @ sigmavirus24. Saya akan melihat apakah saya bisa mendapatkan beberapa menit dengan Kenneth di beberapa titik segera untuk membicarakan ini dengan dia.

Saya berada di kapal yang sama dengan @bryanhelmig dan @netheosgithub , saya memiliki generator di mana saya tahu sebelumnya berapa ukuran potongan gabungan, dan memiliki server yang tidak mendukung unggahan chunked (aplikasi WSGI, WSGI menurut saya penelitian tidak mendukung pengkodean chunked sama sekali). Data dari generator terlalu besar untuk dimasukkan ke dalam RAM, jadi menggabungkan potongan sebelum dan meneruskannya ke permintaan tidak mungkin.
Apakah ada perkembangan baru terkait masalah ini?

@jbaiter maka yang Anda butuhkan adalah melewatkan objek yang berperilaku seperti file dengan __len__ ditentukan. Ambil, misalnya, MultipartEncoder sabuk alat . Anda ingin streaming, jadi secara umum yang Anda butuhkan adalah sesuatu seperti ini yang memiliki metode read dan cara menentukan panjangnya (sebaiknya dengan menerapkan __len__ ). API objek Anda dapat terlihat seperti:

class JBaiterStreamer(object):
    def __init__(self, size, iterator):
        self.size = size
        self.iterator = iterator

    def __len__(self):
        return self.size

    def read(self, *args):
        try:
            return next(self.iterator)
        except StopIteration:
            return b''

Memang saya belum mencoba untuk melihat apakah kode itu akan berfungsi, tetapi sesuatu seperti itu seharusnya.

Terima kasih @sigmavirus24 :+1:, saya melakukan itu, saya hanya ingin tahu apakah sekarang ada cara yang lebih elegan untuk melakukannya

Mungkin ada cara yang baik untuk menyediakan ini melalui toolbelt, eh @Lukasa ?

@sigmavirus24 Tentu saja. =)

@ sigmavirus24 "Kami tidak melakukan kesalahan dengan mengirimkannya terlepas dari penyandiannya, server melakukan hal yang salah dengan tidak mengabaikannya." sebenarnya sekarang salah:

RFC 7230: "Pengirim TIDAK HARUS mengirim bidang header Panjang Konten dalam pesan apa pun yang berisi bidang header Transfer-Encoding."

@ztane ini adalah kasus yang berbeda dari apa yang dimaksud dengan pertanyaan stack overflow. Di masa mendatang, harap baca diskusi dengan cermat sebelum membuat pemberitahuan untuk semua pesertanya.

@sigmavirus24 yang disebabkan oleh "transfer-Encoding: chunked, content-length set => body not chunked." Dan RFC 7230 melarang pengaturan Content-Length dengan Transfer-Encoding .

@ztane terima kasih karena terus tidak membaca diskusi ini. Bug khusus ini adalah tentang seseorang yang mengatur tajuk Panjang Konten dengan tangan dan mengharapkan permintaan untuk tidak menggunakan unggahan yang dipotong ketika mereka menyediakan generator (yang selalu memicu unggahan yang dipotong dengan permintaan). Tautan stackoverflow yang Anda berikan dan yang Anda bicarakan adalah bug berbeda yang sedang ditangani di tempat lain. Anda mengacaukan diskusi ini dengan detail yang tidak relevan karena kami mengizinkan pengguna untuk menembak diri mereka sendiri. Misalnya, kami mengizinkan pengguna untuk menentukan tajuk panjang konten yang salah atau tajuk host yang tidak valid, atau pada dasarnya apa pun yang mereka inginkan. Kami tidak mengawasi semuanya, kami juga tidak. Harap fokuskan percakapan Anda pada masalah _correct_ di masa mendatang.

Saya menemukan ini ketika menggunakan boto3 untuk melakukan PUT dari aliran terhadap AWS S3 (https://github.com/boto/botocore/issues/911). Dalam kasus saya, ukuran aliran diketahui -- itu adalah objek dari penyedia lain. Saat menggunakan tanda tangan S3 versi 2, "Transfer-Encoding: chunked" tidak didukung dan S3 mengembalikan kesalahan 501. Dokumentasi boto3 tampaknya menyiratkan bahwa pengaturan Content-Length akan mengatasi masalah ini, karena mereka menyatakan sebagai berikut:

ContentLength (integer) -- Size of the body in bytes. This parameter is useful when
the size of the body cannot be determined automatically. [1]

Sementara boto3 harus melakukan dua hal -- memperbaiki dokumentasinya dan memastikan bahwa "Transfer-Encoding: chunked" tidak disetel -- untuk permintaan konsumen tidak langsung, perilaku ini sulit untuk di-debug. Jika header Content-Length diabaikan, memunculkan pengecualian setidaknya akan memperjelas apa yang sedang terjadi. Seperti, itu dikaburkan oleh fakta bahwa S3 hanya mengembalikan kesalahan 501 dengan penjelasan bahwa salah satu header tidak didukung (tetapi tidak menentukan header).

Solusi yang disarankan untuk membungkusnya dan memberikan panjangnya berfungsi, tetapi tampaknya jelek. Apakah memperluas API untuk memungkinkan pengalihan penyandian chunked (sambil menjaga perilaku saat ini sebagai default) menjadi cara yang cocok untuk maju (sebagai lawan menggunakan header Content-Length sebagai flag)?

[1] http://boto3.readthedocs.io/en/latest/reference/services/s3.html#S3.Client.put_object

@timuralp saya tetap menentang menambahkan bendera untuk ini. Benar-benar tidak dapat diterima untuk memiliki implementasi HTTP/1.1 yang tidak dapat menangani penyandian transfer chunked pada tahun 2016. Sudah menjadi persyaratan spesifikasi begitu lama sehingga spesifikasi pertama yang mengharuskannya hampir cukup tua untuk memilih di Amerika Serikat: Saya tidak 't berpikir kita dapat terus memotong entitas kendur karena tidak melakukannya.

Dari sudut pandang saya, bug di sini tetap bahwa kami mungkin salah memancarkan Content-Length dan Transfer-Encoding. Tentu saja, perspektif saya tidak mengikat. ;)

Selanjutnya, jika boto3 ingin secara paksa mengganti header Content-Length, mereka harus menggunakan alur permintaan yang telah disiapkan sehingga mereka dapat menghapus header Transfer-Encoding .

@Lukasa @sigmavirus24 cukup adil -- terima kasih atas balasannya yang cepat. Saya akan terus mencari untuk memperbaiki masalah boto di proyek itu.

Cara saya mengatasi ketidakmampuan untuk mengontrol apakah ada sesuatu yang dipotong adalah dengan mengontrol apakah POST saya menggunakan bagian "data" atau "file" dari pemanggilan metode. Tampaknya bekerja dengan baik.

    req = Request('POST',url='http://apiendpointurl',
        headers=headers,
        data=fs)
    prepped = req.prepare()

    if 'Transfer-Encoding' in prepped.headers and prepped.headers['Transfer-Encoding'] == 'chunked':
        res=postAsFiles(headers, fs)
    else:
        res=postAsData(headers,fs)

di mana satu-satunya perbedaan antara postAsFiles dan postAsData adalah ini:

def postAsData(headers, fs):
    return requests.post(
        url='http://apiendpointurl',
        headers=headers,
        data=fs)

def postAsFiles(headers, fs):
    return requests.post(
        url='http://apiendpointurl',
        headers=headers,
        files=fs)

Dengan #3897, Permintaan 3.0.0 akan memunculkan pengecualian dalam hal ini mencegah pengiriman kedua header.

Pengguna yang ingin mengirim header Content-Length mereka sendiri akan dapat memodifikasi header menggunakan alur PreparedRequests . Perhatikan bahwa Content-Length masih tidak akan berfungsi untuk data yang diteruskan sebagai generator. Jika pengguna HARUS menentukan Panjang Konten, generator perlu dikonsumsi dan diteruskan dalam sesuatu dengan representasi string/seperti file. Dalam kebanyakan kasus, tajuk ini seharusnya dibiarkan begitu saja untuk penanganan otomatis oleh Permintaan.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat