예제 #1
0
    def respond_to_client(self, response: HttpResponse):
        with wrap_context('responding to client'):
            self.send_response_only(response.status_code)

            if has_header(response.headers, 'Content-Encoding'):
                del response.headers['Content-Encoding']
                if self.config.verbose >= 2:
                    log.debug('removing Content-Encoding header')

            if not has_header(response.headers, 'Content-Length') and \
                    not has_header(response.headers, 'Transfer-Encoding') and response.content:
                response.headers['Content-Length'] = str(len(response.content))
                log.warn('adding missing Content-Length header')

            if has_header(response.headers, 'Content-Length') and has_header(
                    response.headers, 'Transfer-Encoding'):
                del response.headers['Content-Length']
                log.warn(
                    'removed Content-Length header conflicting with Transfer-Encoding'
                )

            for name, value in response.headers.items():
                self.send_header(name, value)
            self.end_headers()

            if self.config.allow_chunking and response.headers.get(
                    'Transfer-Encoding') == 'chunked':
                send_chunked_response(self.wfile, response.content)
            else:
                self.wfile.write(response.content)
            self.close_connection = True
            if self.config.verbose >= 2:
                log.debug('> response sent',
                          client_addr=self.client_address[0],
                          client_port=self.client_address[1])
예제 #2
0
def proxy_request(request: HttpRequest, default_url: str, timeout: int,
                  verbose: int) -> HttpResponse:
    dst_url = request.dst_url if request.dst_url else default_url
    with logerr():
        with wrap_context('proxying to URL',
                          dst_url=dst_url,
                          path=request.path,
                          content=request.content):
            url = f'{dst_url}{request.path}'
            if verbose:
                log.debug(f'>> proxying to', url=url)
            response = requests.request(request.method,
                                        url,
                                        verify=False,
                                        allow_redirects=False,
                                        stream=False,
                                        timeout=timeout,
                                        headers=request.headers,
                                        data=request.content)
            content: bytes = response.content
            http_response = HttpResponse(status_code=response.status_code,
                                         headers=dict(response.headers),
                                         content=content)
            return http_response.log('<< received', verbose)

    # Bad Gateway response
    error_msg = f'Proxying failed: {dst_url}'
    return HttpResponse(status_code=502,
                        headers={
                            'X-Man-Error': 'proxying failed',
                        }).set_content(error_msg)
예제 #3
0
    def generate_response(self, request_0: HttpRequest) -> HttpResponse:
        with wrap_context('generating response'):
            request = request_0.transform(self.extensions.transform_request)
            if request != request_0 and self.config.verbose >= 2:
                log.debug('request transformed')

            immediate_reponse = self.find_immediate_response(request)
            if immediate_reponse:
                return immediate_reponse.log('> immediate response',
                                             self.config.verbose)

            self.cache.clear_old()
            if self.cache.has_cached_response(request):
                return self.cache.replay_response(request).log(
                    '> Cache: returning cached response', self.config.verbose)

            if self.config.replay and self.config.verbose:
                log.warn('request not found in cache', path=request.path)
            response: HttpResponse = proxy_request(
                request,
                default_url=self.config.dst_url,
                timeout=self.config.timeout,
                verbose=self.config.verbose)

            if self.cache.saving_enabled(request, response):
                self.cache.save_response(request, response)

            return response
예제 #4
0
 def replay_response(self, request: HttpRequest) -> HttpResponse:
     request_hash = self._request_hash(request)
     if self.config.replay_throttle:
         if self.config.verbose:
             log.debug('Cache: Throttled response')
         return too_many_requests_response
     return self.cache[request_hash].response
예제 #5
0
def transform_response(request: HttpRequest,
                       response: HttpResponse) -> HttpResponse:
    """Transforms each Response before sending it."""
    if request.path.startswith('/some/api'):
        log.debug('Found Ya', path=request.path)
        response = response.set_content('{"payload": "anythingyouwish"}"')
    return response
예제 #6
0
def transform_request(request: HttpRequest) -> HttpRequest:
    """Transforms each incoming Request before further processing (caching, forwarding)."""
    match = re.match(r'^/some/path/(.+?)(/[a-z]+)(/.*)', request.path)
    if match:
        request.path = match.expand(r'\3')
        log.debug('request path transformed', path=request.path)
    return request
예제 #7
0
def calculate_volume(song: AudioSegment) -> float:
    volume = song.max_dBFS
    if volume < -0.1:
        return volume

    lower_vol = 10
    tmp_clip = '.anti_clip.mp3'
    log.debug('detecting clipping...')
    lowered = song.apply_gain(-lower_vol)
    lowered.export(tmp_clip, format="mp3")
    lowered = AudioSegment.from_mp3(tmp_clip)
    os.remove(tmp_clip)
    return lowered.max_dBFS + lower_vol
예제 #8
0
 def clear_old(self):
     if not self.config.replay_clear_cache:
         return
     to_remove = []
     now_timestamp: float = now_seconds()
     for request_hash, entry in self.cache.items():
         if now_timestamp - entry.request.timestamp > self.config.replay_clear_cache_seconds:
             to_remove.append(request_hash)
     for request_hash in to_remove:
         del self.cache[request_hash]
     if to_remove:
         if self.config.verbose:
             log.debug('Cache: cleared old cache entries',
                       removed=len(to_remove))
예제 #9
0
def bulk_rename(
    pattern: str,
    replacement_pattern: Optional[str],
    testing: bool = True,
    full: bool = False,
    recursive: bool = False,
    padding: int = 0,
) -> List[Match]:
    """
    Rename (or match) multiple files at once
    :param pattern: regex pattern to match filenames
    :param replacement: replacement regex pattern for renamed files. 
    Use \\1 syntax to make use of matched groups
    :param testing: True - just testing replacement pattern, False - do actual renaming files
    :param full: whether to enforce matching full filename against pattern
    :param recursive: whether to search directories recursively
    :param padding: applies padding with zeros with given length on matched numerical groups
    """
    log.debug('matching regex pattern',
              pattern=pattern,
              replacement=replacement_pattern,
              testing_mode=testing,
              full_match=full,
              recursive=recursive,
              padding=padding)

    matches: List[Match] = match_files(Path(), pattern, replacement_pattern,
                                       recursive, full, padding)
    for match in matches:
        match.log_info(testing)

    if replacement_pattern:
        find_duplicates(matches)

    if testing:
        if matches:
            log.info('files matched', count=len(matches))
        else:
            log.info('no files matched', count=len(matches))
    elif replacement_pattern:
        rename_matches(matches)
        if matches:
            log.info('files renamed', count=len(matches))
        else:
            log.info('no files renamed', count=len(matches))
    else:
        raise RuntimeError('replacement pattern is required for renaming')

    return matches
예제 #10
0
 def save_response(self, request: HttpRequest, response: HttpResponse):
     request_hash = self._request_hash(request)
     if request_hash not in self.cache:
         self.cache[request_hash] = CacheEntry(request, response)
         if self.config.record and self.config.record_file:
             serializable = list(self.cache.values())
             txt = json.dumps(serializable,
                              sort_keys=True,
                              indent=4,
                              cls=EnhancedJSONEncoder)
             Path(self.config.record_file).write_text(txt)
         ctx = {}
         if self.config.verbose:
             ctx['traits'] = str(self._request_traits(request))
         log.debug(f'+ Cache: new request-response recorded',
                   hash=request_hash,
                   total_entries=len(self.cache),
                   **ctx)
예제 #11
0
def wrap_shell(cmd):
    cmd = cmd.strip()
    log.debug(f'> {cmd}')
    if not settings.DRY_RUN:
        shell(cmd)