summaryrefslogtreecommitdiff
path: root/contrib/uwsgi.rb
blob: b0f8798c68b4a732c92023ce2cd3959fb68e0493 (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
require 'socket'
require 'rack/content_length'
require 'rack/rewindable_input'
require 'stringio'

module Rack
  module Handler
    class Uwsgi

      def self.run(app, options={})
	if ENV['UWSGI_FD']
          server = UNIXServer.for_fd(ENV['UWSGI_FD'].to_i)
	else
          server = TCPServer.new(options[:Host], options[:Port])
	end
	while client = server.accept
          serve client, app
	end
      end

      def self.serve(client, app)

	head, sender = client.recvfrom(4)

	unless head
	  client.close
	  return
	end
	
	mod1, size, mod2 = head.unpack('CvC')

	if size == 0 or size.nil?
	  client.close
	  return
	end

	vars, sender = client.recvfrom(size)

	if vars.length != size
	  client.close
	  return
	end

	env = Hash.new

	i = 0
	while i < size
          kl = vars[i, 2].unpack('v')[0]
          i = i + 2
          key = vars[i, kl]
	  i = i + kl
          vl = vars[i, 2].unpack('v')[0]
          i = i + 2
	  value = vars[i, vl]
          i = i + vl
          env[key] = value
	end



        env.delete "HTTP_CONTENT_LENGTH"
        env.delete "HTTP_CONTENT_TYPE"

        env["SCRIPT_NAME"] = ""  if env["SCRIPT_NAME"] == "/"
        env["QUERY_STRING"] ||= ""
        env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
        env["REQUEST_PATH"] ||= "/"
        env.delete "PATH_INFO"  if env["PATH_INFO"] == ""
        env.delete "CONTENT_TYPE"  if env["CONTENT_TYPE"] == ""
        env.delete "CONTENT_LENGTH"  if env["CONTENT_LENGTH"] == ""
        
	
	if env["CONTENT_LENGTH"].to_i > 4096
		rack_input = Rack::RewindableInput::Tempfile.new('Rack_uwsgi_Input')
		rack_input.chmod(0000)
		rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
      		rack_input.binmode
      		if RUBY_PLATFORM !~ /(mswin|mingw|cygwin|java)/
        	  rack_input.unlink
		end

		remains = env["CONTENT_LENGTH"].to_i
		while remains > 0
			if remains >= 4096
				buf, sender = client.recvfrom(4096)
			else
				buf, sender = client.recvfrom(remains)
			end

			rack_input.write( buf )
			remains -= buf.length
		end

	elsif env["CONTENT_LENGTH"].to_i > 0
		rack_input =  StringIO.new(client.recvfrom(env["CONTENT_LENGTH"].to_i)[0])
	else
		rack_input = StringIO.new('')
	end

	rack_input.rewind

        env.update({"rack.version" => [1,1],
                     "rack.input" => rack_input,
                     "rack.errors" => $stderr,

                     "rack.multithread" => false,
                     "rack.multiprocess" => true,
                     "rack.run_once" => false,

                     "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
                   })


        app = Rack::ContentLength.new(app)

        begin
          status, headers, body = app.call(env)
          begin
            send_headers client, env["HTTP_VERSION"] ,status, headers
            send_body client, body
          ensure
            body.close  if body.respond_to? :close
          end
	rescue Errno::EPIPE, Errno::ECONNRESET
        ensure
	  rack_input.close
	  client.close
        end
      end

      def self.send_headers(client, protocol, status, headers)
	client.print "#{protocol} #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}\r\n"
        headers.each { |k, vs|
          vs.split("\n").each { |v|
            client.print "#{k}: #{v}\r\n"
          }
        }
        client.print "\r\n"
        client.flush
      end

      def self.send_body(client, body)
        body.each { |part|
          client.print part
          client.flush
        }
      end
    end
  end
end