示例#1
0
def send_file_data(
    request_handler: tornado.web.RequestHandler,
    inpath: str,
    range_pair_list=None,
):
    with open(inpath, 'rb') as infile:
        if range_pair_list is not None:
            total_number_of_chunks = 0

            for range_pair in range_pair_list:
                remaining_bytes = range_pair[1] - range_pair[0]
                infile.seek(range_pair[0])

                number_of_chunks = int(
                    math.ceil(remaining_bytes / RESPONSE_FILE_CHUNKS_SIZE))
                total_number_of_chunks += number_of_chunks

            pbar = tqdm.tqdm(total=total_number_of_chunks)
            for range_pair in range_pair_list:
                remaining_bytes = range_pair[1] - range_pair[0]
                infile.seek(range_pair[0])

                number_of_chunks = int(
                    math.ceil(remaining_bytes / RESPONSE_FILE_CHUNKS_SIZE))
                for i in range(number_of_chunks):
                    read_data_size = int(
                        min(RESPONSE_FILE_CHUNKS_SIZE, remaining_bytes))
                    data = infile.read(read_data_size)
                    request_handler.write(data)
                    remaining_bytes -= read_data_size

        else:
            # send whole file
            # the content size header and status code should have already been set before calling this function
            filesize = os.stat(inpath).st_size
            number_of_chunks = int(
                math.ceil(filesize / RESPONSE_FILE_CHUNKS_SIZE))
            pbar = tqdm.tqdm(range(number_of_chunks))
            remote_address = request_handler.request.connection.stream.socket.getpeername(
            )
            description = f'{inpath} > {remote_address}'
            pbar.set_description(description)
            for _ in pbar:
                data = infile.read(RESPONSE_FILE_CHUNKS_SIZE)
                request_handler.write(data)
示例#2
0
 def log_request(self, handler: tornado.web.RequestHandler) -> None:
     status_code = handler.get_status()
     if not self.debug and status_code in [200, 204, 206, 304]:
         # don't log successful requests in release mode
         return
     if status_code < 400:
         log_method = access_log.info
     elif status_code < 500:
         log_method = access_log.warning
     else:
         log_method = access_log.error
     request_time = 1000.0 * handler.request.request_time()
     user = handler.current_user
     username = "******"
     if user is not None and 'username' in user:
         username = user['username']
     log_method(f"{status_code} {handler._request_summary()} "
                f"[{username}] {request_time:.2f}ms")
示例#3
0
def extract_parameters(handler: tornado.web.RequestHandler):
    arg1 = handler.get_query_argument("arg1", default="")
    arg2 = handler.get_query_argument("arg2", default="")
    arg3 = handler.get_query_argument("arg3", default="")
    return arg1, arg2, arg3
示例#4
0
def extract_parameters(handler: tornado.web.RequestHandler):
    vertex1 = str(handler.get_query_argument("vertex1", default=None))
    vertex2 = str(handler.get_query_argument("vertex2", default=None))
    key = str(handler.get_query_argument("key", default=None))
    weight = str(handler.get_query_argument("weight", default=None))
    return vertex1, vertex2, key, weight
示例#5
0
def handle_sort_request(
    field_name: str,
    request_handler: tornado.web.RequestHandler,
):
    pbar = tqdm.tqdm(GAME_INFO_LIST)
    pbar.set_description(f'checking {field_name}')
    is_invalid_field = False
    for game_info_dict in pbar:
        if field_name not in game_info_dict:
            is_invalid_field = True
            break

    if is_invalid_field:
        request_handler.set_status(400)
        request_handler.set_header('Content-Type', 'application/json')
        response_obj = {
            'message': 'invalid field name',
            'field_name': field_name,
        }

        response_str = json.dumps(response_obj)
        request_handler.write(response_str)
        return

    if len(GAME_INFO_LIST) == 0:
        request_handler.set_status(400)
        request_handler.set_header('Content-Type', 'application/json')
        response_obj = {
            'message': 'empty game list',
        }

        response_str = json.dumps(response_obj)
        request_handler.write(response_str)
        return

    if type(GAME_INFO_LIST[0][field_name]) not in [
            int, float, str, bytes, bool
    ]:
        request_handler.set_status(400)
        request_handler.set_header('Content-Type', 'application/json')
        response_obj = {
            'message': 'invalid field type',
            'field_name': field_name,
        }

        response_str = json.dumps(response_obj)
        request_handler.write(response_str)
        return

    # sort
    start_time_ns = time.perf_counter_ns()
    GAME_INFO_LIST.sort(key=lambda x: x[field_name])
    end_time_ns = time.perf_counter_ns()
    taken_time_ns = end_time_ns - start_time_ns

    response_obj = {
        'message': 'sorted',
        'field_name': field_name,
        'taken_time_ns': taken_time_ns,
    }

    response_str = json.dumps(response_obj)
    request_handler.set_status(200)
    request_handler.set_header('Content-Type', 'application/json')
    request_handler.write(response_str)
