How to intercept and rewrite damaged JSON calls with Middleware - ruby-on-rails

I'm trying to find the best place to do this. My middleware looks like this :
class Fixer
def initialize app
#app = app
end
def call env
if env["HTTP_ORIGIN"] == "https://where_i_expect_it_to_come_from.com/"
env['rack.input'] = StringIO.new('{"yo":"momma"}') # <-- But this info is not actually written before the call is passed!
end
env['rack.input'].rewind
status, headers, response = #app.call env
return [status, headers, response]
end
end
Rails.application.config.middleware.insert_before ActionDispatch::ParamsParser, Fixer
It seems that even when I rewrite the call here, the info is not actually rewritten properly. Any ideas how I can have my content written before its bubbled up?

i think this problem is the same as here: https://stackoverflow.com/questions/22712663/getting-a-routing-error-when-my-routes-are-clearly-marked and here I have a third party service that I believe is sending me malformed JSON
from what i understand you want to fix some incoming request-data.
as a simplified example:
curl -X PUT -d "user[name]=something" http://localhost:3000/users/123
i change something to phoet in the fixer
class Fixer
def initialize(app)
#app = app
end
def call(env)
env['rack.input'] = StringIO.new("user[name]=phoet")
#app.call(env)
end
end
and add it to application.rb
config.middleware.insert_before "ActionDispatch::ParamsParser", "Fixer"
when i request my app now, i see the logs
[localhost] [fdff4eb0-8387-41] Started PUT "/users/123" for 127.0.0.1 at 2014-03-30 13:16:02 -0400
[localhost] [fdff4eb0-8387-41] Processing by UsersController#update as */*
[localhost] [fdff4eb0-8387-41] Parameters: {"user"=>{"name"=>"phoet"}, "id"=>"123"}

Related

Rack custom middleware giving error "ActionDispatch::Http::Parameters::ParseError"

