Coder Social home page Coder Social logo

david's Introduction

David

Gem Version Build Status Coverage Status Code Climate Docker Hub Build Status

David is a CoAP server with Rack interface to bring the illustrious family of Rack compatible web frameworks into the Internet of Things. It is tested with MRI >= 2.3, and JRuby >= 9.1. David version ~> 0.5.0 aims for Rack 2 (and Rails 5). Compatibility to Rails 4 is available in version ~> 0.4.5.

Quick Start

Just include David in your Gemfile!

gem 'david'

It will hook into Rack and make itself the default handler, so running rails s starts David. If you want to start WEBrick for example, you can do so by executing rails s webrick.

For now, you have to remove the web-console gem from the Gemfile (which is HTTP specific anyway) if you use Rails/David in CoAP only mode. You probably also want to disable CSRF protection by removing the protect_from_forgery line from app/controllers/application_controller.rb (or use :null_session if you know what you are doing).

The coap-rails-dummy repository documents changes to a newly generated Ruby on Rails application for a quick start.

After the server is started, the Rails application is available at coap://[::1]:3000/ by default. (Although you have to set a route for / in config/routes.rb, of course.)

Copper is a CoAP client for Firefox and can be used for development. The Ruby coap gem is used by David for example for message parsing and also includes a command line utility (named coap) that can also be used for development.

As CoAP is a protocol for constrained environments and machine to machine communications, returning HTML from your controllers will not be of much use. JSON for example is more suitable in that context. The Accept header is set to "application/json" by default, so that Rails responds with the JSON resource representation. David works well with the default ways to handle JSON responses from controllers such as render json:. You can also utilize Jbuilder templates for easy generation of more complex JSON structures.

CBOR can be used to compress your JSON. Automatic transcoding between JSON and CBOR is activated by setting the Rack environment option CBOR or config.coap.cbor in your Rails application config to true.

Tested Rack Frameworks

By providing a Rack interface, David does not only work with Rails but also with the following Rack compatible web frameworks.

Configuration

The following table lists available configuration options for the CoAP server. Rack keys can be specified with the -O option of rackup. The listed Rails keys can be accessed for example from the config/application.rb file of your Rails application.

Rack key Rails key Default Semantics
Block coap.block true Blockwise transfers
CBOR coap.cbor false JSON/CBOR transcoding
DefaultFormat coap.default_format Default Content-Type
Host ::1 / :: Server listening host
Log info Log level (none or debug)
MinimalMapping false Minimal HTTP status codes mapping
Multicast coap.multicast true Multicast support
MulticastGroups coap.multicast_groups ff02::fd, ff05::fd Multicast group configuration
Observe coap.observe true Observe support
coap.only true Removes (HTTP) middleware
Port 5683 Server listening port
coap.resource_discovery true Provision of .well-known/core

The server can be started with debug log level for example with the following command provided that a rackup config file (config.ru) exists like in a Rails application.

rackup -O Log=debug

In a Rails application, CBOR transcoding is activated for any controller and action by inserting the third line of the following code into config/application.rb.

module Example
  class Application < Rails::Application
    config.coap.cbor = true
  end
end

In Copper for example the default block size for Blockwise Transfers is set to 64 bytes. That's even small for most exception messages. It is recommended to set the block size to the maximum (1024B) during development.

Discovery

The CoAP Discovery will be activated by default. A .well-known/core resource automatically returns the resources you defined in Rails. You can annotate this resources with attributes like an interface description (if) or the content type (ct). (See RFC6690 or the code for further documentation.)

class ThingsController < ApplicationController
  discoverable \
    default: { if: 'urn:things', ct: 'application/cbor' },
    index:   { if: 'urn:index' }

  def show
    render json: Thing.find(params[:id])
  end

  def index
    render json: Thing.all
  end
end

Rack environment

David sets the following server (and protocol) specific Rack environment entries that can be read from your Rack application if necessary.

Key Value class Semantics
coap.version Integer Protocol version of CoAP request
coap.multicast Boolean Marks whether request was received via multicast
coap.dtls String DTLS mode (as defined in section 9 of RFC7252)
coap.dtls.id String DTLS identity
coap.cbor Object Ruby object deserialized from CBOR

Benchmarks

David handles about 12,500 requests per second in MRI and 14,000 in JRuby (tested in MRI 2.3.0 and JRuby 1.7.19 with up to 10,000 concurrent clients on a single core of a Core i7-3520M CPU running Linux 3.18.6).

