rev 0.1.0 → 0.1.1

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.
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Copyright (c) 2005 Zed A. Shaw
3
+ * You can redistribute it and/or modify it under the same terms as Ruby.
4
+ */
5
+
6
+ #ifndef http11_parser_h
7
+ #define http11_parser_h
8
+
9
+ #include <sys/types.h>
10
+
11
+ #if defined(_WIN32)
12
+ #include <stddef.h>
13
+ #endif
14
+
15
+ typedef void (*element_cb)(void *data, const char *at, size_t length);
16
+ typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);
17
+
18
+ typedef struct httpclient_parser {
19
+ int cs;
20
+ size_t body_start;
21
+ int content_len;
22
+ size_t nread;
23
+ size_t mark;
24
+ size_t field_start;
25
+ size_t field_len;
26
+
27
+ void *data;
28
+
29
+ field_cb http_field;
30
+ element_cb reason_phrase;
31
+ element_cb status_code;
32
+ element_cb chunk_size;
33
+ element_cb http_version;
34
+ element_cb header_done;
35
+ element_cb last_chunk;
36
+
37
+
38
+ } httpclient_parser;
39
+
40
+ int httpclient_parser_init(httpclient_parser *parser);
41
+ int httpclient_parser_finish(httpclient_parser *parser);
42
+ size_t httpclient_parser_execute(httpclient_parser *parser, const char *data, size_t len, size_t off);
43
+ int httpclient_parser_has_error(httpclient_parser *parser);
44
+ int httpclient_parser_is_finished(httpclient_parser *parser);
45
+
46
+ #define httpclient_parser_nread(parser) (parser)->nread
47
+
48
+ #endif
@@ -28,6 +28,7 @@ static VALUE Rev_Watcher_enable(VALUE self);
28
28
  static VALUE Rev_Watcher_disable(VALUE self);
29
29
  static VALUE Rev_Watcher_evloop(VALUE self);
30
30
  static VALUE Rev_Watcher_attached(VALUE self);
31
+ static VALUE Rev_Watcher_enabled(VALUE self);
31
32
 
32
33
  void Init_rev_watcher()
33
34
  {
@@ -42,6 +43,7 @@ void Init_rev_watcher()
42
43
  rb_define_method(cRev_Watcher, "disable", Rev_Watcher_disable, 0);
43
44
  rb_define_method(cRev_Watcher, "evloop", Rev_Watcher_evloop, 0);
44
45
  rb_define_method(cRev_Watcher, "attached?", Rev_Watcher_attached, 0);
46
+ rb_define_method(cRev_Watcher, "enabled?", Rev_Watcher_enabled, 0);
45
47
  }
46
48
 
47
49
  static VALUE Rev_Watcher_allocate(VALUE klass)
@@ -220,3 +222,17 @@ static VALUE Rev_Watcher_attached(VALUE self)
220
222
  {
221
223
  return Rev_Watcher_evloop(self) != Qnil;
222
224
  }
225
+
226
+ /**
227
+ * call-seq:
228
+ * Rev::Watcher.enabled? -> Boolean
229
+ *
230
+ * Is the watcher currently enabled?
231
+ */
232
+ static VALUE Rev_Watcher_enabled(VALUE self)
233
+ {
234
+ struct Rev_Watcher *watcher_data;
235
+ Data_Get_Struct(self, struct Rev_Watcher, watcher_data);
236
+
237
+ return watcher_data->enabled ? Qtrue : Qfalse;
238
+ }
@@ -8,6 +8,9 @@ require File.dirname(__FILE__) + '/../rev'
8
8
 
9
9
  module Rev
10
10
  class BufferedIO < IOWatcher
11
+ # Maximum number of bytes to consume at once
12
+ INPUT_SIZE = 16384
13
+
11
14
  def initialize(io)
12
15
  # Output buffer
13
16
  @write_buffer = ''
@@ -41,7 +44,7 @@ module Rev
41
44
  def write(data)
42
45
  # Attempt a zero copy write
43
46
  if @write_buffer.empty?
44
- written = @io.write_nonblock data
47
+ written = write_nonblock data
45
48
 
