File: truncator.rb

package info (click to toggle)
ruby-mongo 2.21.3-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 14,764 kB
  • sloc: ruby: 108,806; makefile: 5; sh: 2
file content (142 lines) | stat: -rw-r--r-- 4,620 bytes parent folder | download
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
# frozen_string_literal: true

# Copyright (C) 2016-2023 MongoDB Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

module Mongo
  class Server
    class AppMetadata
      # Implements the metadata truncation logic described in the handshake
      # spec.
      #
      # @api private
      class Truncator
        # @return [ BSON::Document ] the document being truncated.
        attr_reader :document

        # The max application metadata document byte size.
        MAX_DOCUMENT_SIZE = 512

        # Creates a new Truncator instance and tries enforcing the maximum
        # document size on the given document.
        #
        # @param [ BSON::Document] document The document to (potentially)
        #   truncate.
        #
        # @note The document is modified in-place; if you wish to keep the
        #   original unchanged, you must deep-clone it prior to sending it to
        #   a truncator.
        def initialize(document)
          @document = document
          try_truncate!
        end

        # The current size of the document, in bytes, as a serialized BSON
        # document.
        #
        # @return [ Integer ] the size of the document
        def size
          @document.to_bson.to_s.length
        end

        # Whether the document fits within the required maximum document size.
        #
        # @return [ true | false ] if the document is okay or not.
        def ok?
          size <= MAX_DOCUMENT_SIZE
        end

        private

        # How many extra bytes must be trimmed before the document may be
        # considered #ok?.
        #
        # @return [ Integer ] how many bytes larger the document is than the
        #   maximum document size.
        def excess
          size - MAX_DOCUMENT_SIZE
        end

        # Attempt to truncate the document using the documented metadata
        # priorities (see the handshake specification).
        def try_truncate!
          %i[ env_fields os_fields env platform ].each do |target|
            break if ok?

            send(:"try_truncate_#{target}!")
          end
        end

        # Attempt to truncate or remove the {{:platform}} key from the
        # document.
        def try_truncate_platform!
          @document.delete(:platform) unless try_truncate_string(@document[:platform])
        end

        # Attempt to truncate the keys in the {{:env}} subdocument.
        def try_truncate_env_fields!
          try_truncate_hash(@document[:env], reserved: %w[ name ])
        end

        # Attempt to truncate the keys in the {{:os}} subdocument.
        def try_truncate_os_fields!
          try_truncate_hash(@document[:os], reserved: %w[ type ])
        end

        # Remove the {{:env}} key from the document.
        def try_truncate_env!
          @document.delete(:env)
        end

        # A helper method for truncating a string (in-place) by whatever
        # {{#excess}} is required.
        #
        # @param [ String ] string the string value to truncate.
        #
        # @note the parameter is modified in-place.
        def try_truncate_string(string)
          length = string&.length || 0

          return false if excess > length

          string[(length - excess)..-1] = ''
        end

        # A helper method for removing the keys of a Hash (in-place) until
        # the document is the necessary size. The keys are considered in order
        # (using the Hash's native key ordering), and each will be removed from
        # the hash in turn, until the document is the necessary size.
        #
        # Any keys in the {{reserved}} list will be ignored.
        #
        # @param [ Hash | nil ] hash the Hash instance to consider.
        # @param [ Array ] reserved the list of keys to ignore in the hash.
        #
        # @note the hash parameter is modified in-place.
        def try_truncate_hash(hash, reserved: [])
          return false unless hash

          keys = hash.keys - reserved
          keys.each do |key|
            hash.delete(key)

            return true if ok?
          end

          false
        end
      end
    end
  end
end