@@ -67,6 +67,13 @@ resolve_full_httpd () {
6767 httpd_only=" ${httpd%% * } " # cut on first space
6868 return
6969 ;;
70+ * python* )
71+ # server is started by running via generated gitweb.py in
72+ # $fqgitdir/gitweb
73+ full_httpd=" $fqgitdir /gitweb/gitweb.py"
74+ httpd_only=" ${httpd%% * } " # cut on first space
75+ return
76+ ;;
7077 esac
7178
7279 httpd_only=" $( echo $httpd | cut -f1 -d' ' ) "
@@ -110,7 +117,7 @@ start_httpd () {
110117
111118 # don't quote $full_httpd, there can be arguments to it (-f)
112119 case " $httpd " in
113- * mongoose* |* plackup* )
120+ * mongoose* |* plackup* | * python * )
114121 # These servers don't have a daemon mode so we'll have to fork it
115122 $full_httpd " $conf " &
116123 # Save the pid before doing anything else (we'll print it later)
@@ -595,6 +602,121 @@ EOF
595602 rm -f " $conf "
596603}
597604
605+ python_conf () {
606+ # Python's builtin http.server and its CGI support is very limited.
607+ # CGI handler is capable of running CGI script only from inside a directory.
608+ # Trying to set cgi_directories=["/"] will add double slash to SCRIPT_NAME
609+ # and that in turn breaks gitweb's relative link generation.
610+
611+ # create a simple web root where $fqgitdir/gitweb/$httpd_only is our root
612+ mkdir -p " $fqgitdir /gitweb/$httpd_only /cgi-bin"
613+ # Python http.server follows the symlinks
614+ ln -sf " $root /gitweb.cgi" " $fqgitdir /gitweb/$httpd_only /cgi-bin/gitweb.cgi"
615+ ln -sf " $root /static" " $fqgitdir /gitweb/$httpd_only /"
616+
617+ # generate a standalone 'python http.server' script in $fqgitdir/gitweb
618+ # This asumes that python is in user's $PATH
619+ # This script is Python 2 and 3 compatible
620+ cat > " $fqgitdir /gitweb/gitweb.py" << EOF
621+ #!/usr/bin/env python
622+ import os
623+ import sys
624+
625+ # Open log file in line buffering mode
626+ accesslogfile = open("$fqgitdir /gitweb/access.log", 'a', buffering=1)
627+ errorlogfile = open("$fqgitdir /gitweb/error.log", 'a', buffering=1)
628+
629+ # and replace our stdout and stderr with log files
630+ # also do a lowlevel duplicate of the logfile file descriptors so that
631+ # our CGI child process writes any stderr warning also to the log file
632+ _orig_stdout_fd = sys.stdout.fileno()
633+ sys.stdout.close()
634+ os.dup2(accesslogfile.fileno(), _orig_stdout_fd)
635+ sys.stdout = accesslogfile
636+
637+ _orig_stderr_fd = sys.stderr.fileno()
638+ sys.stderr.close()
639+ os.dup2(errorlogfile.fileno(), _orig_stderr_fd)
640+ sys.stderr = errorlogfile
641+
642+ from functools import partial
643+
644+ if sys.version_info < (3, 0): # Python 2
645+ from CGIHTTPServer import CGIHTTPRequestHandler
646+ from BaseHTTPServer import HTTPServer as ServerClass
647+ else: # Python 3
648+ from http.server import CGIHTTPRequestHandler
649+ from http.server import HTTPServer as ServerClass
650+
651+
652+ # Those environment variables will be passed to the cgi script
653+ os.environ.update({
654+ "GIT_EXEC_PATH": "$GIT_EXEC_PATH ",
655+ "GIT_DIR": "$GIT_DIR ",
656+ "GITWEB_CONFIG": "$GITWEB_CONFIG "
657+ })
658+
659+
660+ class GitWebRequestHandler(CGIHTTPRequestHandler):
661+
662+ def log_message(self, format, *args):
663+ # Write access logs to stdout
664+ sys.stdout.write("%s - - [%s] %s\n" %
665+ (self.address_string(),
666+ self.log_date_time_string(),
667+ format%args))
668+
669+ def do_HEAD(self):
670+ self.redirect_path()
671+ CGIHTTPRequestHandler.do_HEAD(self)
672+
673+ def do_GET(self):
674+ if self.path == "/":
675+ self.send_response(303, "See Other")
676+ self.send_header("Location", "/cgi-bin/gitweb.cgi")
677+ self.end_headers()
678+ return
679+ self.redirect_path()
680+ CGIHTTPRequestHandler.do_GET(self)
681+
682+ def do_POST(self):
683+ self.redirect_path()
684+ CGIHTTPRequestHandler.do_POST(self)
685+
686+ # rewrite path of every request that is not gitweb.cgi to out of cgi-bin
687+ def redirect_path(self):
688+ if not self.path.startswith("/cgi-bin/gitweb.cgi"):
689+ self.path = self.path.replace("/cgi-bin/", "/")
690+
691+ # gitweb.cgi is the only thing that is ever going to be run here.
692+ # Ignore everything else
693+ def is_cgi(self):
694+ result = False
695+ if self.path.startswith('/cgi-bin/gitweb.cgi'):
696+ result = CGIHTTPRequestHandler.is_cgi(self)
697+ return result
698+
699+
700+ bind = "127.0.0.1"
701+ if "$local " == "true":
702+ bind = "0.0.0.0"
703+
704+ # Set our http root directory
705+ # This is a work around for a missing directory argument in older Python versions
706+ # as this was added to SimpleHTTPRequestHandler in Python 3.7
707+ os.chdir("$fqgitdir /gitweb/$httpd_only /")
708+
709+ GitWebRequestHandler.protocol_version = "HTTP/1.0"
710+ httpd = ServerClass((bind, $port ), GitWebRequestHandler)
711+
712+ sa = httpd.socket.getsockname()
713+ print("Serving HTTP on", sa[0], "port", sa[1], "...")
714+ httpd.serve_forever()
715+ EOF
716+
717+ chmod a+x " $fqgitdir /gitweb/gitweb.py"
718+ }
719+
598720gitweb_conf () {
599721 cat > " $fqgitdir /gitweb/gitweb_config.perl" << EOF
600722#!@@PERL@@
@@ -623,6 +745,9 @@ configure_httpd() {
623745 * plackup* )
624746 plackup_conf
625747 ;;
748+ * python* )
749+ python_conf
750+ ;;
626751 * )
627752 echo " Unknown httpd specified: $httpd "
628753 exit 1
0 commit comments