kaiwren-patron 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008 The Hive http://www.thehive.com/
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,62 @@
1
+ = Ruby HTTP Client
2
+
3
+ == WHY FORK?
4
+
5
+ This is a fork of toland/patron that makes the minor change of renaming @headers in Patron::Response to @header, allowing us to mix in Net::HTTPHeader at a later point in time.
6
+
7
+ This makes life easier for me in maintaining API compatibility in Wrest (http://github.com/kaiwren/wrest) which is a HTTP/REST lib which can use either Net::HTTP or libcurl for the core HTTP functionality.
8
+
9
+ == SYNOPSIS
10
+
11
+ Patron is a Ruby HTTP client library based on libcurl. It does not try to expose
12
+ the full "power" (read complexity) of libcurl but instead tries to provide a
13
+ sane API while taking advantage of libcurl under the hood.
14
+
15
+
16
+ == USAGE
17
+
18
+ Usage is very simple. First, you instantiate a Session object. You can set a few
19
+ default options on the Session instance that will be used by all subsequent
20
+ requests:
21
+
22
+ sess = Patron::Session.new
23
+ sess.timeout = 10
24
+ sess.base_url = "http://myserver.com:9900"
25
+ sess.headers['User-Agent'] = 'myapp/1.0'
26
+
27
+ The Session is used to make HTTP requests.
28
+
29
+ resp = sess.get("/foo/bar")
30
+
31
+ Requests return a Response object:
32
+
33
+ if resp.status < 400
34
+ puts resp.body
35
+ end
36
+
37
+ The GET, HEAD, PUT, POST and DELETE operations are all supported.
38
+
39
+ sess.put("/foo/baz", "some data")
40
+ sess.delete("/foo/baz")
41
+
42
+ You can ship custom headers with a single request:
43
+
44
+ sess.post("/foo/stuff", "some data", {"Content-Type" => "text/plain"})
45
+
46
+ That is pretty much all there is to it.
47
+
48
+
49
+ == REQUIREMENTS
50
+
51
+ You need a recent version of libcurl in order to install this gem. On MacOS X
52
+ the provided libcurl is sufficient. You will have to install the libcurl
53
+ development packages on Debian or Ubuntu. Other Linux systems are probably
54
+ similar. Windows users are on your own. Good luck with that.
55
+
56
+
57
+ == INSTALL
58
+
59
+ sudo gem install patron
60
+
61
+
62
+ Copyright (c) 2008 The Hive
@@ -0,0 +1,135 @@
1
+ ## -------------------------------------------------------------------
2
+ ##
3
+ ## Copyright (c) 2008 The Hive http://www.thehive.com/
4
+ ##
5
+ ## Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ ## of this software and associated documentation files (the "Software"), to deal
7
+ ## in the Software without restriction, including without limitation the rights
8
+ ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ ## copies of the Software, and to permit persons to whom the Software is
10
+ ## furnished to do so, subject to the following conditions:
11
+ ##
12
+ ## The above copyright notice and this permission notice shall be included in
13
+ ## all copies or substantial portions of the Software.
14
+ ##
15
+ ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ ## THE SOFTWARE.
22
+ ##
23
+ ## -------------------------------------------------------------------
24
+ require 'yaml'
25
+ require 'rake/clean'
26
+ require 'rake/rdoctask'
27
+ require 'spec/rake/spectask'
28
+ require 'jeweler'
29
+ require 'yard'
30
+
31
+ require 'rbconfig'
32
+ include Config
33
+
34
+ EXT_DIR = 'ext/patron'
35
+ SESSION_SO = "#{EXT_DIR}/session_ext.#{CONFIG['DLEXT']}"
36
+ SESSION_SRC = "#{EXT_DIR}/session_ext.c"
37
+
38
+ CLEAN.include FileList["#{EXT_DIR}/*"].exclude(/^.*\.(rb|c)$/)
39
+ CLOBBER.include %w( doc coverage pkg )
40
+
41
+ module Git
42
+ class Lib
43
+ def tag(tag)
44
+ # Force an annotated tag
45
+ command('tag', [tag, '-a', '-m', tag])
46
+ end
47
+ end
48
+ end
49
+
50
+ Jeweler::Tasks.new do |s|
51
+ s.name = 'kaiwren-patron'
52
+ s.platform = Gem::Platform::RUBY
53
+ s.author = 'Phillip Toland'
54
+ s.email = 'phil.toland@gmail.com'
55
+ s.homepage = 'http://github.com/toland/Patron'
56
+ s.rubyforge_project = 'patron'
57
+ s.summary = 'Patron HTTP client'
58
+ s.description = 'Ruby HTTP client library based on libcurl'
59
+
60
+ s.extensions << 'ext/patron/extconf.rb'
61
+ s.require_paths << 'ext'
62
+
63
+ s.files = FileList['README.txt',
64
+ 'VERSION.yml',
65
+ 'LICENSE',
66
+ 'Rakefile',
67
+ 'lib/**/*',
68
+ 'spec/*',
69
+ 'ext/patron/*.{rb,c}']
70
+
71
+ # rdoc
72
+ s.has_rdoc = true
73
+ s.extra_rdoc_files = ['README.txt']
74
+ s.rdoc_options = ['--quiet',
75
+ '--title', "Patron documentation",
76
+ '--opname', 'index.html',
77
+ '--line-numbers',
78
+ '--main', 'README.txt',
79
+ '--inline-source']
80
+ end
81
+
82
+ file SESSION_SO => SESSION_SRC do
83
+ cd EXT_DIR do
84
+ ruby 'extconf.rb'
85
+ sh 'make'
86
+ end
87
+ end
88
+
89
+ desc "Compile extension"
90
+ task :compile => SESSION_SO
91
+
92
+ desc "Start an IRB shell"
93
+ task :shell => :compile do
94
+ sh 'irb -I./lib -I./ext -r patron'
95
+ end
96
+
97
+ Rake::RDocTask.new do |rdoc|
98
+ rdoc.rdoc_dir = 'rdoc'
99
+ rdoc.title = 'Patron documentation'
100
+ rdoc.main = 'README.txt'
101
+ rdoc.options << '--line-numbers' << '--inline-source'
102
+ rdoc.rdoc_files.include('README.txt')
103
+ rdoc.rdoc_files.include('lib/**/*.rb')
104
+ end
105
+
106
+ YARD::Rake::YardocTask.new do |t|
107
+ t.files = ['lib/**/*.rb']
108
+ t.options = ['--readme', 'README.txt']
109
+ end
110
+
111
+ desc "Run specs"
112
+ Spec::Rake::SpecTask.new(:spec) do |t|
113
+ t.spec_opts = ['--options', "spec/spec.opts"]
114
+ t.spec_files = FileList['spec/**/*_spec.rb']
115
+ end
116
+
117
+ task :spec => [:compile]
118
+
119
+ desc "Run specs with RCov"
120
+ Spec::Rake::SpecTask.new('spec:rcov') do |t|
121
+ t.spec_files = FileList['spec/**/*_spec.rb']
122
+ t.rcov = true
123
+ t.rcov_opts << '--sort coverage'
124
+ t.rcov_opts << '--comments'
125
+ t.rcov_opts << '--exclude spec'
126
+ t.rcov_opts << '--exclude lib/magneto.rb'
127
+ t.rcov_opts << '--exclude /Library/Ruby/Gems'
128
+ end
129
+
130
+ Jeweler::RubyforgeTasks.new do |t|
131
+ t.remote_doc_path = ''
132
+ t.doc_task = :yardoc
133
+ end
134
+
135
+ task :default => :spec
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 4
3
+ :major: 0
4
+ :minor: 4
@@ -0,0 +1,50 @@
1
+ ## -------------------------------------------------------------------
2
+ ##
3
+ ## Copyright (c) 2008 The Hive http://www.thehive.com/
4
+ ##
5
+ ## Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ ## of this software and associated documentation files (the "Software"), to deal
7
+ ## in the Software without restriction, including without limitation the rights
8
+ ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ ## copies of the Software, and to permit persons to whom the Software is
10
+ ## furnished to do so, subject to the following conditions:
11
+ ##
12
+ ## The above copyright notice and this permission notice shall be included in
13
+ ## all copies or substantial portions of the Software.
14
+ ##
15
+ ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ ## THE SOFTWARE.
22
+ ##
23
+ ## -------------------------------------------------------------------
24
+
25
+ if RUBY_PLATFORM =~ /darwin10\.0/
26
+ ENV['ARCHFLAGS'] = '-arch x86_64'
27
+ end
28
+
29
+ require 'mkmf'
30
+ require 'rbconfig'
31
+
32
+ if find_executable('curl-config')
33
+ $CFLAGS << " #{`curl-config --cflags`.strip}"
34
+ $LIBS << " #{`curl-config --libs`.strip}"
35
+ elsif !have_library('curl') or !have_header('curl/curl.h')
36
+ fail <<-EOM
37
+ Can't find libcurl or curl/curl.h
38
+
39
+ Try passing --with-curl-dir or --with-curl-lib and --with-curl-include
40
+ options to extconf.
41
+ EOM
42
+ end
43
+
44
+ if CONFIG['CC'] =~ /gcc/
45
+ $CFLAGS << ' -Wall'
46
+ end
47
+
48
+ $defs.push("-DHAVE_TBR") if have_func('rb_thread_blocking_region')
49
+
50
+ create_makefile 'patron/session_ext'
@@ -0,0 +1,444 @@
1
+ // -------------------------------------------------------------------
2
+ //
3
+ // Patron HTTP Client: Interface to libcurl
4
+ // Copyright (c) 2008 The Hive http://www.thehive.com/
5
+ //
6
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ // of this software and associated documentation files (the "Software"), to deal
8
+ // in the Software without restriction, including without limitation the rights
9
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ // copies of the Software, and to permit persons to whom the Software is
11
+ // furnished to do so, subject to the following conditions:
12
+ //
13
+ // The above copyright notice and this permission notice shall be included in
14
+ // all copies or substantial portions of the Software.
15
+ //
16
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ // THE SOFTWARE.
23
+ //
24
+ // -------------------------------------------------------------------
25
+ #include <ruby.h>
26
+ #include <curl/curl.h>
27
+
28
+ static VALUE mPatron = Qnil;
29
+ static VALUE cSession = Qnil;
30
+ static VALUE cRequest = Qnil;
31
+ static VALUE ePatronError = Qnil;
32
+ static VALUE eUnsupportedProtocol = Qnil;
33
+ static VALUE eURLFormatError = Qnil;
34
+ static VALUE eHostResolutionError = Qnil;
35
+ static VALUE eConnectionFailed = Qnil;
36
+ static VALUE ePartialFileError = Qnil;
37
+ static VALUE eTimeoutError = Qnil;
38
+ static VALUE eTooManyRedirects = Qnil;
39
+
40
+
41
+ struct curl_state {
42
+ CURL* handle;
43
+ char* upload_buf;
44
+ FILE* download_file;
45
+ FILE* upload_file;
46
+ char error_buf[CURL_ERROR_SIZE];
47
+ struct curl_slist* headers;
48
+ };
49
+
50
+
51
+ //------------------------------------------------------------------------------
52
+ // Curl Callbacks
53
+ //
54
+
55
+ // Takes data streamed from libcurl and writes it to a Ruby string buffer.
56
+ static size_t session_write_handler(char* stream, size_t size, size_t nmemb, VALUE out) {
57
+ rb_str_buf_cat(out, stream, size * nmemb);
58
+ return size * nmemb;
59
+ }
60
+
61
+ static size_t session_read_handler(char* stream, size_t size, size_t nmemb, char **buffer) {
62
+ size_t result = 0;
63
+
64
+ if (buffer != NULL && *buffer != NULL) {
65
+ int len = size * nmemb;
66
+ char *s1 = strncpy(stream, *buffer, len);
67
+ result = strlen(s1);
68
+ *buffer += result;
69
+ }
70
+
71
+ return result;
72
+ }
73
+
74
+ //------------------------------------------------------------------------------
75
+ // Object allocation
76
+ //
77
+
78
+ // Cleans up the Curl handle when the Session object is garbage collected.
79
+ void session_free(struct curl_state *curl) {
80
+ curl_easy_cleanup(curl->handle);
81
+ free(curl);
82
+ }
83
+
84
+ // Allocates curl_state data needed for a new Session object.
85
+ VALUE session_alloc(VALUE klass) {
86
+ struct curl_state* curl;
87
+ VALUE obj = Data_Make_Struct(klass, struct curl_state, NULL, session_free, curl);
88
+ return obj;
89
+ }
90
+
91
+
92
+ //------------------------------------------------------------------------------
93
+ // Method implementations
94
+ //
95
+
96
+ // Returns the version of the embedded libcurl as a string.
97
+ VALUE libcurl_version(VALUE klass) {
98
+ char* value = curl_version();
99
+ return rb_str_new2(value);
100
+ }
101
+
102
+ // Initializes the libcurl handle on object initialization.
103
+ // NOTE: This must be called from Session#initialize.
104
+ VALUE session_ext_initialize(VALUE self) {
105
+ struct curl_state *state;
106
+ Data_Get_Struct(self, struct curl_state, state);
107
+
108
+ state->handle = curl_easy_init();
109
+
110
+ return self;
111
+ }
112
+
113
+ // URL escapes the provided string.
114
+ VALUE session_escape(VALUE self, VALUE value) {
115
+ struct curl_state *state;
116
+ Data_Get_Struct(self, struct curl_state, state);
117
+
118
+ VALUE string = StringValue(value);
119
+ char* escaped = curl_easy_escape(state->handle,
120
+ RSTRING_PTR(string),
121
+ RSTRING_LEN(string));
122
+
123
+ VALUE retval = rb_str_new2(escaped);
124
+ curl_free(escaped);
125
+
126
+ return retval;
127
+ }
128
+
129
+ // Unescapes the provided string.
130
+ VALUE session_unescape(VALUE self, VALUE value) {
131
+ struct curl_state *state;
132
+ Data_Get_Struct(self, struct curl_state, state);
133
+
134
+ VALUE string = StringValue(value);
135
+ char* unescaped = curl_easy_unescape(state->handle,
136
+ RSTRING_PTR(string),
137
+ RSTRING_LEN(string),
138
+ NULL);
139
+
140
+ VALUE retval = rb_str_new2(unescaped);
141
+ curl_free(unescaped);
142
+
143
+ return retval;
144
+ }
145
+
146
+ // Callback used to iterate over the HTTP headers and store them in an slist.
147
+ static VALUE each_http_header(VALUE header, VALUE self) {
148
+ struct curl_state *state;
149
+ Data_Get_Struct(self, struct curl_state, state);
150
+
151
+ VALUE name = rb_obj_as_string(rb_ary_entry(header, 0));
152
+ VALUE value = rb_obj_as_string(rb_ary_entry(header, 1));
153
+
154
+ VALUE header_str = Qnil;
155
+ header_str = rb_str_plus(name, rb_str_new2(": "));
156
+ header_str = rb_str_plus(header_str, value);
157
+
158
+ state->headers = curl_slist_append(state->headers, StringValuePtr(header_str));
159
+ return Qnil;
160
+ }
161
+
162
+ static void set_chunked_encoding(struct curl_state *state) {
163
+ state->headers = curl_slist_append(state->headers, "Transfer-Encoding: chunked");
164
+ }
165
+
166
+ static FILE* open_file(VALUE filename, char* perms) {
167
+ FILE* handle = fopen(StringValuePtr(filename), perms);
168
+ if (!handle) {
169
+ rb_raise(rb_eArgError, "Unable to open specified file.");
170
+ }
171
+
172
+ return handle;
173
+ }
174
+
175
+ // Set the options on the Curl handle from a Request object. Takes each field
176
+ // in the Request object and uses it to set the appropriate option on the Curl
177
+ // handle.
178
+ static void set_options_from_request(VALUE self, VALUE request) {
179
+ struct curl_state *state;
180
+ Data_Get_Struct(self, struct curl_state, state);
181
+
182
+ CURL* curl = state->handle;
183
+
184
+ VALUE headers = rb_iv_get(request, "@header");
185
+ if (!NIL_P(headers)) {
186
+ if (rb_type(headers) != T_HASH) {
187
+ rb_raise(rb_eArgError, "Headers must be passed in a hash.");
188
+ }
189
+
190
+ rb_iterate(rb_each, headers, each_http_header, self);
191
+ }
192
+
193
+ ID action = SYM2ID(rb_iv_get(request, "@action"));
194
+ if (action == rb_intern("get")) {
195
+ curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
196
+
197
+ VALUE download_file = rb_iv_get(request, "@file_name");
198
+ if (!NIL_P(download_file)) {
199
+ state->download_file = open_file(download_file, "w");
200
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, state->download_file);
201
+ } else {
202
+ state->download_file = NULL;
203
+ }
204
+ } else if (action == rb_intern("post") || action == rb_intern("put")) {
205
+ VALUE data = rb_iv_get(request, "@upload_data");
206
+ VALUE filename = rb_iv_get(request, "@file_name");
207
+
208
+ if (!NIL_P(data)) {
209
+ state->upload_buf = StringValuePtr(data);
210
+ int len = RSTRING_LEN(data);
211
+
212
+ if (action == rb_intern("post")) {
213
+ curl_easy_setopt(curl, CURLOPT_POST, 1);
214
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, state->upload_buf);
215
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len);
216
+ } else {
217
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
218
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, &session_read_handler);
219
+ curl_easy_setopt(curl, CURLOPT_READDATA, &state->upload_buf);
220
+ curl_easy_setopt(curl, CURLOPT_INFILESIZE, len);
221
+ }
222
+ } else if (!NIL_P(filename)) {
223
+ set_chunked_encoding(state);
224
+
225
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
226
+
227
+ if (action == rb_intern("post")) {
228
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
229
+ }
230
+
231
+ state->upload_file = open_file(filename, "r");
232
+ curl_easy_setopt(curl, CURLOPT_READDATA, state->upload_file);
233
+ } else {
234
+ rb_raise(rb_eArgError, "Must provide either data or a filename when doing a PUT or POST");
235
+ }
236
+ } else if (action == rb_intern("head")) {
237
+ curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
238
+ } else {
239
+ VALUE action_name = rb_funcall(request, rb_intern("action_name"), 0);
240
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, StringValuePtr(action_name));
241
+ }
242
+
243
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, state->headers);
244
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, state->error_buf);
245
+
246
+ VALUE url = rb_iv_get(request, "@url");
247
+ if (NIL_P(url)) {
248
+ rb_raise(rb_eArgError, "Must provide a URL");
249
+ }
250
+ curl_easy_setopt(curl, CURLOPT_URL, StringValuePtr(url));
251
+
252
+ VALUE timeout = rb_iv_get(request, "@timeout");
253
+ if (!NIL_P(timeout)) {
254
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, FIX2INT(timeout));
255
+ }
256
+
257
+ timeout = rb_iv_get(request, "@connect_timeout");
258
+ if (!NIL_P(timeout)) {
259
+ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, FIX2INT(timeout));
260
+ }
261
+
262
+ VALUE redirects = rb_iv_get(request, "@max_redirects");
263
+ if (!NIL_P(redirects)) {
264
+ int r = FIX2INT(redirects);
265
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, r == 0 ? 0 : 1);
266
+ curl_easy_setopt(curl, CURLOPT_MAXREDIRS, r);
267
+ }
268
+
269
+ VALUE proxy = rb_iv_get(request, "@proxy");
270
+ if (!NIL_P(proxy)) {
271
+ curl_easy_setopt(curl, CURLOPT_PROXY, StringValuePtr(proxy));
272
+ }
273
+
274
+ VALUE credentials = rb_funcall(request, rb_intern("credentials"), 0);
275
+ if (!NIL_P(credentials)) {
276
+ curl_easy_setopt(curl, CURLOPT_HTTPAUTH, rb_iv_get(request, "@auth_type"));
277
+ curl_easy_setopt(curl, CURLOPT_USERPWD, StringValuePtr(credentials));
278
+ }
279
+
280
+ VALUE insecure = rb_iv_get(request, "@insecure");
281
+ if(!NIL_P(insecure)) {
282
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
283
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
284
+ }
285
+ }
286
+
287
+ // Use the info in a Curl handle to create a new Response object.
288
+ static VALUE create_response(CURL* curl) {
289
+ VALUE response = rb_class_new_instance(0, 0,
290
+ rb_const_get(mPatron, rb_intern("Response")));
291
+
292
+ char* url = NULL;
293
+ curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
294
+ rb_iv_set(response, "@url", rb_str_new2(url));
295
+
296
+ long code = 0;
297
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
298
+ rb_iv_set(response, "@status", INT2NUM(code));
299
+
300
+ long count = 0;
301
+ curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &count);
302
+ rb_iv_set(response, "@redirect_count", INT2NUM(count));
303
+
304
+ return response;
305
+ }
306
+
307
+ // Raise an exception based on the Curl error code.
308
+ static VALUE select_error(CURLcode code) {
309
+ VALUE error = Qnil;
310
+ switch (code) {
311
+ case CURLE_UNSUPPORTED_PROTOCOL: error = eUnsupportedProtocol; break;
312
+ case CURLE_URL_MALFORMAT: error = eURLFormatError; break;
313
+ case CURLE_COULDNT_RESOLVE_HOST: error = eHostResolutionError; break;
314
+ case CURLE_COULDNT_CONNECT: error = eConnectionFailed; break;
315
+ case CURLE_PARTIAL_FILE: error = ePartialFileError; break;
316
+ case CURLE_OPERATION_TIMEDOUT: error = eTimeoutError; break;
317
+ case CURLE_TOO_MANY_REDIRECTS: error = eTooManyRedirects; break;
318
+
319
+ default: error = ePatronError;
320
+ }
321
+
322
+ return error;
323
+ }
324
+
325
+ // Perform the actual HTTP request by calling libcurl.
326
+ static VALUE perform_request(VALUE self) {
327
+ struct curl_state *state;
328
+ Data_Get_Struct(self, struct curl_state, state);
329
+
330
+ CURL* curl = state->handle;
331
+
332
+ // headers
333
+ VALUE header_buffer = rb_str_buf_new(32768);
334
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, &session_write_handler);
335
+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, header_buffer);
336
+
337
+ // body
338
+ VALUE body_buffer = Qnil;
339
+ if (!state->download_file) {
340
+ body_buffer = rb_str_buf_new(32768);
341
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &session_write_handler);
342
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, body_buffer);
343
+ }
344
+
345
+ #ifdef HAVE_TBR
346
+ CURLcode ret = rb_thread_blocking_region(curl_easy_perform, curl, RUBY_UBF_IO, 0);
347
+ #else
348
+ CURLcode ret = curl_easy_perform(curl);
349
+ #endif
350
+ if (CURLE_OK == ret) {
351
+ VALUE response = create_response(curl);
352
+ if (!NIL_P(body_buffer)) {
353
+ rb_iv_set(response, "@body", body_buffer);
354
+ }
355
+ rb_funcall(response, rb_intern("parse_headers"), 1, header_buffer);
356
+ return response;
357
+ } else {
358
+ rb_raise(select_error(ret), state->error_buf);
359
+ }
360
+ }
361
+
362
+ // Cleanup after each request by resetting the Curl handle and deallocating all
363
+ // request related objects such as the header slist.
364
+ static VALUE cleanup(VALUE self) {
365
+ struct curl_state *state;
366
+ Data_Get_Struct(self, struct curl_state, state);
367
+
368
+ curl_easy_reset(state->handle);
369
+
370
+ if (state->headers) {
371
+ curl_slist_free_all(state->headers);
372
+ state->headers = NULL;
373
+ }
374
+
375
+ if (state->download_file) {
376
+ fclose(state->download_file);
377
+ state->download_file = NULL;
378
+ }
379
+
380
+ if (state->upload_file) {
381
+ fclose(state->upload_file);
382
+ state->upload_file = NULL;
383
+ }
384
+
385
+ state->upload_buf = NULL;
386
+
387
+ return Qnil;
388
+ }
389
+
390
+ VALUE session_handle_request(VALUE self, VALUE request) {
391
+ set_options_from_request(self, request);
392
+ return rb_ensure(&perform_request, self, &cleanup, self);
393
+ }
394
+
395
+ VALUE enable_cookie_session(VALUE self, VALUE file) {
396
+ struct curl_state *state;
397
+ Data_Get_Struct(self, struct curl_state, state);
398
+ CURL* curl = state->handle;
399
+ char* file_path = RSTRING_PTR(file);
400
+ if (file_path != NULL && strlen(file_path) != 0) {
401
+ curl_easy_setopt(curl, CURLOPT_COOKIEJAR, file_path);
402
+ }
403
+ curl_easy_setopt(curl, CURLOPT_COOKIEFILE, file_path);
404
+ return Qnil;
405
+ }
406
+
407
+ //------------------------------------------------------------------------------
408
+ // Extension initialization
409
+ //
410
+
411
+ void Init_session_ext() {
412
+ curl_global_init(CURL_GLOBAL_ALL);
413
+ rb_require("patron/error");
414
+
415
+ mPatron = rb_define_module("Patron");
416
+
417
+ ePatronError = rb_const_get(mPatron, rb_intern("Error"));
418
+
419
+ eUnsupportedProtocol = rb_const_get(mPatron, rb_intern("UnsupportedProtocol"));
420
+ eURLFormatError = rb_const_get(mPatron, rb_intern("URLFormatError"));
421
+ eHostResolutionError = rb_const_get(mPatron, rb_intern("HostResolutionError"));
422
+ eConnectionFailed = rb_const_get(mPatron, rb_intern("ConnectionFailed"));
423
+ ePartialFileError = rb_const_get(mPatron, rb_intern("PartialFileError"));
424
+ eTimeoutError = rb_const_get(mPatron, rb_intern("TimeoutError"));
425
+ eTooManyRedirects = rb_const_get(mPatron, rb_intern("TooManyRedirects"));
426
+
427
+
428
+ rb_define_module_function(mPatron, "libcurl_version", libcurl_version, 0);
429
+
430
+ cSession = rb_define_class_under(mPatron, "Session", rb_cObject);
431
+ cRequest = rb_define_class_under(mPatron, "Request", rb_cObject);
432
+ rb_define_alloc_func(cSession, session_alloc);
433
+
434
+ rb_define_method(cSession, "ext_initialize", session_ext_initialize, 0);
435
+ rb_define_method(cSession, "escape", session_escape, 1);
436
+ rb_define_method(cSession, "unescape", session_unescape, 1);
437
+ rb_define_method(cSession, "handle_request", session_handle_request, 1);
438
+ rb_define_method(cSession, "enable_cookie_session", enable_cookie_session, 1);
439
+
440
+ rb_define_const(cRequest, "AuthBasic", CURLAUTH_BASIC);
441
+ rb_define_const(cRequest, "AuthDigest", CURLAUTH_DIGEST);
442
+ rb_define_const(cRequest, "AuthAny", CURLAUTH_ANY);
443
+
444
+ }