pitchfork 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -224,7 +224,7 @@ static int is_chunked(VALUE v)
224
224
  return rb_funcall(cHttpParser, id_is_chunked_p, 1, v) != Qfalse;
225
225
  }
226
226
 
227
- static void write_value(struct http_parser *hp,
227
+ static void write_value(VALUE self, struct http_parser *hp,
228
228
  const char *buffer, const char *p)
229
229
  {
230
230
  VALUE f = find_common_field(PTR_TO(start.field), hp->s.field_len);
@@ -244,7 +244,7 @@ static void write_value(struct http_parser *hp,
244
244
  * rack env variable.
245
245
  */
246
246
  if (CONST_MEM_EQ("VERSION", field, flen)) {
247
- hp->cont = Qnil;
247
+ RB_OBJ_WRITE(self, &hp->cont, Qnil);
248
248
  return;
249
249
  }
250
250
  f = uncommon_field(field, flen);
@@ -296,16 +296,16 @@ static void write_value(struct http_parser *hp,
296
296
 
297
297
  e = rb_hash_aref(hp->env, f);
298
298
  if (NIL_P(e)) {
299
- hp->cont = rb_hash_aset(hp->env, f, v);
299
+ RB_OBJ_WRITE(self, &hp->cont, rb_hash_aset(hp->env, f, v));
300
300
  } else if (f == g_http_host) {
301
301
  /*
302
302
  * ignored, absolute URLs in REQUEST_URI take precedence over
303
303
  * the Host: header (ref: rfc 2616, section 5.2.1)
304
304
  */
305
- hp->cont = Qnil;
305
+ RB_OBJ_WRITE(self, &hp->cont, Qnil);
306
306
  } else {
307
307
  rb_str_buf_cat(e, ",", 1);
308
- hp->cont = rb_str_buf_append(e, v);
308
+ RB_OBJ_WRITE(self, &hp->cont, rb_str_buf_append(e, v));
309
309
  }
310
310
  }
311
311
 
@@ -321,7 +321,7 @@ static void write_value(struct http_parser *hp,
321
321
  action downcase_char { downcase_char(deconst(fpc)); }
322
322
  action write_field { hp->s.field_len = LEN(start.field, fpc); }
323
323
  action start_value { MARK(mark, fpc); }
324
- action write_value { write_value(hp, buffer, fpc); }
324
+ action write_value { write_value(self, hp, buffer, fpc); }
325
325
  action write_cont_value { write_cont_value(hp, buffer, fpc); }
326
326
  action request_method { request_method(hp, PTR_TO(mark), LEN(mark, fpc)); }
327
327
  action scheme {
@@ -439,7 +439,7 @@ static void http_parser_init(struct http_parser *hp)
439
439
 
440
440
  /** exec **/
441
441
  static void
442
- http_parser_execute(struct http_parser *hp, char *buffer, size_t len)
442
+ http_parser_execute(VALUE self, struct http_parser *hp, char *buffer, size_t len)
443
443
  {
444
444
  const char *p, *pe;
445
445
  int cs = hp->cs;
@@ -485,9 +485,13 @@ static size_t hp_memsize(const void *ptr)
485
485
  }
486
486
 
487
487
  static const rb_data_type_t hp_type = {
488
- "pitchfork_http",
489
- { hp_mark, RUBY_TYPED_DEFAULT_FREE, hp_memsize, /* reserved */ },
490
- /* parent, data, [ flags ] */
488
+ .wrap_struct_name = "pitchfork_http_parser",
489
+ .function = {
490
+ .dmark = hp_mark,
491
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
492
+ .dsize = hp_memsize,
493
+ },
494
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
491
495
  };
492
496
 
493
497
  static struct http_parser *data_get(VALUE self)
@@ -618,8 +622,8 @@ static VALUE HttpParser_init(VALUE self)
618
622
  struct http_parser *hp = data_get(self);
619
623
 
620
624
  http_parser_init(hp);
621
- hp->buf = rb_str_new(NULL, 0);
622
- hp->env = rb_hash_new();
625
+ RB_OBJ_WRITE(self, &hp->buf, rb_str_new(NULL, 0));
626
+ RB_OBJ_WRITE(self, &hp->env, rb_hash_new());
623
627
 
624
628
  return self;
625
629
  }
@@ -700,7 +704,7 @@ static VALUE HttpParser_parse(VALUE self)
700
704
  if (HP_FL_TEST(hp, TO_CLEAR))
701
705
  HttpParser_clear(self);
702
706
 
703
- http_parser_execute(hp, RSTRING_PTR(data), RSTRING_LEN(data));
707
+ http_parser_execute(self, hp, RSTRING_PTR(data), RSTRING_LEN(data));
704
708
  if (hp->offset > MAX_HEADER_LEN)
705
709
  parser_raise(e413, "HTTP header is too large");
706
710
 
@@ -756,8 +760,8 @@ static VALUE HttpParser_headers(VALUE self, VALUE env, VALUE buf)
756
760
  {
757
761
  struct http_parser *hp = data_get(self);
758
762
 
759
- hp->env = env;
760
- hp->buf = buf;
763
+ RB_OBJ_WRITE(self, &hp->buf, buf);
764
+ RB_OBJ_WRITE(self, &hp->env, env);
761
765
 
762
766
  return HttpParser_parse(self);
763
767
  }
@@ -885,9 +889,9 @@ static VALUE HttpParser_filter_body(VALUE self, VALUE dst, VALUE src)
885
889
  rb_str_resize(dst, srclen); /* we can never copy more than srclen bytes */
886
890
 
887
891
  hp->s.dest_offset = 0;
888
- hp->cont = dst;
889
- hp->buf = src;
890
- http_parser_execute(hp, srcptr, srclen);
892
+ RB_OBJ_WRITE(self, &hp->cont, dst);
893
+ RB_OBJ_WRITE(self, &hp->buf, src);
894
+ http_parser_execute(self, hp, srcptr, srclen);
891
895
  if (hp->cs == http_parser_error)
892
896
  parser_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
893
897
 
@@ -917,7 +921,7 @@ static VALUE HttpParser_filter_body(VALUE self, VALUE dst, VALUE src)
917
921
  * This causes copy-on-write behavior to be triggered anyways
918
922
  * when the +src+ buffer is modified (when reading off the socket).
919
923
  */
920
- hp->buf = src;
924
+ RB_OBJ_WRITE(self, &hp->buf, src);
921
925
  memcpy(RSTRING_PTR(dst), srcptr, nr);
922
926
  hp->len.content -= nr;
923
927
  if (hp->len.content == 0) {
@@ -31,30 +31,29 @@ module Pitchfork
31
31
  :logger => Logger.new($stderr),
32
32
  :worker_processes => 1,
33
33
  :after_fork => lambda { |server, worker|
34
- server.logger.info("worker=#{worker.nr} gen=#{worker.generation} pid=#{$$} spawned")
35
- },
36
- :before_fork => lambda { |server, worker|
37
- server.logger.info("worker=#{worker.nr} gen=#{worker.generation} spawning...")
38
- },
34
+ server.logger.info("worker=#{worker.nr} gen=#{worker.generation} pid=#{$$} spawned")
35
+ },
36
+ :after_promotion => lambda { |server, worker|
37
+ server.logger.info("gen=#{worker.generation} pid=#{$$} promoted")
38
+ },
39
39
  :after_worker_exit => lambda { |server, worker, status|
40
- m = if worker.nil?
41
- "repead unknown process (#{status.inspect})"
42
- elsif worker.mold?
43
- "mold pid=#{worker.pid rescue 'unknown'} gen=#{worker.generation rescue 'unknown'} reaped (#{status.inspect})"
44
- else
45
- "worker=#{worker.nr rescue 'unknown'} pid=#{worker.pid rescue 'unknown'} gen=#{worker.generation rescue 'unknown'} reaped (#{status.inspect})"
46
- end
47
- if status.success?
48
- server.logger.info(m)
49
- else
50
- server.logger.error(m)
51
- end
52
- },
40
+ m = if worker.nil?
41
+ "repead unknown process (#{status.inspect})"
42
+ elsif worker.mold?
43
+ "mold pid=#{worker.pid rescue 'unknown'} gen=#{worker.generation rescue 'unknown'} reaped (#{status.inspect})"
44
+ else
45
+ "worker=#{worker.nr rescue 'unknown'} pid=#{worker.pid rescue 'unknown'} gen=#{worker.generation rescue 'unknown'} reaped (#{status.inspect})"
46
+ end
47
+ if status.success?
48
+ server.logger.info(m)
49
+ else
50
+ server.logger.error(m)
51
+ end
52
+ },
53
53
  :after_worker_ready => lambda { |server, worker|
54
- server.logger.info("worker=#{worker.nr} ready")
55
- },
54
+ server.logger.info("worker=#{worker.nr} gen=#{worker.generation} ready")
55
+ },
56
56
  :early_hints => false,
57
- :mold_selector => MoldSelector::LeastSharedMemory.new,
58
57
  :refork_condition => nil,
59
58
  :check_client_connection => false,
60
59
  :rewindable_input => true,
@@ -85,9 +84,6 @@ module Pitchfork
85
84
 
86
85
  RACKUP[:set_listener] and
87
86
  set[:listeners] << "#{RACKUP[:host]}:#{RACKUP[:port]}"
88
-
89
- RACKUP[:no_default_middleware] and
90
- set[:default_middleware] = false
91
87
  end
92
88
 
93
89
  def commit!(server, options = {}) #:nodoc:
@@ -123,14 +119,14 @@ module Pitchfork
123
119
  set[:logger] = obj
124
120
  end
125
121
 
126
- def before_fork(*args, &block)
127
- set_hook(:before_fork, block_given? ? block : args[0])
128
- end
129
-
130
122
  def after_fork(*args, &block)
131
123
  set_hook(:after_fork, block_given? ? block : args[0])
132
124
  end
133
125
 
126
+ def after_promotion(*args, &block)
127
+ set_hook(:after_promotion, block_given? ? block : args[0])
128
+ end
129
+
134
130
  def after_worker_ready(*args, &block)
135
131
  set_hook(:after_worker_ready, block_given? ? block : args[0])
136
132
  end
@@ -139,10 +135,6 @@ module Pitchfork
139
135
  set_hook(:after_worker_exit, block_given? ? block : args[0], 3)
140
136
  end
141
137
 
142
- def mold_selector(*args, &block)
143
- set_hook(:mold_selector, block_given? ? block : args[0], 3)
144
- end
145
-
146
138
  def timeout(seconds)
147
139
  set_int(:timeout, seconds, 3)
148
140
  # POSIX says 31 days is the smallest allowed maximum timeout for select()
@@ -154,10 +146,6 @@ module Pitchfork
154
146
  set_int(:worker_processes, nr, 1)
155
147
  end
156
148
 
157
- def default_middleware(bool)
158
- set_bool(:default_middleware, bool)
159
- end
160
-
161
149
  def early_hints(bool)
162
150
  set_bool(:early_hints, bool)
163
151
  end
@@ -208,8 +196,12 @@ module Pitchfork
208
196
  # Defines the number of requests per-worker after which a new generation
209
197
  # should be spawned.
210
198
  #
199
+ # +false+ can be used to mark a final generation, otherwise the last request
200
+ # count is re-used indefinitely.
201
+ #
211
202
  # example:
212
203
  #. refork_after [50, 100, 1000]
204
+ #. refork_after [50, 100, 1000, false]
213
205
  #
214
206
  # Note that reforking is only available on Linux. Other Unix-like systems
215
207
  # don't have this capability.
@@ -0,0 +1,51 @@
1
+ require 'tempfile'
2
+
3
+ module Pitchfork
4
+ class Flock
5
+ Error = Class.new(StandardError)
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ @file = Tempfile.create([name, '.lock'])
10
+ @file.write("#{Process.pid}\n")
11
+ @file.flush
12
+ @owned = false
13
+ end
14
+
15
+ def at_fork
16
+ @owned = false
17
+ @file.close
18
+ @file = File.open(@file.path, "w")
19
+ nil
20
+ end
21
+
22
+ def unlink
23
+ File.unlink(@file.path)
24
+ rescue Errno::ENOENT
25
+ false
26
+ end
27
+
28
+ def try_lock
29
+ raise Error, "Pitchfork::Flock(#{@name}) trying to lock an already owned lock" if @owned
30
+
31
+ if @file.flock(File::LOCK_EX | File::LOCK_NB)
32
+ @owned = true
33
+ else
34
+ false
35
+ end
36
+ end
37
+
38
+ def unlock
39
+ raise Error, "Pitchfork::Flock(#{@name}) trying to unlock a non-owned lock" unless @owned
40
+
41
+ begin
42
+ if @file.flock(File::LOCK_UN)
43
+ @owned = false
44
+ true
45
+ else
46
+ false
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -20,6 +20,18 @@ module Pitchfork
20
20
  "#{code} #{STATUS_CODES[code]}\r\n\r\n"
21
21
  end
22
22
 
23
+ def append_header(buf, key, value)
24
+ case value
25
+ when Array # Rack 3
26
+ value.each { |v| buf << "#{key}: #{v}\r\n" }
27
+ when /\n/ # Rack 2
28
+ # avoiding blank, key-only cookies with /\n+/
29
+ value.split(/\n+/).each { |v| buf << "#{key}: #{v}\r\n" }
30
+ else
31
+ buf << "#{key}: #{value}\r\n"
32
+ end
33
+ end
34
+
23
35
  # writes the rack_response to socket as an HTTP response
24
36
  def http_response_write(socket, status, headers, body,
25
37
  req = Pitchfork::HttpParser.new)
@@ -41,12 +53,7 @@ module Pitchfork
41
53
  # key in Rack < 1.5
42
54
  hijack = value
43
55
  else
44
- if value =~ /\n/
45
- # avoiding blank, key-only cookies with /\n+/
46
- value.split(/\n+/).each { |v| buf << "#{key}: #{v}\r\n" }
47
- else
48
- buf << "#{key}: #{value}\r\n"
49
- end
56
+ append_header(buf, key, value)
50
57
  end
51
58
  end
52
59
  socket.write(buf << "\r\n".freeze)