Caveats

  • Incoming block-wise transfer is not supported.
  • Automatically generating .well-known/core is only supported in Rails.

Copyright

The code is published under the MIT license (see the LICENSE file).

Authors

david's People

Contributors

felix-letkemann avatar nmeum avatar nning avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

david's Issues

David eating parameter when using cbor

If I transmit the following JSON over CoAP, everything works fine:
[52403729, 13437171, 123456789, -1171, 3794, 4, 21212121, 1000]

If I transmit the same encoded as CBOR, the "params" variable looks like
[0, 13437171, 123456789, -1171, 3794, 4, 21212121, 1000]

The first parameter seems to be lost. Switching back to non-cbor solves the problem - but I want to use cbor. The strange thing is, that all the other parameters are transmitted correctly. The problem only affects the first one. First I thought that the problem might be my cbor encoder. So I had a look into the request object. I found the following lines:

@params={"X?"=>{"52195380, 13498559, 123456789, -1171, 3794, 4, 21212121, 1000"=>nil}}>

So the correct value is transmitted - but lost somewhere. The correct value makes it into the request object but not to the params variable.

Freezing with #NoMethodError using Rails 5.1.2 and coap.only = false

I am trying to use david in my Rails 5.1.2 application alongside a normal http output (user frontend, etc) to get this connected to some iot devices.
Unfortunately, the application can not be used with http anymore after adding the david gem. First, I realized that http is simply disabled by the gem but I found the option coap.only = false.
Now, instead of immediately not answering, the framework freezes with the error message:

2017-07-03 15:21:12 +0200: Rack app error handling request { GET / }
#<NoMethodError: undefined method 'error' for nil:NilClass>
/Users/felixletkemann/.rvm/gems/ruby-2.4.0/gems/david-0.5.0/lib/david/show_exceptions.rb:31:in 'render_exception'
/Users/felixletkemann/.rvm/gems/ruby-2.4.0/gems/david-0.5.0/lib/david/show_exceptions.rb:20:in 'rescue in _call'
/Users/felixletkemann/.rvm/gems/ruby-2.4.0/gems/david-0.5.0/lib/david/show_exceptions.rb:12:in '_call'
/Users/felixletkemann/.rvm/gems/ruby-2.4.0/gems/david-0.5.0/lib/david/show_exceptions.rb:8:in 'call'
/Users/felixletkemann/.rvm/gems/ruby-2.4.0/gems/railties-5.1.2/lib/rails/rack/logger.rb:36:in 'call_app'
/Users/felixletkemann/.rvm/gems/ruby-2.4.0/gems/railties-5.1.2/lib/rails/rack/logger.rb:24:in 'block in call'
/Users/felixletkemann/.rvm/gems/ruby-2.4.0/gems/activesupport-5.1.2/lib/active_support/tagged_logging.rb:69:in 'block in tagged'
/Users/felixletkemann/.rvm/gems/ruby-2.4.0/gems/activesupport-5.1.2/lib/active_support/tagged_logging.rb:26:in 'tagged'
/Users/felixletkemann/.rvm/gems/ruby-2.4.0/gems/activesupport-5.1.2/lib/active_support/tagged_logging.rb:69:in 'tagged'
/Users/felixletkemann/.rvm/gems/ruby-2.4.0/gems/railties-5.1.2/lib/rails/rack/logger.rb:24:in 'call'

My current configuration (application.rb) is:

class Application < Rails::Application
  config.load_defaults 5.1
  config.coap.only = false
  config.coap.observe = false
  config.coap.log = :debug    
end

When the framework is frozen, I can stop it pressing Ctrl+C twice and I will get get the error message mentioned above. If I don't set the log level to :debug, I don't get anything.
Reaching the CoAP routes using Firefox and Copper works just fine. So, I can use david for the intended purpose but it breaks the rest of the framework.

Yours Sincerely,
Felix

Add support for incoming block-wise transfers

When I do a request to my rails application using postman, I get a hash in the parameters variable, looking like:
=> <ActionController::Parameters {"book"=>{"author"=>"ernest hemingway", "isbn"=>"1234567"}, "format"=>"json", "controller"=>"api/v1/book", "action"=>"create"} permitted: false>

When I do the same request over CoAP using Copper in Firefox, I get:

=> <ActionController::Parameters {"{ book: { author: \"ernest hemingway\", isbn: \"1234567"=>nil, "format"=>"json", "controller"=>"api/v1/book", "action"=>"create"} permitted: false>

