Issue while processing HEAD and GET request in rails 3 - ruby-on-rails

Currently we are facing issue for processing the HEAD and GET request. Let me explain the detailed scenario
We have integrated inbound and outbound SMS facility in our application.
But from last 2-3 months we are getting 2-3 times GET request from the SMS service provider and it is affecting on our system.
After long discussion with SMS service provider, they are saying "Both Head and Get requests are handled similarly from your end"
I also referred this link. You can find respective logs at this link
So can any one suggest how to resolve this issue.
EDIT
After research we found that we are getting all the parameters in both HEAD and GET request because of this server is processing it.

I think the problem might be the ActionDispatch::Head middleware.
Part of that is following code:
def call(env)
if env["REQUEST_METHOD"] == "HEAD"
env["REQUEST_METHOD"] = "GET"
env["rack.methodoverride.original_method"] = "HEAD"
status, headers, _ = #app.call(env)
[status, headers, []]
else
#app.call(env)
end
end
So essentially the middleware changes the request-method before the router even gets the request.
If you want your router to handle the difference between HEAD and GET requests, you can remove the middleware by adding
config.middleware.delete "ActionDispatch::Head"
to your application.rb
Otherwise you should be able to access that variable in your controller like this:
if request.env["rack.methodoverride.original_method"]=='HEAD'
#do head processing here
head :ok, :additional_header => 'value'
else
#do get processing here
end
If you worry about performance, I suggest writing your own middleware to handle these requests. Railscasts has a few good tutorials on this.
Also note, that other middlewares, like Rack::Cache might interfere in this process as well. So you should insert your middleware on top:
config.middleware.insert_before 0, "YourMiddleware"

I would just implement my own :head response as the example in https://stackoverflow.com/a/10453525/5446866
if request.head?
head :ok # or whatever
else
# your other complex stuff here
end
You could also add a route specific to head request. for example
match '/validate_messages/sms' => 'validate_messages#noop', via: :head
and then in your controller
def noop
head :ok
end
basically, you have to implement what you want to do with HEAD request otherwise it will go ahead and use your GET handler
Hope that helps

Related

Rails not redirecting on PUT request, sending 406 back instead

I'm working on a bell schedule creator for a school project I'm working on. The bell schedule creation on the client is handled with a React component, and when it comes time to update, the component calls this fetch request:
fetch(`/bell-schedules/${this.state.updatingId}`, {
method: 'PUT',
mode: 'cors',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'Accept': 'text/html',
},
redirect: "follow",
referrerPolicy: 'no-referrer',
body: JSON.stringify({ name: this.state.name, schedule: json})})
On the server, these requests are handled by the BellSchedulesController, and the method for handling this type of route is coded as so:
# PUT /bell-schedules/:id
def update
set_bell_schedule
respond_to do |format|
if #schedule.update(name: params[:name], schedule: params[:schedule])
redirect_to action: 'index'
else
head 400
end
end
end
Index is a simple method that gets all the bell schedules and renders them in a table; that method works fine, other routes point to it and it renders normally. Unfortunately, when I send this PUT request, rather than getting a redirect to that page showing the table of bell schedules, I instead get back a 406 Not Acceptable. I also know that the bell schedule is actually updating, I've inspected the entry in the rails console.
How do I handle redirects in PUT requests? I'm also having an issue similar to this in a POST request, but I've found a hack around it there; that hack won't work in this one.
Googling your problem it seems to come from the respond_to ... |format| block, try getting rid of it and see if it helps. You don't seem to be using it correctly, you are not doing anything with the format and maybe that's why it's blowing up.
Edit: the issue appears to be two-fold - the use of respond_to and the redirect status. Updating my answer as follows:
Wrap redirect_to in format.html. As #joel-blum suggested, removing the respond_to block entirely also solves this issue; however, I still required the 303 status for this to work locally
Use a 303 (:see_other) status code for the redirect
def update
set_bell_schedule
respond_to do |format|
if #schedule.update(name: params[:name], schedule: params[:schedule])
format.html { redirect_to action: 'index', status: :see_other }
else
head 400
end
end
end
Explanation
respond_to
When using respond_to in your controller actions, you need to specify what format(s) your responses are targeted for. In this case, your request is Accept: text/html, so you need to specify that as the target format. format.all or removing respond_to entirely would also work here, unless you need to return different responses based on the Accept header.
303 Redirect
Standard 301/302 redirects for dangerous HTTP methods (anything other than GET) generally won't be honored by the browser.
The browser will attempt to redirect to index using the same HTTP method that the request was initially made in: PUT. This will result in a 404. Setting the redirect status to 303 ensures that the browser performs the index redirect with a GET request.
A typical redirect response to a GET request indicates that the requested resource has moved (temporarily or permanently).
For PATCH/PUT/POST/DELETE, The browser has to assume that the resource was found at the expected location, but its data has changed since the initial request was made. The server is just informing the client that the response for this action exists at another location.
For the sanctity of the HTTP contract, a different status code is needed to indicate this situation. 303 informs the client of the location of this response location and suggests that it can be accessed with a GET request.

Why does Rails render templates for HEAD requests?

For HEAD requests, Rails seems to perform all the usual steps including rendering templates and then discards the respective output (sends back an empty response).
I can't think of any way that rendering templates in case of a HEAD request makes any sense (unless you have some actual business logic that gets executed in the templates, but that should never be the case).
So my question would be: Do you agree with me or are there any possible side-effects I didn't foresee?
The reason I'm asking is because I'm thinking of filing a Rails issue and possibly submit a pull request for a feature which disables default (non-explicit) template rendering for HEAD requests.
Good point Remo, however, I am not completely agree.
For every http verb, we manually need to write code to handle things. Similar thing with HEAD. HEAD request will follow execution style of GET request unless we don't handle it.
An example can be:
def index
if request.head?
head :created
else
# handle GET request
Rails.logger.info "Derp #{request.method}"
end
end

