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