Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/hgweb/request.py @ 36914:cd6ae9ab7bd8
hgweb: remove dead wsgirequest code
All responses now go through our modern response type. All code related
to response handling can be deleted.
Differential Revision: https://phab.mercurial-scm.org/D2830
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Sun, 11 Mar 2018 15:55:38 -0700 |
parents | 219b23359f4c |
children | 84110a1d0f7d |
comparison
equal
deleted
inserted
replaced
36913:c1de7efca574 | 36914:cd6ae9ab7bd8 |
---|---|
6 # This software may be used and distributed according to the terms of the | 6 # This software may be used and distributed according to the terms of the |
7 # GNU General Public License version 2 or any later version. | 7 # GNU General Public License version 2 or any later version. |
8 | 8 |
9 from __future__ import absolute_import | 9 from __future__ import absolute_import |
10 | 10 |
11 import errno | |
12 import socket | |
13 import wsgiref.headers as wsgiheaders | 11 import wsgiref.headers as wsgiheaders |
14 #import wsgiref.validate | 12 #import wsgiref.validate |
15 | |
16 from .common import ( | |
17 ErrorResponse, | |
18 statusmessage, | |
19 ) | |
20 | 13 |
21 from ..thirdparty import ( | 14 from ..thirdparty import ( |
22 attr, | 15 attr, |
23 ) | 16 ) |
24 from .. import ( | 17 from .. import ( |
607 self.multiprocess = wsgienv[r'wsgi.multiprocess'] | 600 self.multiprocess = wsgienv[r'wsgi.multiprocess'] |
608 self.run_once = wsgienv[r'wsgi.run_once'] | 601 self.run_once = wsgienv[r'wsgi.run_once'] |
609 self.env = wsgienv | 602 self.env = wsgienv |
610 self.req = parserequestfromenv(wsgienv, inp, altbaseurl=altbaseurl) | 603 self.req = parserequestfromenv(wsgienv, inp, altbaseurl=altbaseurl) |
611 self.res = wsgiresponse(self.req, start_response) | 604 self.res = wsgiresponse(self.req, start_response) |
612 self._start_response = start_response | |
613 self.server_write = None | |
614 self.headers = [] | |
615 | |
616 def respond(self, status, type, filename=None, body=None): | |
617 if not isinstance(type, str): | |
618 type = pycompat.sysstr(type) | |
619 if self._start_response is not None: | |
620 self.headers.append((r'Content-Type', type)) | |
621 if filename: | |
622 filename = (filename.rpartition('/')[-1] | |
623 .replace('\\', '\\\\').replace('"', '\\"')) | |
624 self.headers.append(('Content-Disposition', | |
625 'inline; filename="%s"' % filename)) | |
626 if body is not None: | |
627 self.headers.append((r'Content-Length', str(len(body)))) | |
628 | |
629 for k, v in self.headers: | |
630 if not isinstance(v, str): | |
631 raise TypeError('header value must be string: %r' % (v,)) | |
632 | |
633 if isinstance(status, ErrorResponse): | |
634 self.headers.extend(status.headers) | |
635 status = statusmessage(status.code, pycompat.bytestr(status)) | |
636 elif status == 200: | |
637 status = '200 Script output follows' | |
638 elif isinstance(status, int): | |
639 status = statusmessage(status) | |
640 | |
641 # Various HTTP clients (notably httplib) won't read the HTTP | |
642 # response until the HTTP request has been sent in full. If servers | |
643 # (us) send a response before the HTTP request has been fully sent, | |
644 # the connection may deadlock because neither end is reading. | |
645 # | |
646 # We work around this by "draining" the request data before | |
647 # sending any response in some conditions. | |
648 drain = False | |
649 close = False | |
650 | |
651 # If the client sent Expect: 100-continue, we assume it is smart | |
652 # enough to deal with the server sending a response before reading | |
653 # the request. (httplib doesn't do this.) | |
654 if self.env.get(r'HTTP_EXPECT', r'').lower() == r'100-continue': | |
655 pass | |
656 # Only tend to request methods that have bodies. Strictly speaking, | |
657 # we should sniff for a body. But this is fine for our existing | |
658 # WSGI applications. | |
659 elif self.env[r'REQUEST_METHOD'] not in (r'POST', r'PUT'): | |
660 pass | |
661 else: | |
662 # If we don't know how much data to read, there's no guarantee | |
663 # that we can drain the request responsibly. The WSGI | |
664 # specification only says that servers *should* ensure the | |
665 # input stream doesn't overrun the actual request. So there's | |
666 # no guarantee that reading until EOF won't corrupt the stream | |
667 # state. | |
668 if not isinstance(self.req.bodyfh, util.cappedreader): | |
669 close = True | |
670 else: | |
671 # We /could/ only drain certain HTTP response codes. But 200 | |
672 # and non-200 wire protocol responses both require draining. | |
673 # Since we have a capped reader in place for all situations | |
674 # where we drain, it is safe to read from that stream. We'll | |
675 # either do a drain or no-op if we're already at EOF. | |
676 drain = True | |
677 | |
678 if close: | |
679 self.headers.append((r'Connection', r'Close')) | |
680 | |
681 if drain: | |
682 assert isinstance(self.req.bodyfh, util.cappedreader) | |
683 while True: | |
684 chunk = self.req.bodyfh.read(32768) | |
685 if not chunk: | |
686 break | |
687 | |
688 self.server_write = self._start_response( | |
689 pycompat.sysstr(status), self.headers) | |
690 self._start_response = None | |
691 self.headers = [] | |
692 if body is not None: | |
693 self.write(body) | |
694 self.server_write = None | |
695 | |
696 def write(self, thing): | |
697 if thing: | |
698 try: | |
699 self.server_write(thing) | |
700 except socket.error as inst: | |
701 if inst[0] != errno.ECONNRESET: | |
702 raise | |
703 | |
704 def flush(self): | |
705 return None | |
706 | 605 |
707 def wsgiapplication(app_maker): | 606 def wsgiapplication(app_maker): |
708 '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir() | 607 '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir() |
709 can and should now be used as a WSGI application.''' | 608 can and should now be used as a WSGI application.''' |
710 application = app_maker() | 609 application = app_maker() |