Something must be doing strange things with the params variable.

.well-known/core not available using sinatra

While the readme of David lists sinatra as one of the frameworks which are compatible with david, I could not get david to work with sinatra so far.
Adding the david gem (together with cbor) will make rackup talk over coap just as desired. Unfortunately, there is no .well-known/core route to tell the CoAP client what is available.
Also, I can not use the discoverable option in my controller, since the "discoverable" option is depending on the railties.
In order to be compatible with all the other frameworks, there should be a way to make the .well-known/core route available.
Do you have any working example with sinatra and david?

how to do rspec testing for /.well-known/core

I naturally want to do rspec testing of my CoAP application.
It also speaks HTTP(S), although I imagine that production that it will be two completely different deploys.

I have changed things so that david is not automatically chosen for "rails server", so that I can develop in both worlds the same time. So one has to do something like: "bin/rails server david -b ::2 -p 5684"

rspec requests skip the framework layer and goes directly to routes. I can test my controllers via config/routes.rb just fine. But I can't test the resource discovery process, doing a GET to /.well-known/core?rt=est.ace to get the location of the resource and then using it.

I propose that the /.well-known/core processing no longer be special cased in lib/david/resource_discovery.rb's call, but instead be referenced in the applications' routes.rb.

The rails5 way might be to use a concern, I see that this how active-scaffold has gone, but the old rails4 "as_routes" may work as well for this.

passing options to david

according to lib/rack/handler/david.rb, David accepts a variety of options, like --Log.
But, when invoked from under Thunar/rails server, these options do not get passed through.
Going through the code, it appears that we simply aren't integrated correctly there, and it's not clear to me how to fix that. Did it ever work? Is it supposed to?

[Potential] Security Vulnerabilities within ruby-coap/david

Hello developers of ruby-coap/david,

My name is Bruno, and I'm an MSc. student in Brazil within the Institute of Computing from the University of Campinas.
As part of my research on the application of fuzzing techniques for robustness and security black-box testing of CoAP implementations, I've tested your library. The sample used in my research was compiled from distribution/commit nning/coap@86c8419. The application used to test it was bin/david from b9413ce @ 2018-03-04.

I'm contacting you because the application mentioned above was one of the samples for which our tool was able to detect robustness and/or security issues. In a broad sense, every failure we found can actually be classified as a security vulnerability, because they impact availability --- the application either aborts or needs forceful restart in order to restore servicing CoAP requests. However, we didn't go as far as performing a thorough root-cause analysis for those failures, since it would be unfeasible for us (more than 100 failures were detected across 25 samples, each one using a different CoAP library, spanning 8 programming languages) and thus out-of-scope of this particular research.

We think that one of our main contributions is the opportunity to make a real-world impact on IoT security by reporting those failures to CoAP libraries' maintainers, with a comprehensible and easy way to replicate them so developers can further investigate and fix those failures. So, in order to follow up with a responsible disclosure process, we ask for a proper e-mail address (or any other form of contact) so we can send you:

  • A script to reproduce the failures;
  • A pcap file used by the script, containing the packets causing the failures;
  • A logfile with the stacktraces we got for each reported failure.

We expect a reply anytime soon.
Please let us know if which form of contact should we use --- or if it's ok to use this channel.

Thanks & Regards,
Bruno Melo.

David ignores config.coap.only in Rails 5.2

I am using david alongside puma to provide a CoAP + HTTP API with Rails 5.2.

While running puma, I noticed that the coap.only option inside my application.rb is ignored on server startup and important middleware classes like Rack::MethodOverride and ActionDispatch::ShowExceptions get removed. That is because in david's default config coap.only is set to true and the options set in application.rb are not yet evaluated at the time the HTTP middleware gets removed.

For everyone who experiences the same issue, I solved this by patching david's default config by placing

require 'david/railties/config'
David::Railties::Config.class_eval do
  config.coap.only = false
end

before the Bundler.require(*Rails.groups) line in my application.rb.

Cheers,
Alex

["Invalid query parameters: invalid byte sequence in UTF-8"] when sending CBOR request to sinatra app with david

When trying to use CBOR in my sinatra application, I get rack errors. It looks like the cbor part does not work for some reason.

