kcar 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,55 @@
1
+ kcar is copyrighted free software by all contributors, see logs in
2
+ revision control for names and email addresses of all of them. You can
3
+ redistribute it and/or modify it under either the terms of the
4
+ {GPL2}[http://www.gnu.org/licenses/gpl-2.0.txt] (see link:COPYING) or
5
+ the conditions below:
6
+
7
+ 1. You may make and give away verbatim copies of the source form of the
8
+ software without restriction, provided that you duplicate all of the
9
+ original copyright notices and associated disclaimers.
10
+
11
+ 2. You may modify your copy of the software in any way, provided that
12
+ you do at least ONE of the following:
13
+
14
+ a) place your modifications in the Public Domain or otherwise make them
15
+ Freely Available, such as by posting said modifications to Usenet or an
16
+ equivalent medium, or by allowing the author to include your
17
+ modifications in the software.
18
+
19
+ b) use the modified software only within your corporation or
20
+ organization.
21
+
22
+ c) rename any non-standard executables so the names do not conflict with
23
+ standard executables, which must also be provided.
24
+
25
+ d) make other distribution arrangements with the author.
26
+
27
+ 3. You may distribute the software in object code or executable
28
+ form, provided that you do at least ONE of the following:
29
+
30
+ a) distribute the executables and library files of the software,
31
+ together with instructions (in the manual page or equivalent) on where
32
+ to get the original distribution.
33
+
34
+ b) accompany the distribution with the machine-readable source of the
35
+ software.
36
+
37
+ c) give non-standard executables non-standard names, with
38
+ instructions on where to get the original software distribution.
39
+
40
+ d) make other distribution arrangements with the author.
41
+
42
+ 4. You may modify and include the part of the software into any other
43
+ software (possibly commercial). But some files in the distribution
44
+ are not written by the author, so that they are not under this terms.
45
+
46
+ 5. The scripts and library files supplied as input to or produced as
47
+ output from the software do not automatically fall under the
48
+ copyright of the software, but belong to whomever generated them,
49
+ and may be sold commercially, and may be aggregated with this
50
+ software.
51
+
52
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
53
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
54
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
55
+ PURPOSE.
data/README ADDED
@@ -0,0 +1,86 @@
1
+ = kcar - bytestream to Rack response converter
2
+
3
+ kcar features an HTTP parser that will convert a bytestream into a
4
+ 3-element array suitable for use as a Rack response. It is IO interface
5
+ agnostic, so it may be used with HTTP streams over Unix domain sockets,
6
+ regular files, FIFOs, StringIOs as well as traditional TCP sockets.
7
+
8
+ A drop-in, Net::HTTP-compatible interface is planned.
9
+
10
+ == Features
11
+
12
+ * RFC2616-compliant Ragel+C parser adapted from Unicorn and Mongrel
13
+
14
+ * decodes chunked response bodies with an optional pass-through mode
15
+ (to avoid rechunking with Rack::Chunked)
16
+
17
+ * handles odd things like trailers and multiline headers
18
+
19
+ * streaming interface for response bodies allows for incremental
20
+ processing of arbitrarily large responses.
21
+
22
+ == Problems
23
+
24
+ * kcar is only lightly tested and is not yet aware of all quirks found in
25
+ all real (possibly broken) web servers.
26
+
27
+ == Install
28
+
29
+ If you're using a packaged Ruby distribution, make sure you have a C
30
+ compiler and the matching Ruby development libraries and headers.
31
+
32
+ If you use RubyGems:
33
+
34
+ gem install kcar
35
+
36
+ Otherwise grab the latest tarball from:
37
+
38
+ http://bogomips.org/kcar/files/
39
+
40
+ Unpack it, and run "ruby setup.rb"
41
+
42
+ == Usage:
43
+
44
+ While you can use the Kcar::Parser directly, you'll usually want the
45
+ higher-level interface of Kcar::Response:
46
+
47
+ require 'rack' # for Rack::Utils::HeaderHash, which is optional
48
+ require 'socket' # for TCPSocket
49
+ require 'kcar'
50
+ sock = TCPSocket.new('example.com', 80)
51
+ sock.write("GET / HTTP/1.0\r\n\r\n")
52
+
53
+ # instead of a Rack::Utils::HeaderHash object below, you can also
54
+ # pass a regular Hash or Array object.
55
+ response = Kcar::Response.new(sock, Rack::Utils::HeaderHash.new)
56
+ status, headers, body = response.rack
57
+
58
+ You can now do further processing on the status, headers, or iterate
59
+ through the body with body.each.
60
+
61
+ == Development
62
+
63
+ You can get the latest source via git from the following locations:
64
+
65
+ git://git.bogomips.org/kcar.git
66
+ git://repo.or.cz/kcar.git (mirror)
67
+
68
+ You may browse the code from the web and download the latest snapshot
69
+ tarballs here:
70
+
71
+ * http://git.bogomips.org/cgit/kcar.git (cgit)
72
+ * http://repo.or.cz/w/kcar.git (gitweb)
73
+
74
+ Inline patches (from "git format-patch") to the mailing list are
75
+ preferred because they allow code review and comments in the reply to
76
+ the patch.
77
+
78
+ We will adhere to mostly the same conventions for patch submissions as
79
+ git itself. See the Documentation/SubmittingPatches document
80
+ distributed with git on on patch submission guidelines to follow. Just
81
+ don't email the git mailing list or maintainer with kcar patches.
82
+
83
+ == Contact
84
+
85
+ All feedback (bug reports, user/development discussion, patches, pull
86
+ requests) go to the mailing list: mailto:kcar@librelist.com
data/Rakefile ADDED
@@ -0,0 +1,149 @@
1
+ desc "read news article from STDIN and post to rubyforge"
2
+ task :publish_news do
3
+ require 'rubyforge'
4
+ IO.select([STDIN], nil, nil, 1) or abort "E: news must be read from stdin"
5
+ msg = STDIN.readlines
6
+ subject = msg.shift
7
+ blank = msg.shift
8
+ blank == "\n" or abort "no newline after subject!"
9
+ subject.strip!
10
+ body = msg.join("").strip!
11
+
12
+ rf = RubyForge.new.configure
13
+ rf.login
14
+ rf.post_news('rainbows', subject, body)
15
+ end
16
+
17
+ def tags
18
+ timefmt = '%Y-%m-%dT%H:%M:%SZ'
19
+ @tags ||= `git tag -l`.split(/\n/).map do |tag|
20
+ next if tag == "v0.0.0"
21
+ if %r{\Av[\d\.]+\z} =~ tag
22
+ header, subject, body = `git cat-file tag #{tag}`.split(/\n\n/, 3)
23
+ header = header.split(/\n/)
24
+ tagger = header.grep(/\Atagger /).first
25
+ body ||= "initial"
26
+ {
27
+ :time => Time.at(tagger.split(/ /)[-2].to_i).utc.strftime(timefmt),
28
+ :tagger_name => %r{^tagger ([^<]+)}.match(tagger)[1],
29
+ :tagger_email => %r{<([^>]+)>}.match(tagger)[1],
30
+ :id => `git rev-parse refs/tags/#{tag}`.chomp!,
31
+ :tag => tag,
32
+ :subject => subject,
33
+ :body => body,
34
+ }
35
+ end
36
+ end.compact.sort { |a,b| b[:time] <=> a[:time] }
37
+ end
38
+
39
+ cgit_url = "http://git.bogomips.org/cgit/kcar.git"
40
+ git_url = ENV['GIT_URL'] || 'git://git.bogomips.org/kcar.git'
41
+
42
+ desc 'prints news as an Atom feed'
43
+ task :news_atom do
44
+ require 'nokogiri'
45
+ new_tags = tags[0,10]
46
+ puts(Nokogiri::XML::Builder.new do
47
+ feed :xmlns => "http://www.w3.org/2005/Atom" do
48
+ id! "http://kcar.rubyforge.org/NEWS.atom.xml"
49
+ title "Kcar news"
50
+ subtitle File.readlines("README").first
51
+ link! :rel => 'alternate', :type => 'text/html',
52
+ :href => 'http://bogomips.org/kcar/'
53
+ updated( (new_tags.first[:time] rescue nil) || Time.now )
54
+ new_tags.each do |tag|
55
+ entry do
56
+ title tag[:subject]
57
+ updated tag[:time]
58
+ published tag[:time]
59
+ author {
60
+ name tag[:tagger_name]
61
+ email tag[:tagger_email]
62
+ }
63
+ url = "#{cgit_url}/tag/?id=#{tag[:tag]}"
64
+ link! :rel => "alternate", :type => "text/html", :href =>url
65
+ id! url
66
+ content(:type => 'text') { tag[:body] }
67
+ end
68
+ end
69
+ end
70
+ end.to_xml)
71
+ end
72
+
73
+ desc 'prints RDoc-formatted news'
74
+ task :news_rdoc do
75
+ tags.each do |tag|
76
+ time = tag[:time].tr!('T', ' ').gsub!(/:\d\dZ/, ' UTC')
77
+ puts "=== #{tag[:tag].sub(/^v/, '')} / #{time}"
78
+ puts ""
79
+
80
+ body = tag[:body]
81
+ puts tag[:body].gsub(/^/sm, " ").gsub(/[ \t]+$/sm, "")
82
+ puts ""
83
+ end
84
+ end
85
+
86
+ desc "print release changelog for Rubyforge"
87
+ task :release_changes do
88
+ version = ENV['VERSION'] or abort "VERSION= needed"
89
+ version = "v#{version}"
90
+ vtags = tags.map { |tag| tag[:tag] =~ /\Av/ and tag[:tag] }.sort
91
+ prev = vtags[vtags.index(version) - 1]
92
+ system('git', 'diff', '--stat', prev, version) or abort $?
93
+ puts ""
94
+ system('git', 'log', "#{prev}..#{version}") or abort $?
95
+ end
96
+
97
+ desc "print release notes for Rubyforge"
98
+ task :release_notes do
99
+ require 'rubygems'
100
+
101
+ spec = Gem::Specification.load('kcar.gemspec')
102
+ puts spec.description.strip
103
+ puts ""
104
+ puts "* #{spec.homepage}"
105
+ puts "* #{spec.email}"
106
+ puts "* #{git_url}"
107
+
108
+ _, _, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3)
109
+ print "\nChanges:\n\n"
110
+ puts body
111
+ end
112
+
113
+ desc "post to RAA"
114
+ task :raa_update do
115
+ require 'rubygems'
116
+ require 'net/http'
117
+ require 'net/netrc'
118
+ rc = Net::Netrc.locate('kcar-raa') or abort "~/.netrc not found"
119
+ password = rc.password
120
+
121
+ s = Gem::Specification.load('kcar.gemspec')
122
+ desc = [ s.description.strip ]
123
+ desc << ""
124
+ desc << "* #{s.email}"
125
+ desc << "* #{git_url}"
126
+ desc << "* #{cgit_url}"
127
+ desc = desc.join("\n")
128
+ uri = URI.parse('http://raa.ruby-lang.org/regist.rhtml')
129
+ form = {
130
+ :name => s.name,
131
+ :short_description => s.summary,
132
+ :version => s.version.to_s,
133
+ :status => 'experimental',
134
+ :owner => s.authors.first,
135
+ :email => s.email,
136
+ :category_major => 'Library',
137
+ :category_minor => 'WWW',
138
+ :url => s.homepage,
139
+ :download => 'http://rubyforge.org/frs/?group_id=8977',
140
+ :license => "Ruby's",
141
+ :description_style => 'Plain',
142
+ :description => desc,
143
+ :pass => password,
144
+ :submit => 'Update',
145
+ }
146
+ res = Net::HTTP.post_form(uri, form)
147
+ p res
148
+ puts res.body
149
+ end
data/TODO ADDED
@@ -0,0 +1,6 @@
1
+ Patches to kcar@librelist.com (via git format-patch + git send-email) for
2
+ these would be greatly appreciated
3
+
4
+ * optional drop-in monkey patch for Net::HTTP
5
+ * optional EventMachine support
6
+ * optional Rev support
data/ext/kcar/c_util.h ADDED
@@ -0,0 +1,105 @@
1
+ /*
2
+ * Generic C functions and macros go here, there are no dependencies
3
+ * on Unicorn internal structures or the Ruby C API in here.
4
+ */
5
+
6
+ #ifndef UH_util_h
7
+ #define UH_util_h
8
+
9
+ #include <unistd.h>
10
+ #include <assert.h>
11
+
12
+ #define MIN(a,b) (a < b ? a : b)
13
+ #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
14
+
15
+ #ifndef SIZEOF_OFF_T
16
+ # define SIZEOF_OFF_T 4
17
+ # warning SIZEOF_OFF_T not defined, guessing 4. Did you run extconf.rb?
18
+ #endif
19
+
20
+ #if SIZEOF_OFF_T == 4
21
+ # define UH_OFF_T_MAX 0x7fffffff
22
+ #elif SIZEOF_OFF_T == 8
23
+ # if SIZEOF_LONG == 4
24
+ # define UH_OFF_T_MAX 0x7fffffffffffffffLL
25
+ # else
26
+ # define UH_OFF_T_MAX 0x7fffffffffffffff
27
+ # endif
28
+ #else
29
+ # error off_t size unknown for this platform!
30
+ #endif /* SIZEOF_OFF_T check */
31
+
32
+ /*
33
+ * ragel enforces fpc as a const, and merely casting can make picky
34
+ * compilers unhappy, so we have this little helper do our dirty work
35
+ */
36
+ static inline void *deconst(const void *in)
37
+ {
38
+ union { const void *in; void *out; } tmp;
39
+
40
+ tmp.in = in;
41
+
42
+ return tmp.out;
43
+ }
44
+
45
+ static int hexchar2int(int xdigit)
46
+ {
47
+ if (xdigit >= 'A' && xdigit <= 'F')
48
+ return xdigit - 'A' + 10;
49
+ if (xdigit >= 'a' && xdigit <= 'f')
50
+ return xdigit - 'a' + 10;
51
+
52
+ /* Ragel already does runtime range checking for us in Unicorn: */
53
+ assert(xdigit >= '0' && xdigit <= '9' && "invalid digit character");
54
+
55
+ return xdigit - '0';
56
+ }
57
+
58
+ /*
59
+ * multiplies +i+ by +base+ and increments the result by the parsed
60
+ * integer value of +xdigit+. +xdigit+ is a character byte
61
+ * representing a number the range of 0..(base-1)
62
+ * returns the new value of +i+ on success
63
+ * returns -1 on errors (including overflow)
64
+ */
65
+ static off_t step_incr(off_t i, int xdigit, const int base)
66
+ {
67
+ static const off_t max = UH_OFF_T_MAX;
68
+ const off_t next_max = (max - (max % base)) / base;
69
+ off_t offset = hexchar2int(xdigit);
70
+
71
+ if (offset > (base - 1))
72
+ return -1;
73
+ if (i > next_max)
74
+ return -1;
75
+ i *= base;
76
+
77
+ if ((offset > (base - 1)) || ((max - i) < offset))
78
+ return -1;
79
+
80
+ return i + offset;
81
+ }
82
+
83
+ /*
84
+ * parses a non-negative length according to base-10 and
85
+ * returns it as an off_t value. Returns -1 on errors
86
+ * (including overflow).
87
+ */
88
+ static off_t parse_length(const char *value, size_t length)
89
+ {
90
+ off_t rv;
91
+
92
+ for (rv = 0; length-- && rv >= 0; ++value) {
93
+ if (*value >= '0' && *value <= '9')
94
+ rv = step_incr(rv, *value, 10);
95
+ else
96
+ return -1;
97
+ }
98
+
99
+ return rv;
100
+ }
101
+
102
+ #define CONST_MEM_EQ(const_p, buf, len) \
103
+ ((sizeof(const_p) - 1) == len && !memcmp(const_p, buf, sizeof(const_p) - 1))
104
+
105
+ #endif /* UH_util_h */
@@ -0,0 +1,82 @@
1
+ #ifndef ext_help_h
2
+ #define ext_help_h
3
+
4
+ #ifndef RSTRING_PTR
5
+ #define RSTRING_PTR(s) (RSTRING(s)->ptr)
6
+ #endif /* !defined(RSTRING_PTR) */
7
+ #ifndef RSTRING_LEN
8
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
9
+ #endif /* !defined(RSTRING_LEN) */
10
+
11
+ #ifndef RUBINIUS
12
+ # define rb_str_update(x) do {} while (0)
13
+ # define rb_str_flush(x) do {} while (0)
14
+ #endif /* !RUBINIUS */
15
+
16
+ #ifndef HAVE_RB_STR_SET_LEN
17
+ # ifdef RUBINIUS
18
+ # define rb_str_set_len(str,len) rb_str_resize(str,len)
19
+ # else /* 1.8.6 optimized version */
20
+ /* this is taken from Ruby 1.8.7, 1.8.6 may not have it */
21
+ static void rb_18_str_set_len(VALUE str, long len)
22
+ {
23
+ RSTRING(str)->len = len;
24
+ RSTRING(str)->ptr[len] = '\0';
25
+ rb_str_flush(str);
26
+ }
27
+ # define rb_str_set_len(str,len) rb_18_str_set_len(str,len)
28
+ # endif /* ! RUBINIUS */
29
+ #endif /* !defined(HAVE_RB_STR_SET_LEN) */
30
+
31
+ /* not all Ruby implementations support frozen objects (Rubinius does not) */
32
+ #if defined(OBJ_FROZEN)
33
+ # define assert_frozen(f) assert(OBJ_FROZEN(f) && "unfrozen object")
34
+ #else
35
+ # define assert_frozen(f) do {} while (0)
36
+ #endif /* !defined(OBJ_FROZEN) */
37
+
38
+ #if !defined(OFFT2NUM)
39
+ # if SIZEOF_OFF_T == SIZEOF_LONG
40
+ # define OFFT2NUM(n) LONG2NUM(n)
41
+ # else
42
+ # define OFFT2NUM(n) LL2NUM(n)
43
+ # endif
44
+ #endif /* ! defined(OFFT2NUM) */
45
+
46
+ #ifndef HAVE_RB_STR_MODIFY
47
+ # define rb_str_modify(x) do {} while (0)
48
+ #endif /* ! defined(HAVE_RB_STR_MODIFY) */
49
+
50
+ static inline int str_cstr_eq(VALUE val, const char *ptr, long len)
51
+ {
52
+ return (RSTRING_LEN(val) == len && !memcmp(ptr, RSTRING_PTR(val), len));
53
+ }
54
+
55
+ #define STR_CSTR_EQ(val, const_str) \
56
+ str_cstr_eq(val, const_str, sizeof(const_str) - 1)
57
+
58
+ static int cstr_case_eq(const char *a, long alen, const char *b, long blen)
59
+ {
60
+ if (alen == blen) {
61
+ for (; blen--; ++a, ++b) {
62
+ if ((*a == *b) || ((*a >= 'A' && *a <= 'Z') && (*a | 0x20) == *b))
63
+ continue;
64
+ return 0;
65
+ }
66
+ return 1;
67
+ }
68
+ return 0;
69
+ }
70
+
71
+ /* strcasecmp isn't locale independent */
72
+ static int str_cstr_case_eq(VALUE val, const char *ptr, long len)
73
+ {
74
+ return cstr_case_eq(RSTRING_PTR(val), RSTRING_LEN(val), ptr, len);
75
+ }
76
+
77
+ #define STR_CSTR_CASE_EQ(val, const_str) \
78
+ str_cstr_case_eq(val, const_str, sizeof(const_str) - 1)
79
+ #define CSTR_CASE_EQ(ptr, len, const_str) \
80
+ cstr_case_eq(ptr, len, const_str, sizeof(const_str) - 1)
81
+
82
+ #endif /* ext_help_h */