Пример #1
0
    def backend(self, request, environ):
        """
        WSGI Response producer for HTTP POST Git Smart HTTP requests.
        Reads commands and data from HTTP POST's body.
        returns an iterator obj with contents of git command's
        response to stdout
        """
        git_command = self._get_fixedpath(request.path_info)
        if git_command not in self.commands:
            log.debug('command %s not allowed' % git_command)
            return exc.HTTPMethodNotAllowed()

        if 'CONTENT_LENGTH' in environ:
            inputstream = FileWrapper(environ['wsgi.input'],
                                      request.content_length)
        else:
            inputstream = environ['wsgi.input']

        try:
            gitenv = os.environ
            # forget all configs
            gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
            opts = dict(env=gitenv, cwd=os.getcwd())
            cmd = r'git %s --stateless-rpc "%s"' % (git_command[4:],
                                                    self.content_path),
            log.debug('handling cmd %s' % cmd)
            out = subprocessio.SubprocessIOChunker(cmd,
                                                   inputstream=inputstream,
                                                   **opts)
        except EnvironmentError, e:
            log.error(traceback.format_exc())
            raise exc.HTTPExpectationFailed()
Пример #2
0
    def inforefs(self, request, environ):
        """WSGI Response producer for HTTP GET Git Smart HTTP /info/refs request."""

        git_command = request.GET['service']
        if git_command not in self.commands:
            return exc.HTTPMethodNotAllowed()

        # note to self:
        # please, resist the urge to add '\n' to git capture and increment line count by 1.
        # The code in Git client not only does NOT need '\n', but actually blows up
        # if you sprinkle "flush" (0000) as "0001\n".
        # It reads binary, per number of bytes specified.
        # if you do add '\n' as part of data, count it.
        smart_server_advert = '# service=%s' % git_command
        try:
            out = subprocessio.SubprocessIOChunker(
                r'git %s --stateless-rpc --advertise-refs "%s"' %
                (git_command[4:], self.content_path),
                starting_values=[
                    str(
                        hex(len(smart_server_advert) + 4)[2:].rjust(4, '0') +
                        smart_server_advert + '0000')
                ])
        except EnvironmentError, e:
            raise exc.HTTPExpectationFailed()
Пример #3
0
    def backend(self, request, environ):
        """
        WSGI Response producer for HTTP POST Git Smart HTTP requests.
        Reads commands and data from HTTP POST's body.
        returns an iterator obj with contents of git command's response to stdout
        """

        git_command = request.path_info.strip('/')
        if git_command not in self.commands:
            return exc.HTTPMethodNotAllowed()

        if 'CONTENT_LENGTH' in environ:
            inputstream = FileWrapper(environ['wsgi.input'], request.content_length)
        else:
            inputstream = environ['wsgi.input']

        try:
            out = subprocessio.SubprocessIOChunker(
                r'git %s --stateless-rpc "%s"' % (git_command[4:], self.content_path),
                inputstream = inputstream
                )
        except EnvironmentError as e:
            log.exception(e)
            raise exc.HTTPExpectationFailed()

        if git_command in ['git-receive-pack']:
            # updating refs manually after each push. Needed for pre-1.7.0.4 git clients using regular HTTP mode.
            subprocess.call('git --git-dir "%s" update-server-info' % self.content_path, shell=True)

        resp = Response()
        resp.content_type = 'application/x-%s-result' % git_command.encode('utf8')
        resp.app_iter = out
        return resp
Пример #4
0
    def inforefs(self, request, environ):
        """
        WSGI Response producer for HTTP GET Git Smart
        HTTP /info/refs request.
        """

        git_command = request.GET.get('service')
        if git_command not in self.commands:
            log.debug('command %s not allowed' % git_command)
            return exc.HTTPMethodNotAllowed()

        # note to self:
        # please, resist the urge to add '\n' to git capture and increment
        # line count by 1.
        # The code in Git client not only does NOT need '\n', but actually
        # blows up if you sprinkle "flush" (0000) as "0001\n".
        # It reads binary, per number of bytes specified.
        # if you do add '\n' as part of data, count it.
        server_advert = '# service=%s' % git_command
        packet_len = str(hex(len(server_advert) + 4)[2:].rjust(4, '0')).lower()
        _git_path = rhodecode.CONFIG.get('git_path', 'git')
        try:
            out = subprocessio.SubprocessIOChunker(
                r'%s %s --stateless-rpc --advertise-refs "%s"' %
                (_git_path, git_command[4:], self.content_path),
                starting_values=[packet_len + server_advert + '0000'])
        except EnvironmentError, e:
            log.error(traceback.format_exc())
            raise exc.HTTPExpectationFailed()