[2017-09-13 12:56:51] DEBUG  #<struct CoRE::CoAP::Message ver=1, tt=:non, mcode=:post, mid=2385, options={:max_age=>60, :token=>0, :uri_path=>["th"], :content_format=>13362}, payload="\x82\x19\ng\x19\x10\x86">
#<Rack::BodyProxy:0x00000002d59b78 @body=#<Rack::Lint:0x00000002d84b70 @app=#<Rack::TempfileReaper:0x000000026c39a8 @app=#<Rack::Logger:0x000000027af268 @app=CoapServer, @level=1>>, @content_length="56", @body=#<Rack::BodyProxy:0x00000002d74310 @body=["Invalid query parameters: invalid byte sequence in UTF-8"], @block=#<Proc:0x00000002d742c0@/home/deploy/itsmybike-coap-adapter/shared/bundle/ruby/2.4.0/gems/rack-2.0.3/lib/rack/tempfile_reaper.rb:16>, @closed=false>, @head_request=false>, @block=#<Proc:0x00000002d59b28@/home/deploy/itsmybike-coap-adapter/shared/bundle/ruby/2.4.0/gems/rack-2.0.3/lib/rack/common_logger.rb:35>, @closed=false>
80.187.116.85 - - [13/Sep/2017:12:56:51 +0200] "POST /th " 400 56 0.0055
[2017-09-13 12:56:51] INFO  -> [80.187.116.85]:20143: non 4.00 / (block 0)
[2017-09-13 12:56:51] DEBUG  #<struct CoRE::CoAP::Message ver=1, tt=:non, mcode=[4, 0], mid=2385, options={:token=>0, :content_format=>nil, :etag=>nil}, payload="Invalid query parameters: invalid byte sequence in UTF-8">

I already tried to enable the cbor-flag that is false by default by starting the using the following command:

bundle exec rackup -p 5683 -o 0.0.0.0 -O CBOR=true -O Log=debug

I also tried to fix this by adding the rack utf-8 sanitizer gem to my sinatra project. Maybe this bug is caused by a rack problem and not by a david problem. As long as I don't know, I would assume this to be a problem specific to david.

Starting the HelloWorld example crashes setsockopt in multicast

When I try to run the HelloWorld example as run Rack::HelloWorld using rackup config.ru, I get the following error:

[2015-02-08 09:48:55] INFO  David 0.4.0 on ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-darwin13.0]
[2015-02-08 09:48:55] INFO  Starting on [::1]:9292
[2015-02-08 09:48:55] ERROR  Actor crashed!
Errno::EINVAL: Invalid argument - setsockopt(2)
        /Users/matus/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/david-0.4.0/lib/david/server/multicast.rb:44:in `setsockopt'
        /Users/matus/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/david-0.4.0/lib/david/server/multicast.rb:44:in `multicast_listen_ipv6'
        /Users/matus/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/david-0.4.0/lib/david/server/multicast.rb:11:in `block in multicast_initialize!'
        /Users/matus/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/david-0.4.0/lib/david/server/multicast.rb:11:in `each'
        /Users/matus/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/david-0.4.0/lib/david/server/multicast.rb:11:in `multicast_initialize!'
        /Users/matus/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/david-0.4.0/lib/david/server.rb:32:in `initialize'
        /Users/matus/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/celluloid-0.16.0/lib/celluloid/calls.rb:26:in `public_send'
        /Users/matus/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/celluloid-0.16.0/lib/celluloid/calls.rb:26:in `dispatch'
        /Users/matus/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/celluloid-0.16.0/lib/celluloid/calls.rb:63:in `dispatch'
        /Users/matus/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/celluloid-0.16.0/lib/celluloid/cell.rb:60:in `block in invoke'
        /Users/matus/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/celluloid-0.16.0/lib/celluloid/cell.rb:71:in `block in task'
        /Users/matus/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/celluloid-0.16.0/lib/celluloid/actor.rb:357:in `block in task'
        /Users/matus/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/celluloid-0.16.0/lib/celluloid/tasks.rb:57:in `block in initialize'
        /Users/matus/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/celluloid-0.16.0/lib/celluloid/tasks/task_fiber.rb:15:in `block in create'
[2015-02-08 09:48:55] ERROR  Actor crashed!

The error is repeated several times.

Am I doing something wrong?

I am on Mac OS X and use david 0.4.0.

'No such middleware to insert before'...

I've created a new empty rails project, added david to the Gemfile and installed it, but when I try to start the project (rails s) I get the following error message:

/var/lib/gems/1.9.1/gems/actionpack-4.2.4/lib/action_dispatch/middleware/stack.rb:125:in 'asset_index': No such middleware to insert before: ActionDispatch::DebugExceptions (RuntimeError)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.