summaryrefslogtreecommitdiff
path: root/config/initializers/puma_client_tempfile_patch.rb
blob: 972eeaf0c83bd00cdd329aa58c8614ed04b332b6 (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
# frozen_string_literal: true

if Gitlab::Runtime.puma?
  # This patch represents https://github.com/puma/puma/pull/2613. If
  # this PR is accepted in the next Puma release, we can remove this
  # entire file.
  #
  # The patch itself is quite large because the tempfile creation in
  # Puma is inside these fairly long methods. The actual changes are
  # just two lines, commented with 'GitLab' to make them easier to find.
  raise "Remove this monkey patch: #{__FILE__}" unless Puma::Const::VERSION == '5.1.1'

  module Puma
    class Client
      private

      def setup_body
        @body_read_start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)

        if @env[HTTP_EXPECT] == CONTINUE
          # TODO allow a hook here to check the headers before
          # going forward
          @io << HTTP_11_100
          @io.flush
        end

        @read_header = false

        body = @parser.body

        te = @env[TRANSFER_ENCODING2]

        if te
          if te.include?(",")
            te.split(",").each do |part|
              if CHUNKED.casecmp(part.strip) == 0 # rubocop:disable Metrics/BlockNesting
                return setup_chunked_body(body)
              end
            end
          elsif CHUNKED.casecmp(te) == 0
            return setup_chunked_body(body)
          end
        end

        @chunked_body = false

        cl = @env[CONTENT_LENGTH]

        unless cl
          @buffer = body.empty? ? nil : body
          @body = EmptyBody
          set_ready
          return true
        end

        remain = cl.to_i - body.bytesize

        if remain <= 0
          @body = StringIO.new(body)
          @buffer = nil
          set_ready
          return true
        end

        if remain > MAX_BODY
          @body = Tempfile.new(Const::PUMA_TMP_BASE)
          @body.binmode
          @body.unlink # GitLab: this is the changed part
          @tempfile = @body
        else
          # The body[0,0] trick is to get an empty string in the same
          # encoding as body.
          @body = StringIO.new body[0,0] # rubocop:disable Layout/SpaceAfterComma
        end

        @body.write body

        @body_remain = remain

        return false # rubocop:disable Style/RedundantReturn
      end

      def setup_chunked_body(body)
        @chunked_body = true
        @partial_part_left = 0
        @prev_chunk = ""

        @body = Tempfile.new(Const::PUMA_TMP_BASE)
        @body.binmode
        @body.unlink # GitLab: this is the changed part
        @tempfile = @body
        @chunked_content_length = 0

        if decode_chunk(body)
          @env[CONTENT_LENGTH] = @chunked_content_length
          return true # rubocop:disable Style/RedundantReturn
        end
      end
    end
  end
end