yahns 1.12.5 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/Documentation/yahns-rackup.pod +0 -10
  3. data/Documentation/yahns_config.pod +3 -0
  4. data/GIT-VERSION-FILE +1 -1
  5. data/GIT-VERSION-GEN +1 -1
  6. data/NEWS +80 -0
  7. data/examples/init.sh +34 -9
  8. data/examples/logrotate.conf +5 -0
  9. data/examples/yahns.socket +17 -0
  10. data/examples/yahns@.service +50 -0
  11. data/examples/yahns_rack_basic.conf.rb +0 -6
  12. data/extras/autoindex.rb +3 -2
  13. data/extras/exec_cgi.rb +1 -0
  14. data/extras/try_gzip_static.rb +19 -5
  15. data/lib/yahns/chunk_body.rb +27 -0
  16. data/lib/yahns/fdmap.rb +7 -4
  17. data/lib/yahns/http_client.rb +39 -10
  18. data/lib/yahns/http_response.rb +41 -22
  19. data/lib/yahns/openssl_client.rb +7 -3
  20. data/lib/yahns/proxy_http_response.rb +132 -159
  21. data/lib/yahns/proxy_pass.rb +6 -170
  22. data/lib/yahns/queue_epoll.rb +1 -0
  23. data/lib/yahns/queue_kqueue.rb +1 -0
  24. data/lib/yahns/req_res.rb +164 -0
  25. data/lib/yahns/server.rb +2 -1
  26. data/lib/yahns/server_mp.rb +1 -1
  27. data/lib/yahns/version.rb +1 -1
  28. data/lib/yahns/wbuf.rb +5 -6
  29. data/lib/yahns/wbuf_common.rb +5 -10
  30. data/lib/yahns/wbuf_lite.rb +111 -0
  31. data/man/yahns-rackup.1 +29 -29
  32. data/man/yahns_config.5 +47 -35
  33. data/test/helper.rb +12 -0
  34. data/test/test_auto_chunk.rb +56 -0
  35. data/test/test_extras_exec_cgi.rb +1 -3
  36. data/test/test_extras_try_gzip_static.rb +30 -16
  37. data/test/test_output_buffering.rb +5 -1
  38. data/test/test_proxy_pass.rb +2 -2
  39. data/test/test_proxy_pass_no_buffering.rb +170 -0
  40. data/test/test_reopen_logs.rb +5 -1
  41. data/test/test_response.rb +42 -0
  42. data/test/test_server.rb +35 -0
  43. data/test/test_ssl.rb +0 -6
  44. data/test/test_tmpio.rb +4 -0
  45. data/test/test_wbuf.rb +11 -4
  46. metadata +10 -4
  47. data/lib/yahns/sendfile_compat.rb +0 -24