I am writing a custom middleware for my rails app, to decrypt/encrypt parameters in a post request. I have configured the middleware in 'config/environments/staging.rb'
config.middleware.use CustomMiddleware
In order to read the request params i am reading the env obj like this
rack_input = env['rack.input'].read
I am able to access the parameter and manipulate as needed. But when i pass over on the env obj to the app object which is the receiver. I am getting
"ActionDispatch::Http::Parameters::ParseError"
Full error
Error occurred while parsing request parameters.
Contents:
F, [2017-11-15T02:13:50.230088 #24060] FATAL -- : [9aa25c7e-56e7-4894-ba30-3a01f60ae4fc]
F, [2017-11-15T02:13:50.230340 #24060] FATAL -- : [9aa25c7e-56e7-4894-ba30-3a01f60ae4fc] ActionDispatch::Http::Parameters::ParseError (no implicit conversion of nil into String):
F, [2017-11-15T02:13:50.230563 #24060] FATAL -- : [9aa25c7e-56e7-4894-ba30-3a01f60ae4fc]
F, [2017-11-15T02:13:50.230782 #24060] FATAL -- : [9aa25c7e-56e7-4894-ba30-3a01f60ae4fc] app/middleware/custom_middleware.rb:34:in `call'
My code is somewhat like this
class CustomMiddleware
include Encryption
def initialize(app)
#app = app
end
def set_payload_params(env)
rack_input = env['rack.input'].read
#args = JSON.parse(rack_input) rescue {}
end
def call(env)
request = Rack::Request.new(env)
set_payload_params(env)
payload = #args['payload']
decrypt_params
status, headers, response = #app.call(env)
encrypt_params
end
If i don't read the env object and hard code the parameters, there is no such parsing error. And my controller is responding with data w.r.t to hardcode parameters.
My Rails version is 5.1.4
My ruby version is 2.4.2
I have done a similar middleware in another rails app(4.2.8) which is working fine.
Any insight on this will be of much help. Thanks in advance.
This error occurs cause after params are empty after read in middleware (this is stream).
rack_input = env['rack.input'].read
After execute this code rack params are blank. Now you need to make
env['rack.input'].rewind
or
request.body.rewind
Now your request has params again and Rails can simply continue job.
Added a hack to set content-type as "" in custom middleware, which avoided parsing on the input before forwarding the input to next component in the stack.
class CustomMiddleware
include Encryption
def initialize(app)
#app = app end
def set_payload_params(env)
rack_input = env['rack.input'].read
#args = JSON.parse(rack_input) rescue {} end
def call(env)
env['CONTENT_TYPE'] = ''
request = Rack::Request.new(env)
set_payload_params(env)
payload = #args['payload']
decrypt_params
status, headers, response = #app.call(env)
encrypt_params
end

Force headers on all Rails 404/500 responses

I am trying to set the 'X-Frame-Options' header on all responses returned by my Rails application. It seems like this header is not set on 404 or 500 type responses. How can I configure Rails to always include this header?
It appears I somehow need to hook into Rails to ensure these headers are always set.
I am having success using the below middleware as my 'exceptions_app'.
class XSecurityHandler
def initialize(app)
#app = app
end
def call(env)
_status, headers, response = #app.call(env)
headers['X-Frame-Options'] = "SAMEORIGIN"
headers['X-Content-Type-Options'] = "nosniff"
[status(env), headers, response]
end
private
def status(env)
path = env["ORIGINAL_FULLPATH"]
if path == "/404"
404
elsif path == "/422"
422
else
500
end
end
end
Set default headers in config/application.rb:
config.action_dispatch.default_headers['X-Frame-Options'] = 'SAMEORIGIN'
but it wont work, if you have configured reverse proxy (like nginx) to serve static assets which does not exists (404), but i think you know about this :)

WEBrick alter HTTP response headers on a per-file extension basis

Is it possible to modify the WEBrick HTTP response headers globally for a specific file extension, for example to serve all files with svgz extension to include the HTTP Header "Content-Encoding: gzip" in the HTTP response? I can't seem to figure out how to do this.
lib/dps/compression.rb
module Dps
class Compression
def initialize(app)
#app = app
end
def call(env)
status, headers, response = #app.call(env)
if File.extname(env['REQUEST_URI']) == ".svgz" && status == 200
headers["Content-Encoding"] = "gzip"
else
nil
end
[status, headers, response]
end
end
end
config/application.rb
config.autoload_paths += Dir["#{config.root}/lib/**/"]
config.middleware.insert_before("ActionDispatch::Static", "Dps::Compression")

How can I return a 404 for a specific URL with Rack middleware

A site I manage is getting constant requests for a javascript file that no longer exists, from an older version of the site. These requests take up a lot of resources because they get routed through Rails every time to return a 404. I am thinking it would be much better to have Rack handle that specific URL and return 404 itself. Is that correct? If so, how would I set that up?
I have been checking out this blog post which I think is kinda the way to move forward (ie, inheritance from some existing Rack module):
http://icelab.com.au/articles/wrapping-rack-middleware-to-exclude-certain-urls-for-rails-streaming-responses/
So I ended up writing my own little bit of middleware:
module Rack
class NotFoundUrls
def initialize(app, exclude)
#app = app
#exclude = exclude
end
def call(env)
status, headers, response = #app.call(env)
req = Rack::Request.new(env)
return [status, headers, response] if !#exclude.include?(URI.unescape(req.fullpath))
content = 'Not Found'
[404, {'Content-Type' => 'text/html', 'Content-Length' => content.size.to_s}, [content]]
end
end
end
and then adding this to the config.ru file:
use Rack::NotFoundUrls, ['/javascripts/some.old.file.js']
It's the first time I've done this so let me know if there's any glaring mistakes...
The rack-contrib gem includes a Rack::NotFound middleware component (among many other useful elements) which should do the job:
https://github.com/rack/rack-contrib/

Trouble on responding to a HTTP Request when counting records

I am using Ruby on Rails 3 and I am trying to implement an API. I would like to solve some strange problems that I have on returning data after a web client HTTP GET Request. In few words, problems are in the response body returned values for which I get "too much" "" (see the examples belowe) and, sometime, in returning JSON data.
On the web service side in my Rack middleware I have:
class Testing
def initialize(app)
#app = app
end
def call(env)
accounts = Account.find([1,2,3])
resp_test = accounts.count
[200, {}, resp_test] # No [200, {'Content-Type' => 'application/json'}, resp_test]
end
end
On the client side if I see the response I have
# debug response.body
---
In this case the problem is the accounts.count that returns a value of "" in the response body. It is possible that accounts.count doesn't do what it should do.
I also encountered some problems when I didn't return JSON data. For example, debugging variables on the client side, sometime I got a body response value of "" if I didn't return JSON DATA like this:
# On the service side in the Rack middleware file
[200, {}, resp_test] # No[200, {'Content-Type' => 'application/json'}, resp_test.to_json]
The response are:
# Case don't returning JSON data
# debug response.body
---
# Case returning JSON data
# debug response.body
--- test_value
What is the problem? If it is accounts.count or Account.find([1,2,3]), how can I make that to work in order to return correct value?
first your middleware should be after ActiveRecord::ConnectionAdapters::ConnectionManagement when calling rake middleware
second try to return resp_test.to_s

Resources