示例#6
0
def handle_game_binaries_request(
    quoted_url: str,
    request_handler: tornado.web.RequestHandler,
):
    # if the request header contains 'Last-Modified', 'If-Modified-Since' and 'If-Unmodified-Since' return 304 Not Modified

    for key in request_handler.request.headers:
        if key.lower() in ['if-modified-since', 'if-unmodified-since']:
            request_handler.set_status(304)
            return

    url = urllib.parse.unquote(quoted_url)
    if url not in GAME_BINARY_URL_INFO_DICT.keys():
        # not found
        request_handler.set_status(404)
        request_handler.set_header('Content-Type', 'application/json')
        response_obj = {
            'message': 'this url is not in cache',
            'url': url,
        }

        response_str = json.dumps(response_obj)
        request_handler.write(response_str)

        return True

    body_content_cache_key = GAME_BINARY_URL_INFO_DICT[url]

    content_bs = cacherequests.get_body_content(body_content_cache_key)
    if content_bs is None:
        request_handler.set_status(404)
        request_handler.set_header('Content-Type', 'application/json')
        response_obj = {
            'message': 'content is no longer in cache',
            'url': url,
        }

        response_str = json.dumps(response_obj)
        request_handler.write(response_str)
        return True

    if len(content_bs) == 0:
        request_handler.set_status(404)
        request_handler.set_header('Content-Type', 'application/json')
        response_obj = {
            'message': 'content is empty',
            'url': url,
        }

        response_str = json.dumps(response_obj)
        request_handler.write(response_str)
        return True

    # I didn't store the response header, so we don't have the actual value

    request_handler.set_status(200)
    request_handler.set_header('Last-Modified', DEFAULT_LAST_MODIFIED_VALUE)
    request_handler.set_header('Content-Type', 'application/octet-stream')
    request_handler.set_header('Content-Length', str(len(content_bs)))
    request_handler.write(content_bs)
    return True
示例#7
0
def handle_image_request(
    quoted_image_url: str,
    request_handler: tornado.web.RequestHandler,
):
    # if the request header contains 'Last-Modified', 'If-Modified-Since' and 'If-Unmodified-Since' return 304 Not Modified

    for key in request_handler.request.headers:
        if key.lower() in ['if-modified-since', 'if-unmodified-since']:
            request_handler.set_status(304)
            return

    image_url = urllib.parse.unquote(quoted_image_url)
    if image_url not in IMAGE_URL_INFO_DICT.keys():
        # not found
        request_handler.set_status(404)
        request_handler.set_header('Content-Type', 'application/json')
        response_obj = {
            'message': 'this image url is not in cache',
            'path': image_url,
        }

        response_str = json.dumps(response_obj)
        request_handler.write(response_str)

        return

    body_content_cache_key = IMAGE_URL_INFO_DICT[image_url]

    image_content_bs = cacherequests.get_body_content(body_content_cache_key)
    if image_content_bs is None:
        request_handler.set_status(404)
        request_handler.set_header('Content-Type', 'application/json')
        response_obj = {
            'message': 'image content is no longer in cache',
            'path': image_url,
        }

        response_str = json.dumps(response_obj)
        request_handler.write(response_str)
        return

    # detect image type
    ext = os.path.splitext(image_url)[1]
    ext = ext.lower()

    if ext in MIME_TYPE_DICT:
        mime_type = MIME_TYPE_DICT[ext]
    else:
        image_type_str = detect_image_type_from_content(image_content_bs)
        if image_type_str is None:
            mime_type = 'application/octet-stream'
        else:
            mime_type = f'image/{image_type_str}'

    # I didn't store the response header, so we don't have the actual value

    request_handler.set_status(200)
    request_handler.set_header('Last-Modified', DEFAULT_LAST_MODIFIED_VALUE)
    request_handler.set_header('Content-Type', mime_type)
    request_handler.set_header('Content-Length', str(len(image_content_bs)))
    request_handler.write(image_content_bs)
    return
