def parse_request(self, conn: socket) -> Request: # Все чтение сокета делал через файлы, так удобнее читать построчно file = conn.makefile('rb') data = file.readline(1024*64) # Нагуглил, что хедеры обязаны быть в этой кодировке line = data.decode('iso-8859-1') line = line.strip().split() if len(line) != 3: # Ожидаем метод, цель и протокол raise e.BadRequest('Request first line have to be 3 parts') method, target, proto = line # Перенаправляем / на /index.html if target == '/': target = '/index.html' # Других скорее всего и не будет, но на всякий if proto != 'HTTP/1.1': raise e.UnexpectedProto('Unexpected HTTP version') headers = self.parse_headers(file) host = headers.get('Host') if not host or host.strip() not in [self.__host, f'{self.__host}:{self.__port}']: raise e.BadRequest('Not found') return Request(method, target, proto, headers, file)
def _cleanSearchSort(self, sort): sort = '%s:asc' % sort if ':' not in sort else sort scol, sdir = sort.lower().split(':') lookup = {} for s in self.ALLOWED_SORT: lookup[s.lower()] = s if scol not in lookup: raise exceptions.BadRequest('Unknown sort column: %s' % scol) if sdir not in ('asc', 'desc'): raise exceptions.BadRequest('Unknown sort dir: %s' % sdir) return '%s:%s' % (lookup[scol], sdir)
def handle_response(self, response, content): """Validate HTTP response """ status = response.status_code if status in (301, 302, 303, 307): raise exceptions.Redirection(response, content) elif 200 <= status <= 299: return json.loads(content) if content else {} elif status == 400: raise exceptions.BadRequest(response, content) elif status == 401: raise exceptions.UnauthorizedAccess(response, content) elif status == 403: raise exceptions.ForbiddenAccess(response, content) elif status == 404: raise exceptions.ResourceNotFound(response, content) elif status == 405: raise exceptions.MethodNotAllowed(response, content) elif status == 409: raise exceptions.ResourceConflict(response, content) elif status == 410: raise exceptions.ResourceGone(response, content) elif status == 422: raise exceptions.ResourceInvalid(response, content) elif 401 <= status <= 499: raise exceptions.ClientError(response, content) elif 500 <= status <= 599: raise exceptions.ServerError(response, content) else: raise exceptions.ConnectionError( response, content, "Unknown response code: #{response.code}")
def query(self, path, method=None, **kwargs): method = method or self.session.get url = self.buildUrl(path, includeToken=True) # If URL is empty, try refresh resources and return empty set for now if not url: util.WARN_LOG( "Empty server url, returning None and refreshing resources") plexapp.refreshResources(True) return None util.LOG('{0} {1}'.format( method.__name__.upper(), re.sub('X-Plex-Token=[^&]+', 'X-Plex-Token=****', url))) try: response = method(url, **kwargs) if response.status_code not in (200, 201): codename = http.status_codes.get(response.status_code, ['Unknown'])[0] raise exceptions.BadRequest('({0}) {1}'.format( response.status_code, codename)) data = response.text.encode('utf8') except asyncadapter.TimeoutException: util.ERROR() plexapp.refreshResources(True) return None except http.requests.ConnectionError: util.ERROR() return None return ElementTree.fromstring(data) if data else None
def clean_models(self): """ Invoked by ``validate`` Calls ``full_clean()`` on all model instances in ``request.data``. Returns: None Raises: exceptions.BadRequest: When ``full_clean`` first throws a ValidationError. The exceptions.BadRequest exception is raised at the very first time that we encounter a ValidationError. """ # TODO: Add the exclude parameter in the signature of the method. # Call full_clean with ``exclude``, so that we can exclude any models # fields we want from the validation. for element in isinstance(self.request.data, self.model) \ and [self.request.data] or self.request.data: try: element.full_clean() except ValidationError, e: # When a ValidationError exception e is raised by # ``model.clean_fields``, it has the parameter: # e.message_dict = {'field1': 'error string', # 'field2': 'error string, ...} # When it's raised by ``clean`` e has the parameter: # e.message_dict = {NON_FIELD_ERRORS: [<error string>]} raise exceptions.BadRequest(e.message_dict)
def _init(self): response = http.POST(self.INIT) if response.status_code != http.codes.created: codename = http.status_codes.get(response.status_code)[0] raise exceptions.BadRequest('({0}) {1}'.format(response.status_code, codename)) data = ElementTree.fromstring(response.text.encode('utf-8')) self.pin = data.find('code').text self.id = data.find('id').text
def query(self, path, method=None, token=None, **kwargs): method = method or http.requests.get url = self.getURL(path) util.LOG('{0} {1}'.format(method.__name__.upper(), url)) response = method(url, headers=self.headers(token), timeout=util.TIMEOUT, **kwargs) if response.status_code not in (200, 201): codename = http.status_codes.get(response.status_code)[0] raise exceptions.BadRequest('({0}) {1}'.format(response.status_code, codename)) data = response.text.encode('utf8') return ElementTree.fromstring(data) if data else None
def query(self, path, method=None, **kwargs): method = method or self.session.get url = self.buildUrl(path, includeToken=True) util.LOG('{0} {1}'.format( method.__name__.upper(), re.sub('X-Plex-Token=[^&]+', 'X-Plex-Token=****', url))) response = method(url, **kwargs) if response.status_code not in (200, 201): codename = http.status_codes.get(response.status_code, ['Unknown'])[0] raise exceptions.BadRequest('({0}) {1}'.format( response.status_code, codename)) data = response.text.encode('utf8') return ElementTree.fromstring(data) if data else None
def listChoices(self, category, libtype=None, **kwargs): """ List choices for the specified filter category. kwargs can be any of the same kwargs in self.search() to help narrow down the choices to only those that matter in your current context. """ if category in kwargs: raise exceptions.BadRequest( 'Cannot include kwarg equal to specified category: %s' % category) args = {} for subcategory, value in kwargs.items(): args[category] = self._cleanSearchFilter(subcategory, value) if libtype is not None: args['type'] = plexobjects.searchType(libtype) query = '/library/sections/%s/%s%s' % (self.key, category, util.joinArgs(args)) return plexobjects.listItems(self.server, query, bytag=True)
def _do_request(self): if self._token: self._headers['Authorization'] = 'Bearer {0}'.format(self._token) response = requests.request(self._method, url=self._url, headers=self._headers, data=self._payload) try: response = response.json() except json.decoder.JSONDecodeError: raise Exception(response.text) if 'error' in response: raise Exception('Error: {0}. {1}.'.format(response.get('error'), response.get('message'))) elif response == "{'message': ''}": raise exceptions.BadRequest() return response
def _cleanSearchFilter(self, category, value, libtype=None): # check a few things before we begin if category not in self.ALLOWED_FILTERS: raise exceptions.BadRequest('Unknown filter category: %s' % category) if category in self.BOOLEAN_FILTERS: return '1' if value else '0' if not isinstance(value, (list, tuple)): value = [value] # convert list of values to list of keys or ids result = set() choices = self.listChoices(category, libtype) lookup = {} for c in choices: lookup[c.title.lower()] = c.key allowed = set(c.key for c in choices) for item in value: item = str( item.id if isinstance(item, media.MediaTag) else item).lower() # find most logical choice(s) to use in url if item in allowed: result.add(item) continue if item in lookup: result.add(lookup[item]) continue matches = [k for t, k in lookup.items() if item in t] if matches: map(result.add, matches) continue # nothing matched; use raw item value util.LOG( 'Filter value not listed, using raw item value: {0}'.format( item)) result.add(item) return ','.join(result)
def _request(self, op, url, data=None, blocking=True, retry_after_auth=False): retried = 0 while True: try: if (op.upper() == 'GET'): (status, content) = self._http_get(url, headers=self._headers, query_params=data) elif (op.upper() == 'POST'): (status, content) = self._http_post(url, body=data, headers=self._headers) elif (op.upper() == 'DELETE'): (status, content) = self._http_delete(url, body=data, headers=self._headers) elif (op.upper() == 'PUT'): (status, content) = self._http_put(url, body=data, headers=self._headers) else: raise ValueError except ConnectionError as e: if not blocking: raise retried += 1 time.sleep(1) self._create_api_server_session() continue if status == 200: return content # Exception Response, see if it can be resolved if status == 401: if retry_after_auth: raise exceptions.AuthenticationFailed(content) else: self._headers = self._authenticate(content, self._headers) # Recursive call after authentication (max 1 level) content = self._request(op, url, data=data, retry_after_auth=True) return content elif status == 404: raise exceptions.NoIdError( 'Error: oper %s url %s body %s response %s' % (op, url, data, content)) elif status == 403: raise exceptions.PermissionDenied(content) elif status == 409: raise exceptions.RefsExistError(content) elif status == 504: # Request sent to API server, but no response came within lb timeout raise exceptions.TimeOutError('Gateway Timeout 504') elif status in [502, 503]: # 502: API server died after accepting request, so retry # 503: no API server available even before sending the request if not blocking: raise exceptions.ServiceUnavailableError( 'Service Unavailable Timeout %d' % status) retried += 1 time.sleep(1) continue elif status == 400: raise exceptions.BadRequest(status, content) else: # Unknown Error raise exceptions.HttpError(status, content)