pitchfork 0.1.1 → 0.2.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.

Potentially problematic release.


This version of pitchfork might be problematic. Click here for more details.

@@ -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)