summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/linetext2.rb
blob: 9fdf28bd31106d1cc9d0f52de9eed96e6dedc0c1 (plain)
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
#--
#
# Author:: Francis Cianfrocca (gmail: blackhedd)
# Homepage::  http://rubyeventmachine.com
# Date:: 15 November 2006
# 
# See EventMachine and EventMachine::Connection for documentation and
# usage examples.
#
#----------------------------------------------------------------------------
#
# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
# Gmail: blackhedd
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of either: 1) the GNU General Public License
# as published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version; or 2) Ruby's License.
# 
# See the file COPYING for complete licensing information.
#
#---------------------------------------------------------------------------
#
# 

module EventMachine
  module Protocols
    # In the grand, time-honored tradition of re-inventing the wheel, we offer
    # here YET ANOTHER protocol that handles line-oriented data with interspersed
    # binary text. This one trades away some of the performance optimizations of
    # EventMachine::Protocols::LineAndTextProtocol in order to get better correctness
    # with regard to binary text blocks that can switch back to line mode. It also
    # permits the line-delimiter to change in midstream.
    # This was originally written to support Stomp.
    module LineText2
      # TODO! We're not enforcing the limits on header lengths and text-lengths.
      # When we get around to that, call #receive_error if the user defined it, otherwise
      # throw exceptions.

      MaxBinaryLength = 32*1024*1024

      #--
      # Will loop internally until there's no data left to read.
      # That way the user-defined handlers we call can modify the
      # handling characteristics on a per-token basis.
      #
      def receive_data data
        return unless (data and data.length > 0)

        # Do this stuff in lieu of a constructor.
        @lt2_mode ||= :lines
        @lt2_delimiter ||= "\n"
        @lt2_linebuffer ||= []

        remaining_data = data

        while remaining_data.length > 0
          if @lt2_mode == :lines
            delimiter_string = case @lt2_delimiter
            when Regexp
              remaining_data.slice(@lt2_delimiter)
            else
              @lt2_delimiter
            end
            ix = remaining_data.index(delimiter_string) if delimiter_string
            if ix
              @lt2_linebuffer << remaining_data[0...ix]
              ln = @lt2_linebuffer.join
              @lt2_linebuffer.clear
              if @lt2_delimiter == "\n"
                ln.chomp!
              end
              receive_line ln
              remaining_data = remaining_data[(ix+delimiter_string.length)..-1]
            else
              @lt2_linebuffer << remaining_data
              remaining_data = ""
            end
          elsif @lt2_mode == :text
            if @lt2_textsize
              needed = @lt2_textsize - @lt2_textpos
              will_take = if remaining_data.length > needed
                            needed
                          else
                            remaining_data.length
                          end

              @lt2_textbuffer << remaining_data[0...will_take]
              tail = remaining_data[will_take..-1]

              @lt2_textpos += will_take
              if @lt2_textpos >= @lt2_textsize
                # Reset line mode (the default behavior) BEFORE calling the
                # receive_binary_data. This makes it possible for user code
                # to call set_text_mode, enabling chains of text blocks
                # (which can possibly be of different sizes).
                set_line_mode
                receive_binary_data @lt2_textbuffer.join
                receive_end_of_binary_data
              end

              remaining_data = tail
            else
              receive_binary_data remaining_data
              remaining_data = ""
            end
          end
        end
      end

      # The line delimiter may be a regular expression or a string.  Anything
      # passed to set_delimiter other than a regular expression will be
      # converted to a string.
      def set_delimiter delim
        @lt2_delimiter = case delim
        when Regexp
          delim
        else
          delim.to_s
        end
      end

      # Called internally but also exposed to user code, for the case in which
      # processing of binary data creates a need to transition back to line mode.
      # We support an optional parameter to "throw back" some data, which might
      # be an umprocessed chunk of the transmitted binary data, or something else
      # entirely.
      def set_line_mode data=""
        @lt2_mode = :lines
        (@lt2_linebuffer ||= []).clear
        receive_data data.to_s
      end

      def set_text_mode size=nil
        if size == 0
          set_line_mode
        else
          @lt2_mode = :text
          (@lt2_textbuffer ||= []).clear
          @lt2_textsize = size # which can be nil, signifying no limit
          @lt2_textpos = 0
        end
      end

      # Alias for #set_text_mode, added for back-compatibility with LineAndTextProtocol.
      def set_binary_mode size=nil
        set_text_mode size
      end

      # In case of a dropped connection, we'll send a partial buffer to user code
      # when in sized text mode. User overrides of #receive_binary_data need to
      # be aware that they may get a short buffer.
      def unbind
        @lt2_mode ||= nil
        if @lt2_mode == :text and @lt2_textpos > 0
          receive_binary_data @lt2_textbuffer.join
        end
      end

      # Stub. Should be subclassed by user code.
      def receive_line ln
        # no-op
      end

      # Stub. Should be subclassed by user code.
      def receive_binary_data data
        # no-op
      end

      # Stub. Should be subclassed by user code.
      # This is called when transitioning internally from text mode
      # back to line mode. Useful when client code doesn't want
      # to keep track of how much data it's received.
      def receive_end_of_binary_data
        # no-op
      end
    end
  end
end