@@ -0,0 +1,164 @@
1
+ # -*- encoding: binary -*-
2
+ # Copyright (C) 2013-2016 all contributors <yahns-public@yhbt.net>
3
+ # License: GPL-3.0+ (https://www.gnu.org/licenses/gpl-3.0.txt)
4
+ # frozen_string_literal: true
5
+ # Only used by Yahns::ProxyPass
6
+ require 'kcar' # gem install kcar
7
+ require 'kgio'
8
+
9
+ class Yahns::ReqRes < Kgio::Socket # :nodoc:
10
+ attr_accessor :resbuf
11
+ attr_accessor :proxy_trailers
12
+ attr_accessor :alive
13
+ attr_reader :proxy_pass
14
+
15
+ def req_start(c, req, input, chunked, proxy_pass)
16
+ @hdr = @resbuf = nil
17
+ @yahns_client = c
18
+ @rrstate = input ? [ req, input, chunked ] : req
19
+ @proxy_pass = proxy_pass
20
+ Thread.current[:yahns_queue].queue_add(self, Yahns::Queue::QEV_WR)
21
+ end
22
+
23
+ def yahns_step # yahns event loop entry point
24
+ c = @yahns_client
25
+ case req = @rrstate
26
+ when Kcar::Parser # reading response...
27
+ buf = Thread.current[:yahns_rbuf]
28
+
29
+ case resbuf = @resbuf # where are we at the response?
30
+ when nil # common case, catch the response header in a single read
31
+
32
+ case rv = kgio_tryread(0x2000, buf)
33
+ when String
34
+ if res = req.headers(@hdr = [], rv)
35
+ return c.proxy_response_start(res, rv, req, self)
36
+ else # ugh, big headers or tricked response
37
+ # we must reinitialize the thread-local rbuf if it may
38
+ # live beyond the current thread
39
+ buf = Thread.current[:yahns_rbuf] = ''.dup
40
+ @resbuf = rv
41
+ end
42
+ # continue looping in middle "case @resbuf" loop
43
+ when :wait_readable
44
+ return rv # spurious wakeup
45
+ when nil
46
+ return c.proxy_err_response(502, self, 'upstream EOF (headers)')
47
+ end # NOT looping here
48
+
49
+ when String # continue reading trickled response headers from upstream
50
+
51
+ case rv = kgio_tryread(0x2000, buf)
52
+ when String then res = req.headers(@hdr, resbuf << rv) and break
53
+ when :wait_readable then return rv
54
+ when nil
55
+ return c.proxy_err_response(502, self, 'upstream EOF (big headers)')
56
+ end while true
57
+ @resbuf = false
58
+
59
+ return c.proxy_response_start(res, resbuf, req, self)
60
+
61
+ when Yahns::WbufCommon # streaming/buffering the response body
62
+
63
+ return c.proxy_response_finish(req, self)
64
+
65
+ end while true # case @resbuf
66
+
67
+ when Array # [ (str|vec), rack.input, chunked? ]
68
+ send_req_body(req) # returns nil or :wait_writable
69
+ when String # buffered request header
70
+ send_req_buf(req)
71
+ end
72
+ rescue => e
73
+ # avoid polluting logs with a giant backtrace when the problem isn't
74
+ # fixable in code.
75
+ case e
76
+ when Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EPIPE
77
+ e.set_backtrace([])
78
+ end
79
+ c.proxy_err_response(502, self, e)
80
+ end
81
+
82
+ def send_req_body_chunk(buf)
83
+ case rv = String === buf ? kgio_trywrite(buf) : kgio_trywritev(buf)
84
+ when String, Array
85
+ buf.replace(rv) # retry loop on partial write
86
+ when :wait_writable, nil
87
+ # :wait_writable = upstream is reading slowly and making us wait
88
+ return rv
89
+ else
90
+ abort "BUG: #{rv.inspect} from kgio_trywrite*"
91
+ end while true
92
+ end
93
+
94
+ # returns :wait_readable if complete, :wait_writable if not
95
+ def send_req_body(req) # @rrstate == [ (str|vec), rack.input, chunked? ]
96
+ buf, input, chunked = req
97
+
98
+ # send the first buffered chunk or vector
99
+ rv = send_req_body_chunk(buf) and return rv # :wait_writable
100
+
101
+ # yay, sent the first chunk, now read the body!
102
+ rbuf = buf
103
+ if chunked
104
+ if String === buf # initial body
105
+ req[0] = buf = []
106
+ else
107
+ # try to reuse the biggest non-frozen buffer we just wrote;
108
+ rbuf = buf.max_by(&:size)
109
+ rbuf = ''.dup if rbuf.frozen? # unlikely...
110
+ end
111
+ end
112
+
113
+ # Note: input (env['rack.input']) is fully-buffered by default so
114
+ # we should not be waiting on a slow network resource when reading
115
+ # input. However, some weird configs may disable this on LANs
116
+ # and we may wait indefinitely on input.read here...
117
+ while input.read(0x2000, rbuf)
118
+ if chunked
119
+ buf[0] = "#{rbuf.size.to_s(16)}\r\n".freeze
120
+ buf[1] = rbuf
121
+ buf[2] = "\r\n".freeze
122
+ end
123
+ rv = send_req_body_chunk(buf) and return rv # :wait_writable
124
+ end
125
+
126
+ rbuf.clear # all done, clear the big buffer
127
+
128
+ # we cannot use respond_to?(:close) here since Rack::Lint::InputWrapper
129
+ # tries to prevent that (and hijack means all Rack specs go out the door)
130
+ case input
131
+ when Yahns::TeeInput, IO
132
+ input.close
133
+ end
134
+
135
+ # note: we do not send any trailer, they are folded into the header
136
+ # because this relies on full request buffering
137
+ # prepare_wait_readable is called by send_req_buf
138
+ chunked ? send_req_buf("0\r\n\r\n".freeze) : prepare_wait_readable
139
+ rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN
140
+ # no more reading off the client socket, just prepare to forward
141
+ # the rejection response from the upstream (if any)
142
+ @yahns_client.to_io.shutdown(Socket::SHUT_RD)
143
+ prepare_wait_readable
144
+ end
145
+
146
+ def prepare_wait_readable
147
+ @rrstate = Kcar::Parser.new
148
+ :wait_readable # all done sending the request, wait for response
149
+ end
150
+
151
+ # n.b. buf must be a detached string not shared with
152
+ # Thread.current[:yahns_rbuf] of any thread
153
+ def send_req_buf(buf)
154
+ case rv = kgio_trywrite(buf)
155
+ when String
156
+ buf = rv # retry inner loop
157
+ when :wait_writable
158
+ @rrstate = buf
159
+ return :wait_writable
160
+ when nil
161
+ return prepare_wait_readable
162
+ end while true
163
+ end
164
+ end # class ReqRes
data/lib/yahns/server.rb CHANGED
@@ -496,7 +496,8 @@ class Yahns::Server # :nodoc:
496
496
  def dropping(fdmap)
