unicorn Ruby/Rack server user+dev discussion/patches/pulls/bugs/help
 help / color / mirror / code / Atom feed
blob b4c95fc79090b9ba4a52422ae5da43e156610a60 3769 bytes (raw)
name: lib/unicorn/http_request.rb 	 # note: path name is non-authoritative(*)

  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
 
# -*- encoding: binary -*-
# :enddoc:
# no stable API here
require 'unicorn_http'

# TODO: remove redundant names
Unicorn.const_set(:HttpRequest, Unicorn::HttpParser)
class Unicorn::HttpParser

  # default parameters we merge into the request env for Rack handlers
  DEFAULTS = {
    "rack.errors" => $stderr,
    "rack.multiprocess" => true,
    "rack.multithread" => false,
    "rack.run_once" => false,
    "rack.version" => [1, 2],
    "rack.hijack?" => true,
    "SCRIPT_NAME" => "",

    # this is not in the Rack spec, but some apps may rely on it
    "SERVER_SOFTWARE" => "Unicorn #{Unicorn::Const::UNICORN_VERSION}"
  }

  NULL_IO = StringIO.new("")

  # :stopdoc:
  # A frozen format for this is about 15% faster
  # Drop these frozen strings when Ruby 2.2 becomes more prevalent,
  # 2.2+ optimizes hash assignments when used with literal string keys
  HTTP_RESPONSE_START = [ 'HTTP', '/1.1 ']
  @@input_class = Unicorn::TeeInput
  @@check_client_connection = false

  # TCP_TIME_WAIT: 6, TCP_CLOSE: 7, TCP_CLOSE_WAIT: 8, TCP_LAST_ACK: 9
  IGNORED_CHECK_CLIENT_SOCKET_STATES = (6..9)

  def self.input_class
    @@input_class
  end

  def self.input_class=(klass)
    @@input_class = klass
  end

  def self.check_client_connection
    @@check_client_connection
  end

  def self.check_client_connection=(bool)
    @@check_client_connection = bool
  end

  # :startdoc:

  # Does the majority of the IO processing.  It has been written in
  # Ruby using about 8 different IO processing strategies.
  #
  # It is currently carefully constructed to make sure that it gets
  # the best possible performance for the common case: GET requests
  # that are fully complete after a single read(2)
  #
  # Anyone who thinks they can make it faster is more than welcome to
  # take a crack at it.
  #
  # returns an environment hash suitable for Rack if successful
  # This does minimal exception trapping and it is up to the caller
  # to handle any socket errors (e.g. user aborted upload).
  def read(socket)
    clear
    e = env

    # From http://www.ietf.org/rfc/rfc3875:
    # "Script authors should be aware that the REMOTE_ADDR and
    #  REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
    #  may not identify the ultimate source of the request.  They
    #  identify the client for the immediate request to the server;
    #  that client may be a proxy, gateway, or other intermediary
    #  acting on behalf of the actual source client."
    e['REMOTE_ADDR'] = socket.kgio_addr

    # short circuit the common case with small GET requests first
    socket.kgio_read!(16384, buf)
    if parse.nil?
      # Parser is not done, queue up more data to read and continue parsing
      # an Exception thrown from the parser will throw us out of the loop
      false until add_parse(socket.kgio_read!(16384))
    end

    # detect if the socket is valid by checking client connection.
    if @@check_client_connection
      if defined?(Raindrops::TCP_Info)
        tcp_info = Raindrops::TCP_Info.new(socket)
        if IGNORED_CHECK_CLIENT_SOCKET_STATES.cover?(tcp_info.state)
          socket.close
          raise Errno::EPIPE
        end
      elsif headers?
        self.response_start_sent = true
        HTTP_RESPONSE_START.each { |c| socket.write(c) }
      end
    end

    e['rack.input'] = 0 == content_length ?
                      NULL_IO : @@input_class.new(socket, self)

    # for Rack hijacking in Rack 1.5 and later
    e['unicorn.socket'] = socket
    e['rack.hijack'] = self

    e.merge!(DEFAULTS)
  end

  # for rack.hijack, we respond to this method so no extra allocation
  # of a proc object
  def call
    env['rack.hijack_io'] = env['unicorn.socket']
  end

  def hijacked?
    env.include?('rack.hijack_io'.freeze)
  end
end

debug log:

solving b4c95fc ...
found b4c95fc in https://yhbt.net/unicorn-public/CAO3HKM7u0kx79Ae4HM+vc4Op6PZxpU3tm-maL1rKJgc1x6i3=A@mail.gmail.com/
found 0c1f9bb in https://yhbt.net/unicorn.git/
preparing index
index prepared:
100644 0c1f9bb9fb4e4e06b42124b071b240960b2289f6	lib/unicorn/http_request.rb

applying [1/1] https://yhbt.net/unicorn-public/CAO3HKM7u0kx79Ae4HM+vc4Op6PZxpU3tm-maL1rKJgc1x6i3=A@mail.gmail.com/
diff --git a/lib/unicorn/http_request.rb b/lib/unicorn/http_request.rb
index 0c1f9bb..b4c95fc 100644

Checking patch lib/unicorn/http_request.rb...
Applied patch lib/unicorn/http_request.rb cleanly.

index at:
100644 b4c95fc79090b9ba4a52422ae5da43e156610a60	lib/unicorn/http_request.rb

(*) Git path names are given by the tree(s) the blob belongs to.
    Blobs themselves have no identifier aside from the hash of its contents.^

Code repositories for project(s) associated with this public inbox

	https://yhbt.net/unicorn.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).