Пример #5
0
    def complete(self):
        """Attempts to mark the post or comment entity completed.
    """
        entity = self.get_entity()

        if entity is None:
            raise exc.HTTPExpectationFailed('entity disappeared!')
        elif entity.status == 'complete':
            # let this response return 200 and finish
            logging.warning(
                'post/comment stolen and finished. did my lease expire?')
            return
        elif entity.status == 'new':
            raise exc.HTTPExpectationFailed(
                'post/comment went backward from processing to new!')

        assert entity.status == 'processing'
        entity.status = 'complete'
        entity.save()
Пример #6
0
def code2exception(code, detail):
    """Transforms a code + detail into a WebOb exception"""
    if code == 400:
        return exc.HTTPBadRequest(detail)
    if code == 401:
        return exc.HTTPUnauthorized(detail)
    if code == 402:
        return exc.HTTPPaymentRequired(detail)
    if code == 403:
        return exc.HTTPForbidden(detail)
    if code == 404:
        return exc.HTTPNotFound(detail)
    if code == 405:
        return exc.HTTPMethodNotAllowed(detail)
    if code == 406:
        return exc.HTTPNotAcceptable(detail)
    if code == 407:
        return exc.HTTPProxyAuthenticationRequired(detail)
    if code == 408:
        return exc.HTTPRequestTimeout(detail)
    if code == 409:
        return exc.HTTPConflict(detail)
    if code == 410:
        return exc.HTTPGone(detail)
    if code == 411:
        return exc.HTTPLengthRequired(detail)
    if code == 412:
        return exc.HTTPPreconditionFailed(detail)
    if code == 413:
        return exc.HTTPRequestEntityTooLarge(detail)
    if code == 414:
        return exc.HTTPRequestURITooLong(detail)
    if code == 415:
        return exc.HTTPUnsupportedMediaType(detail)
    if code == 416:
        return exc.HTTPRequestRangeNotSatisfiable(detail)
    if code == 417:
        return exc.HTTPExpectationFailed(detail)
    if code == 500:
        return exc.HTTPInternalServerError(detail)
    if code == 501:
        return exc.HTTPNotImplemented(detail)
    if code == 502:
        return exc.HTTPBadGateway(detail)
    if code == 503:
        return exc.HTTPServiceUnavailable(detail)
    if code == 504:
        return exc.HTTPGatewayTimeout(detail)
    if code == 505:
        return exc.HTTPVersionNotSupported(detail)

    raise NotImplementedError(code)
Пример #7
0
    def backend(self, req, environ):
        """
        WSGI Response producer for HTTP POST Git Smart HTTP requests.
        Reads commands and data from HTTP POST's body.
        returns an iterator obj with contents of git command's
        response to stdout
        """
        _git_path = kallithea.CONFIG.get('git_path', 'git')
        git_command = self._get_fixedpath(req.path_info)
        if git_command not in self.commands:
            log.debug('command %s not allowed', git_command)
            return exc.HTTPMethodNotAllowed()

        if 'CONTENT_LENGTH' in environ:
            inputstream = FileWrapper(environ['wsgi.input'],
                                      req.content_length)
        else:
            inputstream = environ['wsgi.input']

        gitenv = dict(os.environ)
        # forget all configs
        gitenv['GIT_CONFIG_NOGLOBAL'] = '1'
        cmd = [
            _git_path, git_command[4:], '--stateless-rpc', self.content_path
        ]
        log.debug('handling cmd %s', cmd)
        try:
            out = subprocessio.SubprocessIOChunker(
                cmd,
                inputstream=inputstream,
                env=gitenv,
                cwd=self.content_path,
            )
        except EnvironmentError as e:
            log.error(traceback.format_exc())
            raise exc.HTTPExpectationFailed()

        if git_command in ['git-receive-pack']:
            # updating refs manually after each push.
            # Needed for pre-1.7.0.4 git clients using regular HTTP mode.
            from kallithea.lib.vcs import get_repo
            from dulwich.server import update_server_info
            repo = get_repo(self.content_path)
            if repo:
                update_server_info(repo._repo)

        resp = Response()
        resp.content_type = 'application/x-%s-result' % git_command.encode(
            'utf-8')
        resp.charset = None
        resp.app_iter = out
        return resp