497
497
  if drop_acceptors[0] || fdmap.size > 0
498
498
  timeout = @shutdown_expire < Yahns.now ? -1 : @shutdown_timeout
499
- fdmap.desperate_expire(timeout)
499
+ n = fdmap.desperate_expire(timeout)
500
+ $0 = "yahns quitting, #{n} FD(s) remain"
500
501
  true
501
502
  else
502
503
  false
@@ -159,7 +159,7 @@ module Yahns::ServerMP # :nodoc:
159
159
  def mp_sig_handle(watch, alive)
160
160
  # not performance critical
161
161
  watch.delete_if { |io| io.to_io.closed? }
162
- if r = IO.select(watch, nil, nil, alive ? nil : 0.01)
162
+ if r = IO.select(watch, nil, nil, alive ? nil : 0.1)
163
163
  r[0].each(&:yahns_step)
164
164
  end
165
165
  case @sig_queue.shift
data/lib/yahns/version.rb CHANGED
@@ -1 +1 @@
1
- Yahns::VERSION = '1.12.5'.freeze # :nodoc:
1
+ Yahns::VERSION = '1.13.0'.freeze # :nodoc:
data/lib/yahns/wbuf.rb CHANGED
@@ -31,15 +31,13 @@ require_relative 'wbuf_common'
31
31
  class Yahns::Wbuf # :nodoc:
32
32
  include Yahns::WbufCommon
33
33
  attr_reader :busy
34
- attr_reader :wbuf_persist
35
34
 
36
- def initialize(body, persist, tmpdir, busy)
35
+ def initialize(body, persist)
37
36
  @tmpio = nil
38
- @tmpdir = tmpdir
39
37
  @sf_offset = @sf_count = 0
40
38
  @wbuf_persist = persist # whether or not we keep the connection alive
41
- @body = body
42
- @busy = busy # may be false
39
+ @body = body # something we call #close on when done writing
40
+ @busy = false
43
41
  end
