File size: 13,371 Bytes
27867f1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
"""
MIT License

Copyright (C) 2023 ROCKY4546
https://github.com/rocky4546

This file is part of Cabernet

Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the “Software”), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
"""

import functools
import http
import http.client
import json
import logging
import os
import re
import requests.exceptions
import sys
import socket
import time
import traceback
import urllib
import urllib.parse
import urllib.error
import urllib3
from functools import update_wrapper


def handle_url_except(f=None, timeout=None):
    """
    timeout not currently used
    """
    if f is None:
        return functools.partial(handle_url_except, timeout=timeout)

    def wrapper_func(self, *args, **kwargs):
        ex_save = None
        # arg0 = uri, arg1=retries
        if len(args) == 0:
            arg0 = 'None'
            retries = 2
        elif len(args) == 1:
            arg0 = args[0]
            retries = 2
        else:
            arg0 = args[0]
            retries = args[1]
        i = retries
        is_done = 0
        while i > is_done:
            i -= 1
            try:
                x = f(self, *args, **kwargs)
                return x
            except UnicodeDecodeError as ex:
                ex_save = ex
                self.logger.info("UnicodeDecodeError in function {}(), retrying {} {} {}"
                                 .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0), ))
            except ConnectionRefusedError as ex:
                ex_save = ex
                self.logger.info("ConnectionRefusedError in function {}(), retrying (): {} {} {}"
                                 .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
            except ConnectionResetError as ex:
                ex_save = ex
                self.logger.info("ConnectionResetError in function {}(), retrying {} {} {}"
                                 .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))

            except requests.exceptions.InvalidURL as ex:
                ex_save = ex
                self.logger.info("InvalidURL Error in function {}(), retrying {} {} {}"
                                 .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))

            except (socket.timeout, requests.exceptions.ConnectTimeout, requests.exceptions.ReadTimeout) as ex:
                ex_save = ex
                self.logger.info("Socket Timeout Error in function {}(), retrying {} {} {}"
                                 .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))

            except requests.exceptions.ConnectionError as ex:
                ex_save = ex
                if hasattr(ex.args[0], 'reason'):
                    reason = ex.args[0].reason
                else:
                    reason = None
                if isinstance(reason, urllib3.exceptions.NewConnectionError):
                    self.logger.info("ConnectionError:ConnectionRefused in function {}(), retrying (): {} {} {}"
                                     .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
                    time.sleep(2)
                else:
                    self.logger.info("ConnectionError in function {}(), retrying (): {} {} {}"
                                     .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
            except http.client.RemoteDisconnected as ex:
                ex_save = ex
                self.logger.info('Remote Server Disconnect Error in function {}(), retrying {} {} {}'
                                 .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
            except http.client.InvalidURL as ex:
                url_tuple = urllib.parse.urlparse(arg0)
                url_list = list(url_tuple)
                url_list[2] = urllib.parse.quote(url_list[2])
                url_list[3] = urllib.parse.quote(url_list[3])
                url_list[4] = urllib.parse.quote(url_list[4])
                url_list[5] = urllib.parse.quote(url_list[5])
                new_url = urllib.parse.urlunparse(url_list)
                args_list = list(args)
                args_list[0] = new_url
                args = tuple(args_list)

                ex_save = ex
                self.logger.info('InvalidURL Error, encoding and trying again. In function {}() {} {} {}'
                                 .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))

            except (requests.exceptions.HTTPError, urllib.error.HTTPError) as ex:
                ex_save = ex
                self.logger.info("HTTPError in function {}(), retrying {} {} {}"
                                 .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0), ))
            except urllib.error.URLError as ex:
                ex_save = ex
                if isinstance(ex.reason, ConnectionRefusedError):
                    self.logger.info("URLError:ConnectionRefusedError in function {}(): {} {} {}"
                                     .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
                    count = 5
                    while count > 0:
                        try:
                            x = f(self, *args, **kwargs)
                            return x
                        except urllib.error.URLError as ex2:
                            self.logger.debug("{} URLError:ConnectionRefusedError in function {}(): {} {} {}"
                                              .format(count, f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
                            count -= 1
                            time.sleep(.5)
                        except Exception as ex3:
                            break
                else:
                    self.logger.info("URLError in function {}(), retrying (): {} {} {}"
                                     .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
            except http.client.IncompleteRead as ex:
                ex_save = ex
                self.logger.info('Partial Data Error from url received in function {}(), retrying. {} {} {}'
                                 .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
            except ValueError as ex:
                ex_save = ex
                self.logger.info('ValueError in function {}(), aborting. {} {} {}'
                                 .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
            except (requests.exceptions.ProxyError, requests.exceptions.SSLError, \
                    requests.exceptions.TooManyRedirects, requests.exceptions.InvalidHeader, \
                    requests.exceptions.InvalidProxyURL, requests.exceptions.ChunkedEncodingError, \
                    requests.exceptions.ContentDecodingError, requests.exceptions.StreamConsumedError, \
                    requests.exceptions.RetryError, requests.exceptions.UnrewindableBodyError, \
                    requests.exceptions.RequestsWarning, requests.exceptions.FileModeWarning, \
                    requests.exceptions.RequestsDependencyWarning) as ex:
                ex_save = ex
                self.logger.info('General Requests Error in function {}(), retrying. {} {} {}'
                                 .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
            except (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema) as ex:
                ex_save = ex
                self.logger.info('Missing Schema Error in function {}(), retrying. {} {} {}'
                                 .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))
            time.sleep(1.0)
        self.logger.notice('Multiple HTTP Errors, unable to get url data, skipping {}() {} {} {}'
                           .format(f.__qualname__, os.getpid(), str(ex_save), str(arg0)))

        return None

    return update_wrapper(wrapper_func, f)


def handle_json_except(f):
    def wrapper_func(self, *args, **kwargs):
        try:
            return f(self, *args, **kwargs)
        except (json.JSONDecodeError) as jsonError:
            self.logger.error("JSONError in function {}(): {}".format(f.__qualname__, str(jsonError)))
            return None

    return update_wrapper(wrapper_func, f)


class Backup:
    """
    Decorator for collecting and processing export/backup methods
    """

    backup2func = {}

    def __init__(self, *pattern):
        self.pattern = pattern

    def __call__(self, call_class_fn):
        if call_class_fn is None:
            return call_class_fn
        else:
            for p in self.pattern:
                Backup.backup2func[p] = call_class_fn
            return call_class_fn

    @classmethod
    def log_backups(cls):
        logger = logging.getLogger(__name__)
        for name in Backup.backup2func.keys():
            logger.debug('Registering BACKUP {}'.format(name))

    @classmethod
    def call_backup(cls, _name, *args, **kwargs):
        """
        Based on function, will create class instance and call
        the function with no parameters. *args are
        passed into the class constructor while **kwargs are
        passed into the instance function
        """
        if _name in Backup.backup2func:
            fn = Backup.backup2func[_name]
            module = fn.__module__
            class_fn = fn.__qualname__
            (cls_name, fn_name) = class_fn.split('.')
            cls = vars(sys.modules[module])[cls_name]
            inst = cls(*args)
            inst_fn = getattr(inst, fn_name)
            inst_fn(**kwargs)
            return True
        else:
            return False


class Restore:
    """
    Decorator for collecting and processing import/restore methods
    """

    restore2func = {}

    def __init__(self, *pattern):
        self.pattern = pattern

    def __call__(self, call_class_fn):
        if call_class_fn is None:
            return call_class_fn
        else:
            for p in self.pattern:
                Restore.restore2func[p] = call_class_fn
            return call_class_fn

    @classmethod
    def log_backups(cls):
        logger = logging.getLogger(__name__)
        for name in Restore.restore2func.keys():
            logger.debug('Registering RESTORE {}'.format(name))

    @classmethod
    def call_restore(cls, _name, *args, **kwargs):
        """
        Based on function, will create class instance and call
        the function with no parameters. *args are
        passed into the class constructor while **kwargs are
        passed into the instance function
        """
        if _name in Restore.restore2func:
            fn = Restore.restore2func[_name]
            module = fn.__module__
            class_fn = fn.__qualname__
            (cls_name, fn_name) = class_fn.split('.')
            cls = vars(sys.modules[module])[cls_name]
            inst = cls(*args)
            inst_fn = getattr(inst, fn_name)
            msg = inst_fn(**kwargs)
            return msg
        else:
            return None


class Request:
    """
    Adds urls to functions for GET and POST methods
    """

    def __init__(self):
        self.url2func = {}
        self.method = None

    def route(self, *pattern):
        def wrap(func):
            for p in pattern:
                if p.startswith('RE:'):
                    p = re.compile(p.replace('RE:', ''))
                self.url2func[p] = func
            return func

        return wrap

    def log_urls(self):
        logger = logging.getLogger(__name__)
        for name in self.url2func.keys():
            logger.debug('Registering {} URL: {}'.format(self.method, name))

    def call_url(self, _webserver, _name, *args, **kwargs):
        if _name in self.url2func:
            self.url2func[_name](_webserver, *args, **kwargs)
            return True
        else:
            for uri in self.url2func.keys():
                if type(uri) is re.Pattern:
                    if len(uri.findall(_name)) > 0:
                        self.url2func[uri](_webserver, *args, **kwargs)
                        return True
            return False


class GetRequest(Request):

    def __init__(self):
        super().__init__()
        self.method = 'GET'


class PostRequest(Request):

    def __init__(self):
        super().__init__()
        self.method = 'POST'


class FileRequest(Request):
    """
    Adds HTDOCS areas to be processed by function
    """

    def __init__(self):
        super().__init__()
        self.method = 'GET'

    def call_url(self, _webserver, _name, *args, **kwargs):
        for key in self.url2func.keys():
            if _name.startswith(key):
                self.url2func[key](_webserver, *args, **kwargs)
                return True
        return False


getrequest = GetRequest()
gettunerrequest = GetRequest()
postrequest = PostRequest()
filerequest = FileRequest()