em-http-request 0.2.7 → 0.2.9
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 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
|
}
|