44
42
 
45
43
  def wbuf_writev(buf)
@@ -59,7 +57,8 @@ class Yahns::Wbuf # :nodoc:
59
57
  @busy = rv
60
58
  end until @busy
61
59
 
62
- @tmpio ||= Yahns::TmpIO.new(@tmpdir)
60
+ @tmpio ||= Yahns::TmpIO.new(c.class.output_buffer_tmpdir)
61
+ # n.b.: we rely on O_APPEND in TmpIO, here
63
62
  @sf_count += String === buf ? @tmpio.write(buf) : wbuf_writev(buf)
64
63
 
65
64
  # we spent some time copying to the FS, try to write to
@@ -2,14 +2,6 @@
2
2
  # Copyright (C) 2013-2016 all contributors <yahns-public@yhbt.net>
3
3
  # License: GPL-3.0+ (https://www.gnu.org/licenses/gpl-3.0.txt)
4
4
  # frozen_string_literal: true
5
- begin
6
- raise LoadError, "SENDFILE_BROKEN env set" if ENV["SENDFILE_BROKEN"]
7
- require 'sendfile'
8
- rescue LoadError
9
- require_relative 'sendfile_compat'
10
- IO.__send__ :include, Yahns::SendfileCompat
11
- end
12
-
13
5
  module Yahns::WbufCommon # :nodoc:
14
6
  # returns true / false for persistent/non-persistent connections
15
7
  # returns :wait_*able when blocked
@@ -19,7 +11,10 @@ module Yahns::WbufCommon # :nodoc:
19
11
  def wbuf_flush(client)
20
12
  case rv = client.trysendfile(@tmpio, @sf_offset, @sf_count)
21
13
  when Integer
22
- return wbuf_close(client) if (@sf_count -= rv) == 0 # all sent!
14
+ if (@sf_count -= rv) == 0 # all sent!
15
+ @sf_offset = 0
16
+ return wbuf_close(client)
17
+ end
23
18
 
24
19
  @sf_offset += rv # keep going otherwise
25
20
  when :wait_writable, :wait_readable
@@ -48,7 +43,7 @@ module Yahns::WbufCommon # :nodoc:
48
43
  if @wbuf_persist.respond_to?(:call) # hijack
49
44
  client.response_hijacked(@wbuf_persist) # :ignore
50
45
  else
51
- @wbuf_persist # true or false or Yahns::StreamFile
46
+ @wbuf_persist # true, false, :ignore, or Yahns::StreamFile
52
47
  end
53
48
  end
54
49
  end