Getting accurate timings in logs for streaming responses?

Using Rails 3.2.14. I'm streaming a controller action response, as follows.
def my_controller_action
stream = StreamingClass.new(*args) #responds to each
response.sending_file= true
headers.merge!(
'Content-Disposition' => 'inline',
'Content-Transfer-Encoding' => 'binary',
'Cache-Control' => 'no-cache'
)
self.status = 200
self.content_type= 'application/json'
self.response_body = stream
end
The streaming works just fine, but the problem is that the controller action returns before the streaming is completed (i.e. before each is called on the 'stream' object). It basically returns immediately after assigning the 'stream' object to self.response_body.
I'm using the lograge gem to tidy up our logging. Lograge basically subscribes to the 'process_action.action_controller' notifications. It is logging the timings (i.e. duration, db_runtime, etc...) based on the actual controller return time, without tracking any time spent on the stream object code.
The heavy lifting occurs in a StreamingClass method, but I'm completely missing this info from the logs. Is there some way to include the streaming response timings in the logs?
I'm running into this same issue also. It seems to me that the only way to tell when the streaming is complete is to wrap the response_body in a proxy object whose close method includes additional logic which records the stats you need. You could probably use something like this:
class BodyProxy
def initialize(body)
#body = body
end
def each(&block)
#body.each(&block)
end
def close
#body.close if #body.respond_to?(:close)
# Your code here. Probably something involving `Time.now`
end
end
This works because the Rack specification requires that close be called on the body "after iteration".
I'm unfamiliar with Lograge, so I don't know how you would send this information to that gem, but this should be enough to get you started.

HEAD HTTP requests in Rails 3

Rails 3 currently routes a HEAD request to the matching GET route. There is a head? method on the request, but in returns false and the request acts like a get request. Can I detect if the request is a HEAD request?
Reasoning: I get that a HEAD request should return the EXACT same headers as the get, so Rails wants to perform the full GET and then shave off the body. However, I can conform to this request without issuing the same DB calls, etc that the GET would. Does this make sense?
I had this exact issue. It turns out that enabling caching causes this. Turn caching off in your environment and #head? will work as expected.
The issue is that Rack::Cache turns HEAD requests into GET requests so they can be cached. This is arguably the correct behavior, but it interfered with my application.
You can use the request.head? method to find out if it's a HEAD request:
http://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-head-3F
Once you've determined that it is, you can also use the controller's head() method instead of the typical render:
http://guides.rubyonrails.org/layouts_and_rendering.html#using-head-to-build-header-only-responses
So I'd simply check request.head? before bothering with the database activities. Then use
head :ok, :custom_header => 'value'
def index
if request.head?
head :created
else
Rails.logger.info "Derp #{request.method}"
end
end
Hmm. The above controller method works like I'd expect on Ruby v1.9.3-p194 and Rails v3.2.3; 201's w/o response body for the HEAD requests and 200's w/ for the GET's.

Include params/request information in Rails logger?

I'm trying to get some more information into my Rails logs, specifically the requested URI or current params, if available (and I appreciate that they won't always be). However I just don't seem able to. Here's what I've done so far:
#config/environments/production.rb
config.logger = Logger.new(config.log_path)
config.log_level = :error
config.logger.level = Logger::ERROR
#config/environment.rb
class Logger
def format_message(level, time, progname, msg)
"**********************************************************************\n#{level} #{time.to_s(:db)} -- #{msg}\n"
end
end
So I can customize the message fine, yet I don't seem to be able to access the params/request variables here. Does anyone know if this is possible, and if so how? Or if there's a better way to get this information? (Perhaps even something Redis based?)
Thanks loads,
Dan
(Responding a long time after this was asked, but maybe it will help the next person.)
I just did something similar.
1) you need to override your logger separately from logging request-leve details. Looks like you've figured customizing your logger out. Answer is here:
Rails logger format string configuration
2) I log the request and response of all requests into my service. Note, that Rails puts a tonne of stuff into the headers, so just straight dumping the request or the headers is probably a bad idea. Also of note, my application is primarily accessed via an API. If yours is primarily a web-app, as I'm guessing most people's are, you probably don't want to inspect the response.body as it will contain your html.
class ApplicationController < ActionController::Base
around_filter :global_request_logging
...
def global_request_logging
http_request_header_keys = request.headers.keys.select{|header_name| header_name.match("^HTTP.*")}
http_request_headers = request.headers.select{|header_name, header_value| http_request_header_keys.index(header_name)}
logger.info "Received #{request.method.inspect} to #{request.url.inspect} from #{request.remote_ip.inspect}. Processing with headers #{http_request_headers.inspect} and params #{params.inspect}"
begin
yield
ensure
logger.info "Responding with #{response.status.inspect} => #{response.body.inspect}"
end
end
end
This should work! :) cheers.
logger.info({:user_agent =>
request.user_agent, :remote_ip =>
request.remote_ip}.inspect)
logger.info(params.inspect)
By the by.. This should be placed in your controllers action. Ex: If you place it in your create action it should also log the user_agent i.e the browser, remote_ip i.e the remote ip of the user and all the params.
you should look in the request class
like puts request.uri.
check here for more detail http://api.rubyonrails.org/classes/ActionController/AbstractRequest.html

Resources