yarv-diff:222
From: ko1 atdot.net
Date: 11 Feb 2006 05:42:52 -0000
Subject: [yarv-diff:222] r379 - in trunk: . ext/digest ext/digest/sha2 ext/etc ext/win32ole lib lib/net lib/test/unit missing rb sample test test/ruby
Author: ko1
Date: 2006-02-11 14:42:50 +0900 (Sat, 11 Feb 2006)
New Revision: 379
Added:
trunk/lib/net/
trunk/lib/net/.document
trunk/lib/net/ftp.rb
trunk/lib/net/http.rb
trunk/lib/net/https.rb
trunk/lib/net/imap.rb
trunk/lib/net/pop.rb
trunk/lib/net/protocol.rb
trunk/lib/net/smtp.rb
trunk/lib/net/telnet.rb
trunk/lib/open-uri.rb
trunk/lib/timeout.rb
Modified:
trunk/
trunk/ChangeLog
trunk/array.c
trunk/compar.c
trunk/dln.c
trunk/enum.c
trunk/enumerator.c
trunk/error.c
trunk/eval.c
trunk/eval_thread.c
trunk/ext/digest/digest.c
trunk/ext/digest/digest.h
trunk/ext/digest/sha2/sha2.c
trunk/ext/etc/etc.c
trunk/ext/win32ole/win32ole.c
trunk/hash.c
trunk/intern.h
trunk/io.c
trunk/lib/cgi.rb
trunk/lib/complex.rb
trunk/lib/delegate.rb
trunk/lib/erb.rb
trunk/lib/fileutils.rb
trunk/lib/matrix.rb
trunk/lib/mkmf.rb
trunk/lib/optparse.rb
trunk/lib/ostruct.rb
trunk/lib/pp.rb
trunk/lib/test/unit/autorunner.rb
trunk/lib/tmpdir.rb
trunk/main.c
trunk/missing.h
trunk/missing/flock.c
trunk/missing/isinf.c
trunk/missing/vsnprintf.c
trunk/node.h
trunk/object.c
trunk/parse.y
trunk/rb/mklog.rb
trunk/ruby.c
trunk/sample/test.rb
trunk/sprintf.c
trunk/st.c
trunk/test.rb
trunk/test/ruby/test_whileuntil.rb
trunk/test/runner.rb
trunk/thread.c
trunk/thread_pthread.h
trunk/thread_win32.h
trunk/time.c
trunk/yarvcore.c
Log:
r552@leremita: ko1 | 2006-02-11 14:42:07 +0900
* rb/mklog.rb : use svk
* error.c : remove newline
* eval.c (rb_block_call) : added
* eval_thread.c : remove some unused functions, comments
* thread.c : add comments (move from eval_thread.c) and support Mutex
* thread.c (rb_thread_select) : supported
* thread_pthread.h (native_mutex_trylock) : added (macro)
* thread_win32.h (native_mutex_trylock) : added
* yarvcore.c : remove unused code
* array.c : import ruby 1.9
* compar.c : ditto
* dln.c : ditto
* enum.c : ditto
* enumerator.c : ditto
* ext/digest/digest.c : ditto
* ext/digest/digest.h : ditto
* ext/digest/sha2/sha2.c : ditto
* ext/etc/etc.c : ditto
* ext/win32ole/win32ole.c : ditto
* hash.c : ditto
* intern.h : ditto
* io.c : ditto
* main.c : ditto
* missing.h : ditto
* missing/flock.c : ditto
* missing/isinf.c : ditto
* missing/vsnprintf.c : ditto
* lib/cgi.rb : ditto
* lib/complex.rb : ditto
* lib/delegate.rb : ditto
* lib/erb.rb : ditto
* lib/fileutils.rb : ditto
* lib/matrix.rb : ditto
* lib/mkmf.rb : ditto
* lib/optparse.rb : ditto
* lib/ostruct.rb : ditto
* lib/pp.rb : ditto
* lib/timeout.rb : ditto
* lib/tmpdir.rb : ditto
* lib/test/unit/autorunner.rb : ditto
* node.h : ditto
* object.c : ditto
* parse.y : ditto
* ruby.c : ditto
* sample/test.rb : ditto
* sprintf.c : ditto
* st.c : ditto
* test/ruby/test_whileuntil.rb : ditto
* test/runner.rb : ditto
* time.c : ditto
* lib/net/.document : added
* lib/net/ftp.rb : ditto
* lib/net/http.rb : ditto
* lib/net/https.rb : ditto
* lib/net/imap.rb : ditto
* lib/net/pop.rb : ditto
* lib/net/protocol.rb : ditto
* lib/net/smtp.rb : ditto
* lib/net/telnet.rb : ditto
* lib/open-uri.rb : ditto
Property changes on: trunk
___________________________________________________________________
Name: svk:merge
+ 81cd9672-7512-7e48-ae48-6936450e977d:/local/yarv/trunk:552
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/ChangeLog 2006-02-11 05:42:50 UTC (rev 379)
@@ -4,6 +4,129 @@
# from Mon, 03 May 2004 01:24:19 +0900
#
+2006-02-11(Sat) 14:29:01 +0900 Koichi Sasada <ko1 atdot.net>
+
+ * rb/mklog.rb : use svk
+
+ * error.c : remove newline
+
+ * eval.c (rb_block_call) : added
+
+ * eval_thread.c : remove some unused functions, comments
+
+ * thread.c : add comments (move from eval_thread.c) and support Mutex
+
+ * thread.c (rb_thread_select) : supported
+
+ * thread_pthread.h (native_mutex_trylock) : added (macro)
+
+ * thread_win32.h (native_mutex_trylock) : added
+
+ * yarvcore.c : remove unused code
+
+ * array.c : import ruby 1.9
+
+ * compar.c : ditto
+
+ * dln.c : ditto
+
+ * enum.c : ditto
+
+ * enumerator.c : ditto
+
+ * ext/digest/digest.c : ditto
+
+ * ext/digest/digest.h : ditto
+
+ * ext/digest/sha2/sha2.c : ditto
+
+ * ext/etc/etc.c : ditto
+
+ * ext/win32ole/win32ole.c : ditto
+
+ * hash.c : ditto
+
+ * intern.h : ditto
+
+ * io.c : ditto
+
+ * main.c : ditto
+
+ * missing.h : ditto
+
+ * missing/flock.c : ditto
+
+ * missing/isinf.c : ditto
+
+ * missing/vsnprintf.c : ditto
+
+ * lib/cgi.rb : ditto
+
+ * lib/complex.rb : ditto
+
+ * lib/delegate.rb : ditto
+
+ * lib/erb.rb : ditto
+
+ * lib/fileutils.rb : ditto
+
+ * lib/matrix.rb : ditto
+
+ * lib/mkmf.rb : ditto
+
+ * lib/optparse.rb : ditto
+
+ * lib/ostruct.rb : ditto
+
+ * lib/pp.rb : ditto
+
+ * lib/timeout.rb : ditto
+
+ * lib/tmpdir.rb : ditto
+
+ * lib/test/unit/autorunner.rb : ditto
+
+ * node.h : ditto
+
+ * object.c : ditto
+
+ * parse.y : ditto
+
+ * ruby.c : ditto
+
+ * sample/test.rb : ditto
+
+ * sprintf.c : ditto
+
+ * st.c : ditto
+
+ * test/ruby/test_whileuntil.rb : ditto
+
+ * test/runner.rb : ditto
+
+ * time.c : ditto
+
+ * lib/net/.document : added
+
+ * lib/net/ftp.rb : ditto
+
+ * lib/net/http.rb : ditto
+
+ * lib/net/https.rb : ditto
+
+ * lib/net/imap.rb : ditto
+
+ * lib/net/pop.rb : ditto
+
+ * lib/net/protocol.rb : ditto
+
+ * lib/net/smtp.rb : ditto
+
+ * lib/net/telnet.rb : ditto
+
+ * lib/open-uri.rb : ditto
+
+
2006-02-10(Fri) 08:07:34 +0900 Koichi Sasada <ko1 atdot.net>
* compile.c, insns.def, yarvcore.h : support defined?(private_method) and
Modified: trunk/array.c
===================================================================
--- trunk/array.c 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/array.c 2006-02-11 05:42:50 UTC (rev 379)
@@ -2,8 +2,8 @@
array.c -
- $Author: ocean $
- $Date: 2005/11/15 07:37:39 $
+ $Author: akr $
+ $Date: 2005/12/12 16:46:59 $
created at: Fri Aug 6 09:46:12 JST 1993
Copyright (C) 1993-2003 Yukihiro Matsumoto
@@ -2517,7 +2517,8 @@
static VALUE
rb_ary_diff(VALUE ary1, VALUE ary2)
{
- VALUE ary3, hash;
+ VALUE ary3;
+ volatile VALUE hash;
long i;
hash = ary_make_hash(to_ary(ary2), 0);
Modified: trunk/compar.c
===================================================================
--- trunk/compar.c 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/compar.c 2006-02-11 05:42:50 UTC (rev 379)
@@ -3,7 +3,7 @@
compar.c -
$Author: matz $
- $Date: 2005/09/12 15:23:54 $
+ $Date: 2005/12/12 00:35:09 $
created at: Thu Aug 26 14:39:48 JST 1993
Copyright (C) 1993-2003 Yukihiro Matsumoto
@@ -53,7 +53,7 @@
{
VALUE c = rb_funcall(a[0], cmp, 1, a[1]);
- if (NIL_P(c)) return Qnil;
+ if (NIL_P(c)) return Qfalse;
if (rb_cmpint(c, a[0], a[1]) == 0) return Qtrue;
return Qfalse;
}
@@ -61,7 +61,7 @@
static VALUE
cmp_failed(void)
{
- return Qnil;
+ return Qfalse;
}
/*
Modified: trunk/dln.c
===================================================================
--- trunk/dln.c 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/dln.c 2006-02-11 05:42:50 UTC (rev 379)
@@ -2,8 +2,8 @@
dln.c -
- $Author: ocean $
- $Date: 2005/09/12 11:03:24 $
+ $Author: nobu $
+ $Date: 2006/01/25 13:29:44 $
created at: Tue Jan 18 17:05:06 JST 1994
Copyright (C) 1993-2003 Yukihiro Matsumoto
@@ -89,8 +89,6 @@
# include <image.h>
#endif
-int eaccess(const char *, int);
-
#ifndef NO_DLN_LOAD
#if defined(HAVE_DLOPEN) && !defined(USE_DLN_A_OUT) && !defined(_AIX) && !defined(__APPLE__) && !defined(_UNICOSMP)
@@ -1645,6 +1643,7 @@
char *p = win32;
char *dst = posix;
+ posix[0] = '\0';
for (p = win32; *p; p++)
if (*p == ';') {
*p = 0;
Modified: trunk/enum.c
===================================================================
--- trunk/enum.c 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/enum.c 2006-02-11 05:42:50 UTC (rev 379)
@@ -17,12 +17,6 @@
VALUE rb_mEnumerable;
static ID id_each, id_eqq, id_cmp;
-VALUE
-rb_each(VALUE obj)
-{
- return rb_funcall(obj, id_each, 0, 0);
-}
-
static VALUE
grep_i(VALUE i, VALUE *arg)
{
@@ -68,7 +62,7 @@
arg[0] = pat;
arg[1] = ary;
- rb_iterate(rb_each, obj, rb_block_given_p() ? grep_iter_i : grep_i, (VALUE)arg);
+ rb_block_call(obj, id_each, 0, 0, rb_block_given_p() ? grep_iter_i : grep_i, (VALUE)arg);
return ary;
}
@@ -117,13 +111,13 @@
rb_scan_args(argc, argv, "1", &item);
args[0] = item;
args[1] = 0;
- rb_iterate(rb_each, obj, count_i, (VALUE)&args);
+ rb_block_call(obj, id_each, 0, 0, count_i, (VALUE)&args);
return INT2NUM(args[1]);
}
else {
long n = 0;
- rb_iterate(rb_each, obj, count_iter_i, (VALUE)&n);
+ rb_block_call(obj, id_each, 0, 0, count_iter_i, (VALUE)&n);
return INT2NUM(n);
}
}
@@ -161,7 +155,7 @@
rb_scan_args(argc, argv, "01", &if_none);
RETURN_ENUMERATOR(obj, argc, argv);
- rb_iterate(rb_each, obj, find_i, (VALUE)&memo);
+ rb_block_call(obj, id_each, 0, 0, find_i, (VALUE)&memo);
if (memo != Qundef) {
return memo;
}
@@ -203,7 +197,7 @@
RETURN_ENUMERATOR(obj, 0, 0);
memo[0] = Qundef;
memo[1] = 0;
- rb_iterate(rb_each, obj, find_index_i, (VALUE)memo);
+ rb_block_call(obj, id_each, 0, 0, find_index_i, (VALUE)memo);
if (memo[0] != Qundef) {
return memo[0];
}
@@ -240,7 +234,7 @@
RETURN_ENUMERATOR(obj, 0, 0);
ary = rb_ary_new();
- rb_iterate(rb_each, obj, find_all_i, ary);
+ rb_block_call(obj, id_each, 0, 0, find_all_i, ary);
return ary;
}
@@ -273,7 +267,7 @@
RETURN_ENUMERATOR(obj, 0, 0);
ary = rb_ary_new();
- rb_iterate(rb_each, obj, reject_i, ary);
+ rb_block_call(obj, id_each, 0, 0, reject_i, ary);
return ary;
}
@@ -315,7 +309,7 @@
RETURN_ENUMERATOR(obj, 0, 0);
ary = rb_ary_new();
- rb_iterate(rb_each, obj, collect_i, ary);
+ rb_block_call(obj, id_each, 0, 0, collect_i, ary);
return ary;
}
@@ -335,7 +329,7 @@
{
VALUE ary = rb_ary_new();
- rb_iterate(rb_each, obj, collect_all, ary);
+ rb_block_call(obj, id_each, 0, 0, collect_all, ary);
return ary;
}
@@ -390,7 +384,7 @@
if (rb_scan_args(argc, argv, "01", &memo) == 0)
memo = Qundef;
- rb_iterate(rb_each, obj, inject_i, (VALUE)&memo);
+ rb_block_call(obj, id_each, 0, 0, inject_i, (VALUE)&memo);
if (memo == Qundef) return Qnil;
return memo;
}
@@ -428,7 +422,7 @@
ary[0] = rb_ary_new();
ary[1] = rb_ary_new();
- rb_iterate(rb_each, obj, partition_i, (VALUE)ary);
+ rb_block_call(obj, id_each, 0, 0, partition_i, (VALUE)ary);
return rb_assoc_new(ary[0], ary[1]);
}
@@ -470,7 +464,7 @@
RETURN_ENUMERATOR(obj, 0, 0);
hash = rb_hash_new();
- rb_iterate(rb_each, obj, group_by_i, hash);
+ rb_block_call(obj, id_each, 0, 0, group_by_i, hash);
return hash;
}
@@ -520,7 +514,7 @@
ary[0] = n;
ary[1] = rb_ary_new2(NUM2LONG(n));
}
- rb_iterate(rb_each, obj, first_i, (VALUE)ary);
+ rb_block_call(obj, id_each, 0, 0, first_i, (VALUE)ary);
return ary[1];
}
@@ -656,7 +650,7 @@
ary = rb_ary_new();
}
RBASIC(ary)->klass = 0;
- rb_iterate(rb_each, obj, sort_by_i, ary);
+ rb_block_call(obj, id_each, 0, 0, sort_by_i, ary);
if (RARRAY(ary)->len > 1) {
ruby_qsort(RARRAY(ary)->ptr, RARRAY(ary)->len, sizeof(VALUE), sort_by_cmp, 0);
}
@@ -712,7 +706,7 @@
{
VALUE result = Qtrue;
- rb_iterate(rb_each, obj, rb_block_given_p() ? all_iter_i : all_i, (VALUE)&result);
+ rb_block_call(obj, id_each, 0, 0, rb_block_given_p() ? all_iter_i : all_i, (VALUE)&result);
return result;
}
@@ -759,7 +753,7 @@
{
VALUE result = Qfalse;
- rb_iterate(rb_each, obj, rb_block_given_p() ? any_iter_i : any_i, (VALUE)&result);
+ rb_block_call(obj, id_each, 0, 0, rb_block_given_p() ? any_iter_i : any_i, (VALUE)&result);
return result;
}
@@ -812,7 +806,7 @@
{
VALUE result = Qundef;
- rb_iterate(rb_each, obj, rb_block_given_p() ? one_iter_i : one_i, (VALUE)&result);
+ rb_block_call(obj, id_each, 0, 0, rb_block_given_p() ? one_iter_i : one_i, (VALUE)&result);
if (result == Qundef) return Qfalse;
return result;
}
@@ -857,7 +851,7 @@
{
VALUE result = Qtrue;
- rb_iterate(rb_each, obj, rb_block_given_p() ? none_iter_i : none_i, (VALUE)&result);
+ rb_block_call(obj, id_each, 0, 0, rb_block_given_p() ? none_iter_i : none_i, (VALUE)&result);
return result;
}
@@ -915,7 +909,7 @@
{
VALUE result = Qundef;
- rb_iterate(rb_each, obj, rb_block_given_p() ? min_ii : min_i, (VALUE)&result);
+ rb_block_call(obj, id_each, 0, 0, rb_block_given_p() ? min_ii : min_i, (VALUE)&result);
if (result == Qundef) return Qnil;
return result;
}
@@ -973,7 +967,7 @@
{
VALUE result = Qundef;
- rb_iterate(rb_each, obj, rb_block_given_p() ? max_ii : max_i, (VALUE)&result);
+ rb_block_call(obj, id_each, 0, 0, rb_block_given_p() ? max_ii : max_i, (VALUE)&result);
if (result == Qundef) return Qnil;
return result;
}
@@ -1015,7 +1009,7 @@
memo[0] = Qundef;
memo[1] = Qnil;
- rb_iterate(rb_each, obj, min_by_i, (VALUE)memo);
+ rb_block_call(obj, id_each, 0, 0, min_by_i, (VALUE)memo);
return memo[1];
}
@@ -1056,7 +1050,7 @@
memo[0] = Qundef;
memo[1] = Qnil;
- rb_iterate(rb_each, obj, max_by_i, (VALUE)memo);
+ rb_block_call(obj, id_each, 0, 0, max_by_i, (VALUE)memo);
return memo[1];
}
@@ -1090,7 +1084,7 @@
memo[0] = val;
memo[1] = Qfalse;
- rb_iterate(rb_each, obj, member_i, (VALUE)memo);
+ rb_block_call(obj, id_each, 0, 0, member_i, (VALUE)memo);
return memo[1];
}
@@ -1124,7 +1118,7 @@
RETURN_ENUMERATOR(obj, 0, 0);
- rb_iterate(rb_each, obj, each_with_index_i, (VALUE)&memo);
+ rb_block_call(obj, id_each, 0, 0, each_with_index_i, (VALUE)&memo);
return obj;
}
@@ -1188,7 +1182,7 @@
memo[0] = result;
memo[1] = rb_ary_new4(argc, argv);
memo[2] = 0;
- rb_iterate(rb_each, obj, zip_i, (VALUE)memo);
+ rb_block_call(obj, id_each, 0, 0, zip_i, (VALUE)memo);
return result;
}
Modified: trunk/enumerator.c
===================================================================
--- trunk/enumerator.c 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/enumerator.c 2006-02-11 05:42:50 UTC (rev 379)
@@ -2,13 +2,13 @@
enumerator.c - provides Enumerator class
- $Author: nobu $
+ $Author: matz $
Copyright (C) 2001-2003 Akinori MUSHA
$Idaemons: /home/cvs/rb/enumerator/enumerator.c,v 1.1.1.1 2001/07/15 10:12:48 knu Exp $
$RoughId: enumerator.c,v 1.6 2003/07/27 11:03:24 nobu Exp $
- $Id: enumerator.c,v 1.9 2005/10/27 11:35:24 nobu Exp $
+ $Id: enumerator.c,v 1.10 2006/02/03 09:15:42 matz Exp $
************************************************/
@@ -32,18 +32,6 @@
return rb_proc_call(proc, args);
}
-static VALUE
-method_call(VALUE method, VALUE args)
-{
- int argc = 0;
- VALUE *argv = 0;
- if (args) {
- argc = RARRAY(args)->len;
- argv = RARRAY(args)->ptr;
- }
- return rb_method_call(argc, argv, method);
-}
-
struct enumerator {
VALUE method;
VALUE proc;
@@ -168,7 +156,7 @@
args[0] = rb_ary_new2(size);
args[1] = (VALUE)size;
- rb_iterate(rb_each, obj, each_slice_i, (VALUE)args);
+ rb_block_call(obj, rb_intern("each"), 0, 0, each_slice_i, (VALUE)args);
ary = args[0];
if (RARRAY(ary)->len > 0) rb_yield(ary);
@@ -235,7 +223,7 @@
args[0] = rb_ary_new2(size);
args[1] = (VALUE)size;
- rb_iterate(rb_each, obj, each_cons_i, (VALUE)args);
+ rb_block_call(obj, rb_intern("each"), 0, 0, each_cons_i, (VALUE)args);
return Qnil;
}
@@ -315,14 +303,6 @@
return enumerator_init(enumerator_allocate(rb_cEnumerator), obj, meth, argc, argv);
}
-static VALUE
-enumerator_iter(VALUE memo)
-{
- struct enumerator *e = (struct enumerator *)memo;
-
- return method_call(e->method, e->args);
-}
-
/*
* call-seq:
* enum.each {...}
@@ -335,8 +315,14 @@
enumerator_each(VALUE obj)
{
struct enumerator *e = enumerator_ptr(obj);
+ int argc = 0;
+ VALUE *argv = 0;
- return rb_iterate(enumerator_iter, (VALUE)e, e->iter, (VALUE)e);
+ if (e->args) {
+ argc = RARRAY(e->args)->len;
+ argv = RARRAY(e->args)->ptr;
+ }
+ return rb_block_call(e->method, rb_intern("call"), argc, argv, e->iter, (VALUE)e);
}
static VALUE
@@ -360,8 +346,16 @@
{
struct enumerator *e = enumerator_ptr(obj);
VALUE memo = 0;
+ int argc = 0;
+ VALUE *argv = 0;
- return rb_iterate(enumerator_iter, (VALUE)e,
+ if (e->args) {
+ argc = RARRAY(e->args)->len;
+ argv = RARRAY(e->args)->ptr;
+ }
+ return rb_block_call(e->method, rb_intern("call"), argc, argv, e->iter, (VALUE)e);
+
+ return rb_block_call(e->method, rb_intern("call"), argc, argv,
enumerator_with_index_i, (VALUE)&memo);
}
Modified: trunk/error.c
===================================================================
--- trunk/error.c 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/error.c 2006-02-11 05:42:50 UTC (rev 379)
@@ -70,7 +70,6 @@
rb_compile_error(const char *fmt, ...)
{
va_list args;
-
va_start(args, fmt);
err_print(fmt, args);
va_end(args);
Modified: trunk/eval.c
===================================================================
--- trunk/eval.c 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/eval.c 2006-02-11 05:42:50 UTC (rev 379)
@@ -1316,7 +1316,37 @@
return retval;
}
+struct iter_method_arg {
+ VALUE obj;
+ ID mid;
+ int argc;
+ VALUE *argv;
+};
+
+static VALUE
+iterate_method(VALUE obj)
+{
+ struct iter_method_arg *arg;
+
+ arg = (struct iter_method_arg*)obj;
+ return rb_call(CLASS_OF(arg->obj), arg->obj, arg->mid,
+ arg->argc, arg->argv, NOEX_PRIVATE);
+}
+
VALUE
+rb_block_call(VALUE obj, ID mid, int argc, VALUE *argv,
+ VALUE (*bl_proc)(ANYARGS), VALUE data2)
+{
+ struct iter_method_arg arg;
+
+ arg.obj = obj;
+ arg.mid = mid;
+ arg.argc = argc;
+ arg.argv = argv;
+ return rb_iterate(iterate_method, (VALUE)&arg, bl_proc, data2);
+}
+
+VALUE
#ifdef HAVE_STDARG_PROTOTYPES
rb_rescue2(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE (*r_proc)(ANYARGS), VALUE data2, ...)
#else
Modified: trunk/eval_thread.c
===================================================================
--- trunk/eval_thread.c 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/eval_thread.c 2006-02-11 05:42:50 UTC (rev 379)
@@ -691,23 +691,6 @@
return n;
}
-static int
-find_bad_fds(dst, src, max)
- rb_fdset_t *dst, *src;
- int max;
-{
- int i, test = Qfalse;
-
- if (max >= rb_fd_max(src)) max = rb_fd_max(src) - 1;
- for (i=0; i<=max; i++) {
- if (FD_ISSET(i, src) && !FD_ISSET(i, dst)) {
- FD_CLR(i, src);
- test = Qtrue;
- }
- }
- return test;
-}
-
void
rb_thread_schedule()
{
@@ -717,89 +700,6 @@
/* UNSUPPORTED(rb_thread_schedule); */
}
-int
-rb_thread_select(max, read, write, except, timeout)
- int max;
- fd_set *read, *write, *except;
- struct timeval *timeout;
-{
- UNSUPPORTED(rb_thread_select);
- return 0;
-}
-
-static int
-rb_thread_join(rb_thread_t th, double limit)
-{
- return Qtrue;
-}
-
-/*
- * call-seq:
- * thr.join => thr
- * thr.join(limit) => thr
- *
- * The calling thread will suspend execution and run <i>thr</i>. Does not
- * return until <i>thr</i> exits or until <i>limit</i> seconds have passed. If
- * the time limit expires, <code>nil</code> will be returned, otherwise
- * <i>thr</i> is returned.
- *
- * Any threads not joined will be killed when the main program exits. If
- * <i>thr</i> had previously raised an exception and the
- * <code>abort_on_exception</code> and <code>$DEBUG</code> flags are not set
- * (so the exception has not yet been processed) it will be processed at this
- * time.
- *
- * a = Thread.new { print "a"; sleep(10); print "b"; print "c" }
- * x = Thread.new { print "x"; Thread.pass; print "y"; print "z" }
- * x.join # Let x thread finish, a will be killed on exit.
- *
- * <em>produces:</em>
- *
- * axyz
- *
- * The following example illustrates the <i>limit</i> parameter.
- *
- * y = Thread.new { 4.times { sleep 0.1; puts 'tick... ' }}
- * puts "Waiting" until y.join(0.15)
- *
- * <em>produces:</em>
- *
- * tick...
- * Waiting
- * tick...
- * Waitingtick...
- *
- *
- * tick...
- */
-
-static VALUE
-rb_thread_join_m(argc, argv, thread)
- int argc;
- VALUE *argv;
- VALUE thread;
-{
- VALUE limit;
- double delay = DELAY_INFTY;
- rb_thread_t th = rb_thread_check(thread);
-
- rb_scan_args(argc, argv, "01", &limit);
- if (!NIL_P(limit)) delay = rb_num2dbl(limit);
- if (!rb_thread_join(th, delay))
- return Qnil;
- return thread;
-}
-
-
-/*
- * call-seq:
- * Thread.current => thread
- *
- * Returns the currently executing thread.
- *
- * Thread.current #=> #<Thread:0x401bdf4c run>
- */
-
VALUE
rb_thread_current()
{
Modified: trunk/ext/digest/digest.c
===================================================================
--- trunk/ext/digest/digest.c 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/ext/digest/digest.c 2006-02-11 05:42:50 UTC (rev 379)
@@ -2,14 +2,14 @@
digest.c -
- $Author: matz $
+ $Author: akr $
created at: Fri May 25 08:57:27 JST 2001
Copyright (C) 1995-2001 Yukihiro Matsumoto
Copyright (C) 2001 Akinori MUSHA
$RoughId: digest.c,v 1.16 2001/07/13 15:38:27 knu Exp $
- $Id: digest.c,v 1.15 2004/09/17 09:24:11 matz Exp $
+ $Id: digest.c,v 1.17 2005/12/12 05:15:33 akr Exp $
************************************************/
@@ -41,8 +41,7 @@
*/
static algo_t *
-get_digest_base_metadata(klass)
- VALUE klass;
+get_digest_base_metadata(VALUE klass)
{
VALUE obj;
algo_t *algo;
@@ -58,10 +57,8 @@
return algo;
}
-static VALUE rb_digest_base_alloc _((VALUE));
static VALUE
-rb_digest_base_alloc(klass)
- VALUE klass;
+rb_digest_base_alloc(VALUE klass)
{
algo_t *algo;
VALUE obj;
@@ -83,15 +80,11 @@
}
static VALUE
-rb_digest_base_s_digest(klass, str)
- VALUE klass;
- VALUE str;
+rb_digest_base_s_digest(VALUE klass, VALUE str)
{
algo_t *algo;
void *pctx;
- size_t len;
- unsigned char *digest;
- VALUE obj = rb_digest_base_alloc(klass);
+ volatile VALUE obj = rb_digest_base_alloc(klass);
algo = get_digest_base_metadata(klass);
Data_Get_Struct(obj, void, pctx);
@@ -99,28 +92,18 @@
StringValue(str);
algo->update_func(pctx, RSTRING(str)->ptr, RSTRING(str)->len);
- len = algo->digest_len;
+ str = rb_str_new(0, algo->digest_len);
+ algo->final_func(RSTRING(str)->ptr, pctx);
- digest = xmalloc(len);
- algo->final_func(digest, pctx);
-
- obj = rb_str_new(digest, len);
-
- free(digest);
-
- return obj;
+ return str;
}
static VALUE
-rb_digest_base_s_hexdigest(klass, str)
- VALUE klass;
- VALUE str;
+rb_digest_base_s_hexdigest(VALUE klass, VALUE str)
{
algo_t *algo;
void *pctx;
- size_t len;
- unsigned char *hexdigest;
- VALUE obj = rb_digest_base_alloc(klass);
+ volatile VALUE obj = rb_digest_base_alloc(klass);
algo = get_digest_base_metadata(klass);
Data_Get_Struct(obj, void, pctx);
@@ -128,21 +111,14 @@
StringValue(str);
algo->update_func(pctx, RSTRING(str)->ptr, RSTRING(str)->len);
- len = algo->digest_len * 2;
+ str = rb_str_new(0, algo->digest_len * 2);
+ algo->end_func(pctx, RSTRING(str)->ptr);
- hexdigest = xmalloc(len + 1); /* +1 is for '\0' */
- algo->end_func(pctx, hexdigest);
-
- obj = rb_str_new(hexdigest, len);
-
- free(hexdigest);
-
- return obj;
+ return str;
}
static VALUE
-rb_digest_base_copy(copy, obj)
- VALUE copy, obj;
+rb_digest_base_copy(VALUE copy, VALUE obj)
{
algo_t *algo;
void *pctx1, *pctx2;
@@ -161,8 +137,7 @@
}
static VALUE
-rb_digest_base_update(self, str)
- VALUE self, str;
+rb_digest_base_update(VALUE self, VALUE str)
{
algo_t *algo;
void *pctx;
@@ -177,10 +152,7 @@
}
static VALUE
-rb_digest_base_init(argc, argv, self)
- int argc;
- VALUE* argv;
- VALUE self;
+rb_digest_base_init(int argc, VALUE *argv, VALUE self)
{
VALUE arg;
@@ -192,70 +164,53 @@
}
static VALUE
-rb_digest_base_digest(self)
- VALUE self;
+rb_digest_base_digest(VALUE self)
{
algo_t *algo;
void *pctx1, *pctx2;
- unsigned char *digest;
size_t len;
VALUE str;
algo = get_digest_base_metadata(rb_obj_class(self));
Data_Get_Struct(self, void, pctx1);
- len = algo->ctx_size;
+ str = rb_str_new(0, algo->digest_len);
+ len = algo->ctx_size;
pctx2 = xmalloc(len);
memcpy(pctx2, pctx1, len);
- len = algo->digest_len;
-
- digest = xmalloc(len);
- algo->final_func(digest, pctx2);
-
- str = rb_str_new(digest, len);
-
- free(digest);
+ algo->final_func(RSTRING(str)->ptr, pctx2);
free(pctx2);
return str;
}
static VALUE
-rb_digest_base_hexdigest(self)
- VALUE self;
+rb_digest_base_hexdigest(VALUE self)
{
algo_t *algo;
void *pctx1, *pctx2;
- unsigned char *hexdigest;
size_t len;
VALUE str;
algo = get_digest_base_metadata(rb_obj_class(self));
Data_Get_Struct(self, void, pctx1);
- len = algo->ctx_size;
+ str = rb_str_new(0, algo->digest_len * 2);
+ len = algo->ctx_size;
pctx2 = xmalloc(len);
memcpy(pctx2, pctx1, len);
- len = algo->digest_len * 2;
-
- hexdigest = xmalloc(len + 1); /* +1 is for '\0' */
- algo->end_func(pctx2, hexdigest);
-
- str = rb_str_new(hexdigest, len);
-
- free(hexdigest);
+ algo->end_func(pctx2, RSTRING(str)->ptr);
free(pctx2);
return str;
}
static VALUE
-rb_digest_base_equal(self, other)
- VALUE self, other;
+rb_digest_base_equal(VALUE self, VALUE other)
{
algo_t *algo;
VALUE klass;
Modified: trunk/ext/digest/digest.h
===================================================================
--- trunk/ext/digest/digest.h 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/ext/digest/digest.h 2006-02-11 05:42:50 UTC (rev 379)
@@ -2,24 +2,24 @@
digest.c -
- $Author: knu $
+ $Author: matz $
created at: Fri May 25 08:54:56 JST 2001
Copyright (C) 2001 Akinori MUSHA
$RoughId: digest.h,v 1.3 2001/07/13 15:38:27 knu Exp $
- $Id: digest.h,v 1.1 2001/07/13 20:06:13 knu Exp $
+ $Id: digest.h,v 1.2 2005/12/12 00:35:08 matz Exp $
************************************************/
#include "ruby.h"
-typedef void (*hash_init_func_t) _((void *));
-typedef void (*hash_update_func_t) _((void *, unsigned char *, size_t));
-typedef void (*hash_end_func_t) _((void *, unsigned char *));
-typedef void (*hash_final_func_t) _((unsigned char *, void *));
-typedef int (*hash_equal_func_t) _((void *, void *));
+typedef void (*hash_init_func_t)(void *);
+typedef void (*hash_update_func_t)(void *, unsigned char *, size_t);
+typedef void (*hash_end_func_t)(void *, unsigned char *);
+typedef void (*hash_final_func_t)(unsigned char *, void *);
+typedef int (*hash_equal_func_t)(void *, void *);
typedef struct {
size_t digest_len;
Modified: trunk/ext/digest/sha2/sha2.c
===================================================================
--- trunk/ext/digest/sha2/sha2.c 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/ext/digest/sha2/sha2.c 2006-02-11 05:42:50 UTC (rev 379)
@@ -34,7 +34,7 @@
*/
/* $RoughId: sha2.c,v 1.3 2002/02/26 22:03:36 knu Exp $ */
-/* $Id: sha2.c,v 1.4 2003/07/26 15:03:12 matz Exp $ */
+/* $Id: sha2.c,v 1.5 2005/12/29 12:05:01 matz Exp $ */
#include "sha2.h"
#include <stdio.h>
@@ -67,7 +67,7 @@
typedef uint32_t sha2_word32; /* Exactly 4 bytes */
typedef uint64_t sha2_word64; /* Exactly 8 bytes */
-#if defined(__GNUC__) || defined(_HPUX_SOURCE)
+#if defined(__GNUC__) || defined(_HPUX_SOURCE) || defined(__IBMC__)
#define ULL(number) number##ULL
#else
#define ULL(number) (uint64_t)(number)
Modified: trunk/ext/etc/etc.c
===================================================================
--- trunk/ext/etc/etc.c 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/ext/etc/etc.c 2006-02-11 05:42:50 UTC (rev 379)
@@ -3,7 +3,7 @@
etc.c -
$Author: matz $
- $Date: 2005/11/01 13:04:34 $
+ $Date: 2005/12/12 00:35:07 $
created at: Tue Mar 22 18:39:19 JST 1994
************************************************/
@@ -521,6 +521,7 @@
rb_define_module_function(mEtc, "endgrent", etc_endgrent, 0);
rb_define_module_function(mEtc, "getgrent", etc_getgrent, 0);
+ rb_global_variable(&sPasswd);
sPasswd = rb_struct_define("Passwd",
"name", "passwd", "uid", "gid",
#ifdef HAVE_ST_PW_GECOS
@@ -546,14 +547,13 @@
"expire",
#endif
NULL);
- rb_global_variable(&sPasswd);
#ifdef HAVE_GETGRENT
+ rb_global_variable(&sGroup);
sGroup = rb_struct_define("Group", "name",
#ifdef HAVE_ST_GR_PASSWD
"passwd",
#endif
"gid", "mem", NULL);
- rb_global_variable(&sGroup);
#endif
}
Modified: trunk/ext/win32ole/win32ole.c
===================================================================
--- trunk/ext/win32ole/win32ole.c 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/ext/win32ole/win32ole.c 2006-02-11 05:42:50 UTC (rev 379)
@@ -12,7 +12,7 @@
*/
/*
- $Date: 2005/09/23 08:39:24 $
+ $Date: 2006/02/03 09:15:37 $
modified for win32ole (ruby) by Masaki.Suketa <masaki.suketa nifty.ne.jp>
*/
@@ -2218,7 +2218,7 @@
op.dp.cArgs = cNamedArgs + argc - 2;
op.pNamedArgs = ALLOCA_N(OLECHAR*, cNamedArgs + 1);
op.dp.rgvarg = ALLOCA_N(VARIANTARG, op.dp.cArgs);
- rb_iterate(rb_each, param, hash2named_arg, (VALUE)&op);
+ rb_block_call(param, rb_intern("each"), 0, 0, hash2named_arg, (VALUE)&op);
pDispID = ALLOCA_N(DISPID, cNamedArgs + 1);
op.pNamedArgs[0] = ole_mb2wc(StringValuePtr(cmd), -1);
@@ -6904,8 +6904,8 @@
void
Init_win32ole()
{
+ rb_global_variable(&ary_ole_event);
ary_ole_event = rb_ary_new();
- rb_global_variable(&ary_ole_event);
id_events = rb_intern("events");
com_vtbl.QueryInterface = QueryInterface;
@@ -6915,8 +6915,8 @@
com_vtbl.GetTypeInfo = GetTypeInfo;
com_vtbl.GetIDsOfNames = GetIDsOfNames;
com_vtbl.Invoke = Invoke;
+ rb_global_variable(&com_hash);
com_hash = Data_Wrap_Struct(rb_cData, rb_mark_hash, st_free_table, st_init_numtable());
- rb_global_variable(&com_hash);
cWIN32OLE = rb_define_class("WIN32OLE", rb_cObject);
Modified: trunk/hash.c
===================================================================
--- trunk/hash.c 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/hash.c 2006-02-11 05:42:50 UTC (rev 379)
@@ -3,7 +3,7 @@
hash.c -
$Author: ocean $
- $Date: 2005/10/20 02:56:22 $
+ $Date: 2006/02/01 13:27:10 $
created at: Mon Nov 22 18:51:18 JST 1993
Copyright (C) 1993-2003 Yukihiro Matsumoto
@@ -196,8 +196,6 @@
rb_ensure(hash_foreach_call, (VALUE)&arg, hash_foreach_ensure, hash);
}
-static VALUE hash_alloc(VALUE);
-
static VALUE
hash_alloc(VALUE klass)
{
@@ -1690,7 +1688,7 @@
* RTL's environ global variable directly yet.
*/
SetEnvironmentVariable(name,value);
-#elif defined __CYGWIN__
+#elif defined(HAVE_SETENV) && defined(HAVE_UNSETENV)
#undef setenv
#undef unsetenv
if (value)
Modified: trunk/intern.h
===================================================================
--- trunk/intern.h 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/intern.h 2006-02-11 05:42:50 UTC (rev 379)
@@ -3,7 +3,7 @@
intern.h -
$Author: nobu $
- $Date: 2005/11/11 17:11:05 $
+ $Date: 2006/01/25 13:29:44 $
created at: Thu Jun 10 14:22:17 JST 1993
Copyright (C) 1993-2003 Yukihiro Matsumoto
@@ -200,7 +200,6 @@
void ruby_set_current_source(void);
NORETURN(void rb_exc_raise(VALUE));
NORETURN(void rb_exc_fatal(VALUE));
-VALUE rb_make_exception(int argc, VALUE *argv);
VALUE rb_f_exit(int,VALUE*);
VALUE rb_f_abort(int,VALUE*);
void rb_remove_method(VALUE, const char*);
@@ -284,7 +283,6 @@
void rb_thread_atfork(void);
VALUE rb_exec_recursive(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE);
/* file.c */
-int eaccess(const char*, int);
VALUE rb_file_s_expand_path(int, VALUE *);
VALUE rb_file_expand_path(VALUE, VALUE);
void rb_file_const(const char*, VALUE);
Modified: trunk/io.c
===================================================================
--- trunk/io.c 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/io.c 2006-02-11 05:42:50 UTC (rev 379)
@@ -3798,12 +3798,7 @@
void
rb_write_error2(const char *mesg, long len)
{
- if (rb_stderr == orig_stderr || RFILE(orig_stderr)->fptr->fd < 0) {
- fwrite(mesg, sizeof(char), len, stderr);
- }
- else {
rb_io_write(rb_stderr, rb_str_new(mesg, len));
- }
}
void
@@ -5246,7 +5241,7 @@
if (TYPE(current_file) != T_FILE) {
for (;;) {
if (!next_argv()) return argf;
- rb_iterate(rb_each, current_file, rb_yield, 0);
+ rb_block_call(current_file, rb_intern("each"), 0, 0, rb_yield, 0);
next_p = 1;
}
}
Modified: trunk/lib/cgi.rb
===================================================================
--- trunk/lib/cgi.rb 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/lib/cgi.rb 2006-02-11 05:42:50 UTC (rev 379)
@@ -284,7 +284,7 @@
# Standard internet newline sequence
EOL = CR + LF
- REVISION = '$Id: cgi.rb,v 1.83 2005/10/21 09:00:00 matz Exp $' #:nodoc:
+ REVISION = '$Id: cgi.rb,v 1.84 2005/12/29 12:03:55 matz Exp $' #:nodoc:
NEEDS_BINMODE = true if /WIN/ni.match(RUBY_PLATFORM)
@@ -993,22 +993,9 @@
loop do
head = nil
- if 10240 < content_length
- require "tempfile"
- body = Tempfile.new("CGI")
- else
- begin
- require "stringio"
- body = StringIO.new
- rescue LoadError
- require "tempfile"
- body = Tempfile.new("CGI")
- end
- end
- body.binmode if defined? body.binmode
+ body = MorphingBody.new
until head and /#{boundary}(?:#{EOL}|--)/n.match(buf)
-
if (not head) and /#{EOL}#{EOL}/n.match(buf)
buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
head = $1.dup
@@ -1042,6 +1029,7 @@
""
end
+ p body
body.rewind
/Content-Disposition:.* filename="?([^\";]*)"?/ni.match(head)
@@ -1062,7 +1050,7 @@
end
/Content-Disposition:.* name="?([^\";]*)"?/ni.match(head)
- name = $1.dup
+ name = ($1 || "").dup
if params.has_key?(name)
params[name].push(body)
@@ -1081,8 +1069,7 @@
def read_from_cmdline
require "shellwords"
- string =
- unless ARGV.empty? && !$CGI_DONTINPUT
+ string = unless ARGV.empty?
ARGV.join(' ')
else
if STDIN.tty?
@@ -1103,6 +1090,59 @@
end
private :read_from_cmdline
+ # A wrapper class to use a StringIO object as the body and switch
+ # to a TempFile when the passed threshold is passed.
+ class MorphingBody
+ begin
+ require "stringio"
+ @@small_buffer = lambda{StringIO.new}
+ rescue LoadError
+ require "tempfile"
+ @@small_buffer = lambda{
+ n = Tempfile.new("CGI")
+ n.binmode
+ n
+ }
+ end
+
+ def initialize(morph_threshold = 10240)
+ @threshold = morph_threshold
+ @body = small_buffer.call
+ @cur_size = 0
+ @morph_check = true
+ end
+
+ def print(data)
+ if @morph_check && (@cur_size + data.size > @threshold)
+ convert_body
+ end
+ @body.print data
+ end
+ def rewind
+ @body.rewind
+ end
+ def path
+ @body.path
+ end
+
+ # returns the true body object.
+ def extract
+ @body
+ end
+
+ private
+ def convert_body
+ new_body = TempFile.new("CGI")
+ new_body.binmode if defined? @body.binmode
+ new_body.binmode if defined? new_body.binmode
+
+ @body.rewind
+ new_body.print @body.read
+ @body = new_body
+ @morph_check = false
+ end
+ end
+
# Initialize the data from the query.
#
# Handles multipart forms (in particular, forms that involve file uploads).
Modified: trunk/lib/complex.rb
===================================================================
--- trunk/lib/complex.rb 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/lib/complex.rb 2006-02-11 05:42:50 UTC (rev 379)
@@ -103,6 +103,10 @@
undef step
+ def scalar?
+ false
+ end
+
def Complex.generic?(other) # :nodoc:
other.kind_of?(Integer) or
other.kind_of?(Float) or
@@ -306,12 +310,7 @@
end
alias conj conjugate
- #
- # Compares the absolute values of the two numbers.
- #
- def <=> (other)
- self.abs <=> other.abs
- end
+ undef <=>
#
# Test for numerical equality (<tt>a == a + 0<i>i</i></tt>).
Modified: trunk/lib/delegate.rb
===================================================================
--- trunk/lib/delegate.rb 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/lib/delegate.rb 2006-02-11 05:42:50 UTC (rev 379)
@@ -121,6 +121,7 @@
undef_method m
end
+ module MethodDelegation
#
# Pass in the _obj_ to delegate method calls to. All methods supported by
# _obj_ will be delegated to.
@@ -130,13 +131,14 @@
end
# Handles the magic of delegation through \_\_getobj\_\_.
- def method_missing(m, *args)
+ def method_missing(m, *args, &block)
begin
target = self.__getobj__
unless target.respond_to?(m)
- super(m, *args)
+ super(m, *args, &block)
+ else
+ target.__send__(m, *args, &block)
end
- target.__send__(m, *args)
rescue Exception
$@.delete_if{|s| /^#{__FILE__}:\d+:in `method_missing'$/ =~ s} #`
::Kernel::raise
@@ -153,6 +155,21 @@
end
#
+ # Returns true if two objects are considered same.
+ #
+ def ==(obj)
+ return true if obj.equal?(self)
+ self.__getobj__ == obj
+ end
+
+ #
+ # Returns true only if two objects are identical.
+ #
+ def equal?(obj)
+ self.object_id == obj.object_id
+ end
+
+ #
# This method must be overridden by subclasses and should return the object
# method calls are being delegated to.
#
@@ -176,6 +193,21 @@
def marshal_load(obj)
__setobj__(obj)
end
+
+ # Clone support for the object returned by \_\_getobj\_\_.
+ def clone
+ new = super
+ new.__setobj__(__getobj__.clone)
+ new
+ end
+ # Duplication support for the object returned by \_\_getobj\_\_.
+ def dup
+ new = super
+ new.__setobj__(__getobj__.dup)
+ new
+ end
+ end
+ include MethodDelegation
end
#
@@ -208,19 +240,6 @@
raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
@_sd_obj = obj
end
-
- # Clone support for the object returned by \_\_getobj\_\_.
- def clone
- copy = super
- copy.__setobj__(__getobj__.clone)
- copy
- end
- # Duplication support for the object returned by \_\_getobj\_\_.
- def dup
- copy = super
- copy.__setobj__(__getobj__.dup)
- copy
- end
end
# :stopdoc:
@@ -243,24 +262,12 @@
klass = Class.new
methods = superclass.public_instance_methods(true)
methods -= [
- "__id__", "object_id", "__send__", "respond_to?",
+ "__id__", "object_id", "__send__", "respond_to?", "==", "equal?",
"initialize", "method_missing", "__getobj__", "__setobj__",
"clone", "dup", "marshal_dump", "marshal_load",
]
klass.module_eval {
- def initialize(obj) # :nodoc:
- @_dc_obj = obj
- end
- def method_missing(m, *args) # :nodoc:
- unless @_dc_obj.respond_to?(m)
- super(m, *args)
- end
- @_dc_obj.__send__(m, *args)
- end
- def respond_to?(m) # :nodoc:
- return true if super
- return @_dc_obj.respond_to?(m)
- end
+ include Delegator::MethodDelegation
def __getobj__ # :nodoc:
@_dc_obj
end
@@ -268,14 +275,6 @@
raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
@_dc_obj = obj
end
- def clone # :nodoc:
- super
- __setobj__(__getobj__.clone)
- end
- def dup # :nodoc:
- super
- __setobj__(__getobj__.dup)
- end
}
for method in methods
begin
@@ -309,15 +308,23 @@
p ary.class
ary.push 25
p ary
+ ary.push 42
+ ary.each {|x| p x}
foo = Object.new
def foo.test
25
end
+ def foo.iter
+ yield self
+ end
def foo.error
raise 'this is OK'
end
foo2 = SimpleDelegator.new(foo)
+ p foo2
+ foo2.instance_eval{print "foo\n"}
p foo.test == foo2.test # => true
+ p foo2.iter{[55,true]} # => true
foo2.error # raise error!
end
Modified: trunk/lib/erb.rb
===================================================================
--- trunk/lib/erb.rb 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/lib/erb.rb 2006-02-11 05:42:50 UTC (rev 379)
@@ -236,7 +236,7 @@
# Rails, the web application framework, uses ERB to create views.
#
class ERB
- Revision = '$Date: 2005/02/12 16:34:45 $' #'
+ Revision = '$Date: 2006/01/10 15:21:45 $' #'
# Returns revision information for the erb.rb module.
def self.version
@@ -558,7 +558,7 @@
out.push(content)
end
when '<%='
- out.push("#{ put_cmd}((#{content}).to_s)")
+ out.push("#{ insert_cmd}((#{content}).to_s)")
when '<%#'
# out.push("# #{content.dump}")
end
@@ -607,11 +607,12 @@
def initialize(trim_mode)
@percent, @trim_mode = prepare_trim_mode(trim_mode)
@put_cmd = 'print'
+ @insert_cmd = @put_cmd
@pre_cmd = []
@post_cmd = []
end
attr_reader :percent, :trim_mode
- attr_accessor :put_cmd, :pre_cmd, :post_cmd
+ attr_accessor :put_cmd, :insert_cmd, :pre_cmd, :post_cmd
end
end
@@ -705,6 +706,7 @@
#
def set_eoutvar(compiler, eoutvar = '_erbout')
compiler.put_cmd = "#{eoutvar}.concat"
+ compiler.insert_cmd = "#{eoutvar}.concat"
cmd = []
cmd.push "#{eoutvar} = ''"
@@ -822,5 +824,3 @@
module_function :def_erb_method
end
end
-
-
Modified: trunk/lib/fileutils.rb
===================================================================
--- trunk/lib/fileutils.rb 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/lib/fileutils.rb 2006-02-11 05:42:50 UTC (rev 379)
@@ -274,7 +274,7 @@
# <b><tt>ln(old, new, options = {})</tt></b>
#
# Creates a hard link +new+ which points to +old+.
- # If +new+ already exists and it is a directory, creates a symbolic link +new/old+.
+ # If +new+ already exists and it is a directory, creates a link +new/old+.
# If +new+ already exists and it is not a directory, raises Errno::EEXIST.
# But if :force option is set, overwrite +new+.
#
@@ -500,6 +500,7 @@
File.rename s, d
rescue Errno::EXDEV
copy_entry s, d, true
+ File.unlink s
end
rescue SystemCallError
raise unless options[:force]
Modified: trunk/lib/matrix.rb
===================================================================
--- trunk/lib/matrix.rb 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/lib/matrix.rb 2006-02-11 05:42:50 UTC (rev 379)
@@ -2,8 +2,8 @@
#--
# matrix.rb -
# $Release Version: 1.0$
-# $Revision: 1.11 $
-# $Date: 1999/10/06 11:01:53 $
+# $Revision: 1.13 $
+# $Date: 2001/12/09 14:22:23 $
# Original Version from Smalltalk-80 version
# on July 23, 1985 at 8:37:17 am
# by Keiju ISHITSUKA
@@ -105,7 +105,7 @@
# * <tt> #inspect </tt>
#
class Matrix
- @RCS_ID='-$Id: matrix.rb,v 1.11 1999/10/06 11:01:53 keiju Exp keiju $-'
+ @RCS_ID='-$Id: matrix.rb,v 1.13 2001/12/09 14:22:23 keiju Exp keiju $-'
# extend Exception2MessageMapper
include ExceptionForMatrix
@@ -265,7 +265,16 @@
def [](i, j)
@rows[i][j]
end
+ alias element []
+ alias component []
+ def []=(i, j, v)
+ @rows[i][j] = v
+ end
+ alias set_element []=
+ alias set_component []=
+ private :[]=, :set_element, :set_component
+
#
# Returns the number of rows.
#
@@ -320,7 +329,7 @@
#
# Returns a matrix that is the result of iteration of the given block over all
# elements of the matrix.
- # Matrix[ [1,2], [3,4] ].collect { |i| i**2 }
+ # Matrix[ [1,2], [3,4] ].collect { |e| e**2 }
# => 1 4
# 9 16
#
@@ -668,9 +677,13 @@
#
# Returns the determinant of the matrix. If the matrix is not square, the
- # result is 0.
+ # result is 0. This method's algorism is Gaussian elimination method
+ # and using Numeric#quo(). Beware that using Float values, with their
+ # usual lack of precision, can affect the value returned by this method. Use
+ # Rational values or Matrix#det_e instead if this is important to you.
+ #
# Matrix[[7,6], [3,9]].determinant
- # => 63
+ # => 63.0
#
def determinant
return 0 unless square?
@@ -705,9 +718,54 @@
alias det determinant
#
- # Returns the rank of the matrix. Beware that using Float values, with their
- # usual lack of precision, can affect the value returned by this method. Use
- # Rational values instead if this is important to you.
+ # Returns the determinant of the matrix. If the matrix is not square, the
+ # result is 0. This method's algorism is Gaussian elimination method.
+ # This method uses Euclidean algorism. If all elements are integer,
+ # really exact value. But, if an element is a float, can't return
+ # exact value.
+ #
+ # Matrix[[7,6], [3,9]].determinant
+ # => 63
+ #
+ def determinant_e
+ return 0 unless square?
+
+ size = row_size - 1
+ a = to_a
+
+ det = 1
+ k = 0
+ begin
+ if a[k][k].zero?
+ i = k
+ begin
+ return 0 if (i += 1) > size
+ end while a[i][k].zero?
+ a[i], a[k] = a[k], a[i]
+ det *= -1
+ end
+ (k + 1).upto(size) do |i|
+ q = a[i][k] / a[k][k]
+ k.upto(size) do |j|
+ a[i][j] -= a[k][j] * q
+ end
+ unless a[i][k].zero?
+ a[i], a[k] = a[k], a[i]
+ det *= -1
+ redo
+ end
+ end
+ det *= a[k][k]
+ end while (k += 1) <= size
+ det
+ end
+ alias det_e determinant_e
+
+ #
+ # Returns the rank of the matrix. Beware that using Float values,
+ # probably return faild value. Use Rational values or Matrix#rank_e
+ # for getting exact result.
+ #
# Matrix[[7,6], [3,9]].rank
# => 2
#
@@ -770,6 +828,41 @@
end
#
+ # Returns the rank of the matrix. This method uses Euclidean
+ # algorism. If all elements are integer, really exact value. But, if
+ # an element is a float, can't return exact value.
+ #
+ # Matrix[[7,6], [3,9]].rank
+ # => 2
+ #
+ def rank_e
+ a = to_a
+ a_column_size = column_size
+ a_row_size = row_size
+ pi = 0
+ (0 ... a_column_size).each do |j|
+ if i = (pi ... a_row_size).find{|i0| !a[i0][j].zero?}
+ if i != pi
+ a[pi], a[i] = a[i], a[pi]
+ end
+ (pi + 1 ... a_row_size).each do |k|
+ q = a[k][j] / a[pi][j]
+ (pi ... a_column_size).each do |j0|
+ a[k][j0] -= q * a[pi][j0]
+ end
+ if k > pi && !a[k][j].zero?
+ a[k], a[pi] = a[pi], a[k]
+ redo
+ end
+ end
+ pi += 1
+ end
+ end
+ pi
+ end
+
+
+ #
# Returns the trace (sum of diagonal elements) of the matrix.
# Matrix[[7,6], [3,9]].trace
# => 16
@@ -844,6 +937,18 @@
@rows.collect{|row| row.collect{|e| e}}
end
+ def elements_to_f
+ collect{|e| e.to_f}
+ end
+
+ def elements_to_i
+ collect{|e| e.to_i}
+ end
+
+ def elements_to_r
+ collect{|e| e.to_r}
+ end
+
#--
# PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#++
@@ -922,7 +1027,7 @@
when Vector
Scalar.Raise WrongArgType, other.class, "Numeric or Scalar or Matrix"
when Matrix
- self * _M.inverse
+ self * other.inverse
else
x, y = other.coerce(self)
x.quo(y)
@@ -1034,6 +1139,15 @@
def [](i)
@elements[i]
end
+ alias element []
+ alias component []
+
+ def []=(i, v)
+ @elements[i]= v
+ end
+ alias set_element []=
+ alias set_component []=
+ private :[]=, :set_element, :set_component
#
# Returns the number of elements in the vector.
@@ -1236,6 +1350,18 @@
@elements.dup
end
+ def elements_to_f
+ collect{|e| e.to_f}
+ end
+
+ def elements_to_i
+ collect{|e| e.to_i}
+ end
+
+ def elements_to_r
+ collect{|e| e.to_r}
+ end
+
#
# FIXME: describe Vector#coerce.
#
Modified: trunk/lib/mkmf.rb
===================================================================
--- trunk/lib/mkmf.rb 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/lib/mkmf.rb 2006-02-11 05:42:50 UTC (rev 379)
@@ -534,7 +534,7 @@
end
def checking_for(m, fmt = nil)
- f = caller[0][/in `(.*)'$/, 1] and f << ": " #` for vim, ' for xyzzy
+ f = caller[0][/in `(.*)'$/, 1] and f << ": " #` for vim
m = "checking for #{m}... "
message "%s", m
a = r = nil
@@ -793,11 +793,25 @@
$configure_args.fetch(config.tr('_', '-'), *defaults, &block)
end
-def with_config(config, *defaults, &block)
- unless /^--with[-_]/ =~ config
- config = '--with-' + config
+def with_config(config, *defaults)
+ config = config.sub(/^--with[-_]/, '')
+ val = arg_config("--with-"+config) do
+ if arg_config("--without-"+config)
+ false
+ elsif block_given?
+ yield(config, *defaults)
+ else
+ break *defaults
+ end
end
- arg_config(config, *defaults, &block)
+ case val
+ when "yes"
+ true
+ when "no"
+ false
+ else
+ val
+ end
end
def enable_config(config, *defaults)
@@ -1037,7 +1051,7 @@
if File.exist?(File.join(srcdir, target + '.def'))
deffile = "$(srcdir)/$(TARGET).def"
unless EXPORT_PREFIX.empty?
- makedef = %{-pe "sub!(/^(?=\\w)/,'#{EXPORT_PREFIX}') unless 1../^EXPORTS$/i"}
+ makedef = %{-pe "$_.sub!(/^(?=\\w)/,'#{EXPORT_PREFIX}') unless 1../^EXPORTS$/i"}
end
else
makedef = %{-e "puts 'EXPORTS', '#{EXPORT_PREFIX}Init_$(TARGET)'"}
@@ -1088,7 +1102,7 @@
mfile.print CLEANINGS
dirs = []
mfile.print "install: install-so install-rb\n\n"
- sodir = dir = "$(RUBYARCHDIR)"
+ sodir = (dir = "$(RUBYARCHDIR)").dup
mfile.print("install-so: #{dir}\n")
if target
f = "$(DLLIB)"
@@ -1108,10 +1122,10 @@
mfile.print "\t$(INSTALL_PROG) #{f} #{dir}\n"
end
end
- dirs << (dir = "$(RUBYLIBDIR)")
mfile.print("install-rb: pre-install-rb install-rb-default\n")
mfile.print("install-rb-default: pre-install-rb-default\n")
- mfile.print("pre-install-rb pre-install-rb-default: #{dir}\n")
+ mfile.print("pre-install-rb: Makefile\n")
+ mfile.print("pre-install-rb-default: Makefile\n")
for sfx, i in [["-default", [["lib/**/*.rb", "$(RUBYLIBDIR)", "lib"]]], ["", $INSTALLFILES]]
files = install_files(mfile, i, nil, srcprefix) or next
for dir, *files in files
@@ -1382,5 +1396,5 @@
"
if not $extmk and /\A(extconf|makefile).rb\z/ =~ File.basename($0)
- # END {mkmf_failed($0)}
+ END {mkmf_failed($0)}
end
Added: trunk/lib/net/.document
===================================================================
--- trunk/lib/net/.document 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/lib/net/.document 2006-02-11 05:42:50 UTC (rev 379)
@@ -0,0 +1,8 @@
+ftp.rb
+http.rb
+https.rb
+imap.rb
+pop.rb
+smtp.rb
+smtps.rb
+telnet.rb
Added: trunk/lib/net/ftp.rb
===================================================================
--- trunk/lib/net/ftp.rb 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/lib/net/ftp.rb 2006-02-11 05:42:50 UTC (rev 379)
@@ -0,0 +1,964 @@
+#
+# = net/ftp.rb - FTP Client Library
+#
+# Written by Shugo Maeda <shugo ruby-lang.org>.
+#
+# Documentation by Gavin Sinclair, sourced from "Programming Ruby" (Hunt/Thomas)
+# and "Ruby In a Nutshell" (Matsumoto), used with permission.
+#
+# This library is distributed under the terms of the Ruby license.
+# You can freely distribute/modify this library.
+#
+# It is included in the Ruby standard library.
+#
+# See the Net::FTP class for an overview.
+#
+
+require "socket"
+require "monitor"
+
+module Net
+
+ # :stopdoc:
+ class FTPError < StandardError; end
+ class FTPReplyError < FTPError; end
+ class FTPTempError < FTPError; end
+ class FTPPermError < FTPError; end
+ class FTPProtoError < FTPError; end
+ # :startdoc:
+
+ #
+ # This class implements the File Transfer Protocol. If you have used a
+ # command-line FTP program, and are familiar with the commands, you will be
+ # able to use this class easily. Some extra features are included to take
+ # advantage of Ruby's style and strengths.
+ #
+ # == Example
+ #
+ # require 'net/ftp'
+ #
+ # === Example 1
+ #
+ # ftp = Net::FTP.new('ftp.netlab.co.jp')
+ # ftp.login
+ # files = ftp.chdir('pub/lang/ruby/contrib')
+ # files = ftp.list('n*')
+ # ftp.getbinaryfile('nif.rb-0.91.gz', 'nif.gz', 1024)
+ # ftp.close
+ #
+ # === Example 2
+ #
+ # Net::FTP.open('ftp.netlab.co.jp') do |ftp|
+ # ftp.login
+ # files = ftp.chdir('pub/lang/ruby/contrib')
+ # files = ftp.list('n*')
+ # ftp.getbinaryfile('nif.rb-0.91.gz', 'nif.gz', 1024)
+ # end
+ #
+ # == Major Methods
+ #
+ # The following are the methods most likely to be useful to users:
+ # - FTP.open
+ # - #getbinaryfile
+ # - #gettextfile
+ # - #putbinaryfile
+ # - #puttextfile
+ # - #chdir
+ # - #nlst
+ # - #size
+ # - #rename
+ # - #delete
+ #
+ class FTP
+ include MonitorMixin
+
+ # :stopdoc:
+ FTP_PORT = 21
+ CRLF = "\r\n"
+ DEFAULT_BLOCKSIZE = 4096
+ # :startdoc:
+
+ # When +true+, transfers are performed in binary mode. Default: +true+.
+ attr_reader :binary
+
+ # When +true+, the connection is in passive mode. Default: +false+.
+ attr_accessor :passive
+
+ # When +true+, all traffic to and from the server is written
+ # to +$stdout+. Default: +false+.
+ attr_accessor :debug_mode
+
+ # Sets or retrieves the +resume+ status, which decides whether incomplete
+ # transfers are resumed or restarted. Default: +false+.
+ attr_accessor :resume
+
+ # The server's welcome message.
+ attr_reader :welcome
+
+ # The server's last response code.
+ attr_reader :last_response_code
+ alias lastresp last_response_code
+
+ # The server's last response.
+ attr_reader :last_response
+
+ #
+ # A synonym for <tt>FTP.new</tt>, but with a mandatory host parameter.
+ #
+ # If a block is given, it is passed the +FTP+ object, which will be closed
+ # when the block finishes, or when an exception is raised.
+ #
+ def FTP.open(host, user = nil, passwd = nil, acct = nil)
+ if block_given?
+ ftp = new(host, user, passwd, acct)
+ begin
+ yield ftp
+ ensure
+ ftp.close
+ end
+ else
+ new(host, user, passwd, acct)
+ end
+ end
+
+ #
+ # Creates and returns a new +FTP+ object. If a +host+ is given, a connection
+ # is made. Additionally, if the +user+ is given, the given user name,
+ # password, and (optionally) account are used to log in. See #login.
+ #
+ def initialize(host = nil, user = nil, passwd = nil, acct = nil)
+ super()
+ @binary = false
+ @passive = false
+ @debug_mode = false
+ @resume = false
+ if host
+ connect(host)
+ if user
+ login(user, passwd, acct)
+ end
+ end
+ end
+
+ def binary=(newmode)
+ if newmode != @binary
+ @binary = newmode
+ @binary ? voidcmd("TYPE I") : voidcmd("TYPE A")
+ end
+ end
+
+ def with_binary(newmode)
+ oldmode = binary
+ self.binary = newmode
+ begin
+ yield
+ ensure
+ self.binary = oldmode
+ end
+ end
+ private :with_binary
+
+ # Obsolete
+ def return_code
+ $stderr.puts("warning: Net::FTP#return_code is obsolete and do nothing")
+ return "\n"
+ end
+
+ # Obsolete
+ def return_code=(s)
+ $stderr.puts("warning: Net::FTP#return_code= is obsolete and do nothing")
+ end
+
+ def open_socket(host, port)
+ if defined? SOCKSsocket and ENV["SOCKS_SERVER"]
+ @passive = true
+ return SOCKSsocket.open(host, port)
+ else
+ return TCPSocket.open(host, port)
+ end
+ end
+ private :open_socket
+
+ #
+ # Establishes an FTP connection to host, optionally overriding the default
+ # port. If the environment variable +SOCKS_SERVER+ is set, sets up the
+ # connection through a SOCKS proxy. Raises an exception (typically
+ # <tt>Errno::ECONNREFUSED</tt>) if the connection cannot be established.
+ #
+ def connect(host, port = FTP_PORT)
+ if @debug_mode
+ print "connect: ", host, ", ", port, "\n"
+ end
+ synchronize do
+ @sock = open_socket(host, port)
+ voidresp
+ end
+ end
+
+ #
+ # WRITEME or make private
+ #
+ def set_socket(sock, get_greeting = true)
+ synchronize do
+ @sock = sock
+ if get_greeting
+ voidresp
+ end
+ end
+ end
+
+ def sanitize(s)
+ if s =~ /^PASS /i
+ return s[0, 5] + "*" * (s.length - 5)
+ else
+ return s
+ end
+ end
+ private :sanitize
+
+ def putline(line)
+ if @debug_mode
+ print "put: ", sanitize(line), "\n"
+ end
+ line = line + CRLF
+ @sock.write(line)
+ end
+ private :putline
+
+ def getline
+ line = @sock.readline # if get EOF, raise EOFError
+ line.sub!(/(\r\n|\n|\r)\z/n, "")
+ if @debug_mode
+ print "get: ", sanitize(line), "\n"
+ end
+ return line
+ end
+ private :getline
+
+ def getmultiline
+ line = getline
+ buff = line
+ if line[3] == ?-
+ code = line[0, 3]
+ begin
+ line = getline
+ buff << "\n" << line
+ end until line[0, 3] == code and line[3] != ?-
+ end
+ return buff << "\n"
+ end
+ private :getmultiline
+
+ def getresp
+ @last_response = getmultiline
+ @last_response_code = @last_response[0, 3]
+ case @last_response_code
+ when /\A[123]/
+ return @last_response
+ when /\A4/
+ raise FTPTempError, @last_response
+ when /\A5/
+ raise FTPPermError, @last_response
+ else
+ raise FTPProtoError, @last_response
+ end
+ end
+ private :getresp
+
+ def voidresp
+ resp = getresp
+ if resp[0] != ?2
+ raise FTPReplyError, resp
+ end
+ end
+ private :voidresp
+
+ #
+ # Sends a command and returns the response.
+ #
+ def sendcmd(cmd)
+ synchronize do
+ putline(cmd)
+ return getresp
+ end
+ end
+
+ #
+ # Sends a command and expect a response beginning with '2'.
+ #
+ def voidcmd(cmd)
+ synchronize do
+ putline(cmd)
+ voidresp
+ end
+ end
+
+ def sendport(host, port)
+ af = ( sock.peeraddr)[0]
+ if af == "AF_INET"
+ hbytes = host.split(".")
+ pbytes = [port / 256, port % 256]
+ bytes = hbytes + pbytes
+ cmd = "PORT " + bytes.join(",")
+ elsif af == "AF_INET6"
+ cmd = "EPRT |2|" + host + "|" + sprintf("%d", port) + "|"
+ else
+ raise FTPProtoError, host
+ end
+ voidcmd(cmd)
+ end
+ private :sendport
+
+ def makeport
+ sock = TCPServer.open( sock.addr[3], 0)
+ port = sock.addr[1]
+ host = sock.addr[3]
+ resp = sendport(host, port)
+ return sock
+ end
+ private :makeport
+
+ def makepasv
+ if @sock.peeraddr[0] == "AF_INET"
+ host, port = parse227(sendcmd("PASV"))
+ else
+ host, port = parse229(sendcmd("EPSV"))
+ # host, port = parse228(sendcmd("LPSV"))
+ end
+ return host, port
+ end
+ private :makepasv
+
+ def transfercmd(cmd, rest_offset = nil)
+ if @passive
+ host, port = makepasv
+ conn = open_socket(host, port)
+ if @resume and rest_offset
+ resp = sendcmd("REST " + rest_offset.to_s)
+ if resp[0] != ?3
+ raise FTPReplyError, resp
+ end
+ end
+ resp = sendcmd(cmd)
+ if resp[0] != ?1
+ raise FTPReplyError, resp
+ end
+ else
+ sock = makeport
+ if @resume and rest_offset
+ resp = sendcmd("REST " + rest_offset.to_s)
+ if resp[0] != ?3
+ raise FTPReplyError, resp
+ end
+ end
+ resp = sendcmd(cmd)
+ if resp[0] != ?1
+ raise FTPReplyError, resp
+ end
+ conn = sock.accept
+ sock.close
+ end
+ return conn
+ end
+ private :transfercmd
+
+ def getaddress
+ thishost = Socket.gethostname
+ if not thishost.index(".")
+ thishost = Socket.gethostbyname(thishost)[0]
+ end
+ if ENV.has_key?("LOGNAME")
+ realuser = ENV["LOGNAME"]
+ elsif ENV.has_key?("USER")
+ realuser = ENV["USER"]
+ else
+ realuser = "anonymous"
+ end
+ return realuser + "@" + thishost
+ end
+ private :getaddress
+
+ #
+ # Logs in to the remote host. The session must have been previously
+ # connected. If +user+ is the string "anonymous" and the +password+ is
+ # +nil+, a password of <tt>user@host</tt> is synthesized. If the +acct+
+ # parameter is not +nil+, an FTP ACCT command is sent following the
+ # successful login. Raises an exception on error (typically
+ # <tt>Net::FTPPermError</tt>).
+ #
+ def login(user = "anonymous", passwd = nil, acct = nil)
+ if user == "anonymous" and passwd == nil
+ passwd = getaddress
+ end
+
+ resp = ""
+ synchronize do
+ resp = sendcmd('USER ' + user)
+ if resp[0] == ?3
+ resp = sendcmd('PASS ' + passwd)
+ end
+ if resp[0] == ?3
+ resp = sendcmd('ACCT ' + acct)
+ end
+ end
+ if resp[0] != ?2
+ raise FTPReplyError, resp
+ end
+ @welcome = resp
+ self.binary = true
+ end
+
+ #
+ # Puts the connection into binary (image) mode, issues the given command,
+ # and fetches the data returned, passing it to the associated block in
+ # chunks of +blocksize+ characters. Note that +cmd+ is a server command
+ # (such as "RETR myfile").
+ #
+ def retrbinary(cmd, blocksize, rest_offset = nil) # :yield: data
+ synchronize do
+ with_binary(true) do
+ conn = transfercmd(cmd, rest_offset)
+ loop do
+ data = conn.read(blocksize)
+ break if data == nil
+ yield(data)
+ end
+ conn.close
+ voidresp
+ end
+ end
+ end
+
+ #
+ # Puts the connection into ASCII (text) mode, issues the given command, and
+ # passes the resulting data, one line at a time, to the associated block. If
+ # no block is given, prints the lines. Note that +cmd+ is a server command
+ # (such as "RETR myfile").
+ #
+ def retrlines(cmd) # :yield: line
+ synchronize do
+ with_binary(false) do
+ conn = transfercmd(cmd)
+ loop do
+ line = conn.gets
+ break if line == nil
+ if line[-2, 2] == CRLF
+ line = line[0 .. -3]
+ elsif line[-1] == ?\n
+ line = line[0 .. -2]
+ end
+ yield(line)
+ end
+ conn.close
+ voidresp
+ end
+ end
+ end
+
+ #
+ # Puts the connection into binary (image) mode, issues the given server-side
+ # command (such as "STOR myfile"), and sends the contents of the file named
+ # +file+ to the server. If the optional block is given, it also passes it
+ # the data, in chunks of +blocksize+ characters.
+ #
+ def storbinary(cmd, file, blocksize, rest_offset = nil, &block) # :yield: data
+ if rest_offset
+ file.seek(rest_offset, IO::SEEK_SET)
+ end
+ synchronize do
+ with_binary(true) do
+ conn = transfercmd(cmd, rest_offset)
+ loop do
+ buf = file.read(blocksize)
+ break if buf == nil
+ conn.write(buf)
+ yield(buf) if block
+ end
+ conn.close
+ voidresp
+ end
+ end
+ end
+
+ #
+ # Puts the connection into ASCII (text) mode, issues the given server-side
+ # command (such as "STOR myfile"), and sends the contents of the file
+ # named +file+ to the server, one line at a time. If the optional block is
+ # given, it also passes it the lines.
+ #
+ def storlines(cmd, file, &block) # :yield: line
+ synchronize do
+ with_binary(false) do
+ conn = transfercmd(cmd)
+ loop do
+ buf = file.gets
+ break if buf == nil
+ if buf[-2, 2] != CRLF
+ buf = buf.chomp + CRLF
+ end
+ conn.write(buf)
+ yield(buf) if block
+ end
+ conn.close
+ voidresp
+ end
+ end
+ end
+
+ #
+ # Retrieves +remotefile+ in binary mode, storing the result in +localfile+.
+ # If +localfile+ is nil, returns retrieved data.
+ # If a block is supplied, it is passed the retrieved data in +blocksize+
+ # chunks.
+ #
+ def getbinaryfile(remotefile, localfile = File.basename(remotefile),
+ blocksize = DEFAULT_BLOCKSIZE) # :yield: data
+ result = nil
+ if localfile
+ if @resume
+ rest_offset = File.size?(localfile)
+ f = open(localfile, "a")
+ else
+ rest_offset = nil
+ f = open(localfile, "w")
+ end
+ elsif !block_given?
+ result = ""
+ end
+ begin
+ f.binmode if localfile
+ retrbinary("RETR " + remotefile, blocksize, rest_offset) do |data|
+ f.write(data) if localfile
+ yield(data) if block_given?
+ result.concat(data) if result
+ end
+ return result
+ ensure
+ f.close if localfile
+ end
+ end
+
+ #
+ # Retrieves +remotefile+ in ASCII (text) mode, storing the result in
+ # +localfile+.
+ # If +localfile+ is nil, returns retrieved data.
+ # If a block is supplied, it is passed the retrieved data one
+ # line at a time.
+ #
+ def gettextfile(remotefile, localfile = File.basename(remotefile)) # :yield: line
+ result = nil
+ if localfile
+ f = open(localfile, "w")
+ elsif !block_given?
+ result = ""
+ end
+ begin
+ retrlines("RETR " + remotefile) do |line|
+ f.puts(line) if localfile
+ yield(line) if block_given?
+ result.concat(line + "\n") if result
+ end
+ return result
+ ensure
+ f.close if localfile
+ end
+ end
+
+ #
+ # Retrieves +remotefile+ in whatever mode the session is set (text or
+ # binary). See #gettextfile and #getbinaryfile.
+ #
+ def get(remotefile, localfile = File.basename(remotefile),
+ blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data
+ if @binary
+ getbinaryfile(remotefile, localfile, blocksize, &block)
+ else
+ gettextfile(remotefile, localfile, &block)
+ end
+ end
+
+ #
+ # Transfers +localfile+ to the server in binary mode, storing the result in
+ # +remotefile+. If a block is supplied, calls it, passing in the transmitted
+ # data in +blocksize+ chunks.
+ #
+ def putbinaryfile(localfile, remotefile = File.basename(localfile),
+ blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data
+ if @resume
+ begin
+ rest_offset = size(remotefile)
+ rescue Net::FTPPermError
+ rest_offset = nil
+ end
+ else
+ rest_offset = nil
+ end
+ f = open(localfile)
+ begin
+ f.binmode
+ storbinary("STOR " + remotefile, f, blocksize, rest_offset, &block)
+ ensure
+ f.close
+ end
+ end
+
+ #
+ # Transfers +localfile+ to the server in ASCII (text) mode, storing the result
+ # in +remotefile+. If callback or an associated block is supplied, calls it,
+ # passing in the transmitted data one line at a time.
+ #
+ def puttextfile(localfile, remotefile = File.basename(localfile), &block) # :yield: line
+ f = open(localfile)
+ begin
+ storlines("STOR " + remotefile, f, &block)
+ ensure
+ f.close
+ end
+ end
+
+ #
+ # Transfers +localfile+ to the server in whatever mode the session is set
+ # (text or binary). See #puttextfile and #putbinaryfile.
+ #
+ def put(localfile, remotefile = File.basename(localfile),
+ blocksize = DEFAULT_BLOCKSIZE, &block)
+ if @binary
+ putbinaryfile(localfile, remotefile, blocksize, &block)
+ else
+ puttextfile(localfile, remotefile, &block)
+ end
+ end
+
+ #
+ # Sends the ACCT command. TODO: more info.
+ #
+ def acct(account)
+ cmd = "ACCT " + account
+ voidcmd(cmd)
+ end
+
+ #
+ # Returns an array of filenames in the remote directory.
+ #
+ def nlst(dir = nil)
+ cmd = "NLST"
+ if dir
+ cmd = cmd + " " + dir
+ end
+ files = []
+ retrlines(cmd) do |line|
+ files.push(line)
+ end
+ return files
+ end
+
+ #
+ # Returns an array of file information in the directory (the output is like
+ # `ls -l`). If a block is given, it iterates through the listing.
+ #
+ def list(*args, &block) # :yield: line
+ cmd = "LIST"
+ args.each do |arg|
+ cmd = cmd + " " + arg
+ end
+ if block
+ retrlines(cmd, &block)
+ else
+ lines = []
+ retrlines(cmd) do |line|
+ lines << line
+ end
+ return lines
+ end
+ end
+ alias ls list
+ alias dir list
+
+ #
+ # Renames a file on the server.
+ #
+ def rename(fromname, toname)
+ resp = sendcmd("RNFR " + fromname)
+ if resp[0] != ?3
+ raise FTPReplyError, resp
+ end
+ voidcmd("RNTO " + toname)
+ end
+
+ #
+ # Deletes a file on the server.
+ #
+ def delete(filename)
+ resp = sendcmd("DELE " + filename)
+ if resp[0, 3] == "250"
+ return
+ elsif resp[0] == ?5
+ raise FTPPermError, resp
+ else
+ raise FTPReplyError, resp
+ end
+ end
+
+ #
+ # Changes the (remote) directory.
+ #
+ def chdir(dirname)
+ if dirname == ".."
+ begin
+ voidcmd("CDUP")
+ return
+ rescue FTPPermError
+ if $![0, 3] != "500"
+ raise FTPPermError, $!
+ end
+ end
+ end
+ cmd = "CWD " + dirname
+ voidcmd(cmd)
+ end
+
+ #
+ # Returns the size of the given (remote) filename.
+ #
+ def size(filename)
+ with_binary(true) do
+ resp = sendcmd("SIZE " + filename)
+ if resp[0, 3] != "213"
+ raise FTPReplyError, resp
+ end
+ return resp[3..-1].strip.to_i
+ end
+ end
+
+ MDTM_REGEXP = /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/ # :nodoc:
+
+ #
+ # Returns the last modification time of the (remote) file. If +local+ is
+ # +true+, it is returned as a local time, otherwise it's a UTC time.
+ #
+ def mtime(filename, local = false)
+ str = mdtm(filename)
+ ary = str.scan(MDTM_REGEXP)[0].collect {|i| i.to_i}
+ return local ? Time.local(*ary) : Time.gm(*ary)
+ end
+
+ #
+ # Creates a remote directory.
+ #
+ def mkdir(dirname)
+ resp = sendcmd("MKD " + dirname)
+ return parse257(resp)
+ end
+
+ #
+ # Removes a remote directory.
+ #
+ def rmdir(dirname)
+ voidcmd("RMD " + dirname)
+ end
+
+ #
+ # Returns the current remote directory.
+ #
+ def pwd
+ resp = sendcmd("PWD")
+ return parse257(resp)
+ end
+ alias getdir pwd
+
+ #
+ # Returns system information.
+ #
+ def system
+ resp = sendcmd("SYST")
+ if resp[0, 3] != "215"
+ raise FTPReplyError, resp
+ end
+ return resp[4 .. -1]
+ end
+
+ #
+ # Aborts the previous command (ABOR command).
+ #
+ def abort
+ line = "ABOR" + CRLF
+ print "put: ABOR\n" if @debug_mode
+ @sock.send(line, Socket::MSG_OOB)
+ resp = getmultiline
+ unless ["426", "226", "225"].include?(resp[0, 3])
+ raise FTPProtoError, resp
+ end
+ return resp
+ end
+
+ #
+ # Returns the status (STAT command).
+ #
+ def status
+ line = "STAT" + CRLF
+ print "put: STAT\n" if @debug_mode
+ @sock.send(line, Socket::MSG_OOB)
+ return getresp
+ end
+
+ #
+ # Issues the MDTM command. TODO: more info.
+ #
+ def mdtm(filename)
+ resp = sendcmd("MDTM " + filename)
+ if resp[0, 3] == "213"
+ return resp[3 .. -1].strip
+ end
+ end
+
+ #
+ # Issues the HELP command.
+ #
+ def help(arg = nil)
+ cmd = "HELP"
+ if arg
+ cmd = cmd + " " + arg
+ end
+ sendcmd(cmd)
+ end
+
+ #
+ # Exits the FTP session.
+ #
+ def quit
+ voidcmd("QUIT")
+ end
+
+ #
+ # Issues a NOOP command.
+ #
+ def noop
+ voidcmd("NOOP")
+ end
+
+ #
+ # Issues a SITE command.
+ #
+ def site(arg)
+ cmd = "SITE " + arg
+ voidcmd(cmd)
+ end
+
+ #
+ # Closes the connection. Further operations are impossible until you open
+ # a new connection with #connect.
+ #
+ def close
+ @sock.close if @sock and not @sock.closed?
+ end
+
+ #
+ # Returns +true+ iff the connection is closed.
+ #
+ def closed?
+ @sock == nil or @sock.closed?
+ end
+
+ def parse227(resp)
+ if resp[0, 3] != "227"
+ raise FTPReplyError, resp
+ end
+ left = resp.index("(")
+ right = resp.index(")")
+ if left == nil or right == nil
+ raise FTPProtoError, resp
+ end
+ numbers = resp[left + 1 .. right - 1].split(",")
+ if numbers.length != 6
+ raise FTPProtoError, resp
+ end
+ host = numbers[0, 4].join(".")
+ port = (numbers[4].to_i << 8) + numbers[5].to_i
+ return host, port
+ end
+ private :parse227
+
+ def parse228(resp)
+ if resp[0, 3] != "228"
+ raise FTPReplyError, resp
+ end
+ left = resp.index("(")
+ right = resp.index(")")
+ if left == nil or right == nil
+ raise FTPProtoError, resp
+ end
+ numbers = resp[left + 1 .. right - 1].split(",")
+ if numbers[0] == "4"
+ if numbers.length != 9 || numbers[1] != "4" || numbers[2 + 4] != "2"
+ raise FTPProtoError, resp
+ end
+ host = numbers[2, 4].join(".")
+ port = (numbers[7].to_i << 8) + numbers[8].to_i
+ elsif numbers[0] == "6"
+ if numbers.length != 21 || numbers[1] != "16" || numbers[2 + 16] != "2"
+ raise FTPProtoError, resp
+ end
+ v6 = ["", "", "", "", "", "", "", ""]
+ for i in 0 .. 7
+ v6[i] = sprintf("%02x%02x", numbers[(i * 2) + 2].to_i,
+ numbers[(i * 2) + 3].to_i)
+ end
+ host = v6[0, 8].join(":")
+ port = (numbers[19].to_i << 8) + numbers[20].to_i
+ end
+ return host, port
+ end
+ private :parse228
+
+ def parse229(resp)
+ if resp[0, 3] != "229"
+ raise FTPReplyError, resp
+ end
+ left = resp.index("(")
+ right = resp.index(")")
+ if left == nil or right == nil
+ raise FTPProtoError, resp
+ end
+ numbers = resp[left + 1 .. right - 1].split(resp[left + 1, 1])
+ if numbers.length != 4
+ raise FTPProtoError, resp
+ end
+ port = numbers[3].to_i
+ host = ( sock.peeraddr())[3]
+ return host, port
+ end
+ private :parse229
+
+ def parse257(resp)
+ if resp[0, 3] != "257"
+ raise FTPReplyError, resp
+ end
+ if resp[3, 2] != ' "'
+ return ""
+ end
+ dirname = ""
+ i = 5
+ n = resp.length
+ while i < n
+ c = resp[i, 1]
+ i = i + 1
+ if c == '"'
+ if i > n or resp[i, 1] != '"'
+ break
+ end
+ i = i + 1
+ end
+ dirname = dirname + c
+ end
+ return dirname
+ end
+ private :parse257
+ end
+
+end
+
+
+# Documentation comments:
+# - sourced from pickaxe and nutshell, with improvements (hopefully)
+# - three methods should be private (search WRITEME)
+# - two methods need more information (search TODO)
Added: trunk/lib/net/http.rb
===================================================================
--- trunk/lib/net/http.rb 2006-02-09 23:21:27 UTC (rev 378)
+++ trunk/lib/net/http.rb 2006-02-11 05:42:50 UTC (rev 379)
@@ -0,0 +1,2243 @@
+#
+# = net/http.rb
+#
+# Copyright (c) 1999-2006 Yukihiro Matsumoto
+# Copyright (c) 1999-2006 Minero Aoki
+# Copyright (c) 2001 GOTOU Yuuzou
+#
+# Written and maintained by Minero Aoki <aamine loveruby.net>.
+# HTTPS support added by GOTOU Yuuzou <gotoyuzo notwork.org>.
+#
+# This file is derived from "http-access.rb".
+#
+# Documented by Minero Aoki; converted to RDoc by William Webber.
+#
+# This program is free software. You can re-distribute and/or
+# modify this program under the same terms of ruby itself ---
+# Ruby Distribution License or GNU General Public License.
+#
+# See Net::HTTP for an overview and examples.
+#
+# NOTE: You can find Japanese version of this document here:
+# http://www.ruby-lang.org/ja/man/?cmd=view;name=net%2Fhttp.rb
+#
+#--
+# $Id: http.rb,v 1.129 2006/02/05 09:50:38 aamine Exp $
+#++
+
+require 'net/protocol'
+require 'uri'
+
+module Net #:nodoc:
+
+ # :stopdoc:
+ class HTTPBadResponse < StandardError; end
+ class HTTPHeaderSyntaxError < StandardError; end
+ # :startdoc:
+
+ # == What Is This Library?
+ #
+ # This library provides your program functions to access WWW
+ # documents via HTTP, Hyper Text Transfer Protocol version 1.1.
+ # For details of HTTP, refer [RFC2616]
+ # (http://www.ietf.org/rfc/rfc2616.txt).
+ #
+ # == Examples
+ #
+ # === Getting Document From WWW Server
+ #
+ # Example #1: Simple GET+print
+ #
+ # require 'net/http'
+ # Net::HTTP.get_print 'www.example.com', '/index.html'
+ #
+ # Example #2: Simple GET+print by URL
+ #
+ # require 'net/http'
+ # require 'uri'
+ # Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
+ #
+ # Example #3: More generic GET+print
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # url = URI.parse('http://www.example.com/index.html')
+ # res = Net::HTTP.start(url.host, url.port) {|http|
+ # http.get('/index.html')
+ # }
+ # puts res.body
+ #
+ # Example #4: More generic GET+print
+ #
+ # require 'net/http'
+ #
+ # url = URI.parse('http://www.example.com/index.html')
+ # req = Net::HTTP::Get.new(url.path)
+ # res = Net::HTTP.start(url.host, url.port) {|http|
+ # http.request(req)
+ # }
+ # puts res.body
+ #
+ # === Posting Form Data
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # #1: Simple POST
+ # res = Net::HTTP.post_form(URI.parse('http://www.example.com/search.cgi'),
+ # {'q'=>'ruby', 'max'=>'50'})
+ # puts res.body
+ #
+ # #2: POST with basic authentication
+ # res = Net::HTTP.post_form(URI.parse('http://jack:pass www.example.com/todo.cgi'),
+ # {'from'=>'2005-01-01', 'to'=>'2005-03-31'})
+ # puts res.body
+ #
+ # #3: Detailed control
+ # url = URI.parse('http://www.example.com/todo.cgi')
+ # req = Net::HTTP::Post.new(url.path)
+ # req.basic_auth 'jack', 'pass'
+ # req.set_form_data({'from'=>'2005-01-01', 'to'=>'2005-03-31'}, ';')
+ # res = Net::HTTP.new(url.host, url.port).start { http.request(req) }
+ # case res
+ # when Net::HTTPSuccess, Net::HTTPRedirection
+ # # OK
+ # else
+ # res.error!
+ # end
+ #
+ # === Accessing via Proxy
+ #
+ # Net::HTTP.Proxy creates http proxy class. It has same
+ # methods of Net::HTTP but its instances always connect to
+ # proxy, instead of given host.
+ #
+ # require 'net/http'
+ #
+ # proxy_addr = 'your.proxy.host'
+ # proxy_port = 8080
+ # :
+ # Net::HTTP::Proxy(proxy_addr, proxy_port).start('www.example.com') {|http|
+ # # always connect to your.proxy.addr:8080
+ # :
+ # }
+ #
+ # Since Net::HTTP.Proxy returns Net::HTTP itself when proxy_addr is nil,
+ # there's no need to change code if there's proxy or not.
+ #
+ # There are two additional parameters in Net::HTTP.Proxy which allow to
+ # specify proxy user name and password:
+ #
+ # Net::HTTP::Proxy(proxy_addr, proxy_port, proxy_user = nil, proxy_pass = nil)
+ #
+ # You may use them to work with authorization-enabled proxies:
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # proxy_host = 'your.proxy.host'
+ # proxy_port = 8080
+ # uri = URI.parse(ENV['http_proxy'])
+ # proxy_user, proxy_pass = uri.userinfo.split(/:/) if uri.userinfo
+ # Net::HTTP::Proxy(proxy_host, proxy_port,
+ # proxy_user, proxy_pass).start('www.example.com') {|http|
+ # # always connect to your.proxy.addr:8080 using specified username and password
+ # :
+ # }
+ #
+ # Note that net/http never rely on HTTP_PROXY environment variable.
+ # If you want to use proxy, set it explicitly.
+ #
+ # === Following Redirection
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # def fetch(uri_str, limit = 10)
+ # # You should choose better exception.
+ # raise ArgumentError, 'HTTP redirect too deep' if limit == 0
+ #
+ # response = Net::HTTP.get_response(URI.parse(uri_str))
+ # case response
+ # when Net::HTTPSuccess then response
+ # when Net::HTTPRedirection then fetch(response['location'], limit - 1)
+ # else
+ # response.error!
+ # end
+ # end
+ #
+ # print fetch('http://www.ruby-lang.org')
+ #
+ # Net::HTTPSuccess and Net::HTTPRedirection is a HTTPResponse class.
+ # All HTTPResponse objects belong to its own response class which
+ # indicate HTTP result status. For details of response classes,
+ # see section "HTTP Response Classes".
+ #
+ # === Basic Authentication
+ #
+ # require 'net/http'
+ #
+ # Net::HTTP.start('www.example.com') {|http|
+ # req = Net::HTTP::Get.new('/secret-page.html')
+ # req.basic_auth 'account', 'password'
+ # response = http.request(req)
+ # print response.body
+ # }
+ #
+ # === HTTP Request Classes
+ #
+ # Here is HTTP request class hierarchy.
+ #
+ # Net::HTTPRequest
+ # Net::HTTP::Get
+ # Net::HTTP::Head
+ # Net::HTTP::Post
+ # Net::HTTP::Put
+ # Net::HTTP::Proppatch
+ # Net::HTTP::Lock
+ # Net::HTTP::Unlock
+ # Net::HTTP::Options
+ # Net::HTTP::Propfind
+ # Net::HTTP::Delete
+ # Net::HTTP::Move
+ # Net::HTTP::Copy
+ # Net::HTTP::Mkcol
+ # Net::HTTP::Trace
+ #
+ # === HTTP Response Classes
+ #
+ # Here is HTTP response class hierarchy.
+ # All classes are defined in Net module.
+ #
+ # HTTPResponse
+ # HTTPUnknownResponse
+ # HTTPInformation # 1xx
+ # HTTPContinue # 100
+ # HTTPSwitchProtocl # 101
+ # HTTPSuccess # 2xx
+ # HTTPOK # 200
+ # HTTPCreated # 201
+ # HTTPAccepted # 202
+ # HTTPNonAuthoritativeInformation # 203
+ # HTTPNoContent # 204
+ # HTTPResetContent # 205
+ # HTTPPartialContent # 206
+ # HTTPRedirection # 3xx
+ # HTTPMultipleChoice # 300
+ # HTTPMovedPermanently # 301
+ # HTTPFound # 302
+ # HTTPSeeOther # 303
+ # HTTPNotModified # 304
+ # HTTPUseProxy # 305
+ # HTTPTemporaryRedirect # 307
+ # HTTPClientError # 4xx
+ # HTTPBadRequest # 400
+ # HTTPUnauthorized # 401
+ # HTTPPaymentRequired # 402
+ # HTTPForbidden # 403
+ # HTTPNotFound # 404
+ # HTTPMethodNotAllowed # 405
+ # HTTPNotAcceptable # 406
+ # HTTPProxyAuthenticationRequired # 407
+ # HTTPRequestTimeOut # 408
+ # HTTPConflict # 409
+ # HTTPGone # 410
+ # HTTPLengthRequired # 411
+ # HTTPPreconditionFailed # 412
+ # HTTPRequestEntityTooLarge # 413
+ # HTTPRequestURITooLong # 414
+ # HTTPUnsupportedMediaType # 415
+ # HTTPRequestedRangeNotSatisfiable # 416
+ # HTTPExpectationFailed # 417
+ # HTTPServerError # 5xx
+ # HTTPInternalServerError # 500
+ # HTTPNotImplemented # 501
+ # HTTPBadGateway # 502
+ # HTTPServiceUnavailable # 503
+ # HTTPGatewayTimeOut # 504
+ # HTTPVersionNotSupported # 505
+ #
+ # == Switching Net::HTTP versions
+ #
+ # You can use net/http.rb 1.1 features (bundled with Ruby 1.6)
+ # by calling HTTP.version_1_1. Calling Net::HTTP.version_1_2
+ # allows you to use 1.2 features again.
+ #
+ # # example
+ # Net::HTTP.start {|http1| ...(http1 has 1.2 features)... }
+ #
+ # Net::HTTP.version_1_1
+ # Net::HTTP.start {|http2| ...(http2 has 1.1 features)... }
+ #
+ # Net::HTTP.version_1_2
+ # Net::HTTP.start {|http3| ...(http3 has 1.2 features)... }
+ #
+ # This function is NOT thread-safe.
+ #
+ class HTTP < Protocol
+
+ # :stopdoc:
+ Revision = %q$Revision: 1.129 $.split[1]
+ HTTPVersion = '1.1'
+ @newimpl = true
+ # :startdoc:
+
+ # Turns on net/http 1.2 (ruby 1.8) features.
+ # Defaults to ON in ruby 1.8.
+ #
+ # I strongly recommend to call this method always.
+ #
+ # require 'net/http'
+ # Net::HTTP.version_1_2
+ #
+ def HTTP.version_1_2
+ @newimpl = true
+ end
+
+ # Turns on net/http 1.1 (ruby 1.6) features.
+ # Defaults to OFF in ruby 1.8.
+ def HTTP.version_1_1
+ @newimpl = false
+ end
+
+ # true if net/http is in version 1.2 mode.
+ # Defaults to true.
+ def HTTP.version_1_2?
+ @newimpl
+ end
+
+ # true if net/http is in version 1.1 compatible mode.
+ # Defaults to true.
+ def HTTP.version_1_1?
+ not @newimpl
+ end
+
+ class << HTTP
+ alias is_version_1_1? version_1_1? #:nodoc:
+ alias is_version_1_2? version_1_2? #:nodoc:
+ end
+
+ #
+ # short cut methods
+ #
+
+ #
+ # Get body from target and output it to +$stdout+. The
+ # target can either be specified as (+uri+), or as
+ # (+host+, +path+, +port+ = 80); so:
+ #
+ # Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
+ #
+ # or:
+ #
+ # Net::HTTP.get_print 'www.example.com', '/index.html'
+ #
+ def HTTP.get_print(uri_or_host, path = nil, port = nil)
+ get_response(uri_or_host, path, port) {|res|
+ res.read_body do |chunk|
+ $stdout.print chunk
+ end
+ }
+ nil
+ end
+
+ # Send a GET request to the target and return the response
+ # as a string. The target can either be specified as
+ # (+uri+), or as (+host+, +path+, +port+ = 80); so:
+ #
+ # print Net::HTTP.get(URI.parse('http://www.example.com/index.html'))
+ #
+ # or:
+ #
+ # print Net::HTTP.get('www.example.com', '/index.html')
+ #
+ def HTTP.get(uri_or_host, path = nil, port = nil)
+ get_response(uri_or_host, path, port).body
+ end
+
+ # Send a GET request to the target and return the response
+ # as a Net::HTTPResponse object. The target can either be specified as
+ # (+uri+), or as (+host+, +path+, +port+ = 80); so:
+ #
+ # res = Net::HTTP.get_response(URI.parse('http://www.example.com/index.html'))
+ # print res.body
+ #
+ # or:
+ #
+ # res = Net::HTTP.get_response('www.example.com', '/index.html')
+ # print res.body
+ #
+ def HTTP.get_response(uri_or_host, path = nil, port = nil, &block)
+ if path
+ host = uri_or_host
+ new(host, port || HTTP.default_port).start {|http|
+ return http.request_get(path, &block)
+ }
+ else
+ uri = uri_or_host
+ new(uri.host, uri.port).start {|http|
+ return http.request_get(uri.request_uri, &block)
+ }
+ end
+ end
+
+ # Posts HTML form data to the +URL+.
+ # Form data must be represented as a Hash of String to String, e.g:
+ #
+ # { "cmd" => "search", "q" => "ruby", "max" => "50" }
+ #
+ # This method also does Basic Authentication iff +URL+.user exists.
+ #
+ # Example:
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # HTTP.post_form URI.parse('http://www.example.com/search.cgi'),
+ # { "q" => "ruby", "max" => "50" }
+ #
+ def HTTP.post_form(url, params)
+ req = Post.new(url.path)
+ req.form_data = params
+ req.basic_auth url.user, url.password if url.user
+ new(url.host, url.port).start {|http|
+ http.request(req)
+ }
+ end
+
+ #
+ # HTTP session management
+ #
+
+ # The default port to use for HTTP requests; defaults to 80.
+ def HTTP.default_port
+ http_default_port()
+ end
+
+ # The default port to use for HTTP requests; defaults to 80.
+ def HTTP.http_default_port
+ 80
+ end
+
+ # The default port to use for HTTPS requests; defaults to 443.
+ def HTTP.https_default_port
+ 443
+ end
+
+ def HTTP.socket_type #:nodoc: obsolete
+ BufferedIO
+ end
+
+ # creates a new Net::HTTP object and opens its TCP connection and
+ # HTTP session. If the optional block is given, the newly
+ # created Net::HTTP object is passed to it and closed when the
+ # block finishes. In this case, the return value of this method
+ # is the return value of the block. If no block is given, the
+ # return value of this method is the newly created Net::HTTP object
+ # itself, and the caller is responsible for closing it upon completion.
+ def HTTP.start(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil, &block) # :yield: +http+
+ new(address, port, p_addr, p_port, p_user, p_pass).start(&block)
+ end
+
+ class << HTTP
+ alias newobj new
+ end
+
+ # Creates a new Net::HTTP object.
+ # If +proxy_addr+ is given, creates an Net::HTTP object with proxy support.
+ # This method does not open the TCP connection.
+ def HTTP.new(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil)
+ h = Proxy(p_addr, p_port, p_user, p_pass).newobj(address, port)
+ h.instance_eval {
+ @newimpl = ::Net::HTTP.version_1_2?
+ }
+ h
+ end
+
+ # Creates a new Net::HTTP object for the specified +address+.
+ # This method does not open the TCP connection.
+ def initialize(address, port = nil)
+ @address = address
+ @port = (port || HTTP.default_port)
+ @curr_http_version = HTTPVersion
+ @seems_1_0_server = false
+ @close_on_empty_response = false
+ @socket = nil
+ @started = false
+ @open_timeout = nil
+ @read_timeout = 60
+ @debug_output = nil
+ @use_ssl = false
+ @ssl_context = nil
+ end
+
+ def inspect
+ "#<#{self.class} #{@address}:#{@port} open=#{started?}>"
+ end
+
+ # *WARNING* This method causes serious security hole.
+ # Never use this method in production code.
+ #
+ # Set an output stream for debugging.
+ #
+ # http = Net::HTTP.new
+ # http.set_debug_output $stderr
+ # http.start { .... }
+ #
+ def set_debug_output(output)
+ warn 'Net::HTTP#set_debug_output called after HTTP started' if started?
+ @debug_output = output
+ end
+
+ # The host name to connect to.
+ attr_reader :address
+
+ # The port number to connect to.
+ attr_reader :port
+
+ # Seconds to wait until connection is opened.
+ # If the HTTP object cannot open a connection in this many seconds,
+ # it raises a TimeoutError exception.
+ attr_accessor :open_timeout
+
+ # Seconds to wait until reading one block (by one read(2) call).
+ # If the HTTP object cannot open a connection in this many seconds,
+ # it raises a TimeoutError exception.
+ attr_reader :read_timeout
+
+ # Setter for the read_timeout attribute.
+ def read_timeout=(sec)
+ @socket.read_timeout = sec if @socket
+ @read_timeout = sec
+ end
+
+ # returns true if the HTTP session is started.
+ def started?
+ @started
+ end
+
+ alias active? started? #:nodoc: obsolete
+
+ attr_accessor :close_on_empty_response
+
+ # returns true if use SSL/TLS with HTTP.
+ def use_ssl?
+ false # redefined in net/https
+ end
+
+ # Opens TCP connection and HTTP session.
+ #
+ # When this method is called with block, gives a HTTP object
+ # to the block and closes the TCP connection / HTTP session
+ # after the block executed.
+ #
+ # When called with a block, returns the return value of the
+ # block; otherwise, returns self.
+ #
+ def start # :yield: http
+ raise IOError, 'HTTP session already opened' if @started
+ if block_given?
+ begin
+ do_start
+ return yield(self)
+ ensure
+ do_finish
+ end
+ end
+ do_start
+ self
+ end
+
+ def do_start
+ connect
+ @started = true
+ end
+ private :do_start
+
+ def connect
+ D "opening connection to #{conn_address()}..."
+ s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }
+ D "opened"
+ if use_ssl?
+ unless @ssl_context.verify_mode
+ warn "warning: peer certificate won't be verified in this SSL session"
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
+ s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
+ s.sync_close = true
+ end
+ @socket = BufferedIO.new(s)
+ @socket.read_timeout = @read_timeout
+ @socket.debug_output = @debug_output
+ if use_ssl?
+ if proxy?
+ @socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
+ @address, @port, HTTPVersion)
+ @socket.writeline "Host: #{@address}:#{@port}"
+ if proxy_user
+ credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
+ credential.delete!("\r\n")
+ @socket.writeline "Proxy-Authorization: Basic #{credential}"
+ end
+ @socket.writeline ''
+ HTTPResponse.read_new( socket).value
+ end
+ s.connect
+ end
+ on_connect
+ end
+ private :connect
+
+ def on_connect
+ end
+ private :on_connect
+
+ # Finishes HTTP session and closes TCP connection.
+ # Raises IOError if not started.
+ def finish
+ raise IOError, 'HTTP session not yet started' unless started?
+ do_finish
+ end
+
+ def do_finish
+ @started = false
+ @socket.close if @socket and not @socket.closed?
+ @socket = nil
+ end
+ private :do_finish
+
+ #
+ # proxy
+ #
+
+ public
+
+ # no proxy
+ @is_proxy_class = false
+ @proxy_addr = nil
+ @proxy_port = nil
+ @proxy_user = nil
+ @proxy_pass = nil
+
+ # Creates an HTTP proxy class.
+ # Arguments are address/port of proxy host and username/password
+ # if authorization on proxy server is required.
+ # You can replace the HTTP class with created proxy class.
+ #
+ # If ADDRESS is nil, this method returns self (Net::HTTP).
+ #
+ # # Example
+ # proxy_class = Net::HTTP::Proxy('proxy.example.com', 8080)
+ # :
+ # proxy_class.start('www.ruby-lang.org') {|http|
+ # # connecting proxy.foo.org:8080
+ # :
+ # }
+ #
+ def HTTP.Proxy(p_addr, p_port = nil, p_user = nil, p_pass = nil)
+ return self unless p_addr
+ delta = ProxyDelta
+ proxyclass = Class.new(self)
+ proxyclass.module_eval {
+ include delta
+ # with proxy
+ @is_proxy_class = true
+ @proxy_address = p_addr
+ @proxy_port = p_port || default_port()
+ @proxy_user = p_user
+ @proxy_pass = p_pass
+ }
+ proxyclass
+ end
+
+ class << HTTP
+ # returns true if self is a class which was created by HTTP::Proxy.
+ def proxy_class?
+ @is_proxy_class
+ end
+
+ attr_reader :proxy_address
+ attr_reader :proxy_port
+ attr_reader :proxy_user
+ attr_reader :proxy_pass
+ end
+
+ # True if self is a HTTP proxy class.
+ def proxy?
+ self.class.proxy_class?
+ end
+
+ # Address of proxy host. If self does not use a proxy, nil.
+ def proxy_address
+ self.class.proxy_address
+ end
+
+ # Port number of proxy host. If self does not use a proxy, nil.
+ def proxy_port
+ self.class.proxy_port
+ end
+
+ # User name for accessing proxy. If self does not use a proxy, nil.
+ def proxy_user
+ self.class.proxy_user
+ end
+
+ # User password for accessing proxy. If self does not use a proxy, nil.
+ def proxy_pass
+ self.class.proxy_pass
+ end
+
+ alias proxyaddr proxy_address #:nodoc: obsolete
+ alias proxyport proxy_port #:nodoc: obsolete
+
+ private
+
+ # without proxy
+
+ def conn_address
+ address()
+ end
+
+ def conn_port
+ port()
+ end
+
+ def edit_path(path)
+ path
+ end
+
+ module ProxyDelta #:nodoc: internal use only
+ private
+
+ def conn_address
+ proxy_address()
+ end
+
+ def conn_port
+ proxy_port()
+ end
+
+ def edit_path(path)
+ use_ssl? ? path : "http://#{addr_port()}#{path}"
+ end
+ end
+
+ #
+ # HTTP operations
+ #
+
+ public
+
+ # Gets data from +path+ on the connected-to host.
+ # +header+ must be a Hash like { 'Accept' => '*/*', ... }.
+ #
+ # In version 1.1 (ruby 1.6), this method returns a pair of objects,
+ # a Net::HTTPResponse object and the entity body string.
+ # In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse
+ # object.
+ #
+ # If called with a block, yields each fragment of the
+ # entity body in turn as a string as it is read from
+ # the socket. Note that in this case, the returned response
+ # object will *not* contain a (meaningful) body.
+ #
+ # +dest+ argument is obsolete.
+ # It still works but you must not use it.
+ #
+ # In version 1.1, this method might raise an exception for
+ # 3xx (redirect). In this case you can get a HTTPResponse object
+ # by "anException.response".
+ #
+ # In version 1.2, this method never raises exception.
+ #
+ # # version 1.1 (bundled with Ruby 1.6)
+ # response, body = http.get('/index.html')
+ #
+ # # version 1.2 (bundled with Ruby 1.8 or later)
+ # response = http.get('/index.html')
+ #
+ # # using block
+ # File.open('result.txt', 'w') {|f|
+ # http.get('/~foo/') do |str|
+ # f.write str
+ # end
+ # }
+ #
+ def get(path, initheader = nil, dest = nil, &block) # :yield: +body_segment+
+ res = nil
+ request(Get.new(path, initheader)) {|r|
+ r.read_body dest, &block
+ res = r
+ }
+ unless @newimpl
+ res.value
+ return res, res.body
+ end
+
+ res
+ end
+
+ # Gets only the header from +path+ on the connected-to host.
+ # +header+ is a Hash like { 'Accept' => '*/*', ... }.
+ #
+ # This method returns a Net::HTTPResponse object.
+ #
+ # In version 1.1, this method might raise an exception for
+ # 3xx (redirect). On the case you can get a HTTPResponse object
+ # by "anException.response".
+ # In version 1.2, this method never raises an exception.
+ #
+ # response = nil
+ # Net::HTTP.start('some.www.server', 80) {|http|
+ # response = http.head('/index.html')
+ # }
+ # p response['content-type']
+ #
+ def head(path, initheader = nil)
+ res = request(Head.new(path, initheader))
+ res.value unless @newimpl
+ res
+ end
+
+ # Posts +data+ (must be a String) to +path+. +header+ must be a Hash
+ # like { 'Accept' => '*/*', ... }.
+ #
+ # In version 1.1 (ruby 1.6), this method returns a pair of objects, a
+ # Net::HTTPResponse object and an entity body string.
+ # In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse object.
+ #
+ # If called with a block, yields each fragment of the
+ # entity body in turn as a string as it are read from
+ # the socket. Note that in this case, the returned response
+ # object will *not* contain a (meaningful) body.
+ #
+ # +dest+ argument is obsolete.
+ # It still works but you must not use it.
+ #
+ # In version 1.1, this method might raise an exception for
+ # 3xx (redirect). In this case you can get an HTTPResponse object
+ # by "anException.response".
+ # In version 1.2, this method never raises exception.
+ #
+ # # version 1.1
+ # response, body = http.post('/cgi-bin/search.rb', 'query=foo')
+ #
+ # # version 1.2
+ # response = http.post('/cgi-bin/search.rb', 'query=foo')
+ #
+ # # using block
+ # File.open('result.txt', 'w') {|f|
+ # http.post('/cgi-bin/search.rb', 'query=foo') do |str|
+ # f.write str
+ # end
+ # }
+ #
+ def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
+ res = nil
+ request(Post.new(path, initheader), data) {|r|
+ r.read_body dest, &block
+ res = r
+ }
+ unless @newimpl
+ res.value
+ return res, res.body
+ end
+
+ res
+ end
+
+ def put(path, data, initheader = nil) #:nodoc:
+ res = request(Put.new(path, initheader), data)
+ res.value unless @newimpl
+ res
+ end
+
+ # Sends a PROPPATCH request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def proppatch(path, body, initheader = nil)
+ request(Proppatch.new(path, initheader), body)
+ end
+
+ # Sends a LOCK request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def lock(path, body, initheader = nil)
+ request(Lock.new(path, initheader), body)
+ end
+
+ # Sends a UNLOCK request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def unlock(path, body, initheader = nil)
+ request(Unlock.new(path, initheader), body)
+ end
+
+ # Sends a OPTIONS request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def options(path, initheader = nil)
+ request(Options.new(path, initheader))
+ end
+
+ # Sends a PROPFIND request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def propfind(path, body = nil, initheader = {'Depth' => '0'})
+ request(Propfind.new(path, initheader), body)
+ end
+
+ # Sends a DELETE request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def delete(path, initheader = {'Depth' => 'Infinity'})
+ request(Delete.new(path, initheader))
+ end
+
+ # Sends a MOVE request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def move(path, initheader = nil)
+ request(Move.new(path, initheader))
+ end
+
+ # Sends a COPY request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def copy(path, initheader = nil)
+ request(Copy.new(path, initheader))
+ end
+
+ # Sends a MKCOL request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def mkcol(path, body = nil, initheader = nil)
+ request(Mkcol.new(path, initheader), body)
+ end
+
+ # Sends a TRACE request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def trace(path, initheader = nil)
+ request(Trace.new(path, initheader))
+ end
+
+ # Sends a GET request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ #
+ # When called with a block, yields an HTTPResponse object.
+ # The body of this response will not have been read yet;
+ # the caller can process it using HTTPResponse#read_body,
+ # if desired.
+ #
+ # Returns the response.
+ #
+ # This method never raises Net::* exceptions.
+ #
+ # response = http.request_get('/index.html')
+ # # The entity body is already read here.
+ # p response['content-type']
+ # puts response.body
+ #
+ # # using block
+ # http.request_get('/index.html') {|response|
+ # p response['content-type']
+ # response.read_body do |str| # read body now
+ # print str
+ # end
+ # }
+ #
+ def request_get(path, initheader = nil, &block) # :yield: +response+
+ request(Get.new(path, initheader), &block)
+ end
+
+ # Sends a HEAD request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ #
+ # Returns the response.
+ #
+ # This method never raises Net::* exceptions.
+ #
+ # response = http.request_head('/index.html')
+ # p response['content-type']
+ #
+ def request_head(path, initheader = nil, &block)
+ request(Head.new(path, initheader), &block)
+ end
+
+ # Sends a POST request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ #
+ # When called with a block, yields an HTTPResponse object.
+ # The body of this response will not have been read yet;
+ # the caller can process it using HTTPResponse#read_body,
+ # if desired.
+ #
+ # Returns the response.
+ #
+ # This method never raises Net::* exceptions.
+ #
+ # # example
+ # response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...')
+ # p response.status
+ # puts response.body # body is already read
+ #
+ # # using block
+ # http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response|
+ # p response.status
+ # p response['content-type']
+ # response.read_body do |str| # read body now
+ # print str
+ # end
+ # }
+ #
+ def request_post(path, data, initheader = nil, &block) # :yield: +response+
+ request Post.new(path, initheader), data, &block
+ end
+
+ def request_put(path, data, initheader = nil, &block) #:nodoc:
+ request Put.new(path, initheader), data, &block
+ end
+
+ alias get2 request_get #:nodoc: obsolete
+ alias head2 request_head #:nodoc: obsolete
+ alias post2 request_post #:nodoc: obsolete
+ alias put2 request_put #:nodoc: obsolete
+
+
+ # Sends an HTTP request to the HTTP server.
+ # This method also sends DATA string if DATA is given.
+ #
+ # Returns a HTTPResponse object.
+ #
+ # This method never raises Net::* exceptions.
+ #
+ # response = http.send_request('GET', '/index.html')
+ # puts response.body
+ #
+ def send_request(name, path, data = nil, header = nil)
+ r = HTTPGenericRequest.new(name,(data ? true : false),true,path,header)
+ request r, data
+ end
+
+ # Sends an HTTPRequest object REQUEST to the HTTP server.
+ # This method also sends DATA string if REQUEST is a post/put request.
+ # Giving DATA for get/head request causes ArgumentError.
+ #
+ # When called with a block, yields an HTTPResponse object.
+ # The body of this response will not have been read yet;
+ # the caller can process it using HTTPResponse#read_body,
+ # if desired.
+ #
+ # Returns a HTTPResponse object.
+ #
+ # This method never raises Net::* exceptions.
+ #
+ def request(req, body = nil, &block) # :yield: +response+
+ unless started?
+ start {
+ req['connection'] ||= 'close'
+ return request(req, body, &block)
+ }
+ end
+ if proxy_user()
+ unless use_ssl?
+ req.proxy_basic_auth proxy_user(), proxy_pass()
+ end
+ end
+
+ req.set_body_internal body
+ begin_transport req
+ req.exec @socket, @curr_http_version, edit_path(req.path)
+ begin
+ res = HTTPResponse.read_new(@socket)
+ end while res.kind_of?(HTTPContinue)
+ res.reading_body(@socket, req.response_body_permitted?) {
+ yield res if block_given?
+ }
+ end_transport req, res
+
+ res
+ end
+
+ private
+
+ def begin_transport(req)
+ if @socket.closed?
+ connect
+ end
+ if @seems_1_0_server
+ req['connection'] ||= 'close'
+ end
+ if not req.response_body_permitted? and @close_on_empty_response
+ req['connection'] ||= 'close'
+ end
+ req['host'] ||= addr_port()
+ end
+
+ def end_transport(req, res)
+ @curr_http_version = res.http_version
+ if not res.body and @close_on_empty_response
+ D 'Conn close'
+ @socket.close
+ elsif keep_alive?(req, res)
+ D 'Conn keep-alive'
+ if @socket.closed?
+ D 'Conn (but seems 1.0 server)'
+ @seems_1_0_server = true
+ end
+ else
+ D 'Conn close'
+ @socket.close
+ end
+ end
+
+ def keep_alive?(req, res)
+ return false if /close/i =~ req['connection'].to_s
+ return false if @seems_1_0_server
+ return true if /keep-alive/i =~ res['connection'].to_s
+ return false if /close/i =~ res['connection'].to_s
+ return true if /keep-alive/i =~ res['proxy-connection'].to_s
+ return false if /close/i =~ res['proxy-connection'].to_s
+ (@curr_http_version == '1.1')
+ end
+
+ #
+ # utils
+ #
+
+ private
+
+ def addr_port
+ if use_ssl?
+ address() + (port == HTTP.https_default_port ? '' : ":#{port()}")
+ else
+ address() + (port == HTTP.http_default_port ? '' : ":#{port()}")
+ end
+ end
+
+ def D(msg)
+ return unless @debug_output
+ @debug_output << msg
+ @debug_output << "\n"
+ end
+
+ end
+
+ HTTPSession = HTTP
+
+
+ #
+ # Header module.
+ #
+ # Provides access to @header in the mixed-into class as a hash-like
+ # object, except with case-insensitive keys. Also provides
+ # methods for accessing commonly-used header values in a more
+ # convenient format.
+ #
+ module HTTPHeader
+
+ def initialize_http_header(initheader)
+ @header = {}
+ return unless initheader
+ initheader.each do |key, value|
+ warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE
+ @header[key.downcase] = [value.strip]
+ end
+ end
+
+ def size #:nodoc: obsolete
+ @header.size
+ end
+
+ alias length size #:nodoc: obsolete
+
+ # Returns the header field corresponding to the case-insensitive key.
+ # For example, a key of "Content-Type" might return "text/html"
+ def [](key)
+ a = @header[key.downcase] or return nil
+ a.join(', ')
+ end
+
+ # Sets the header field corresponding to the case-insensitive key.
+ def []=(key, val)
+ unless val
+ @header.delete key.downcase
+ return val
+ end
+ @header[key.downcase] = [val]
+ end
+
+ # [Ruby 1.8.3]
+ # Adds header field instead of replace.
+ # Second argument +val+ must be a String.
+ # See also #[]=, #[] and #get_fields.
+ #
+ # request.add_field 'X-My-Header', 'a'
+ # p request['X-My-Header'] #=> "a"
+ # p request.get_fields('X-My-Header') #=> ["a"]
+ # request.add_field 'X-My-Header', 'b'
+ # p request['X-My-Header'] #=> "a, b"
+ # p request.get_fields('X-My-Header') #=> ["a", "b"]
+ # request.add_field 'X-My-Header', 'c'
+ # p request['X-My-Header'] #=> "a, b, c"
+ # p request.get_fields('X-My-Header') #=> ["a", "b", "c"]
+ #
+ def add_field(key, val)
+ if @header.key?(key.downcase)
+ @header[key.downcase].concat [val]
+ else
+ @header[key.downcase] = [val]
+ end
+ end
+
+ # [Ruby 1.8.3]
+ # Returns an array of header field strings corresponding to the
+ # case-insensitive +key+. This method allows you to get duplicated
+ # header fields without any processing. See also #[].
+ #
+ # p response.get_fields('Set-Cookie')
+ # #=> ["session=al98axx; expires=Fri, 31-Dec-1999 23:58:23",
+ # "query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"]
+ # p response['Set-Cookie']
+ # #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"
+ #
+ def get_fields(key)
+ return nil unless @header[key.downcase]
+ @header[key.downcase].dup
+ end
+
+ # Returns the header field corresponding to the case-insensitive key.
+ # Returns the default value +args+, or the result of the block, or nil,
+ # if there's no header field named key. See Hash#fetch
+ def fetch(key, *args, &block) #:yield: +key+
+ a = @header.fetch(key.downcase, *args, &block)
+ a.join(', ')
+ end
+
+ # Iterates for each header names and values.
+ def each_header #:yield: +key+, +value+
+ @header.each do |k,va|
+ yield k, va.join(', ')
+ end
+ end
+
+ alias each each_header
+
+ # Iterates for each header names.
+ def each_name(&block) #:yield: +key+
+ @header.each_key(&block)
+ end
+
+ alias each_key each_name
+
+ # Iterates for each capitalized header names.
+ def each_capitalized_name(&block) #:yield: +key+
+ @header.each_key do |k|
+ yield capitalize(k)
+ end
+ end
+
+ # Iterates for each header values.
+ def each_value #:yield: +value+
+ @header.each_value do |va|
+ yield va.join(', ')
+ end
+ end
+
+ # Removes a header field.
+ def delete(key)
+ @header.delete(key.downcase)
+ end
+
+ # true if +key+ header exists.
+ def key?(key)
+ @header.key?(key.downcase)
+ end
+
+ # Returns a Hash consist of header names and values.
+ def to_hash
+ @header.dup
+ end
+
+ # As for #each_header, except the keys are provided in capitalized form.
+ def each_capitalized
+ @header.each do |k,v|
+ yield capitalize(k), v.join(', ')
+ end
+ end
+
+ alias canonical_each each_capitalized
+
+ def capitalize(name)
+ name.split(/-/).map {|s| s.capitalize }.join('-')
+ end
+ private :capitalize
+
+ # Returns an Array of Range objects which represents Range: header field,
+ # or +nil+ if there is no such header.
+ def range
+ return nil unless @header['range']
+ self['Range'].split(/,/).map {|spec|
+ m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match(spec) or
+ raise HTTPHeaderSyntaxError, "wrong Range: #{spec}"
+ d1 = m[1].to_i
+ d2 = m[2].to_i
+ if m[1] and m[2] then d1..d2
+ elsif m[1] then d1..-1
+ elsif m[2] then -d2..-1
+ else
+ raise HTTPHeaderSyntaxError, 'range is not specified'
+ end
+ }
+ end
+
+ # Set Range: header from Range (arg r) or beginning index and
+ # length from it (arg idx&len).
+ #
+ # req.range = (0..1023)
+ # req.set_range 0, 1023
+ #
+ def set_range(r, e = nil)
+ unless r
+ @header.delete 'range'
+ return r
+ end
+ r = (r...r+e) if e
+ case r
+ when Numeric
+ n = r.to_i
+ rangestr = (n > 0 ? "0-#{n-1}" : "-#{-n}")
+ when Range
+ first = r.first
+ last = r.last
+ last -= 1 if r.exclude_end?
+ if last == -1
+ rangestr = (first > 0 ? "#{first}-" : "-#{-first}")
+ else
+ raise HTTPHeaderSyntaxError, 'range.first is negative' if first < 0
+ raise HTTPHeaderSyntaxError, 'range.last is negative' if last < 0
+ raise HTTPHeaderSyntaxError, 'must be .first < .last' if first > last
+ rangestr = "#{first}-#{last}"
+ end
+ else
+ raise TypeError, 'Range/Integer is required'
+ end
+ @header['range'] = ["bytes=#{rangestr}"]
+ r
+ end
+
+ alias range= set_range
+
+ # Returns an Integer object which represents the Content-Length: header field
+ # or +nil+ if that field is not provided.
+ def content_length
+ return nil unless key?('Content-Length')
+ len = self['Content-Length'].slice(/\d+/) or
+ raise HTTPHeaderSyntaxError, 'wrong Content-Length format'
+ len.to_i
+ end
+
+ def content_length=(len)
+ unless len
+ @header.delete 'content-length'
+ return nil
+ end
+ @header['content-length'] = [len.to_i.to_s]
+ end
+
+ # Returns "true" if the "transfer-encoding" header is present and
+ # set to "chunked". This is an HTTP/1.1 feature, allowing the
+ # the content to be sent in "chunks" without at the outset
+ # stating the entire content length.
+ def chunked?
+ return false unless @header['transfer-encoding']
+ field = self['Transfer-Encoding']
+ (/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false
+ end
+
+ # Returns a Range object which represents Content-Range: header field.
+ # This indicates, for a partial entity body, where this fragment
+ # fits inside the full entity body, as range of byte offsets.
+ def content_range
+ return nil unless @header['content-range']
+ m = %r<bytes\s+(\d+)-(\d+)/(\d+|\*)>i.match(self['Content-Range']) or
+ raise HTTPHeaderSyntaxError, 'wrong Content-Range format'
+ m[1].to_i .. m[2].to_i + 1
+ end
+
+ # The length of the range represented in Content-Range: header.
+ def range_length
+ r = content_range() or return nil
+ r.end - r.begin
+ end
+
+ def content_type
+ "#{main_type()}/#{sub_type()}"
+ end
+
+ def main_type
+ return nil unless @header['content-type']
+ self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip
+ end
+
+ def sub_type
+ return nil unless @header['content-type']
+ self['Content-Type'].split(';').first.to_s.split('/')[1].to_s.strip
+ end
+
+ def type_params
+ result = {}
+ self['Content-Type'].to_s.split(';')[1..-1].each do |param|
+ k, v = *param.split('=', 2)
+ result[k.strip] = v.strip
+ end
+ result
+ end
+
+ def set_content_type(type, params = {})
+ @header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')]
+ end
+
+ alias content_type= set_content_type
+
+ def set_form_data(params, sep = '&')
+ self.body = params.map {|k,v| "#{urlencode(k.to_s)}=#{urlencode(v.to_s)}" }.join(sep)
+ self.content_type = 'application/x-www-form-urlencoded'
+ end
+
+ alias form_data= set_form_data
+
+ def urlencode(str)
+ str.gsub(/[^a-zA-Z0-9_\.\-]/n) {|s| sprintf('%%%02x', s[0]) }
+ end
+ private :urlencode
+
+ # Set the Authorization: header for "Basic" authorization.
+ def basic_auth(account, password)
+ @header['authorization'] = [basic_encode(account, password)]
+ end
+
+ # Set Proxy-Authorization: header for "Basic" authorization.
+ def proxy_basic_auth(account, password)
+ @header['proxy-authorization'] = [basic_encode(account, password)]
+ end
+
+ def basic_encode(account, password)
+ 'Basic ' + ["#{account}:#{password}"].pack('m').delete("\r\n")
+ end
+ private :basic_encode
+
+ end
+
+
+ #
+ # Parent of HTTPRequest class. Do not use this directly; use
+ # a subclass of HTTPRequest.
+ #
+ # Mixes in the HTTPHeader module.
+ #
+ class HTTPGenericRequest
+
+ include HTTPHeader
+
+ def initialize(m, reqbody, resbody, path, initheader = nil)
+ @method = m
+ @request_has_body = reqbody
+ @response_has_body = resbody
+ raise ArgumentError, "HTTP request path is empty" if path.empty?
+ @path = path
+ initialize_http_header initheader
+ self['Accept'] ||= '*/*'
+ @body = nil
+ @body_stream = nil
+ end
+
+ attr_reader :method
+ attr_reader :path
+
+ def inspect
+ "\#<#{self.class} #{@method}>"
+ end
+
+ def request_body_permitted?
+ @request_has_body
+ end
+
+ def response_body_permitted?
+ @response_has_body
+ end
+
+ def body_exist?
+ warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?" if $VERBOSE
+ response_body_permitted?
+ end
+
+ attr_reader :body
+
+ def body=(str)
+ @body = str
+ @body_stream = nil
+ str
+ end
+
+ attr_reader :body_stream
+
+ def body_stream=(input)
+ @body = nil
+ @body_stream = input
+ input
+ end
+
+ def set_body_internal(str) #:nodoc: internal use only
+ raise ArgumentError, "both of body argument and HTTPRequest#body set" if str and (@body or @body_stream)
+ self.body = str if str
+ end
+
+ #
+ # write
+ #
+
+ def exec(sock, ver, path) #:nodoc: internal use only
+ if @body
+ send_request_with_body sock, ver, path, @body
+ elsif @body_stream
+ send_request_with_body_stream sock, ver, path, @body_stream
+ else
+ write_header sock, ver, path
+ end
+ end
+
+ private
+
+ def send_request_with_body(sock, ver, path, body)
+ self.content_length = body.length
+ delete 'Transfer-Encoding'
+ unless content_type()
+ warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
+ set_content_type 'application/x-www-form-urlencoded'
+ end
+ write_header sock, ver, path
+ sock.write body
+ end
+
+ def send_request_with_body_stream(sock, ver, path, f)
+ raise ArgumentError, "Content-Length not given and Transfer-Encoding is not `chunked'" unless content_length() or chunked?
+ unless content_type()
+ warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
+ set_content_type 'application/x-www-form-urlencoded'
+ end
+ write_header sock, ver, path
+ if chunked?
+ while s = f.read(1024)
+ sock.write(sprintf("%x\r\n", s.length) << s << "\r\n")
+ end
+ sock.write "0\r\n\r\n"
+ else
+ while s = f.read(1024)
+ sock.write s
+ end
+ end
+ end
+
+ def write_header(sock, ver, path)
+ buf = "#{@method} #{path} HTTP/#{ver}\r\n"
+ each_capitalized do |k,v|
+ buf << "#{k}: #{v}\r\n"
+ end
+ buf << "\r\n"
+ sock.write buf
+ end
+
+ end
+
+
+ #
+ # HTTP request class. This class wraps request header and entity path.
+ # You *must* use its subclass, Net::HTTP::Get, Post, Head.
+ #
+ class HTTPRequest < HTTPGenericRequest
+
+ # Creates HTTP request object.
+ def initialize(path, initheader = nil)
+ super self.class::METHOD,
+ self.class::REQUEST_HAS_BODY,
+ self.class::RESPONSE_HAS_BODY,
+ path, initheader
+ end
+ end
+
+
+ class HTTP # reopen
+ #
+ # HTTP 1.1 methods --- RFC2616
+ #
+
+ class Get < HTTPRequest
+ METHOD = 'GET'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BO