@@ -0,0 +1,111 @@
1
+ # -*- encoding: binary -*-
2
+ # Copyright (C) 2016 all contributors <yahns-public@yhbt.net>
3
+ # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4
+ # frozen_string_literal: true
5
+ require 'stringio'
6
+ require_relative 'wbuf_common'
7
+
8
+ # This is only used for "proxy_buffering: false"
9
+ class Yahns::WbufLite # :nodoc:
10
+ include Yahns::WbufCommon
11
+ attr_reader :busy
12
+ attr_writer :req_res
13
+
14
+ def initialize(req_res)
15
+ @tmpio = nil
16
+ @sf_offset = @sf_count = 0
17
+ @wbuf_persist = :ignore
18
+ @busy = false
19
+ @req_res = req_res
20
+ end
21
+
22
+ def wbuf_write(c, buf)
23
+ buf = buf.join if Array === buf
24
+ # try to bypass the VFS layer and write directly to the socket
25
+ # if we're all caught up
26
+ case rv = c.kgio_trywrite(buf)
27
+ when String
28
+ buf = rv # retry in loop
29
+ when nil
30
+ return # yay! hopefully we don't have to buffer again
31
+ when :wait_writable, :wait_readable
32
+ @busy = rv
33
+ end until @busy
34
+
35
+ @tmpio ||= StringIO.new(''.dup) # relies on encoding: binary above
36
+ @tmpio.seek(0, 2) # fake O_APPEND behavior
37
+ @sf_count += @tmpio.write(buf)
38
+
39
+ # we spent some time copying to the FS, try to write to
40
+ # the socket again in case some space opened up...
41
+ case rv = c.trysendio(@tmpio, @sf_offset, @sf_count)
42
+ when Integer
43
+ @sf_count -= rv
44
+ @sf_offset += rv
45
+ when :wait_writable, :wait_readable
46
+ @busy = rv
47
+ return rv
48
+ else
49
+ raise "BUG: #{rv.nil? ? 'EOF' : rv.inspect} on " \
50
+ "tmpio.size=#{@tmpio.size} " \
51
+ "sf_offset=#@sf_offset sf_count=#@sf_count"
52
+ end while @sf_count > 0
53
+
54
+ # we're all caught up, try to save some memory if we can help it.
55
+ wbuf_abort
56
+ @busy = false
57
+ nil
58
+ rescue
59
+ @req_res = @req_res.close if @req_res
60
+ raise
61
+ end
62
+
63
+ def wbuf_flush(client)
64
+ case rv = client.trysendio(@tmpio, @sf_offset, @sf_count)
65
+ when Integer
66
+ return wbuf_close(client) if (@sf_count -= rv) == 0 # all sent!
67
+ @sf_offset += rv # keep going otherwise
68
+ when :wait_writable, :wait_readable
69
+ return rv
70
+ else
71
+ raise "BUG: #{rv.nil? ? 'EOF' : rv.inspect} on " \
72
+ "tmpio.size=#{@tmpio.size} " \
73
+ "sf_offset=#@sf_offset sf_count=#@sf_count"
74
+ end while @sf_count > 0
75
+ wbuf_close(client)
76
+ rescue
77
+ @wbuf_persist = false # ensure a hijack response is not called
78
+ @req_res = @req_res.close if @req_res
79
+ wbuf_close(client)
80
+ raise
81
+ end
82
+
83
+ # called by Yahns::HttpClient#step_write
84
+ def wbuf_close(client)
85
+ wbuf_abort if @tmpio
86
+
87
+ # resume the event loop when @blocked is empty
88
+ # The actual Yahns::ReqRes#yahns_step is actually read/write-event
89
+ # agnostic, and we should actually watch for writability here since
90
+ # the req_res socket itself could be completely drained of readable
91
+ # data and just waiting for another request (which we don't support, yet)
92
+ if @req_res
93
+ @busy = false
94
+ client.hijack_cleanup
95
+ Thread.current[:yahns_queue].queue_mod(@req_res, Yahns::Queue::QEV_WR)
96
+ return :ignore
97
+ end
98
+ @wbuf_persist
99
+ rescue
100
+ @req_res = @req_res.close if @req_res
101
+ raise
102
+ end
103
+
104
+ def wbuf_abort
105
+ @sf_offset = @sf_count = 0
106
+ # we can safely truncate since this is a StringIO, we cannot do this
107
+ # with a real file because zero-copy with sendfile means truncating
108
+ # a while could clobber in-flight data
109
+ @tmpio.truncate(0)
110
+ end
111
+ end
data/man/yahns-rackup.1 CHANGED
@@ -1,4 +1,4 @@
1
- .\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16)
1
+ .\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28)
2
2
  .\"
3
3
  .\" Standard preamble:
4
4
  .\" ========================================================================
