em-http-request 0.2.15 → 0.3.0

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/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  *.bundle
2
2
  *.o
3
3
  Makefile
4
- mkmf.log
4
+ mkmf.log
5
+ Gemfile.lock
data/.rspec ADDED
File without changes
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 / 2011-01-15
4
+
5
+ - IMPORTANT: default to non-persistent connections (timeout => 0 now requires :keepalive => true)
6
+ - see: https://github.com/igrigorik/em-http-request/commit/1ca5b608e876c18fa6cfa318d0685dcf5b974e09
7
+
8
+ - added escape_utils dependency to fix slow encode on long string escapes
9
+
10
+ - bugfix: proxy authorization headers
11
+ - bugfix: default to Encoding.default_external on invalid encoding in response
12
+ - bugfix: do not normalize URI's internally
13
+ - bugfix: more robust Encoding detection
14
+
15
+
3
16
  ## 0.2.15 / 2010-11-18
4
17
 
5
18
  - bugfix: follow redirects on missing content-length
data/Gemfile CHANGED
@@ -1,15 +1,3 @@
1
- source :gemcutter
1
+ source "http://rubygems.org"
2
2
 
3
- gem 'eventmachine', '~> 0.12.9'
4
- gem 'addressable', '~> 2.0.0'
5
- gem 'rack'
6
-
7
- gem 'mongrel', '~> 1.2.0.pre2', :require => ['mongrel', 'http11']
8
-
9
- group :development do
10
- gem 'autotest'
11
- gem 'ruby-debug19', :require => 'ruby-debug'
12
- gem 'jeweler'
13
- gem 'rspec', '~> 2.0.0'
14
- gem 'em-websocket'
15
- end
3
+ gemspec
data/README.md CHANGED
@@ -86,6 +86,8 @@ Full basic author support. For OAuth, check examples/oauth-tweet.rb file.
86
86
  POSTing data example
87
87
  --------------------
88
88
 
