rev 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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