h2o / picohttpparser

tiny HTTP parser written in C (used in HTTP::Parser::XS et al.)
1.85k stars 249 forks source link

Not able to properly parse using ruby FFI #43

Open HoneyryderChuck opened 6 years ago

HoneyryderChuck commented 6 years ago

Hi, I'm trying to create an FFI binding for ruby. Unfortunately, haven't been able to progress in the most basic example, as I can't get the verb and path strings back. Since I'm a bit out of ideas on how to further debug it, came asking for advice. I've contained it in a small-purpose script:

# tested with ruby 2.5 and 24, ruby-ffi 1.9.25
require 'ffi'

module Ext
  extend FFI::Library
  ffi_lib './ext/x86_64-darwin/libpico-http-parser-ext.bundle'
  attach_function :phr_parse_request, [:pointer, :size_t, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :size_t], :int
end

REQUEST = +"GET /test?ok=1 HTTP/1.1\r\nUser-Agent: curl/7.18.0\r\nHost: 0.0.0.0:5000\r\nAccept: */*\r\nContent-Length: 5\r\n\r\nWorld".b

verb = FFI::MemoryPointer.new(:pointer)
verb_len = FFI::MemoryPointer.new(:size_t)
path = FFI::MemoryPointer.new(:pointer)
path_len = FFI::MemoryPointer.new(:size_t)
minor_version = FFI::MemoryPointer.new(:int)
header_reader = FFI::MemoryPointer.new(:pointer)
header_reader_len = FFI::MemoryPointer.new(:int)
header_reader_len.write_int(128)

res = Ext.phr_parse_request(REQUEST, REQUEST.bytesize, verb, verb_len, path, path_len, minor_version, header_reader, header_reader_len, 0)

puts "bytes parsed: #{res}"
puts "method: #{verb.read_string(verb_len.read_int).inspect}"
puts "path: #{path.read_string(path_len.read_int).inspect}"
puts "version: HTTP/1.#{minor_version.read_int.inspect}"

# bytes parsed: 104
# method: "0d\x98"
# in `get_bytes': Memory access offset=0 size=10 is out of bounds (IndexError)

@kazuho did you have some success using ffi in any other language? I've seen your perl parser and also @kazeburo 's ruby c-extension binding, but sadly neither could help get to the bottom of this. Some ruby-FFI-specific issue?

larskanis commented 5 years ago

You need to allocate some memory for data not just pointers, like so:

verb = FFI::MemoryPointer.from_string("GET")
verb_len = FFI::MemoryPointer.new(:size_t)
verb_len.write(:size_t, verb.size-1)
header_reader = FFI::MemoryPointer.new(128)
header_reader_len = FFI::MemoryPointer.new(:int)
header_reader_len.write_int(128)