46
49
  # If we lucked out and wrote out the whole buffer, return
47
50
  if written == data.size
@@ -49,12 +52,11 @@ module Rev
49
52
  return data.size
50
53
  end
51
54
 
52
- # Otherwise append the remaining data to the buffer
53
- @write_buffer << data[written..data.size]
54
- else
55
- @write_buffer << data
55
+ # Otherwise slice what we wrote out and begin buffered writing
56
+ data.slice!(0, written) if written
56
57
  end
57
-
58
+
59
+ @write_buffer << data
58
60
  schedule_write
59
61
  data.size
60
62
  end
@@ -64,37 +66,47 @@ module Rev
64
66
  @write_buffer.size
65
67
  end
66
68
 
69
+ # Close the BufferedIO stream
70
+ def close
71
+ detach if attached?
72
+ @writer.detach if @writer and @writer.attached?
73
+ @io.close unless @io.closed?
74
+
75
+ on_close
76
+ end
77
+
78
+ #########
79
+ protected
80
+ #########
81
+
67
82
  # Attempt to write the contents of the output buffer
68
83
  def write_output_buffer
69
84
  return if @write_buffer.empty?
70
85
 
71
- written = @io.write_nonblock @write_buffer
72
- @write_buffer.slice!(written, @write_buffer.size)
86
+ written = write_nonblock @write_buffer
87
+ @write_buffer.slice!(0, written) if written
73
88
 
74
89
  return unless @write_buffer.empty?
75
90
 
76
91
  @writer.disable if @writer and @writer.enabled?
77
92
  on_write_complete
78
93
  end
79
-
80
- # Close the BufferedIO stream
81
- def close
82
- detach if attached?
83
- @writer.detach if @writer and @writer.attached?
84
- @io.close
85
-
86
- on_close
94
+
95
+ # Wrapper for handling reset connections and EAGAIN
96
+ def write_nonblock(data)
97
+ begin
98
+ @io.write_nonblock(data)
99
+ rescue Errno::ECONNRESET, Errno::EPIPE
100
+ close
101
+ rescue Errno::EAGAIN
102
+ end
87
103
  end
88
104
 
89
- #########
90
- protected
91
- #########
92
-
93
105
  # Inherited callback from IOWatcher
94
106
  def on_readable
95
107
  begin
96
- on_read @io.read_nonblock(4096)
97
- rescue EOFError
108
+ on_read @io.read_nonblock(INPUT_SIZE)
109
+ rescue Errno::ECONNRESET, EOFError
98
110
  close
99
111
  end
100
112
  end
@@ -103,8 +115,13 @@ module Rev
103
115
  return if @writer and @writer.enabled?
104
116
  if @writer
105
117
  @writer.enable
106
- else
107
- @writer = Writer.new(@io, self)
118
+ else
119
+ begin
120
+ @writer = Writer.new(@io, self)
121
+ rescue IOError
122
+ return
123
+ end
124
+
108
125
  @writer.attach(evloop)
109
126
  end
110
127
  end
@@ -116,7 +133,7 @@ module Rev
116
133
  end
117
134
 
118
135
  def on_writable
119
- @buffered_io.write_output_buffer
136
+ @buffered_io.__send__(:write_output_buffer)
120
137
  end
121
138
  end
122
139
  end
@@ -37,17 +37,17 @@ module Rev
37
37
  end
38
38
 
39
39
  def initialize(hostname, *nameservers)
40
- if nameservers.nil? or nameservers.empty?
40
+ if nameservers.empty?
41
41
  nameservers = File.read(RESOLV_CONF).scan(/^\s*nameserver\s+([0-9.:]+)/).flatten
42
42
  raise RuntimeError, "no nameservers found in #{RESOLV_CONF}" if nameservers.empty?
43
43
  end
44
44
 
45
45
  @nameservers = nameservers
46
- @request = request_message hostname
47
46
  @question = request_question hostname
48
47
 
49
48
  @socket = UDPSocket.new
50
49
  @timer = Timeout.new(self)
50
+
51
51
  super(@socket)
52
52
  end
53
53
 
@@ -65,7 +65,7 @@ module Rev
65
65
  # Send a request to the DNS server
