349 self.status = None |
349 self.status = None |
350 self.headers = wsgiheaders.Headers([]) |
350 self.headers = wsgiheaders.Headers([]) |
351 |
351 |
352 self._bodybytes = None |
352 self._bodybytes = None |
353 self._bodygen = None |
353 self._bodygen = None |
|
354 self._bodywillwrite = False |
354 self._started = False |
355 self._started = False |
|
356 self._bodywritefn = None |
|
357 |
|
358 def _verifybody(self): |
|
359 if (self._bodybytes is not None or self._bodygen is not None |
|
360 or self._bodywillwrite): |
|
361 raise error.ProgrammingError('cannot define body multiple times') |
355 |
362 |
356 def setbodybytes(self, b): |
363 def setbodybytes(self, b): |
357 """Define the response body as static bytes.""" |
364 """Define the response body as static bytes.""" |
358 if self._bodybytes is not None or self._bodygen is not None: |
365 self._verifybody() |
359 raise error.ProgrammingError('cannot define body multiple times') |
|
360 |
|
361 self._bodybytes = b |
366 self._bodybytes = b |
362 self.headers['Content-Length'] = '%d' % len(b) |
367 self.headers['Content-Length'] = '%d' % len(b) |
363 |
368 |
364 def setbodygen(self, gen): |
369 def setbodygen(self, gen): |
365 """Define the response body as a generator of bytes.""" |
370 """Define the response body as a generator of bytes.""" |
366 if self._bodybytes is not None or self._bodygen is not None: |
371 self._verifybody() |
367 raise error.ProgrammingError('cannot define body multiple times') |
|
368 |
|
369 self._bodygen = gen |
372 self._bodygen = gen |
|
373 |
|
374 def setbodywillwrite(self): |
|
375 """Signal an intent to use write() to emit the response body. |
|
376 |
|
377 **This is the least preferred way to send a body.** |
|
378 |
|
379 It is preferred for WSGI applications to emit a generator of chunks |
|
380 constituting the response body. However, some consumers can't emit |
|
381 data this way. So, WSGI provides a way to obtain a ``write(data)`` |
|
382 function that can be used to synchronously perform an unbuffered |
|
383 write. |
|
384 |
|
385 Calling this function signals an intent to produce the body in this |
|
386 manner. |
|
387 """ |
|
388 self._verifybody() |
|
389 self._bodywillwrite = True |
370 |
390 |
371 def sendresponse(self): |
391 def sendresponse(self): |
372 """Send the generated response to the client. |
392 """Send the generated response to the client. |
373 |
393 |
374 Before this is called, ``status`` must be set and one of |
394 Before this is called, ``status`` must be set and one of |
382 self._started = True |
402 self._started = True |
383 |
403 |
384 if not self.status: |
404 if not self.status: |
385 raise error.ProgrammingError('status line not defined') |
405 raise error.ProgrammingError('status line not defined') |
386 |
406 |
387 if self._bodybytes is None and self._bodygen is None: |
407 if (self._bodybytes is None and self._bodygen is None |
|
408 and not self._bodywillwrite): |
388 raise error.ProgrammingError('response body not defined') |
409 raise error.ProgrammingError('response body not defined') |
389 |
410 |
390 # Various HTTP clients (notably httplib) won't read the HTTP response |
411 # Various HTTP clients (notably httplib) won't read the HTTP response |
391 # until the HTTP request has been sent in full. If servers (us) send a |
412 # until the HTTP request has been sent in full. If servers (us) send a |
392 # response before the HTTP request has been fully sent, the connection |
413 # response before the HTTP request has been fully sent, the connection |
432 while True: |
453 while True: |
433 chunk = self._req.bodyfh.read(32768) |
454 chunk = self._req.bodyfh.read(32768) |
434 if not chunk: |
455 if not chunk: |
435 break |
456 break |
436 |
457 |
437 self._startresponse(pycompat.sysstr(self.status), self.headers.items()) |
458 write = self._startresponse(pycompat.sysstr(self.status), |
|
459 self.headers.items()) |
|
460 |
438 if self._bodybytes: |
461 if self._bodybytes: |
439 yield self._bodybytes |
462 yield self._bodybytes |
440 elif self._bodygen: |
463 elif self._bodygen: |
441 for chunk in self._bodygen: |
464 for chunk in self._bodygen: |
442 yield chunk |
465 yield chunk |
|
466 elif self._bodywillwrite: |
|
467 self._bodywritefn = write |
443 else: |
468 else: |
444 error.ProgrammingError('do not know how to send body') |
469 error.ProgrammingError('do not know how to send body') |
|
470 |
|
471 def getbodyfile(self): |
|
472 """Obtain a file object like object representing the response body. |
|
473 |
|
474 For this to work, you must call ``setbodywillwrite()`` and then |
|
475 ``sendresponse()`` first. ``sendresponse()`` is a generator and the |
|
476 function won't run to completion unless the generator is advanced. The |
|
477 generator yields not items. The easiest way to consume it is with |
|
478 ``list(res.sendresponse())``, which should resolve to an empty list - |
|
479 ``[]``. |
|
480 """ |
|
481 if not self._bodywillwrite: |
|
482 raise error.ProgrammingError('must call setbodywillwrite() first') |
|
483 |
|
484 if not self._started: |
|
485 raise error.ProgrammingError('must call sendresponse() first; did ' |
|
486 'you remember to consume it since it ' |
|
487 'is a generator?') |
|
488 |
|
489 assert self._bodywritefn |
|
490 return offsettrackingwriter(self._bodywritefn) |
445 |
491 |
446 class wsgirequest(object): |
492 class wsgirequest(object): |
447 """Higher-level API for a WSGI request. |
493 """Higher-level API for a WSGI request. |
448 |
494 |
449 WSGI applications are invoked with 2 arguments. They are used to |
495 WSGI applications are invoked with 2 arguments. They are used to |