Пример #8
0
    def inforefs(self, request, unused_environ):
        """
        WSGI Response producer for HTTP GET Git Smart
        HTTP /info/refs request.
        """

        git_command = request.GET.get('service')
        if git_command not in self.commands:
            log.debug('command %s not allowed', git_command)
            return exc.HTTPForbidden()

        # please, resist the urge to add '\n' to git capture and increment
        # line count by 1.
        # by git docs: Documentation/technical/http-protocol.txt#L214 \n is
        # a part of protocol.
        # The code in Git client not only does NOT need '\n', but actually
        # blows up if you sprinkle "flush" (0000) as "0001\n".
        # It reads binary, per number of bytes specified.
        # if you do add '\n' as part of data, count it.
        server_advert = '# service=%s\n' % git_command
        packet_len = str(hex(len(server_advert) + 4)[2:].rjust(4, '0')).lower()
        try:
            gitenv = dict(os.environ)
            # forget all configs
            gitenv['RC_SCM_DATA'] = json.dumps(self.extras)
            command = [
                self.git_path, git_command[4:], '--stateless-rpc',
                '--advertise-refs', self.content_path
            ]
            out = subprocessio.SubprocessIOChunker(
                command,
                env=gitenv,
                starting_values=[packet_len + server_advert + '0000'],
                shell=False)
        except EnvironmentError:
            log.exception('Error processing command')
            raise exc.HTTPExpectationFailed()

        resp = Response()
        resp.content_type = 'application/x-%s-advertisement' % str(git_command)
        resp.charset = None
        resp.app_iter = out

        return resp
Пример #9
0
    def inforefs(self, req, environ):
        """
        WSGI Response producer for HTTP GET Git Smart
        HTTP /info/refs request.
        """

        git_command = req.GET.get('service')
        if git_command not in self.commands:
            log.debug('command %s not allowed', git_command)
            return exc.HTTPMethodNotAllowed()

        # From Documentation/technical/http-protocol.txt shipped with Git:
        #
        # Clients MUST verify the first pkt-line is `# service=$servicename`.
        # Servers MUST set $servicename to be the request parameter value.
        # Servers SHOULD include an LF at the end of this line.
        # Clients MUST ignore an LF at the end of the line.
        #
        #  smart_reply     =  PKT-LINE("# service=$servicename" LF)
        #                     ref_list
        #                     "0000"
        server_advert = '# service=%s\n' % git_command
        packet_len = hex(len(server_advert) + 4)[2:].rjust(4, '0').lower()
        _git_path = kallithea.CONFIG.get('git_path', 'git')
        cmd = [
            _git_path, git_command[4:], '--stateless-rpc', '--advertise-refs',
            self.content_path
        ]
        log.debug('handling cmd %s', cmd)
        try:
            out = subprocessio.SubprocessIOChunker(
                cmd,
                starting_values=[
                    ascii_bytes(packet_len + server_advert + '0000')
                ])
        except EnvironmentError as e:
            log.error(traceback.format_exc())
            raise exc.HTTPExpectationFailed()
        resp = Response()
        resp.content_type = 'application/x-%s-advertisement' % str(git_command)
        resp.charset = None
        resp.app_iter = out
        return resp
Пример #10
0
    def lease(self):
        """Attempts to acquire and lease the post or comment entity.

    Returns the entity on success, otherwise None.
    """
        entity = self.get_entity()

        if entity is None:
            raise exc.HTTPExpectationFailed('entity not found!')
        elif entity.status == 'complete':
            # let this response return 200 and finish
            logging.warning('duplicate task already propagated post/comment')
        elif entity.status == 'processing' and NOW_FN() < entity.leased_until:
            raise exc.HTTPConflict('duplicate task is currently processing!')
        else:
            assert entity.status in ('new', 'processing')
            entity.status = 'processing'
            entity.leased_until = NOW_FN() + self.LEASE_LENGTH
            entity.save()
            return entity
Пример #11
0
    def backend(self, request, environ):
        """
        WSGI Response producer for HTTP POST Git Smart HTTP requests.
        Reads commands and data from HTTP POST's body.
        returns an iterator obj with contents of git command's response to stdout
        """

        git_command = request.path_info.strip('/')
        if git_command not in self.commands:
            return exc.HTTPMethodNotAllowed()

        if 'CONTENT_LENGTH' in environ:
            inputstream = FileWrapper(environ['wsgi.input'],
                                      request.content_length)
        else:
            inputstream = environ['wsgi.input']

        try:
            out = subprocessio.SubprocessIOChunker(
                r'git %s --stateless-rpc "%s"' %
                (git_command[4:], self.content_path),
                inputstream=inputstream)
        except EnvironmentError, e:
            raise exc.HTTPExpectationFailed()