示例#8
0
def handle_game_list_request(request_handler: tornado.web.RequestHandler, ):
    # implement pagination
    params = request_handler.request.query_arguments
    print('handle_game_list_request: params', params)

    start_index = 0
    count = 16
    if 'index' in params:
        try:
            start_index = parse_index_value(params['index'])
        except Exception as ex:
            # invalid index
            stack_trace = traceback.format_exc()
            print(FG_RED, 'EXCEPTION:', ex)
            print(stack_trace, RESET_COLOR)

            request_handler.set_status(400)
            request_handler.set_header('Content-Type', 'application/json')
            response_obj = {
                'message': 'invalid index',
                'exception': str(ex),
                'stacktrace': stack_trace,
            }

            response_str = json.dumps(response_obj)
            request_handler.write(response_str)
            return

    if 'count' in params:
        try:
            count = parse_count_value(params['count'])
        except Exception as ex:
            # invalid count
            stack_trace = traceback.format_exc()
            print(FG_RED, 'EXCEPTION:', ex)
            print(stack_trace, RESET_COLOR)

            request_handler.set_status(400)
            request_handler.set_header('Content-Type', 'application/json')
            response_obj = {
                'message': 'invalid count',
                'exception': str(ex),
                'stacktrace': stack_trace,
            }

            response_str = json.dumps(response_obj)
            request_handler.write(response_str)
            return

    end_index = start_index + count
    end_index = min(end_index, len(GAME_INFO_LIST))

    response_obj = []

    for i in range(start_index, end_index):
        game_info_dict = GAME_INFO_LIST[i]
        game_info_dict = game_info_dict.copy()
        game_info_dict['index'] = i
        response_obj.append(game_info_dict)

    # print(response_obj)
    response_obj = make_obj_json_friendly(response_obj)
    # print(response_obj)
    json_str = json.dumps(response_obj)
    # json_bs = json_str.encode('utf-8')

    request_handler.set_status(200)
    request_handler.set_header('Content-Type',
                               'application/json, charset=utf-8')
    # request_handler.set_header('Content-Length', str(len(json_bs)))
    # request_handler.write(json_bs)
    request_handler.write(json_str)
    return
示例#9
0
def handle_webdata_request(request_handler: tornado.web.RequestHandler, ):
    request_path = request_handler.request.path
    # normalize request path
    try:
        normalized_request_path = normalize_request_path(request_path)
    except InvalidCharacterInPath as ex:
        print(ex)
        # unauthorize
        request_handler.set_status(403)
        request_handler.set_header('Content-Type', 'application/json')
        response_obj = {
            'message': 'invalid character in path',
            'path': request_path,
        }

        response_str = json.dumps(response_obj)
        request_handler.write(response_str)

        return

    # join with webdata directory
    if len(normalized_request_path) == 0:
        local_path = WEBDATA_DIRECTORY
    elif normalized_request_path == '/':
        local_path = WEBDATA_DIRECTORY
    else:
        local_path = os.path.join(WEBDATA_DIRECTORY, normalized_request_path)

    if local_path == WEBDATA_DIRECTORY:
        pass
    elif not is_child_path(WEBDATA_DIRECTORY, local_path):
        # unauthorize
        request_handler.set_status(403)
        request_handler.set_header('Content-Type', 'application/json')
        response_obj = {
            'message': 'unauthorized access',
            'path': request_path,
        }

        response_str = json.dumps(response_obj)
        request_handler.write(response_str)

        return

    # check if file exists
    if not os.path.exists(local_path):
        # not found
        request_handler.set_status(404)
        request_handler.set_header('Content-Type', 'application/json')
        response_obj = {
            'message': 'file not found',
            'path': request_path,
        }

        response_str = json.dumps(response_obj)
        request_handler.write(response_str)

        return

    file_stat = os.stat(local_path)
    if stat.S_ISDIR(file_stat.st_mode):
        # automatically serve index.html
        # directory
        # check if index.html exists
        child_filename_list = os.listdir(local_path)
        for child_filename in child_filename_list:
            lowered_child_filename = child_filename.lower()
            if lowered_child_filename in VALID_HTML_INDEX_FILENAME_LIST:
                child_filepath = os.path.join(local_path, child_filename)
                # send modified time
                os.path.getmtime(child_filepath)
                # TODO
                request_handler.set_status(200)
                request_handler.set_header('Content-Type', 'text/html')
                filesize = os.stat(child_filepath).st_size
                if filesize > 0:
                    send_file_data(request_handler, child_filepath)
                return

        # not found
        # return directory listing
        # TODO option to disable directory listing
        # contruct static html page
        html_str = render_static_directory_listing_html(
            normalized_request_path,
            child_filename_list,
        )

        request_handler.set_status(200)
        request_handler.set_header('Content-Type', 'text/html')
        request_handler.write(html_str)
        return

    if not stat.S_ISREG(file_stat.st_mode):
        # unauthorize
        # this is not a regular file
        request_handler.set_status(403)
        request_handler.set_header('Content-Type', 'application/json')
        response_obj = {
            'message': 'this is not a regular file',
            'path': request_path,
        }

        response_str = json.dumps(response_obj)
        request_handler.write(response_str)

        return

    # regular file
    # TODO parse and support Range header
    request_handler.set_status(200)
    mime_type = get_mime_type_by_filename(local_path)

    if mime_type in TEXT_MIME_TYPE_LIST:
        # set mime type and charset to utf-8
        header_value = f'{mime_type}; charset=utf-8'
        request_handler.set_header('Content-Type', header_value)
    else:
        request_handler.set_header('Content-Type', mime_type)

    filesize = os.stat(local_path).st_size
    request_handler.set_header('Content-Length', str(filesize))

    send_file_data(request_handler, local_path)