emonti-rbkb 0.6.2.1 → 0.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +32 -0
- data/README.rdoc +10 -7
- data/Rakefile +47 -0
- data/bin/feed +5 -0
- data/bin/plugsrv +3 -3
- data/cli_usage.rdoc +44 -9
- data/doctor-bag.jpg +0 -0
- data/lib/rbkb.rb +47 -2
- data/lib/rbkb/cli.rb +8 -6
- data/lib/rbkb/cli/b64.rb +5 -0
- data/lib/rbkb/cli/bgrep.rb +14 -9
- data/lib/rbkb/cli/chars.rb +2 -1
- data/lib/rbkb/cli/crc32.rb +4 -1
- data/lib/rbkb/cli/d64.rb +3 -0
- data/lib/rbkb/cli/dedump.rb +5 -3
- data/lib/rbkb/cli/feed.rb +223 -0
- data/lib/rbkb/cli/hexify.rb +3 -3
- data/lib/rbkb/cli/len.rb +12 -9
- data/lib/rbkb/cli/rstrings.rb +13 -10
- data/lib/rbkb/cli/slice.rb +1 -0
- data/lib/rbkb/cli/telson.rb +21 -57
- data/lib/rbkb/cli/unhexify.rb +2 -6
- data/lib/rbkb/cli/urldec.rb +1 -0
- data/lib/rbkb/cli/urlenc.rb +1 -0
- data/lib/rbkb/extends.rb +41 -6
- data/lib/rbkb/http.rb +20 -0
- data/lib/rbkb/http/base.rb +172 -0
- data/lib/rbkb/http/body.rb +214 -0
- data/lib/rbkb/http/common.rb +74 -0
- data/lib/rbkb/http/headers.rb +356 -0
- data/lib/rbkb/http/parameters.rb +101 -0
- data/lib/rbkb/http/request.rb +58 -0
- data/lib/rbkb/http/response.rb +86 -0
- data/lib/rbkb/plug.rb +3 -3
- data/lib/rbkb/plug/cli.rb +83 -0
- data/lib/rbkb/plug/feed_import.rb +74 -0
- data/lib/rbkb/plug/plug.rb +36 -19
- data/lib/rbkb/plug/unix_domain.rb +75 -0
- data/rbkb.gemspec +38 -0
- data/spec/rbkb_spec.rb +7 -0
- data/spec/spec_helper.rb +16 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/test/test_cli_b64.rb +35 -0
- data/test/test_cli_bgrep.rb +137 -0
- data/test/test_cli_blit.rb +11 -0
- data/test/test_cli_chars.rb +21 -0
- data/test/test_cli_crc32.rb +108 -0
- data/test/test_cli_d64.rb +22 -0
- data/test/test_cli_dedump.rb +118 -0
- data/test/test_cli_feed.rb +11 -0
- data/test/test_cli_helper.rb +96 -0
- data/test/test_cli_hexify.rb +63 -0
- data/test/test_cli_len.rb +96 -0
- data/test/test_cli_rstrings.rb +15 -0
- data/test/test_cli_slice.rb +73 -0
- data/test/test_cli_telson.rb +11 -0
- data/test/test_cli_unhexify.rb +43 -0
- data/test/test_cli_urldec.rb +50 -0
- data/test/test_cli_urlenc.rb +44 -0
- data/test/test_cli_xor.rb +71 -0
- data/test/test_helper.rb +5 -0
- data/test/test_http.rb +27 -0
- data/test/test_http_helper.rb +60 -0
- data/test/test_http_request.rb +136 -0
- data/test/test_http_response.rb +222 -0
- data/test/test_rbkb.rb +19 -0
- metadata +127 -21
data/lib/rbkb/cli/unhexify.rb
CHANGED
@@ -40,13 +40,9 @@ class Rbkb::Cli::Unhexify < Rbkb::Cli::Executable
|
|
40
40
|
@opts[:indat] ||= @stdin.read()
|
41
41
|
|
42
42
|
@opts[:indat].delete!("\r\n")
|
43
|
-
@opts[:delim] ||=
|
43
|
+
@opts[:delim] ||= Regexp.new('\s*')
|
44
44
|
|
45
|
-
|
46
|
-
bail "Error: Failed parsing as hex"
|
47
|
-
end
|
48
|
-
|
49
|
-
@stdout << out
|
45
|
+
@stdout << @opts[:indat].unhexify(@opts[:delim])
|
50
46
|
|
51
47
|
self.exit(0)
|
52
48
|
end
|
data/lib/rbkb/cli/urldec.rb
CHANGED
data/lib/rbkb/cli/urlenc.rb
CHANGED
data/lib/rbkb/extends.rb
CHANGED
@@ -390,11 +390,9 @@ class String
|
|
390
390
|
# If strings are null terminated, the trailing null *IS* included
|
391
391
|
# in the end_offset. Unicode matches will also include null bytes.
|
392
392
|
#
|
393
|
-
#
|
393
|
+
# Todos?
|
394
394
|
# - better unicode support (i.e. not using half-assed unicode)
|
395
395
|
# - support other encodings such as all those the binutils strings does?
|
396
|
-
# - not sure if we want the trailing null in null terminated strings
|
397
|
-
# - not sure if we want wide characters to include their null bytes
|
398
396
|
def strings(opts={})
|
399
397
|
opts[:encoding] ||= :both
|
400
398
|
prx = (opts[:valid] || /[\r\n [:print:]]/)
|
@@ -466,7 +464,7 @@ class String
|
|
466
464
|
|
467
465
|
# returns CRC32 checksum for the string object
|
468
466
|
def crc32
|
469
|
-
##
|
467
|
+
## pure ruby version. slower, but here for reference (found on some forum)
|
470
468
|
# r = 0xFFFFFFFF
|
471
469
|
# self.each_byte do |b|
|
472
470
|
# r ^= b
|
@@ -493,6 +491,34 @@ class String
|
|
493
491
|
ret
|
494
492
|
end
|
495
493
|
|
494
|
+
# Converts a '_' delimited string to CamelCase like 'foo_class' into
|
495
|
+
# 'FooClass'.
|
496
|
+
# See also: camelize_meth, decamelize
|
497
|
+
def camelize
|
498
|
+
self.gsub(/(^|_)([a-z])/) { $2.upcase }
|
499
|
+
end
|
500
|
+
|
501
|
+
# Converts a '_' delimited string to method style camelCase like 'foo_method'
|
502
|
+
# into 'fooMethod'.
|
503
|
+
# See also: camelize, decamelize
|
504
|
+
def camelize_meth
|
505
|
+
self.gsub(/_([a-z])/) { $1.upcase }
|
506
|
+
end
|
507
|
+
|
508
|
+
|
509
|
+
# Converts a CamelCase or camelCase string into '_' delimited form like
|
510
|
+
# 'FooBar' or 'fooBar' into 'foo_bar'.
|
511
|
+
#
|
512
|
+
# Note: This method only handles camel humps. Strings with consecutive
|
513
|
+
# uppercase chars like 'FooBAR' will be converted to 'foo_bar'
|
514
|
+
#
|
515
|
+
# See also: camelize, camelize_meth
|
516
|
+
def decamelize
|
517
|
+
self.gsub(/(^|[a-z])([A-Z])/) do
|
518
|
+
($1.empty?)? $2 : "#{$1}_#{$2}"
|
519
|
+
end.downcase
|
520
|
+
end
|
521
|
+
|
496
522
|
# convert a string to its idiomatic ruby class name
|
497
523
|
def class_name
|
498
524
|
r = ""
|
@@ -523,8 +549,14 @@ class String
|
|
523
549
|
end
|
524
550
|
end
|
525
551
|
|
552
|
+
# Return a self encapsulated in a StringIO object. This is handy.
|
553
|
+
def to_stringio
|
554
|
+
StringIO.new(self)
|
555
|
+
end
|
556
|
+
|
526
557
|
end # class String
|
527
558
|
|
559
|
+
|
528
560
|
class Symbol
|
529
561
|
# looks up this symbol as a constant defined in 'ns' (Object by default)
|
530
562
|
def const_lookup(ns=Object)
|
@@ -535,8 +567,11 @@ end
|
|
535
567
|
class Array
|
536
568
|
# randomizes the order of contents in the Array (self)
|
537
569
|
def randomize ; self.sort_by { rand } ; end
|
538
|
-
end
|
539
570
|
|
571
|
+
# Returns a randomly chosen element from self.
|
572
|
+
# Drew *is* sparta.
|
573
|
+
def rand_elem; self[rand(self.length)] ; end
|
574
|
+
end
|
540
575
|
|
541
576
|
class Float
|
542
577
|
def log2; Math.log(self)/Math.log(2); end
|
@@ -612,7 +647,7 @@ class Numeric
|
|
612
647
|
}.join
|
613
648
|
end
|
614
649
|
|
615
|
-
#
|
650
|
+
# TODO Fix Numeric.to_guid for new to_bytes/char etc.
|
616
651
|
# def to_guid(order=Rbkb::DEFAULT_BYTE_ORDER)
|
617
652
|
# raw = self.to_bytes(order, 16)
|
618
653
|
# a,b,c,d,*e = raw.unpack("VvvnC6").map{|x| x.to_hex}
|
data/lib/rbkb/http.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
# ???Why???? would anyone create their own HTTP implementation in ruby with
|
3
|
+
# so many options out there? Short answer: Net:HTTP and others just don't cut
|
4
|
+
# it in lots of edge cases. I needed something I could control completely.
|
5
|
+
|
6
|
+
module Rbkb
|
7
|
+
module Http
|
8
|
+
VERSION = "0.0.2"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
require "rbkb/http/common.rb"
|
13
|
+
require "rbkb/http/base.rb"
|
14
|
+
require "rbkb/http/request.rb"
|
15
|
+
require "rbkb/http/response.rb"
|
16
|
+
require "rbkb/http/headers.rb"
|
17
|
+
require "rbkb/http/body.rb"
|
18
|
+
require "rbkb/http/parameters.rb"
|
19
|
+
|
20
|
+
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module Rbkb::Http
|
2
|
+
|
3
|
+
# A base class containing some common features for Request and Response
|
4
|
+
# objects.
|
5
|
+
#
|
6
|
+
# Don't use this class directly, it's intended for being overridden
|
7
|
+
# from its derived classes or mixins.
|
8
|
+
class Base
|
9
|
+
include CommonInterface
|
10
|
+
|
11
|
+
def self.parse(*args)
|
12
|
+
new(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Initializes a new Base object
|
16
|
+
def initialize(*args)
|
17
|
+
_common_init(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
# This method parses just HTTP message body. Expects body to be split
|
21
|
+
# from the headers before-hand.
|
22
|
+
def capture_body(bstr)
|
23
|
+
self.body ||= default_body_obj
|
24
|
+
@body.capture(bstr)
|
25
|
+
end
|
26
|
+
|
27
|
+
# XXX stub
|
28
|
+
def first_entity
|
29
|
+
@first_entity
|
30
|
+
end
|
31
|
+
|
32
|
+
# XXX stub
|
33
|
+
def first_entity=(f)
|
34
|
+
@first_entity=(f)
|
35
|
+
end
|
36
|
+
|
37
|
+
# This method parses only HTTP response headers. Expects headers to be
|
38
|
+
# split from the body before-hand.
|
39
|
+
def capture_headers(hstr)
|
40
|
+
self.headers ||= default_headers_obj
|
41
|
+
|
42
|
+
if @body and not @body.capture_complete?
|
43
|
+
return
|
44
|
+
elsif @headers.capture_complete?
|
45
|
+
self.first_entity, @headers = default_headers_obj.capture_full_headers(hstr)
|
46
|
+
else
|
47
|
+
@headers.capture(hstr)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# This method returns the content length from Headers. This is
|
52
|
+
# mostly useful if you are using a BoundBody object for the body.
|
53
|
+
#
|
54
|
+
# Returns nil if no "Content-Length" is not found.
|
55
|
+
#
|
56
|
+
# The opts parameter :ignore_content_length affects this method and
|
57
|
+
# will cause it always to return nil. This is useful, for example,
|
58
|
+
# for the responses to the HTTP HEAD request method, which return
|
59
|
+
# a Content-Length without actual content.
|
60
|
+
#
|
61
|
+
def content_length(hdrs=@headers)
|
62
|
+
raise "headers is nil?" if not hdrs
|
63
|
+
if( (not @opts[:ignore_content_length]) and
|
64
|
+
hdrs.get_header_value("Content-Length").first =~ /^(\d+)$/ )
|
65
|
+
|
66
|
+
$1.to_i
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def attach_new_header(hdr_obj=nil)
|
71
|
+
self.headers = hdr_obj
|
72
|
+
return hdr_obj
|
73
|
+
end
|
74
|
+
|
75
|
+
def attach_new_body(body_obj=nil)
|
76
|
+
self.body = body_obj
|
77
|
+
return body_obj
|
78
|
+
end
|
79
|
+
|
80
|
+
# XXX doc override!
|
81
|
+
def default_headers_obj(*args)
|
82
|
+
Header.new(*args)
|
83
|
+
end
|
84
|
+
|
85
|
+
# XXX doc override!
|
86
|
+
def default_body_obj(*args)
|
87
|
+
Body.new(*args)
|
88
|
+
end
|
89
|
+
|
90
|
+
# This method will non-destructively reset the capture state on this
|
91
|
+
# object and all child entities. Note, however, If child entities are not
|
92
|
+
# defined, it may instantiate new ones.
|
93
|
+
# See also: capture_complete?, reset_capture!
|
94
|
+
def reset_capture
|
95
|
+
if @headers
|
96
|
+
@headers.reset_capture if not @headers.capture_complete?
|
97
|
+
else
|
98
|
+
attach_new_header()
|
99
|
+
end
|
100
|
+
|
101
|
+
if @body
|
102
|
+
@body.reset_capture if not @body.capture_complete?
|
103
|
+
else
|
104
|
+
attach_new_body()
|
105
|
+
end
|
106
|
+
@capture_state = nil
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
# This method will destructively reset the capture state on this object.
|
111
|
+
# It does so by initializing fresh child entities and discarding the old
|
112
|
+
# ones. See also: capture_complete?, reset_capture
|
113
|
+
def reset_capture!
|
114
|
+
attach_new_header()
|
115
|
+
attach_new_body()
|
116
|
+
@capture_state = nil
|
117
|
+
self
|
118
|
+
end
|
119
|
+
|
120
|
+
# Indicates whether this object is ready to capture fresh data, or is
|
121
|
+
# waiting for additional data or a reset from a previous incomplete or
|
122
|
+
# otherwise broken capture. See also: reset_capture, reset_capture!
|
123
|
+
def capture_complete?
|
124
|
+
if( (@headers and not @headers.capture_complete?) or
|
125
|
+
(@body and not @body.capture_complete?) )
|
126
|
+
return false
|
127
|
+
else
|
128
|
+
true
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
attr_reader :body, :headers
|
133
|
+
|
134
|
+
# This accessor will attempt to always do the "right thing" while
|
135
|
+
# setting this object's body entity.
|
136
|
+
#
|
137
|
+
# See also: default_body_obj
|
138
|
+
def body=(b)
|
139
|
+
if @body
|
140
|
+
@body.data = b
|
141
|
+
elsif b.kind_of? Body
|
142
|
+
@body = b.dup
|
143
|
+
@body.opts = b.opts
|
144
|
+
else
|
145
|
+
@body = default_body_obj(b)
|
146
|
+
end
|
147
|
+
@body.base = self
|
148
|
+
return @body
|
149
|
+
end
|
150
|
+
|
151
|
+
# This accessor will attempt to always do the "right thing" while
|
152
|
+
# setting this object's headers entity.
|
153
|
+
#
|
154
|
+
# See also: default_headers_obj
|
155
|
+
def headers=(h)
|
156
|
+
if @headers
|
157
|
+
@headers.data = h
|
158
|
+
elsif h.kind_of? Headers
|
159
|
+
@headers = h.dup
|
160
|
+
@headers.opts = h.opts
|
161
|
+
else
|
162
|
+
@headers = default_headers_obj(h)
|
163
|
+
end
|
164
|
+
@headers.base = self
|
165
|
+
return @body
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
end
|
172
|
+
|
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Rbkb::Http
|
4
|
+
class Body < String
|
5
|
+
include CommonInterface
|
6
|
+
|
7
|
+
def self.parse(str)
|
8
|
+
new().capture(str)
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :expect_length
|
12
|
+
|
13
|
+
def initialize(str=nil, opts=nil)
|
14
|
+
self.opts = opts
|
15
|
+
if Body === str
|
16
|
+
self.replace(str)
|
17
|
+
@opts = str.opts.merge(@opts)
|
18
|
+
elsif String === str
|
19
|
+
super(str)
|
20
|
+
else
|
21
|
+
super()
|
22
|
+
end
|
23
|
+
|
24
|
+
yield(self) if block_given?
|
25
|
+
end
|
26
|
+
|
27
|
+
# The capture method is used when parsing HTTP requests/responses.
|
28
|
+
# This can and probably should be overridden in derived classes.
|
29
|
+
def capture(str)
|
30
|
+
yield(str) if block_given?
|
31
|
+
self.data=(str)
|
32
|
+
end
|
33
|
+
|
34
|
+
# The to_raw method is used when writing HTTP requests/responses.
|
35
|
+
# This can and probably should be overridden in derived classes.
|
36
|
+
def to_raw
|
37
|
+
(block_given?) ? yield(self.data) : self.data
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_reader :base
|
41
|
+
|
42
|
+
def base=(b)
|
43
|
+
if b.nil? or b.is_a? Base
|
44
|
+
@base = b
|
45
|
+
else
|
46
|
+
raise "base must be a Response or Request object or nil"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def data
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
# Sets internal raw string data without any HTTP decoration.
|
55
|
+
def data=(str)
|
56
|
+
self.replace(str.to_s)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the content length from the HTTP base object if
|
60
|
+
# there is one and content-length is available.
|
61
|
+
def get_content_length
|
62
|
+
@base.content_length if @base
|
63
|
+
end
|
64
|
+
|
65
|
+
# This method will non-destructively reset the capture state on this object.
|
66
|
+
# It is non-destructive in that it will not affect existing captured data
|
67
|
+
# if present.
|
68
|
+
def reset_capture
|
69
|
+
@expect_length = nil
|
70
|
+
@base.reset_capture() if @base and @base.capture_complete?
|
71
|
+
end
|
72
|
+
|
73
|
+
# This method will destructively reset the capture state on this object.
|
74
|
+
# This method is destructive in that it will clear any previously captured
|
75
|
+
# data.
|
76
|
+
def reset_capture!
|
77
|
+
reset_capture()
|
78
|
+
self.data=""
|
79
|
+
end
|
80
|
+
|
81
|
+
def capture_complete?
|
82
|
+
not @expect_length
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
# BoundBody is designed for handling an HTTP body when using the usual
|
88
|
+
# "Content-Length: NNN" HTTP header.
|
89
|
+
class BoundBody < Body
|
90
|
+
|
91
|
+
# This method may throw :expect_length with one of the following values
|
92
|
+
# to indicate certain content-length conditions:
|
93
|
+
#
|
94
|
+
# > 0 : Got incomplete data in this capture. The object expects
|
95
|
+
# capture to be called again with more body data.
|
96
|
+
#
|
97
|
+
# < 0 : Got more data than expected, the caller should truncate and
|
98
|
+
# handle the extra data in some way. Note: Calling capture again
|
99
|
+
# on this instance will start a fresh body capture.
|
100
|
+
#
|
101
|
+
# Caller can also detect the above conditions by checking the expect_length
|
102
|
+
# attribute but should still be prepared handle the throw().
|
103
|
+
#
|
104
|
+
# 0/nil: Got exactly what was expected. Caller can proceed with fresh
|
105
|
+
# captures on this or other Body objects.
|
106
|
+
#
|
107
|
+
# See also reset_capture and reset_capture!
|
108
|
+
def capture(str)
|
109
|
+
raise "arg 0 must be a string" unless String === str
|
110
|
+
|
111
|
+
# Start fresh unless we're expecting more data
|
112
|
+
self.data="" unless @expect_length and @expect_length > 0
|
113
|
+
|
114
|
+
if not clen=get_content_length()
|
115
|
+
raise "content-length is unknown. aborting capture"
|
116
|
+
else
|
117
|
+
@expect_length = clen - (self.size + str.size)
|
118
|
+
self << str[0, clen - self.size]
|
119
|
+
if @expect_length > 0
|
120
|
+
throw(:expect_length, @expect_length)
|
121
|
+
elsif @expect_length < 0
|
122
|
+
throw(:expect_length, @expect_length)
|
123
|
+
else
|
124
|
+
reset_capture()
|
125
|
+
end
|
126
|
+
end
|
127
|
+
return self
|
128
|
+
end
|
129
|
+
|
130
|
+
def to_raw(*args)
|
131
|
+
if @base
|
132
|
+
@base.headers.set_header("Content-Length", self.size)
|
133
|
+
end
|
134
|
+
super(*args)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
# ChunkedBody is designed for handling an HTTP body when using a
|
140
|
+
# "Transfer-Encoding: chunked" HTTP header.
|
141
|
+
class ChunkedBody < Body
|
142
|
+
DEFAULT_CHUNK_SIZE = 2048
|
143
|
+
|
144
|
+
# Throws :expect_length with 'true' when given incomplete data and expects
|
145
|
+
# to be called again with more body data to parse.
|
146
|
+
#
|
147
|
+
# The caller can also detect this condition by checking the expect_length
|
148
|
+
# attribute but must still handle the throw().
|
149
|
+
#
|
150
|
+
# See also reset_capture and reset_capture!
|
151
|
+
def capture(str)
|
152
|
+
# chunked encoding is gross...
|
153
|
+
if @expect_length
|
154
|
+
sio = StringIO.new(@last_chunk.to_s + str)
|
155
|
+
else
|
156
|
+
sio = StringIO.new(str)
|
157
|
+
self.data=""
|
158
|
+
end
|
159
|
+
@last_chunk = nil
|
160
|
+
|
161
|
+
@expect_length = true
|
162
|
+
while not sio.eof?
|
163
|
+
unless m=/^([a-fA-F0-9]+)\s*(;[[:print:]\s]*)?\r?\n$/.match(line=sio.readline)
|
164
|
+
raise "invalid chunk at #{line.chomp.inspect}"
|
165
|
+
end
|
166
|
+
if (chunksz = m[1].hex) == 0
|
167
|
+
@expect_length = false
|
168
|
+
# XXX ignore Trailer headers
|
169
|
+
break
|
170
|
+
end
|
171
|
+
|
172
|
+
if ( (not sio.eof?) and
|
173
|
+
(chunk=sio.read(chunksz)) and
|
174
|
+
chunk.size == chunksz and
|
175
|
+
(not sio.eof?) and (extra = sio.readline) and
|
176
|
+
(not sio.eof?) and (extra << sio.readline)
|
177
|
+
)
|
178
|
+
if extra =~ /^\r?\n\r?\n$/
|
179
|
+
yield(chunk) if block_given?
|
180
|
+
self << chunk
|
181
|
+
else
|
182
|
+
raise "expected CRLF"
|
183
|
+
end
|
184
|
+
else
|
185
|
+
@last_chunk = line + chunk.to_s + extra.to_s
|
186
|
+
break
|
187
|
+
end
|
188
|
+
end
|
189
|
+
throw(:expect_length, @expect_length) if @expect_length
|
190
|
+
return self
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
def to_raw(csz=nil)
|
195
|
+
csz ||= (@opts[:output_chunk_size] || DEFAULT_CHUNK_SIZE)
|
196
|
+
unless csz.kind_of? Integer and csz > 0
|
197
|
+
raise "chunk size must be an integer >= 1"
|
198
|
+
end
|
199
|
+
|
200
|
+
out=[]
|
201
|
+
i=0
|
202
|
+
while i <= self.size
|
203
|
+
chunk = self[i, csz]
|
204
|
+
out << "#{chunk.size.to_s(16)}\r\n#{chunk}\r\n\r\n"
|
205
|
+
yield(self, out.last) if block_given?
|
206
|
+
i+=csz
|
207
|
+
end
|
208
|
+
out << "0\r\n"
|
209
|
+
yield(self, out.last) if block_given?
|
210
|
+
return out.join
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|