kcar 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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 */