em-http-request 0.2.7 → 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of em-http-request might be problematic. Click here for more details.
- data/README.rdoc +29 -11
- data/Rakefile +5 -4
- data/VERSION +1 -1
- data/em-http-request.gemspec +97 -0
- data/examples/fetch.rb +1 -1
- data/examples/websocket-handler.rb +28 -0
- data/examples/websocket-server.rb +8 -0
- data/ext/buffer/em_buffer.c +74 -65
- data/lib/em-http-request.rb +1 -0
- data/lib/em-http.rb +6 -3
- data/lib/em-http/client.rb +114 -90
- data/lib/em-http/core_ext/bytesize.rb +6 -0
- data/lib/em-http/http_options.rb +32 -0
- data/lib/em-http/mock.rb +38 -15
- data/lib/em-http/request.rb +78 -102
- data/spec/mock_spec.rb +49 -4
- data/spec/multi_spec.rb +2 -2
- data/spec/request_spec.rb +67 -10
- data/spec/stallion.rb +10 -6
- metadata +39 -14
data/README.rdoc
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
= EM-HTTP-Client
|
2
2
|
|
3
|
-
EventMachine based HTTP Request interface. Supports streaming response processing, uses Ragel HTTP parser.
|
3
|
+
EventMachine based HTTP Request interface. Supports streaming response processing, uses Ragel HTTP parser.
|
4
4
|
- Simple interface for single & parallel requests via deferred callbacks
|
5
5
|
- Automatic gzip & deflate decoding
|
6
6
|
- Basic-Auth & OAuth support
|
7
7
|
- Custom timeouts
|
8
8
|
- Proxy support (with SSL Tunneling)
|
9
|
+
- Auto-follow 3xx redirects with max depth
|
9
10
|
- Bi-directional communication with web-socket services
|
10
11
|
|
11
|
-
Screencast / Demo of using EM-HTTP-Request:
|
12
|
+
Screencast / Demo of using EM-HTTP-Request:
|
12
13
|
- http://everburning.com/news/eventmachine-screencast-em-http-request/
|
13
14
|
|
14
15
|
== Getting started
|
@@ -26,12 +27,12 @@ Screencast / Demo of using EM-HTTP-Request:
|
|
26
27
|
|
27
28
|
EventMachine.run {
|
28
29
|
http = EventMachine::HttpRequest.new('http://127.0.0.1/').get :query => {'keyname' => 'value'}, :timeout => 10
|
29
|
-
|
30
|
+
|
30
31
|
http.callback {
|
31
32
|
p http.response_header.status
|
32
33
|
p http.response_header
|
33
34
|
p http.response
|
34
|
-
|
35
|
+
|
35
36
|
EventMachine.stop
|
36
37
|
}
|
37
38
|
}
|
@@ -41,15 +42,15 @@ Fire and wait for multiple requess to complete via the MultiRequest interface.
|
|
41
42
|
|
42
43
|
EventMachine.run {
|
43
44
|
multi = EventMachine::MultiRequest.new
|
44
|
-
|
45
|
+
|
45
46
|
# add multiple requests to the multi-handler
|
46
47
|
multi.add(EventMachine::HttpRequest.new('http://www.google.com/').get)
|
47
48
|
multi.add(EventMachine::HttpRequest.new('http://www.yahoo.com/').get)
|
48
|
-
|
49
|
+
|
49
50
|
multi.callback {
|
50
51
|
p multi.responses[:succeeded]
|
51
52
|
p multi.responses[:failed]
|
52
|
-
|
53
|
+
|
53
54
|
EventMachine.stop
|
54
55
|
}
|
55
56
|
}
|
@@ -59,7 +60,7 @@ Full basic author support. For OAuth, check examples/oauth-tweet.rb file.
|
|
59
60
|
|
60
61
|
EventMachine.run {
|
61
62
|
http = EventMachine::HttpRequest.new('http://www.website.com/').get :head => {'authorization' => ['user', 'pass']}
|
62
|
-
|
63
|
+
|
63
64
|
http.errback { failed }
|
64
65
|
http.callback {
|
65
66
|
p http.response_header
|
@@ -72,7 +73,7 @@ Full basic author support. For OAuth, check examples/oauth-tweet.rb file.
|
|
72
73
|
EventMachine.run {
|
73
74
|
http1 = EventMachine::HttpRequest.new('http://www.website.com/').post :body => {"key1" => 1, "key2" => [2,3]}
|
74
75
|
http2 = EventMachine::HttpRequest.new('http://www.website.com/').post :body => "some data"
|
75
|
-
|
76
|
+
|
76
77
|
# ...
|
77
78
|
}
|
78
79
|
|
@@ -83,8 +84,14 @@ to the client, it is passed to the stream callback for you to operate on.
|
|
83
84
|
EventMachine.run {
|
84
85
|
http = EventMachine::HttpRequest.new('http://www.website.com/').get
|
85
86
|
http.stream { |chunk| print chunk }
|
87
|
+
}
|
86
88
|
|
87
|
-
|
89
|
+
== Streaming file from disk
|
90
|
+
Allows you to efficiently stream a (large) file from disk via EventMachine's FileStream interface.
|
91
|
+
|
92
|
+
EventMachine.run {
|
93
|
+
http = EventMachine::HttpRequest.new('http://www.website.com/').post :file => 'largefile.txt'
|
94
|
+
http.callback { |chunk| puts "Upload finished!" }
|
88
95
|
}
|
89
96
|
|
90
97
|
== Proxy example
|
@@ -97,6 +104,15 @@ Full transparent proxy support with support for SSL tunneling.
|
|
97
104
|
:authorization => ['username', 'password'] # authorization is optional
|
98
105
|
}
|
99
106
|
|
107
|
+
== Auto-follow 3xx redirects
|
108
|
+
Specify the max depth of redirects to follow, default is 0.
|
109
|
+
|
110
|
+
EventMachine.run {
|
111
|
+
http = EventMachine::HttpRequest.new('http://www.google.com/').get :redirect => 1
|
112
|
+
http.callback { p http.last_effective_url }
|
113
|
+
}
|
114
|
+
|
115
|
+
|
100
116
|
== WebSocket example
|
101
117
|
Bi-directional communication with WebSockets: simply pass in a ws:// resource and the client will
|
102
118
|
negotiate the connection upgrade for you. On successfull handshake the callback is invoked, and
|
@@ -106,7 +122,7 @@ server at will by calling the "send" method!
|
|
106
122
|
|
107
123
|
EventMachine.run {
|
108
124
|
http = EventMachine::HttpRequest.new("ws://yourservice.com/websocket").get :timeout => 0
|
109
|
-
|
125
|
+
|
110
126
|
http.errback { puts "oops" }
|
111
127
|
http.callback {
|
112
128
|
puts "WebSocket connected!"
|
@@ -117,4 +133,6 @@ server at will by calling the "send" method!
|
|
117
133
|
puts "Recieved: #{msg}"
|
118
134
|
http.send "Pong: #{msg}"
|
119
135
|
}
|
136
|
+
|
137
|
+
http.disconnect { puts "oops, dropped connection?" }
|
120
138
|
}
|
data/Rakefile
CHANGED
@@ -35,7 +35,7 @@ task :ragel do
|
|
35
35
|
end
|
36
36
|
|
37
37
|
task :spec do
|
38
|
-
|
38
|
+
sh 'spec spec/*_spec.rb'
|
39
39
|
end
|
40
40
|
|
41
41
|
def make(makedir)
|
@@ -55,8 +55,8 @@ def setup_extension(dir, extension)
|
|
55
55
|
"#{ext}/extconf.rb",
|
56
56
|
"#{ext}/Makefile",
|
57
57
|
"lib"
|
58
|
-
]
|
59
|
-
|
58
|
+
]
|
59
|
+
|
60
60
|
task "lib" do
|
61
61
|
directory "lib"
|
62
62
|
end
|
@@ -92,13 +92,14 @@ begin
|
|
92
92
|
gemspec.email = "ilya@igvita.com"
|
93
93
|
gemspec.homepage = "http://github.com/igrigorik/em-http-request"
|
94
94
|
gemspec.authors = ["Ilya Grigorik"]
|
95
|
+
gemspec.required_ruby_version = ">= 1.8.6"
|
95
96
|
gemspec.extensions = ["ext/buffer/extconf.rb" , "ext/http11_client/extconf.rb"]
|
96
97
|
gemspec.add_dependency('eventmachine', '>= 0.12.9')
|
97
98
|
gemspec.add_dependency('addressable', '>= 2.0.0')
|
98
99
|
gemspec.rubyforge_project = "em-http-request"
|
99
100
|
gemspec.files = FileList[`git ls-files`.split]
|
100
101
|
end
|
101
|
-
|
102
|
+
|
102
103
|
Jeweler::GemcutterTasks.new
|
103
104
|
rescue LoadError
|
104
105
|
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.9
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{em-http-request}
|
8
|
+
s.version = "0.2.9"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Ilya Grigorik"]
|
12
|
+
s.date = %q{2010-07-07}
|
13
|
+
s.description = %q{EventMachine based, async HTTP Request interface}
|
14
|
+
s.email = %q{ilya@igvita.com}
|
15
|
+
s.extensions = ["ext/buffer/extconf.rb", "ext/http11_client/extconf.rb"]
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"LICENSE",
|
18
|
+
"README.rdoc"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"em-http-request.gemspec",
|
27
|
+
"examples/fetch.rb",
|
28
|
+
"examples/fibered-http.rb",
|
29
|
+
"examples/oauth-tweet.rb",
|
30
|
+
"examples/websocket-handler.rb",
|
31
|
+
"examples/websocket-server.rb",
|
32
|
+
"ext/buffer/em_buffer.c",
|
33
|
+
"ext/buffer/extconf.rb",
|
34
|
+
"ext/http11_client/ext_help.h",
|
35
|
+
"ext/http11_client/extconf.rb",
|
36
|
+
"ext/http11_client/http11_client.c",
|
37
|
+
"ext/http11_client/http11_parser.c",
|
38
|
+
"ext/http11_client/http11_parser.h",
|
39
|
+
"ext/http11_client/http11_parser.rl",
|
40
|
+
"lib/em-http-request.rb",
|
41
|
+
"lib/em-http.rb",
|
42
|
+
"lib/em-http/client.rb",
|
43
|
+
"lib/em-http/core_ext/bytesize.rb",
|
44
|
+
"lib/em-http/core_ext/hash.rb",
|
45
|
+
"lib/em-http/decoders.rb",
|
46
|
+
"lib/em-http/http_options.rb",
|
47
|
+
"lib/em-http/mock.rb",
|
48
|
+
"lib/em-http/multi.rb",
|
49
|
+
"lib/em-http/request.rb",
|
50
|
+
"spec/fixtures/google.ca",
|
51
|
+
"spec/hash_spec.rb",
|
52
|
+
"spec/helper.rb",
|
53
|
+
"spec/mock_spec.rb",
|
54
|
+
"spec/multi_spec.rb",
|
55
|
+
"spec/request_spec.rb",
|
56
|
+
"spec/stallion.rb",
|
57
|
+
"spec/stub_server.rb"
|
58
|
+
]
|
59
|
+
s.homepage = %q{http://github.com/igrigorik/em-http-request}
|
60
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
61
|
+
s.require_paths = ["lib"]
|
62
|
+
s.required_ruby_version = Gem::Requirement.new(">= 1.8.6")
|
63
|
+
s.rubyforge_project = %q{em-http-request}
|
64
|
+
s.rubygems_version = %q{1.3.6}
|
65
|
+
s.summary = %q{EventMachine based, async HTTP Request interface}
|
66
|
+
s.test_files = [
|
67
|
+
"spec/hash_spec.rb",
|
68
|
+
"spec/helper.rb",
|
69
|
+
"spec/mock_spec.rb",
|
70
|
+
"spec/multi_spec.rb",
|
71
|
+
"spec/request_spec.rb",
|
72
|
+
"spec/stallion.rb",
|
73
|
+
"spec/stub_server.rb",
|
74
|
+
"examples/fetch.rb",
|
75
|
+
"examples/fibered-http.rb",
|
76
|
+
"examples/oauth-tweet.rb",
|
77
|
+
"examples/websocket-handler.rb",
|
78
|
+
"examples/websocket-server.rb"
|
79
|
+
]
|
80
|
+
|
81
|
+
if s.respond_to? :specification_version then
|
82
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
83
|
+
s.specification_version = 3
|
84
|
+
|
85
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
86
|
+
s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.9"])
|
87
|
+
s.add_runtime_dependency(%q<addressable>, [">= 2.0.0"])
|
88
|
+
else
|
89
|
+
s.add_dependency(%q<eventmachine>, [">= 0.12.9"])
|
90
|
+
s.add_dependency(%q<addressable>, [">= 2.0.0"])
|
91
|
+
end
|
92
|
+
else
|
93
|
+
s.add_dependency(%q<eventmachine>, [">= 0.12.9"])
|
94
|
+
s.add_dependency(%q<addressable>, [">= 2.0.0"])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
data/examples/fetch.rb
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'lib/em-http'
|
3
|
+
|
4
|
+
module KBHandler
|
5
|
+
include EM::Protocols::LineText2
|
6
|
+
|
7
|
+
def receive_line(data)
|
8
|
+
p "Want to send: #{data}"
|
9
|
+
p "Error status: #{$http.error?}"
|
10
|
+
$http.send(data)
|
11
|
+
p "After send"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
EventMachine.run {
|
16
|
+
$http = EventMachine::HttpRequest.new("ws://localhost:8080/").get :timeout => 0
|
17
|
+
|
18
|
+
$http.disconnect { puts 'oops' }
|
19
|
+
$http.callback {
|
20
|
+
puts "WebSocket connected!"
|
21
|
+
}
|
22
|
+
|
23
|
+
$http.stream { |msg|
|
24
|
+
puts "Recieved: #{msg}"
|
25
|
+
}
|
26
|
+
|
27
|
+
EM.open_keyboard(KBHandler)
|
28
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'em-websocket'
|
3
|
+
|
4
|
+
EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |ws|
|
5
|
+
ws.onopen { ws.send "Hello Client!"}
|
6
|
+
ws.onmessage { |msg| p "got: #{msg}"; ws.send "Pong: #{msg}" }
|
7
|
+
ws.onclose { puts "WebSocket closed" }
|
8
|
+
end
|
data/ext/buffer/em_buffer.c
CHANGED
@@ -29,15 +29,15 @@
|
|
29
29
|
#define PURGE_INTERVAL 10
|
30
30
|
|
31
31
|
struct buffer {
|
32
|
-
|
32
|
+
time_t last_purged_at;
|
33
33
|
unsigned size, node_size;
|
34
34
|
struct buffer_node *head, *tail;
|
35
35
|
struct buffer_node *pool_head, *pool_tail;
|
36
|
-
|
36
|
+
|
37
37
|
};
|
38
38
|
|
39
39
|
struct buffer_node {
|
40
|
-
|
40
|
+
time_t last_used_at;
|
41
41
|
unsigned start, end;
|
42
42
|
struct buffer_node *next;
|
43
43
|
unsigned char data[0];
|
@@ -72,7 +72,7 @@ static void buffer_copy(struct buffer *buf, char *str, unsigned len);
|
|
72
72
|
static int buffer_read_from(struct buffer *buf, int fd);
|
73
73
|
static int buffer_write_to(struct buffer *buf, int fd);
|
74
74
|
|
75
|
-
/*
|
75
|
+
/*
|
76
76
|
* High speed buffering geared towards non-blocking I/O.
|
77
77
|
*
|
78
78
|
* Data is stored in a byte queue implemented as a linked list of equal size
|
@@ -94,8 +94,8 @@ void Init_em_buffer()
|
|
94
94
|
rb_define_method(cEm_Buffer, "append", Em_Buffer_append, 1);
|
95
95
|
rb_define_method(cEm_Buffer, "prepend", Em_Buffer_prepend, 1);
|
96
96
|
rb_define_method(cEm_Buffer, "read", Em_Buffer_read, -1);
|
97
|
-
|
98
|
-
|
97
|
+
rb_define_method(cEm_Buffer, "to_str", Em_Buffer_to_str, 0);
|
98
|
+
rb_define_method(cEm_Buffer, "read_from", Em_Buffer_read_from, 1);
|
99
99
|
rb_define_method(cEm_Buffer, "write_to", Em_Buffer_write_to, 1);
|
100
100
|
}
|
101
101
|
|
@@ -118,7 +118,7 @@ static void Em_Buffer_free(struct buffer *buf)
|
|
118
118
|
/**
|
119
119
|
* call-seq:
|
120
120
|
* EventMachine::Buffer.new(size = DEFAULT_NODE_SIZE) -> EventMachine::Buffer
|
121
|
-
*
|
121
|
+
*
|
122
122
|
* Create a new EventMachine::Buffer with linked segments of the given size
|
123
123
|
*/
|
124
124
|
static VALUE Em_Buffer_initialize(int argc, VALUE *argv, VALUE self)
|
@@ -147,7 +147,7 @@ static VALUE Em_Buffer_initialize(int argc, VALUE *argv, VALUE self)
|
|
147
147
|
/**
|
148
148
|
* call-seq:
|
149
149
|
* EventMachine::Buffer#clear -> nil
|
150
|
-
*
|
150
|
+
*
|
151
151
|
* Clear all data from the EventMachine::Buffer
|
152
152
|
*/
|
153
153
|
static VALUE Em_Buffer_clear(VALUE self)
|
@@ -163,10 +163,10 @@ static VALUE Em_Buffer_clear(VALUE self)
|
|
163
163
|
/**
|
164
164
|
* call-seq:
|
165
165
|
* EventMachine::Buffer#size -> Integer
|
166
|
-
*
|
166
|
+
*
|
167
167
|
* Return the size of the buffer in bytes
|
168
168
|
*/
|
169
|
-
static VALUE Em_Buffer_size(VALUE self)
|
169
|
+
static VALUE Em_Buffer_size(VALUE self)
|
170
170
|
{
|
171
171
|
struct buffer *buf;
|
172
172
|
Data_Get_Struct(self, struct buffer, buf);
|
@@ -177,21 +177,21 @@ static VALUE Em_Buffer_size(VALUE self)
|
|
177
177
|
/**
|
178
178
|
* call-seq:
|
179
179
|
* EventMachine::Buffer#empty? -> Boolean
|
180
|
-
*
|
180
|
+
*
|
181
181
|
* Is the buffer empty?
|
182
182
|
*/
|
183
|
-
static VALUE Em_Buffer_empty(VALUE self)
|
183
|
+
static VALUE Em_Buffer_empty(VALUE self)
|
184
184
|
{
|
185
185
|
struct buffer *buf;
|
186
186
|
Data_Get_Struct(self, struct buffer, buf);
|
187
187
|
|
188
|
-
return buf->size > 0 ? Qfalse : Qtrue;
|
188
|
+
return buf->size > 0 ? Qfalse : Qtrue;
|
189
189
|
}
|
190
190
|
|
191
191
|
/**
|
192
192
|
* call-seq:
|
193
193
|
* EventMachine::Buffer#append(data) -> String
|
194
|
-
*
|
194
|
+
*
|
195
195
|
* Append the given data to the end of the buffer
|
196
196
|
*/
|
197
197
|
static VALUE Em_Buffer_append(VALUE self, VALUE data)
|
@@ -209,7 +209,7 @@ static VALUE Em_Buffer_append(VALUE self, VALUE data)
|
|
209
209
|
/**
|
210
210
|
* call-seq:
|
211
211
|
* EventMachine::Buffer#prepend(data) -> String
|
212
|
-
*
|
212
|
+
*
|
213
213
|
* Prepend the given data to the beginning of the buffer
|
214
214
|
*/
|
215
215
|
static VALUE Em_Buffer_prepend(VALUE self, VALUE data)
|
@@ -226,7 +226,7 @@ static VALUE Em_Buffer_prepend(VALUE self, VALUE data)
|
|
226
226
|
/**
|
227
227
|
* call-seq:
|
228
228
|
* EventMachine::Buffer#read(length = nil) -> String
|
229
|
-
*
|
229
|
+
*
|
230
230
|
* Read the specified abount of data from the buffer. If no value
|
231
231
|
* is given the entire contents of the buffer are returned. Any data
|
232
232
|
* read from the buffer is cleared.
|
@@ -263,31 +263,31 @@ static VALUE Em_Buffer_read(int argc, VALUE *argv, VALUE self)
|
|
263
263
|
/**
|
264
264
|
* call-seq:
|
265
265
|
* EventMachine::Buffer#to_str -> String
|
266
|
-
*
|
266
|
+
*
|
267
267
|
* Convert the Buffer to a String. The original buffer is unmodified.
|
268
268
|
*/
|
269
269
|
static VALUE Em_Buffer_to_str(VALUE self) {
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
270
|
+
VALUE str;
|
271
|
+
struct buffer *buf;
|
272
|
+
|
273
|
+
Data_Get_Struct(self, struct buffer, buf);
|
274
|
+
|
275
|
+
str = rb_str_new(0, buf->size);
|
276
|
+
buffer_copy(buf, RSTRING_PTR(str), buf->size);
|
277
|
+
|
278
278
|
return str;
|
279
279
|
}
|
280
280
|
|
281
281
|
/**
|
282
282
|
* call-seq:
|
283
283
|
* EventMachine::Buffer#read_from(io) -> Integer
|
284
|
-
*
|
284
|
+
*
|
285
285
|
* Perform a nonblocking read of the the given IO object and fill
|
286
286
|
* the buffer with any data received. The call will read as much
|
287
287
|
* data as it can until the read would block.
|
288
288
|
*/
|
289
289
|
static VALUE Em_Buffer_read_from(VALUE self, VALUE io) {
|
290
|
-
|
290
|
+
struct buffer *buf;
|
291
291
|
#if HAVE_RB_IO_T
|
292
292
|
rb_io_t *fptr;
|
293
293
|
#else
|
@@ -298,20 +298,24 @@ static VALUE Em_Buffer_read_from(VALUE self, VALUE io) {
|
|
298
298
|
GetOpenFile(rb_convert_type(io, T_FILE, "IO", "to_io"), fptr);
|
299
299
|
rb_io_set_nonblock(fptr);
|
300
300
|
|
301
|
+
#ifdef HAVE_RB_IO_FD
|
302
|
+
return INT2NUM(buffer_read_from(buf, rb_io_fd(io)));
|
303
|
+
#else
|
301
304
|
return INT2NUM(buffer_read_from(buf, FPTR_TO_FD(fptr)));
|
305
|
+
#endif
|
302
306
|
}
|
303
307
|
|
304
308
|
/**
|
305
309
|
* call-seq:
|
306
310
|
* EventMachine::Buffer#write_to(io) -> Integer
|
307
|
-
*
|
311
|
+
*
|
308
312
|
* Perform a nonblocking write of the buffer to the given IO object.
|
309
313
|
* As much data as possible is written until the call would block.
|
310
314
|
* Any data which is written is removed from the buffer.
|
311
315
|
*/
|
312
316
|
static VALUE Em_Buffer_write_to(VALUE self, VALUE io) {
|
313
317
|
struct buffer *buf;
|
314
|
-
#if HAVE_RB_IO_T
|
318
|
+
#if HAVE_RB_IO_T
|
315
319
|
rb_io_t *fptr;
|
316
320
|
#else
|
317
321
|
OpenFile *fptr;
|
@@ -321,11 +325,16 @@ static VALUE Em_Buffer_write_to(VALUE self, VALUE io) {
|
|
321
325
|
GetOpenFile(rb_convert_type(io, T_FILE, "IO", "to_io"), fptr);
|
322
326
|
rb_io_set_nonblock(fptr);
|
323
327
|
|
324
|
-
|
328
|
+
#ifdef HAVE_RB_IO_FD
|
329
|
+
return INT2NUM(buffer_read_from(buf, rb_io_fd(io)));
|
330
|
+
#else
|
331
|
+
return INT2NUM(buffer_read_from(buf, FPTR_TO_FD(fptr)));
|
332
|
+
#endif
|
333
|
+
|
325
334
|
}
|
326
335
|
|
327
336
|
/*
|
328
|
-
* Ruby bindings end here. Below is the actual implementation of
|
337
|
+
* Ruby bindings end here. Below is the actual implementation of
|
329
338
|
* the underlying data structures.
|
330
339
|
*/
|
331
340
|
|
@@ -338,8 +347,8 @@ static struct buffer *buffer_new(void)
|
|
338
347
|
buf->head = buf->tail = buf->pool_head = buf->pool_tail = 0;
|
339
348
|
buf->size = 0;
|
340
349
|
buf->node_size = DEFAULT_NODE_SIZE;
|
341
|
-
|
342
|
-
|
350
|
+
time(&buf->last_purged_at);
|
351
|
+
|
343
352
|
return buf;
|
344
353
|
}
|
345
354
|
|
@@ -359,7 +368,7 @@ static void buffer_clear(struct buffer *buf)
|
|
359
368
|
}
|
360
369
|
|
361
370
|
/* Free a buffer */
|
362
|
-
static void buffer_free(struct buffer *buf)
|
371
|
+
static void buffer_free(struct buffer *buf)
|
363
372
|
{
|
364
373
|
struct buffer_node *tmp;
|
365
374
|
|
@@ -381,11 +390,11 @@ static void buffer_gc(struct buffer *buf)
|
|
381
390
|
time_t now;
|
382
391
|
time(&now);
|
383
392
|
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
393
|
+
/* Only purge if we've passed the purge interval */
|
394
|
+
if(now - buf->last_purged_at < PURGE_INTERVAL)
|
395
|
+
return;
|
396
|
+
|
397
|
+
buf->last_purged_at = now;
|
389
398
|
|
390
399
|
while(buf->pool_head && now - buf->pool_head->last_used_at >= MAX_AGE) {
|
391
400
|
tmp = buf->pool_head;
|
@@ -394,7 +403,7 @@ static void buffer_gc(struct buffer *buf)
|
|
394
403
|
}
|
395
404
|
|
396
405
|
if(!buf->pool_head)
|
397
|
-
|
406
|
+
buf->pool_tail = 0;
|
398
407
|
}
|
399
408
|
|
400
409
|
/* Create a new buffer_node (or pull one from the memory pool) */
|
@@ -494,11 +503,11 @@ static void buffer_append(struct buffer *buf, char *str, unsigned len)
|
|
494
503
|
while(len > 0) {
|
495
504
|
nbytes = buf->node_size - buf->tail->end;
|
496
505
|
if(len < nbytes) nbytes = len;
|
497
|
-
|
506
|
+
|
498
507
|
memcpy(buf->tail->data + buf->tail->end, str, nbytes);
|
499
|
-
str += nbytes;
|
508
|
+
str += nbytes;
|
500
509
|
len -= nbytes;
|
501
|
-
|
510
|
+
|
502
511
|
buf->tail->end += nbytes;
|
503
512
|
|
504
513
|
if(len > 0) {
|
@@ -541,7 +550,7 @@ static void buffer_copy(struct buffer *buf, char *str, unsigned len)
|
|
541
550
|
unsigned nbytes;
|
542
551
|
struct buffer_node *node;
|
543
552
|
|
544
|
-
|
553
|
+
node = buf->head;
|
545
554
|
while(node && len > 0) {
|
546
555
|
nbytes = node->end - node->start;
|
547
556
|
if(len < nbytes) nbytes = len;
|
@@ -551,7 +560,7 @@ static void buffer_copy(struct buffer *buf, char *str, unsigned len)
|
|
551
560
|
len -= nbytes;
|
552
561
|
|
553
562
|
if(node->start + nbytes == node->end)
|
554
|
-
|
563
|
+
node = node->next;
|
555
564
|
}
|
556
565
|
}
|
557
566
|
|
@@ -596,7 +605,7 @@ static int buffer_write_to(struct buffer *buf, int fd)
|
|
596
605
|
/* Append data to the front of the buffer */
|
597
606
|
static int buffer_read_from(struct buffer *buf, int fd)
|
598
607
|
{
|
599
|
-
|
608
|
+
int bytes_read, total_bytes_read = 0;
|
600
609
|
unsigned nbytes;
|
601
610
|
|
602
611
|
/* Empty list needs initialized */
|
@@ -605,26 +614,26 @@ static int buffer_read_from(struct buffer *buf, int fd)
|
|
605
614
|
buf->tail = buf->head;
|
606
615
|
}
|
607
616
|
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
617
|
+
do {
|
618
|
+
nbytes = buf->node_size - buf->tail->end;
|
619
|
+
bytes_read = read(fd, buf->tail->data + buf->tail->end, nbytes);
|
620
|
+
|
621
|
+
if(bytes_read < 1) {
|
622
|
+
if(errno != EAGAIN)
|
614
623
|
rb_sys_fail("read");
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
+
|
625
|
+
return total_bytes_read;
|
626
|
+
}
|
627
|
+
|
628
|
+
total_bytes_read += bytes_read;
|
629
|
+
buf->tail->end += nbytes;
|
630
|
+
buf->size += nbytes;
|
631
|
+
|
632
|
+
if(buf->tail->end == buf->node_size) {
|
624
633
|
buf->tail->next = buffer_node_new(buf);
|
625
634
|
buf->tail = buf->tail->next;
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
635
|
+
}
|
636
|
+
} while(bytes_read == nbytes);
|
637
|
+
|
638
|
+
return total_bytes_read;
|
630
639
|
}
|