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() |