@@ -38,6 +38,8 @@
38
38
  . ds PI \(*p
39
39
  . ds L" ``
40
40
  . ds R" ''
41
+ . ds C`
42
+ . ds C'
41
43
  'br\}
42
44
  .\"
43
45
  .\" Escape single quotes in literal strings from groff's Unicode transform.
@@ -48,17 +50,24 @@
48
50
  .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
49
51
  .\" entries marked with X<> in POD. Of course, you'll have to process the
50
52
  .\" output yourself in some meaningful fashion.
51
- .ie \nF \{\
52
- . de IX
53
- . tm Index:\\$1\t\\n%\t"\\$2"
53
+ .\"
54
+ .\" Avoid warning from groff about undefined register 'F'.
55
+ .de IX
54
56
  ..
55
- . nr % 0
56
- . rr F
57
- .\}
58
- .el \{\
59
- . de IX
57
+ .nr rF 0
58
+ .if \n(.g .if rF .nr rF 1
59
+ .if (\n(rF:(\n(.g==0)) \{
60
+ . if \nF \{
61
+ . de IX
62
+ . tm Index:\\$1\t\\n%\t"\\$2"
60
63
  ..
64
+ . if !\nF==2 \{
65
+ . nr % 0
66
+ . nr F 2
67
+ . \}
68
+ . \}
61
69
  .\}
70
+ .rr rF
62
71
  .\"
63
72
  .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
64
73
  .\" Fear. Run. Save yourself. No user-serviceable parts.
@@ -124,7 +133,7 @@
124
133
  .\" ========================================================================
125
134
  .\"
126
135
  .IX Title "YAHNS-RACKUP 1"
127
- .TH YAHNS-RACKUP 1 "1994-10-02" "yahns 1.12.4.8.gd5d0" "yahns user manual"
136
+ .TH YAHNS-RACKUP 1 "1994-10-02" "yahns 1.12.5.48.g013d" "yahns user manual"
128
137
  .\" For nroff, turn off justification. Always turn off hyphenation; it makes
129
138
  .\" way too many mistakes in technical documents.
130
139
  .if n .ad l
@@ -140,7 +149,7 @@ A \fIrackup\fR\|(1)\-like command to launch Rack applications using yahns.
140
149
  It is expected to start in your application root (\s-1APP_ROOT\s0).
141
150
  .SH "RACKUP FILE"
142
151
  .IX Header "RACKUP FILE"
143
- This defaults to \*(L"config.ru\*(R" in \s-1APP_ROOT\s0. It should be the same
152
+ This defaults to \*(L"config.ru\*(R" in \s-1APP_ROOT. \s0 It should be the same
144
153
  file used by \fIrackup\fR\|(1) and other Rack launchers, it uses the
145
154
  *Rack::Builder* \s-1DSL\s0 documented at:
146
155
  .PP
@@ -157,8 +166,8 @@ default timeout. Lower this if you run out of FDs.
157
166
  Default: 15 (seconds)
158
167
  .IP "\-O listen=ADDRESS[,ADDRESS...]" 4
159
168
  .IX Item "-O listen=ADDRESS[,ADDRESS...]"
160
- Listens on a given \s-1ADDRESS\s0. \s-1ADDRESS\s0 may be in the form of
161
- \&\s-1HOST:PORT\s0 or \s-1PATH\s0, \s-1HOST:PORT\s0 is taken to mean a \s-1TCP\s0 socket
169
+ Listens on a given \s-1ADDRESS. ADDRESS\s0 may be in the form of
170
+ \&\s-1HOST:PORT\s0 or \s-1PATH, HOST:PORT\s0 is taken to mean a \s-1TCP\s0 socket
162
171
  and \s-1PATH\s0 is meant to be a path to a \s-1UNIX\s0 domain socket.
163
172
  Defaults to \*(L"0.0.0.0:9292\*(R" (all addresses on \s-1TCP\s0 port 9292).
164
173
  Multiple addresses may be separated with commas.
@@ -204,11 +213,11 @@ Unless specified via stderr_path and stdout_path, stderr and stdout will
204
213
  also be redirected to \*(L"/dev/null\*(R".
205
214
  .IP "\-E, \-\-env \s-1RACK_ENV\s0" 4
206
215
  .IX Item "-E, --env RACK_ENV"
207
- Run under the given \s-1RACK_ENV\s0. See the \*(L"\s-1RACK\s0 \s-1ENVIRONMENT\s0\*(R" section
216
+ Run under the given \s-1RACK_ENV. \s0 See the \*(L"\s-1RACK ENVIRONMENT\*(R"\s0 section
208
217
  for more details.
209
218
  .IP "\-o, \-\-host \s-1HOST\s0" 4
210
219
  .IX Item "-o, --host HOST"
211
- Listen on a \s-1TCP\s0 socket belonging to \s-1HOST\s0, default is
220
+ Listen on a \s-1TCP\s0 socket belonging to \s-1HOST,\s0 default is
212
221
  \&\*(L"0.0.0.0\*(R" (all addresses).
213
222
  If specified multiple times on the command-line, only the
214
223
  last-specified value takes effect.
@@ -216,7 +225,7 @@ This option only exists for compatibility with the \fIrackup\fR\|(1) command,
216
225
  use of \*(L"\-l\*(R"/\*(L"\-\-listen\*(R" switch is recommended instead.
217
226
  .IP "\-p, \-\-port \s-1PORT\s0" 4
218
227
  .IX Item "-p, --port PORT"
219
- Listen on the specified \s-1TCP\s0 \s-1PORT\s0, default is 9292.
228
+ Listen on the specified \s-1TCP PORT,\s0 default is 9292.
220
229
  If specified multiple times on the command-line, only the last-specified
221
230
  value takes effect.
222
231
  This option only exists for compatibility with the \fIrackup\fR\|(1) command,
@@ -256,24 +265,15 @@ See Rack documentation for a description of the rackup file format.
256
265
  The \s-1RACK_ENV\s0 variable is set by the aforementioned \-E switch.
257
266
  If \s-1RACK_ENV\s0 is already set, it will be used unless \-E is used.
258
267
  See rackup documentation for more details.
259
- .SH "CAVEATS"
260
- .IX Header "CAVEATS"
261
- yahns is strict about buggy, non-compliant Rack applications.
262
- Some existing servers work fine without \*(L"Content-Length\*(R" or
263
- \&\*(L"Transfer-Encoding: chunked\*(R" response headers enforced by Rack::Lint.
264
- Forgetting these headers with yahns causes clients to stall as they
265
- assume more data is coming. Loading the Rack::ContentLength and/or
266
- Rack::Chunked middlewares will set the necessary response headers
267
- and fix your app.
268
268
  .SH "CONTACT"
269
269
  .IX Header "CONTACT"
270
- All feedback welcome via plain-text mail to mailto:yahns\-public@yhbt.net <mailto:yahns-public@yhbt.net>
270
+ All feedback welcome via plain-text mail to <mailto:yahns\-public@yhbt.net>
271
271
  No subscription is necessary to post to the mailing list.
272
- List archives are available at http://yhbt.net/yahns\-public/ <http://yhbt.net/yahns-public/>
272
+ List archives are available at <http://yhbt.net/yahns\-public/>
273
273
  .SH "COPYRIGHT"
274
274
  .IX Header "COPYRIGHT"
275
- Copyright (C) 2013\-2016 all contributors mailto:yahns\-public@yhbt.net <mailto:yahns-public@yhbt.net>
276
- License: \s-1GPL\-3\s0.0+ http://www.gnu.org/licenses/gpl\-3.0.txt <http://www.gnu.org/licenses/gpl-3.0.txt>
275
+ Copyright (C) 2013\-2016 all contributors <mailto:yahns\-public@yhbt.net>
276
+ License: \s-1GPL\-3.0+ \s0<http://www.gnu.org/licenses/gpl\-3.0.txt>
277
277
  .SH "SEE ALSO"
278
278
  .IX Header "SEE ALSO"
279
279
  \&\fIyahns\fR\|(1), \fIyahns_config\fR\|(5),