89
+ For multi-part uploads, please see [this gist](https://gist.github.com/778639).
90
+
89
91
  EventMachine.run {
90
92
  http1 = EventMachine::HttpRequest.new('http://www.website.com/').post :body => {"key1" => 1, "key2" => [2,3]}
91
93
  http2 = EventMachine::HttpRequest.new('http://www.website.com/').post :body => "some data"
@@ -166,3 +168,8 @@ WebSocket example
166
168
 
167
169
  http.disconnect { puts "oops, dropped connection?" }
168
170
  }
171
+
172
+ License
173
+ -------
174
+
175
+ (MIT License) - Copyright (c) 2011 Ilya Grigorik
data/Rakefile CHANGED
@@ -1,11 +1,13 @@
1
1
  require 'bundler'
2
+
2
3
  Bundler.setup
3
4
  Bundler.require :default, :development
4
5
 
5
6
  require 'rake'
6
7
  require 'rake/clean'
7
- require 'rake/rdoctask'
8
8
  require 'rake/gempackagetask'
9
+ require 'rspec/core/rake_task'
10
+
9
11
  require 'fileutils'
10
12
  include FileUtils
11
13
 
@@ -19,15 +21,6 @@ end
19
21
  # Default Rake task is compile
20
22
  task :default => :compile
21
23
 
22
- # RDoc
23
- Rake::RDocTask.new(:rdoc) do |task|
24
- task.rdoc_dir = 'doc'
25
- task.title = 'EventMachine::HttpRequest'
26
- task.options = %w(--title HttpRequest --main README.md --line-numbers)
27
- task.rdoc_files.include(['lib/**/*.rb'])
28
- task.rdoc_files.include(['README.md', 'LICENSE'])
29
- end
30
-
31
24
  # Rebuild parser Ragel
32
25
  task :ragel do
33
26
  Dir.chdir "ext/http11_client" do
@@ -38,8 +31,8 @@ task :ragel do
38
31
  end
39
32
  end
40
33
 
41
- require 'rspec/core/rake_task'
42
- Rspec::Core::RakeTask.new(:spec)
34
+ desc "Run all RSpec tests"
35
+ RSpec::Core::RakeTask.new(:spec)
43
36
 
44
37
  def make(makedir)
45
38
  Dir.chdir(makedir) { sh MAKE }
@@ -84,26 +77,4 @@ setup_extension("http11_client", "http11_client")
84
77
  task :compile => [:em_buffer, :http11_client]
85
78
 
86
79
  CLEAN.include ['build/*', '**/*.o', '**/*.so', '**/*.a', '**/*.log', 'pkg']
87
- CLEAN.include ['ext/buffer/Makefile', 'lib/em_buffer.*', 'lib/http11_client.*']
88
-
89
- begin
90
- require 'jeweler'
91
- Jeweler::Tasks.new do |gemspec|
92
- gemspec.name = "em-http-request"
93
- gemspec.summary = "EventMachine based, async HTTP Request interface"
94
- gemspec.description = gemspec.summary
95
- gemspec.email = "ilya@igvita.com"
96
- gemspec.homepage = "http://github.com/igrigorik/em-http-request"
97
- gemspec.authors = ["Ilya Grigorik"]
98
- gemspec.required_ruby_version = ">= 1.8.6"
99
- gemspec.extensions = ["ext/buffer/extconf.rb" , "ext/http11_client/extconf.rb"]
100
- gemspec.add_dependency('eventmachine', '>= 0.12.9')
101
- gemspec.add_dependency('addressable', '>= 2.0.0')
102
- gemspec.rubyforge_project = "em-http-request"
103
- gemspec.files = FileList[`git ls-files`.split]
104
- end
105
-
106
- Jeweler::GemcutterTasks.new
107
- rescue LoadError
108
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
109
- end
80
+ CLEAN.include ['ext/buffer/Makefile', 'lib/em_buffer.*', 'lib/http11_client.*']
@@ -1,101 +1,32 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
1
  # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "em-http/version"
5
4
 
6
5
  Gem::Specification.new do |s|
7
- s.name = %q{em-http-request}
8
- s.version = "0.2.15"
6
+ s.name = "em-http-request"
7
+ s.version = EventMachine::HttpRequest::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ilya Grigorik"]
10
+ s.email = ["ilya@igvita.com"]
11
+ s.homepage = "http://github.com/igrigorik/em-http-request"
12
+ s.summary = "EventMachine based, async HTTP Request client"
13
+ s.description = s.summary
14
+ s.rubyforge_project = "em-http-request"
9
15
 
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-10-06}
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.md"
19
- ]
20
- s.files = [
21
- ".gitignore",
22
- "Changelog.md",
23
- "Gemfile",
24
- "LICENSE",
25
- "README.md",
26
- "Rakefile",
27
- "VERSION",
28
- "autotest/discover.rb",
29
- "em-http-request.gemspec",
30
- "examples/fetch.rb",
31
- "examples/fibered-http.rb",
32
- "examples/oauth-tweet.rb",
33
- "examples/socks5.rb",
34
- "examples/websocket-handler.rb",
35
- "examples/websocket-server.rb",
36
- "ext/buffer/em_buffer.c",
37
- "ext/buffer/extconf.rb",
38
- "ext/http11_client/ext_help.h",
39
- "ext/http11_client/extconf.rb",
40
- "ext/http11_client/http11_client.c",
41
- "ext/http11_client/http11_parser.c",
42
- "ext/http11_client/http11_parser.h",
43
- "ext/http11_client/http11_parser.rl",
44
- "lib/em-http-request.rb",
45
- "lib/em-http.rb",
46
- "lib/em-http/client.rb",
47
- "lib/em-http/core_ext/bytesize.rb",
48
- "lib/em-http/decoders.rb",
49
- "lib/em-http/http_options.rb",
50
- "lib/em-http/mock.rb",
51
- "lib/em-http/multi.rb",
52
- "lib/em-http/request.rb",
53
- "spec/encoding_spec.rb",
54
- "spec/fixtures/google.ca",
55
- "spec/helper.rb",
56
- "spec/mock_spec.rb",
57
- "spec/multi_spec.rb",
58
- "spec/request_spec.rb",
59
- "spec/stallion.rb",
60
- "spec/stub_server.rb"
61
- ]
62
- s.homepage = %q{http://github.com/igrigorik/em-http-request}
63
- s.rdoc_options = ["--charset=UTF-8"]
64
- s.require_paths = ["lib"]
65
- s.required_ruby_version = Gem::Requirement.new(">= 1.8.6")
66
- s.rubyforge_project = %q{em-http-request}
67
- s.rubygems_version = %q{1.3.7}
68
- s.summary = %q{EventMachine based, async HTTP Request interface}
69
- s.test_files = [
70
- "spec/encoding_spec.rb",
71
- "spec/helper.rb",
72
- "spec/mock_spec.rb",
73
- "spec/multi_spec.rb",
74
- "spec/request_spec.rb",
75
- "spec/stallion.rb",
76
- "spec/stub_server.rb",
77
- "examples/fetch.rb",
78
- "examples/fibered-http.rb",
79
- "examples/oauth-tweet.rb",
80
- "examples/socks5.rb",
81
- "examples/websocket-handler.rb",
82
- "examples/websocket-server.rb"
83
- ]
16
+ s.add_dependency "eventmachine", ">= 0.12.9"
17
+ s.add_dependency "addressable", ">= 2.0.0"
18
+ s.add_dependency "escape_utils"
84
19
 
85
- if s.respond_to? :specification_version then
86
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
87
- s.specification_version = 3
20
+ s.add_development_dependency "rspec"
21
+ s.add_development_dependency "rake"
22
+ s.add_development_dependency "em-websocket"
23
+ s.add_development_dependency "rack"
24
+ s.add_development_dependency "mongrel", "~> 1.2.0.pre2"
88
25
 
89
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
90
- s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.9"])
91
- s.add_runtime_dependency(%q<addressable>, [">= 2.0.0"])
92
- else
93
- s.add_dependency(%q<eventmachine>, [">= 0.12.9"])
94
- s.add_dependency(%q<addressable>, [">= 2.0.0"])
95
- end
96
- else
97
- s.add_dependency(%q<eventmachine>, [">= 0.12.9"])
98
- s.add_dependency(%q<addressable>, [">= 2.0.0"])
99
- end
100
- end
26
+ s.extensions = ["ext/buffer/extconf.rb", "ext/http11_client/extconf.rb"]
101
27
 
28
+ s.files = `git ls-files`.split("\n")
29
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
30
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
31
+ s.require_paths = ["lib"]
32
+ end
@@ -1,53 +1,53 @@
1
- require 'mkmf'
2
-
3
- libs = []
4
-
5
- $defs << "-DRUBY_VERSION_CODE=#{RUBY_VERSION.gsub(/\D/, '')}"
6
-
7
- if have_func('rb_thread_blocking_region')
8
- $defs << '-DHAVE_RB_THREAD_BLOCKING_REGION'
9
- end
10
-
11
- if have_func('rb_str_set_len')
12
- $defs << '-DHAVE_RB_STR_SET_LEN'
13
- end
14
-
15
- if have_header('sys/select.h')
16
- $defs << '-DEV_USE_SELECT'
17
- end
18
-
19
- if have_header('poll.h')
20
- $defs << '-DEV_USE_POLL'
21
- end
22
-
23
- if have_header('sys/epoll.h')
24
- $defs << '-DEV_USE_EPOLL'
25
- end
26
-
27
- if have_header('sys/event.h') and have_header('sys/queue.h')
28
- $defs << '-DEV_USE_KQUEUE'
29
- end
30
-
31
- if have_header('port.h')
32
- $defs << '-DEV_USE_PORT'
33
- end
34
-
35
- if have_header('openssl/ssl.h')
36
- $defs << '-DHAVE_OPENSSL_SSL_H'
37
- libs << '-lssl -lcrypto'
38
- end
39
-
40
- # ncpu detection specifics
41
- case RUBY_PLATFORM
42
- when /linux/
43
- $defs << '-DHAVE_LINUX_PROCFS'
44
- else
45
- if have_func('sysctlbyname', ['sys/param.h', 'sys/sysctl.h'])
46
- $defs << '-DHAVE_SYSCTLBYNAME'
47
- end
48
- end
49
-
50
- $LIBS << ' ' << libs.join(' ')
51
-
52
- dir_config('em_buffer')
53
- create_makefile('em_buffer')
1
+ require 'mkmf'
2
+
3
+ libs = []
4
+
5
+ $defs << "-DRUBY_VERSION_CODE=#{RUBY_VERSION.gsub(/\D/, '')}"
6
+
7
+ if have_func('rb_thread_blocking_region')
8
+ $defs << '-DHAVE_RB_THREAD_BLOCKING_REGION'
9
+ end
10
+
11
+ if have_func('rb_str_set_len')
12
+ $defs << '-DHAVE_RB_STR_SET_LEN'
13
+ end
14
+
15
+ if have_header('sys/select.h')
16
+ $defs << '-DEV_USE_SELECT'
17
+ end
18
+
19
+ if have_header('poll.h')
20
+ $defs << '-DEV_USE_POLL'
21
+ end
22
+
23
+ if have_header('sys/epoll.h')
24
+ $defs << '-DEV_USE_EPOLL'
25
+ end
26
+
27
+ if have_header('sys/event.h') and have_header('sys/queue.h')
28
+ $defs << '-DEV_USE_KQUEUE'
29
+ end
30
+
31
+ if have_header('port.h')
32
+ $defs << '-DEV_USE_PORT'
33
+ end
34
+
35
+ if have_header('openssl/ssl.h')
36
+ $defs << '-DHAVE_OPENSSL_SSL_H'
37
+ libs << '-lssl -lcrypto'
38
+ end
39
+
40
+ # ncpu detection specifics
41
+ case RUBY_PLATFORM
42
+ when /linux/
43
+ $defs << '-DHAVE_LINUX_PROCFS'
44
+ else
45
+ if have_func('sysctlbyname', ['sys/param.h', 'sys/sysctl.h'])
46
+ $defs << '-DHAVE_SYSCTLBYNAME'
47
+ end
48
+ end
49
+
50
+ $LIBS << ' ' << libs.join(' ')
51
+
52
+ dir_config('em_buffer')
53
+ create_makefile('em_buffer')
@@ -1,14 +1,14 @@
1
- #ifndef ext_help_h
2
- #define ext_help_h
3
-
4
- #define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "NULL found for " # T " when shouldn't be.");
5
- #define DATA_GET(from,type,name) Data_Get_Struct(from,type,name); RAISE_NOT_NULL(name);
6
- #define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "Wrong argument type for " # V " required " # T);
7
-
8
- #ifdef DEBUG
9
- #define TRACE() fprintf(stderr, "> %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__)
10
- #else
11
- #define TRACE()
12
- #endif
13
-
14
- #endif
1
+ #ifndef ext_help_h
2
+ #define ext_help_h
3
+
4
+ #define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "NULL found for " # T " when shouldn't be.");
5
+ #define DATA_GET(from,type,name) Data_Get_Struct(from,type,name); RAISE_NOT_NULL(name);
6
+ #define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "Wrong argument type for " # V " required " # T);
7
+
8
+ #ifdef DEBUG
9
+ #define TRACE() fprintf(stderr, "> %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__)
10
+ #else
11
+ #define TRACE()
12
+ #endif
13
+
14
+ #endif
@@ -1,6 +1,6 @@
1
- require 'mkmf'
2
-
3
- dir_config("http11_client")
4
- have_library("c", "main")
5
-
6
- create_makefile("http11_client")
1
+ require 'mkmf'
2
+
3
+ dir_config("http11_client")
4
+ have_library("c", "main")
5
+
6
+ create_makefile("http11_client")
@@ -1,328 +1,328 @@
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
- #include "ruby.h"
7
- #include "ext_help.h"
8
- #include <assert.h>
9
- #include <string.h>
10
- #include "http11_parser.h"
11
- #include <ctype.h>
12
-
13
- static VALUE mEm;
14
- static VALUE cHttpClientParser;
15
- static VALUE eHttpClientParserError;
16
-
17
- #define id_reason rb_intern("@http_reason")
18
- #define id_status rb_intern("@http_status")
19
- #define id_version rb_intern("@http_version")
20
- #define id_body rb_intern("@http_body")
21
- #define id_chunk_size rb_intern("@http_chunk_size")
22
- #define id_last_chunk rb_intern("@last_chunk")
23
-
24
- #ifndef RHASH_TBL
25
- /* rb_hash_lookup() is only in Ruby 1.8.7 */
26
- static VALUE rb_hash_lookup(VALUE hash, VALUE key)
27
- {
28
- VALUE val;
29
-
30
- if (!st_lookup(RHASH(hash)->tbl, key, &val)) {
31
- return Qnil; /* without Hash#default */
32
- }
33
-
34
- return val;
35
- }
36
- #endif
37
-
38
- void client_http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
39
- {
40
- char *ch, *end;
41
- VALUE req = (VALUE)data;
42
- VALUE v = Qnil;
43
- VALUE f = Qnil;
44
- VALUE el = Qnil;
45
-
46
- v = rb_str_new(value, vlen);
47
- f = rb_str_new(field, flen);
48
-
49
- /* Yes Children, rb_str_upcase_bang isn't even available as an intern.h function.
50
- * how incredibly handy to not have that. Nope, I have to do it by hand.*/
51
- for(ch = RSTRING_PTR(f), end = ch + RSTRING_LEN(f); ch < end; ch++) {
52
- if(*ch == '-') {
53
- *ch = '_';
54
- } else {
55
- *ch = toupper(*ch);
56
- }
57
- }
58
-
59
- el = rb_hash_lookup(req, f);
60
- switch(TYPE(el)) {
61
- case T_ARRAY:
62
- rb_ary_push(el, v);
63
- break;
64
- case T_STRING:
65
- rb_hash_aset(req, f, rb_ary_new3(2, el, v));
66
- break;
67
- default:
68
- rb_hash_aset(req, f, v);
69
- break;
70
- }
71
- }
72
-
73
- void client_reason_phrase(void *data, const char *at, size_t length)
74
- {
75
- VALUE req = (VALUE)data;
76
- VALUE v = Qnil;
77
-
78
- v = rb_str_new(at, length);
79
-
80
- rb_ivar_set(req, id_reason, v);
81
- }
82
-
83
- void client_status_code(void *data, const char *at, size_t length)
84
- {
85
- VALUE req = (VALUE)data;
86
- VALUE v = Qnil;
87
-
88
- v = rb_str_new(at, length);
89
-
90
- rb_ivar_set(req, id_status, v);
91
- }
92
-
93
- void client_http_version(void *data, const char *at, size_t length)
94
- {
95
- VALUE req = (VALUE)data;
96
- VALUE v = Qnil;
97
-
98
- v = rb_str_new(at, length);
99
-
100
- rb_ivar_set(req, id_version, v);
101
- }
102
-
103
- /** Finalizes the request header to have a bunch of stuff that's
104
- needed. */
105
- void client_header_done(void *data, const char *at, size_t length)
106
- {
107
- VALUE req = (VALUE)data;
108
- VALUE v = Qnil;
109
-
110
- v = rb_str_new(at, length);
111
- rb_ivar_set(req, id_body, v);
112
- }
113
-
114
- void client_chunk_size(void *data, const char *at, size_t length)
115
- {
116
- VALUE req = (VALUE)data;
117
- VALUE v = Qnil;
118
-
119
- if(length <= 0) {
120
- rb_raise(eHttpClientParserError, "Chunked Encoding gave <= 0 chunk size.");
121
- }
122
-
123
- v = rb_str_new(at, length);
124
-
125
- rb_ivar_set(req, id_chunk_size, v);
126
- }
127
-
128
- void client_last_chunk(void *data, const char *at, size_t length) {
129
- VALUE req = (VALUE)data;
130
- rb_ivar_set(req, id_last_chunk,Qtrue);
131
- }
132
-
133
-
134
- void HttpClientParser_free(void *data) {
135
- TRACE();
136
-
137
- if(data) {
138
- free(data);
139
- }
140
- }
141
-
142
-
143
- VALUE HttpClientParser_alloc(VALUE klass)
144
- {
145
- VALUE obj;
146
- httpclient_parser *hp = ALLOC_N(httpclient_parser, 1);
147
- TRACE();
148
- hp->http_field = client_http_field;
149
- hp->status_code = client_status_code;
150
- hp->reason_phrase = client_reason_phrase;
151
- hp->http_version = client_http_version;
152
- hp->header_done = client_header_done;
153
- hp->chunk_size = client_chunk_size;
154
- hp->last_chunk = client_last_chunk;
155
- httpclient_parser_init(hp);
156
-
157
- obj = Data_Wrap_Struct(klass, NULL, HttpClientParser_free, hp);
158
-
159
- return obj;
160
- }
161
-
162
-
163
- /**
164
- * call-seq:
165
- * parser.new -> parser
166
- *
167
- * Creates a new parser.
168
- */
169
- VALUE HttpClientParser_init(VALUE self)
170
- {
171
- httpclient_parser *http = NULL;
172
- DATA_GET(self, httpclient_parser, http);
173
- httpclient_parser_init(http);
174
-
175
- return self;
176
- }
177
-
178
-
179
- /**
180
- * call-seq:
181
- * parser.reset -> nil
182
- *
183
- * Resets the parser to it's initial state so that you can reuse it
184
- * rather than making new ones.
185
- */
186
- VALUE HttpClientParser_reset(VALUE self)
187
- {
188
- httpclient_parser *http = NULL;
189
- DATA_GET(self, httpclient_parser, http);
190
- httpclient_parser_init(http);
191
-
192
- return Qnil;
193
- }
194
-
195
-
196
- /**
197
- * call-seq:
198
- * parser.finish -> true/false
199
- *
200
- * Finishes a parser early which could put in a "good" or bad state.
201
- * You should call reset after finish it or bad things will happen.
202
- */
203
- VALUE HttpClientParser_finish(VALUE self)
204
- {
205
- httpclient_parser *http = NULL;
206
- DATA_GET(self, httpclient_parser, http);
207
- httpclient_parser_finish(http);
208
-
209
- return httpclient_parser_is_finished(http) ? Qtrue : Qfalse;
210
- }
211
-
212
-
213
- /**
214
- * call-seq:
215
- * parser.execute(req_hash, data, start) -> Integer
216
- *
217
- * Takes a Hash and a String of data, parses the String of data filling in the Hash
218
- * returning an Integer to indicate how much of the data has been read. No matter
219
- * what the return value, you should call HttpClientParser#finished? and HttpClientParser#error?
220
- * to figure out if it's done parsing or there was an error.
221
- *
222
- * This function now throws an exception when there is a parsing error. This makes
223
- * the logic for working with the parser much easier. You can still test for an
224
- * error, but now you need to wrap the parser with an exception handling block.
225
- *
226
- * The third argument allows for parsing a partial request and then continuing
227
- * the parsing from that position. It needs all of the original data as well
228
- * so you have to append to the data buffer as you read.
229
- */
230
- VALUE HttpClientParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
231
- {
232
- httpclient_parser *http = NULL;
233
- int from = 0;
234
- char *dptr = NULL;
235
- long dlen = 0;
236
-
237
- REQUIRE_TYPE(req_hash, T_HASH);
238
- REQUIRE_TYPE(data, T_STRING);
239
- REQUIRE_TYPE(start, T_FIXNUM);
240
-
241
- DATA_GET(self, httpclient_parser, http);
242
-
243
- from = FIX2INT(start);
244
- dptr = RSTRING_PTR(data);
245
- dlen = RSTRING_LEN(data);
246
-
247
- if(from >= dlen) {
248
- rb_raise(eHttpClientParserError, "Requested start is after data buffer end.");
249
- } else {
250
- http->data = (void *)req_hash;
251
- httpclient_parser_execute(http, dptr, dlen, from);
252
-
253
- if(httpclient_parser_has_error(http)) {
254
- rb_raise(eHttpClientParserError, "Invalid HTTP format, parsing fails.");
255
- } else {
256
- return INT2FIX(httpclient_parser_nread(http));
257
- }
258
- }
259
- }
260
-
261
-
262
-
263
- /**
264
- * call-seq:
265
- * parser.error? -> true/false
266
- *
267
- * Tells you whether the parser is in an error state.
268
- */
269
- VALUE HttpClientParser_has_error(VALUE self)
270
- {
271
- httpclient_parser *http = NULL;
272
- DATA_GET(self, httpclient_parser, http);
273
-
274
- return httpclient_parser_has_error(http) ? Qtrue : Qfalse;
275
- }
276
-
277
-
278
- /**
279
- * call-seq:
280
- * parser.finished? -> true/false
281
- *
282
- * Tells you whether the parser is finished or not and in a good state.
283
- */
284
- VALUE HttpClientParser_is_finished(VALUE self)
285
- {
286
- httpclient_parser *http = NULL;
287
- DATA_GET(self, httpclient_parser, http);
288
-
289
- return httpclient_parser_is_finished(http) ? Qtrue : Qfalse;
290
- }
291
-
292
-
293
- /**
294
- * call-seq:
295
- * parser.nread -> Integer
296
- *
297
- * Returns the amount of data processed so far during this processing cycle. It is
298
- * set to 0 on initialize or reset calls and is incremented each time execute is called.
299
- */
300
- VALUE HttpClientParser_nread(VALUE self)
301
- {
302
- httpclient_parser *http = NULL;
303
- DATA_GET(self, httpclient_parser, http);
304
-
305
- return INT2FIX(http->nread);
306
- }
307
-
308
-
309
-
310
- void Init_http11_client()
311
- {
312
-
313
- mEm = rb_define_module("EventMachine");
314
-
315
- eHttpClientParserError = rb_define_class_under(mEm, "HttpClientParserError", rb_eIOError);
316
-
317
- cHttpClientParser = rb_define_class_under(mEm, "HttpClientParser", rb_cObject);
318
- rb_define_alloc_func(cHttpClientParser, HttpClientParser_alloc);
319
- rb_define_method(cHttpClientParser, "initialize", HttpClientParser_init,0);
320
- rb_define_method(cHttpClientParser, "reset", HttpClientParser_reset,0);
321
- rb_define_method(cHttpClientParser, "finish", HttpClientParser_finish,0);
322
- rb_define_method(cHttpClientParser, "execute", HttpClientParser_execute,3);
323
- rb_define_method(cHttpClientParser, "error?", HttpClientParser_has_error,0);
324
- rb_define_method(cHttpClientParser, "finished?", HttpClientParser_is_finished,0);
325
- rb_define_method(cHttpClientParser, "nread", HttpClientParser_nread,0);
326
- }
327
-
328
-
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
+ #include "ruby.h"
7
+ #include "ext_help.h"
8
+ #include <assert.h>
9
+ #include <string.h>
10
+ #include "http11_parser.h"
11
+ #include <ctype.h>
12
+
13
+ static VALUE mEm;
14
+ static VALUE cHttpClientParser;
15
+ static VALUE eHttpClientParserError;
16
+
17
+ #define id_reason rb_intern("@http_reason")
18
+ #define id_status rb_intern("@http_status")
19
+ #define id_version rb_intern("@http_version")
20
+ #define id_body rb_intern("@http_body")
21
+ #define id_chunk_size rb_intern("@http_chunk_size")
22
+ #define id_last_chunk rb_intern("@last_chunk")
23
+
24
+ #ifndef RHASH_TBL
25
+ /* rb_hash_lookup() is only in Ruby 1.8.7 */
26
+ static VALUE rb_hash_lookup(VALUE hash, VALUE key)
27
+ {
28
+ VALUE val;
29
+
30
+ if (!st_lookup(RHASH(hash)->tbl, key, &val)) {
31
+ return Qnil; /* without Hash#default */
32
+ }
33
+
34
+ return val;
35
+ }
36
+ #endif
37
+
38
+ void client_http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
39
+ {
40
+ char *ch, *end;
41
+ VALUE req = (VALUE)data;
42
+ VALUE v = Qnil;
43
+ VALUE f = Qnil;
44
+ VALUE el = Qnil;
45
+
46
+ v = rb_str_new(value, vlen);
47
+ f = rb_str_new(field, flen);
48
+
49
+ /* Yes Children, rb_str_upcase_bang isn't even available as an intern.h function.
50
+ * how incredibly handy to not have that. Nope, I have to do it by hand.*/
51
+ for(ch = RSTRING_PTR(f), end = ch + RSTRING_LEN(f); ch < end; ch++) {
52
+ if(*ch == '-') {
53
+ *ch = '_';
54
+ } else {
55
+ *ch = toupper(*ch);
56
+ }
57
+ }
58
+
59
+ el = rb_hash_lookup(req, f);
60
+ switch(TYPE(el)) {
61
+ case T_ARRAY:
62
+ rb_ary_push(el, v);
63
+ break;
64
+ case T_STRING:
65
+ rb_hash_aset(req, f, rb_ary_new3(2, el, v));
66
+ break;
67
+ default:
68
+ rb_hash_aset(req, f, v);
69
+ break;
70
+ }
71
+ }
72
+
73
+ void client_reason_phrase(void *data, const char *at, size_t length)
74
+ {
75
+ VALUE req = (VALUE)data;
76
+ VALUE v = Qnil;
77
+
78
+ v = rb_str_new(at, length);
79
+
80
+ rb_ivar_set(req, id_reason, v);
81
+ }
82
+
83
+ void client_status_code(void *data, const char *at, size_t length)
84
+ {
85
+ VALUE req = (VALUE)data;
86
+ VALUE v = Qnil;
87
+
88
+ v = rb_str_new(at, length);
89
+
90
+ rb_ivar_set(req, id_status, v);
91
+ }
92
+
93
+ void client_http_version(void *data, const char *at, size_t length)
94
+ {
95
+ VALUE req = (VALUE)data;
96
+ VALUE v = Qnil;
97
+
98
+ v = rb_str_new(at, length);
99
+
100
+ rb_ivar_set(req, id_version, v);
101
+ }
102
+
103
+ /** Finalizes the request header to have a bunch of stuff that's
104
+ needed. */
105
+ void client_header_done(void *data, const char *at, size_t length)
106
+ {
107
+ VALUE req = (VALUE)data;
108
+ VALUE v = Qnil;
109
+
110
+ v = rb_str_new(at, length);
111
+ rb_ivar_set(req, id_body, v);
112
+ }
113
+
114
+ void client_chunk_size(void *data, const char *at, size_t length)
115
+ {
116
+ VALUE req = (VALUE)data;
117
+ VALUE v = Qnil;
118
+
119
+ if(length <= 0) {
120
+ rb_raise(eHttpClientParserError, "Chunked Encoding gave <= 0 chunk size.");
121
+ }
122
+
123
+ v = rb_str_new(at, length);
124
+
125
+ rb_ivar_set(req, id_chunk_size, v);
126
+ }
127
+
128
+ void client_last_chunk(void *data, const char *at, size_t length) {
129
+ VALUE req = (VALUE)data;
130
+ rb_ivar_set(req, id_last_chunk,Qtrue);
131
+ }
132
+
133
+
134
+ void HttpClientParser_free(void *data) {
135
+ TRACE();
136
+
137
+ if(data) {
138
+ free(data);
139
+ }
140
+ }
141
+
142
+
143
+ VALUE HttpClientParser_alloc(VALUE klass)
144
+ {
145
+ VALUE obj;
146
+ httpclient_parser *hp = ALLOC_N(httpclient_parser, 1);
147
+ TRACE();
148
+ hp->http_field = client_http_field;
149
+ hp->status_code = client_status_code;
150
+ hp->reason_phrase = client_reason_phrase;
151
+ hp->http_version = client_http_version;
152
+ hp->header_done = client_header_done;
153
+ hp->chunk_size = client_chunk_size;
154
+ hp->last_chunk = client_last_chunk;
155
+ httpclient_parser_init(hp);
156
+
157
+ obj = Data_Wrap_Struct(klass, NULL, HttpClientParser_free, hp);
158
+
159
+ return obj;
160
+ }
161
+
162
+
163
+ /**
164
+ * call-seq:
165
+ * parser.new -> parser
166
+ *
167
+ * Creates a new parser.
168
+ */
169
+ VALUE HttpClientParser_init(VALUE self)
170
+ {
171
+ httpclient_parser *http = NULL;
172
+ DATA_GET(self, httpclient_parser, http);
173
+ httpclient_parser_init(http);
174
+
175
+ return self;
176
+ }
177
+
178
+
179
+ /**
180
+ * call-seq:
181
+ * parser.reset -> nil
182
+ *
183
+ * Resets the parser to it's initial state so that you can reuse it
184
+ * rather than making new ones.
185
+ */
186
+ VALUE HttpClientParser_reset(VALUE self)
187
+ {
188
+ httpclient_parser *http = NULL;
189
+ DATA_GET(self, httpclient_parser, http);
190
+ httpclient_parser_init(http);
191
+
192
+ return Qnil;
193
+ }
194
+
195
+
196
+ /**
197
+ * call-seq:
198
+ * parser.finish -> true/false
199
+ *
200
+ * Finishes a parser early which could put in a "good" or bad state.
201
+ * You should call reset after finish it or bad things will happen.
202
+ */
203
+ VALUE HttpClientParser_finish(VALUE self)
204
+ {
205
+ httpclient_parser *http = NULL;
206
+ DATA_GET(self, httpclient_parser, http);
207
+ httpclient_parser_finish(http);
208
+
209
+ return httpclient_parser_is_finished(http) ? Qtrue : Qfalse;
210
+ }
211
+
212
+
213
+ /**
214
+ * call-seq:
215
+ * parser.execute(req_hash, data, start) -> Integer
216
+ *
217
+ * Takes a Hash and a String of data, parses the String of data filling in the Hash
218
+ * returning an Integer to indicate how much of the data has been read. No matter
219
+ * what the return value, you should call HttpClientParser#finished? and HttpClientParser#error?
220
+ * to figure out if it's done parsing or there was an error.
221
+ *
222
+ * This function now throws an exception when there is a parsing error. This makes
223
+ * the logic for working with the parser much easier. You can still test for an
224
+ * error, but now you need to wrap the parser with an exception handling block.
225
+ *
226
+ * The third argument allows for parsing a partial request and then continuing
227
+ * the parsing from that position. It needs all of the original data as well
228
+ * so you have to append to the data buffer as you read.
229
+ */
230
+ VALUE HttpClientParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
231
+ {
232
+ httpclient_parser *http = NULL;
233
+ int from = 0;
234
+ char *dptr = NULL;
235
+ long dlen = 0;
236
+
237
+ REQUIRE_TYPE(req_hash, T_HASH);
238
+ REQUIRE_TYPE(data, T_STRING);
239
+ REQUIRE_TYPE(start, T_FIXNUM);
240
+
241
+ DATA_GET(self, httpclient_parser, http);
242
+
243
+ from = FIX2INT(start);
244
+ dptr = RSTRING_PTR(data);
245
+ dlen = RSTRING_LEN(data);
246
+
247
+ if(from >= dlen) {
248
+ rb_raise(eHttpClientParserError, "Requested start is after data buffer end.");
249
+ } else {
250
+ http->data = (void *)req_hash;
251
+ httpclient_parser_execute(http, dptr, dlen, from);
252
+
253
+ if(httpclient_parser_has_error(http)) {
254
+ rb_raise(eHttpClientParserError, "Invalid HTTP format, parsing fails.");
255
+ } else {
256
+ return INT2FIX(httpclient_parser_nread(http));
257
+ }
258
+ }
259
+ }
260
+
261
+
262
+
263
+ /**
264
+ * call-seq:
265
+ * parser.error? -> true/false
266
+ *
267
+ * Tells you whether the parser is in an error state.
268
+ */
269
+ VALUE HttpClientParser_has_error(VALUE self)
270
+ {
271
+ httpclient_parser *http = NULL;
272
+ DATA_GET(self, httpclient_parser, http);
273
+
274
+ return httpclient_parser_has_error(http) ? Qtrue : Qfalse;
275
+ }
276
+
277
+
278
+ /**
279
+ * call-seq:
280
+ * parser.finished? -> true/false
281
+ *
282
+ * Tells you whether the parser is finished or not and in a good state.
283
+ */
284
+ VALUE HttpClientParser_is_finished(VALUE self)
285
+ {
286
+ httpclient_parser *http = NULL;
287
+ DATA_GET(self, httpclient_parser, http);
288
+
289
+ return httpclient_parser_is_finished(http) ? Qtrue : Qfalse;
290
+ }
291
+
292
+
293
+ /**
294
+ * call-seq:
295
+ * parser.nread -> Integer
296
+ *
297
+ * Returns the amount of data processed so far during this processing cycle. It is
298
+ * set to 0 on initialize or reset calls and is incremented each time execute is called.
299
+ */
300
+ VALUE HttpClientParser_nread(VALUE self)
301
+ {
302
+ httpclient_parser *http = NULL;
303
+ DATA_GET(self, httpclient_parser, http);
304
+
305
+ return INT2FIX(http->nread);
306
+ }
307
+
308
+
309
+
310
+ void Init_http11_client()
311
+ {
312
+
313
+ mEm = rb_define_module("EventMachine");
314
+
315
+ eHttpClientParserError = rb_define_class_under(mEm, "HttpClientParserError", rb_eIOError);
316
+
317
+ cHttpClientParser = rb_define_class_under(mEm, "HttpClientParser", rb_cObject);
318
+ rb_define_alloc_func(cHttpClientParser, HttpClientParser_alloc);
319
+ rb_define_method(cHttpClientParser, "initialize", HttpClientParser_init,0);
320
+ rb_define_method(cHttpClientParser, "reset", HttpClientParser_reset,0);
321
+ rb_define_method(cHttpClientParser, "finish", HttpClientParser_finish,0);
322
+ rb_define_method(cHttpClientParser, "execute", HttpClientParser_execute,3);
323
+ rb_define_method(cHttpClientParser, "error?", HttpClientParser_has_error,0);
324
+ rb_define_method(cHttpClientParser, "finished?", HttpClientParser_is_finished,0);
325
+ rb_define_method(cHttpClientParser, "nread", HttpClientParser_nread,0);
326
+ }
327
+
328
+