66
66
  def send_request
67
67
  @socket.connect @nameservers.first, DNS_PORT
68
- @socket.send @request, 0
68
+ @socket.send request_message, 0
69
69
  end
70
70
 
71
71
  # Called when the name has successfully resolved to an address
@@ -83,7 +83,7 @@ module Rev
83
83
  #########
84
84
  protected
85
85
  #########
86
-
86
+
87
87
  # Called by the subclass when the DNS response is available
88
88
  def on_readable
89
89
  datagram = @socket.recvfrom_nonblock(DATAGRAM_SIZE).first
@@ -92,23 +92,9 @@ module Rev
92
92
  detach
93
93
  end
94
94
 
95
- def request_message(hostname)
96
- # Standard query header
97
- message = "\000\002\001\000"
98
-
99
- # One entry
100
- qdcount = 1
101
-
102
- # No answer, authority, or additional records
103
- ancount = nscount = arcount = 0
104
-
105
- message << [qdcount, ancount, nscount, arcount].pack('nnnn')
106
- message << request_question(hostname)
107
- end
108
-
109
95
  def request_question(hostname)
110
96
  # Query name
111
- message = hostname.split('.').map { |s| [s.size].pack('C') << s }.join + "\000"
97
+ message = hostname.split('.').map { |s| [s.size].pack('C') << s }.join + "\0"
112
98
 
113
99
  # Host address query
114
100
  qtype = 1
@@ -119,6 +105,20 @@ module Rev
119
105
  message << [qtype, qclass].pack('nn')
120
106
  end
121
107
 
108
+ def request_message
109
+ # Standard query header
110
+ message = [2, 1, 0].pack('nCC')
111
+
112
+ # One entry
113
+ qdcount = 1
114
+
115
+ # No answer, authority, or additional records
116
+ ancount = nscount = arcount = 0
117
+
118
+ message << [qdcount, ancount, nscount, arcount].pack('nnnn')
119
+ message << @question
120
+ end
121
+
122
122
  def response_address(message)
123
123
  # Confirm the ID field
124
124
  id = message[0..1].unpack('n').first.to_i
@@ -128,7 +128,7 @@ module Rev
128
128
  qr = message[2].unpack('B1').first.to_i
129
129
  return unless qr == 1
130
130
 
131
- # Check the RCODE and ensure there wasn't an error
131
+ # Check the RCODE (lower nibble) and ensure there wasn't an error
132
132
  rcode = message[3].unpack('B8').first[4..7].to_i(2)
133
133
  return unless rcode == 0
134
134
 
@@ -137,22 +137,23 @@ module Rev
137
137
 
138
138
  # We only asked one question
139
139
  return unless qdcount == 1
140
- message = message[12..message.size] # slice! would be nice but I think I hit a 1.9 bug...
140
+ message.slice!(0, 12)
141
141
 
142
142
  # Make sure it's the same question
143
143
  return unless message[0..(@question.size-1)] == @question
144
- message = message[@question.size..message.size]
144
+ message.slice!(0, @question.size)
145
145
 
146
146
  # Extract the RDLENGTH
147
147
  while not message.empty?
148
148
  type = message[2..3].unpack('n').first.to_i
149
149
  rdlength = message[10..11].unpack('n').first.to_i
150
150
  rdata = message[12..(12 + rdlength - 1)]
151
- message = message[(12+rdlength)..message.size]
151
+ message.slice!(0, 12 + rdlength)
152
152
 
153
153
  # Only IPv4 supported
154
154
  next unless rdlength == 4
155
155
 
156
+ # If we got an Internet address back, return it
156
157
  return rdata.unpack('CCCC').join('.') if type == 1
157
158
  end
158
159
 
@@ -169,10 +170,10 @@ module Rev
169
170
  def on_timer
170
171
  @attempts += 1
171
172
  return @resolver.send_request if @attempts <= RETRIES
172
-
173
- @resolver.on_timeout
173
+
174
+ @resolver.__send__(:on_timeout)
174
175
  @resolver.detach
175
176
  end
176
177
  end
177
178
  end
178
- end
179
+ end