SSE (Server-sent events) not working - ruby-on-rails

I'm testing SSE in my Rails app (server: Puma) in Chrome but they are not triggered:
setTimeout((function() {
console.log("log1");
var source = new EventSource('/websites/21/backlinks/realtime_push');
source.addEventListener('pagination', function(e) {
console.log("log2");
var data = JSON.parse(e.data);
$('#pagination').html(data.html);
});
}), 1);
only "log1" is written to console.
In developer tools I see XHR requests every time server pushes something (each second) but the response is empty - not sure if developer tools just don't show it or something else is wrong.
curl http://localhost:3000/websites/21/backlinks/realtime_push
returns:
event: pagination
data: {"html":"pagination"}
event: pagination
data: {"html":"pagination"}
so the data should be sent back...
What could be the problem here?
UPDATE: the problem is this monkey patch for problems with render_to_string from the question here: ActionController::Live with SSE not working properly
"this only appears to fix the problem... although it will cause the controller to actually send the data, the receiving end in JavaScript for some reason still won't get notified of events"
It's strange because in both cases when I use render_to_string and when I don't, I get the same headers with curl:
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-UA-Compatible: chrome=1
Content-Type: text/event-stream
Cache-Control: no-cache
X-Request-Id: 4de7c8a6-a54f-45ef-9013-0447f85438c2
X-Runtime: 0.033030
Transfer-Encoding: chunked
But in one case it works on the JavaScript side and in other it doesn't :/

Related

Shopify not getting rates from custom CarrierService

I'm building a private Shopify app that returns custom shipping rates. Following the API docs, and with some help from this tutorial, I created a functional, proof-of-concept that returns some sample shipping rates in my store.
However, that proof-of-concept app is built in PHP, and the final version must be in Ruby on Rails. So I created a Rails app that returns exactly the same content as the PHP app—but for some reason the rates simply don't show up in the Shopify backend.
The only thing I notice that's different is the HTTP headers (I've tried monkeying around with them to match the PHP app, but to no avail). Is there anything obvious I'm missing here?
Here's a comparison of the HTTP response headers:
PHP:
Connection: Keep-Alive
Content-Encoding: gzip
Content-Length: 186
Content-Type: text/html; charset=UTF-8
Date: Tue, 16 May 2017 13:00:30 GMT
Keep-Alive: timeout=5, max=100
Server: Apache/2.4.18 (Ubuntu)
Vary: Accept-Encoding
Rails:
Cache-Control: max-age=0, private, must-revalidate
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Tue, 16 May 2017 12:57:38 GMT
ETag: W/"ce885edaa10636b3b7459dca958f44dd"
Server: nginx
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Request-Id: 54e2575e-c86a-4f44-a315-d0a3fbbc13f9
X-Runtime: 0.616974
Again, Shopify handles the PHP response fine, but fails silently on the Rails response. Is there anything in that second (Rails) block that Shopify might be rejecting?
Shopify unfortunately provides no error log or way to debug this type of issue—either they pull the rates from your app or not.
Here is my Rails rates_controller.rb:
class RatesController < ApplicationController
def index
ups_rates = {
rates: [
{
service_name: 'Endertech Overnight',
service_code: 'ETON',
total_price: '000',
currency: 'USD',
min_delivery_date: (DateTime.now + 1.days).strftime('%F %T %z'),
max_delivery_date: (DateTime.now + 2.days).strftime('%F %T %z')
},
{
service_name: 'Endertech Regular',
service_code: 'ETREG',
total_price: '000',
currency: 'USD',
min_delivery_date: (DateTime.now + 3.days).strftime('%F %T %z'),
max_delivery_date: (DateTime.now + 7.days).strftime('%F %T %z')
}
]
}
# Tested returning both application/json (default) or text/html
#render json: ups_rates
render body: ups_rates.to_json, content_type: "text/html"
end
end
I suspected maybe Shopify was caching very aggressively, so I've also tried destroying and recreating both my private app and the carrier service, as well as changing the callback_url. So far nothing has had any effect.
After a whole lot of digging, I found the answer: Shopify requests rates via POST, but my app was only responding to GET.
As a quick fix, I created a static route in routes.rb:
Rails.application.routes.draw do
post 'customrates', to: 'rates#index'
end
In retrospect, this was clearly stated in the documentation, I just hadn't noticed (after all, a rate request intuitively seems like a GET action).
For anyone else encountering this issue, try sending a POST via CURL to your callback_url, just to verify it's returning what it should:
curl -X POST http://yourcallbackurl.com

Microsoft Edge PDF inline issue

Our Rails web app generates PDFs using wkhtmltopdf and sends them to the client. This works in every web browser we've tested it with except Edge.
We've tried rendering the response in a couple of different ways, this is how it was originally:
kit = PDFKit.new(#html_content)
render text: kit.to_pdf, content_type: 'application/pdf'
This opens the PDF viewer with the PDF displaying correctly in every browser that we tested with except Edge where the browser displays: Something's keeping this PDF from opening.
In our application logs, there is the POST request which is the form submission and I can see our app send the pdf file response, then there are subsequent GET requests to the form submission url which error because it's not expecting any GET request to that url. I've no idea what's going on here.
The response headers for the request are:
Cache-Control: max-age=0, private, must-revalidate
Connection: Keep-Alive
Content-Length: 34865
Content-Type: application/pdf; charset=utf-8
Date: Thu, 18 Jun 2015 14:35:30 GMT
Etag: "4baf297d1866339e60e8e893300909a0"
Server: WEBrick/1.3.1 (Ruby/2.0.0/2013-06-27)
Set-Cookie: _APP_session=<long cookie>; path=/; HttpOnly
X-Request-Id: 617580a8-4d7d-43c4-8e49-aeaeafba7b79
X-Runtime: 21.868098
X-XSS-Protection: 1; mode=block
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-ua-compatible: chrome=1
I have also tried using send_data like this:
send_data kit.to_pdf, type: 'application/pdf', disposition: 'inline'
Which results in the following response headers but ultimately the same problem:
Cache-Control: private
Connection: Keep-Alive
Content-Disposition: inline
Content-Length: 34866
Content-Transfer-Encoding: binary
Content-Type: application/pdf
Date: Thu, 18 Jun 2015 14:39:42 GMT
Etag: "11db49f1a26444a38fa2b51f3c3336ed"
Server: WEBrick/1.3.1 (Ruby/2.0.0/2013-06-27)
Set-Cookie: _APP_session=<long cookie>; path=/; HttpOnly
X-Request-Id: 501d9832-b07e-4764-8ecc-f1c1e9a6421e
X-Runtime: 7.054236
X-XSS-Protection: 1; mode=block
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-ua-compatible: chrome=1
If I remove the Content-Disposition: inline header from the above it brings up the save file prompt and downloading the file works fine. We need it to load in the browser window though.
I don't believe it to be a duplicate of this question because it works in IE 9, 10 and 11 and is only a problem with Edge.
We've been having what sounds like the same problem with PDF reports that we generate on the server and dispatch inline - the new tab that opens for the viewer appears to re-issue a request for the content instead of displaying the content from the response. Since we use a synthetic one-time-use path (for largely historical reasons to ensure a new version of the report is fetched), the report isn't actually there for the new tab's GET request.
Since we're using 20.10240, I'm not convinced it was actually fixed in 10158.
As with the OP, this seems to apply only to "Content-Disposition: inline"; if we use "attachment" instead, a temporary file is saved locally and the temporary file is opened in the viewer.
It was a bug but Microsoft have fixed it in build 10158! :)

When does Rails respond with 'transfer-encoding' vs. 'content-length'?

I'm building an API on Rails version 4.1.7/Nginx that responds to request from an iOS app. We're seeing some weird caching on the client and we think it has something to do with a small difference in the response that Rails is sending back. My questions...
1) I want to understand why, for the exact same request (with only the Authorization header value changed), Rails sends back transfer-encoding: chunked sometimes and Content-Length: <number> sometimes? I thought that maybe it had something to do with the response size, but in the example responses whose headers I've pasted below, the data returned in the body is EXACTLY the same.
2) Is there a way to force it to use Content-Length? We think that that will fix our caching issues in our iOS app.
Response #1
HTTP/1.1 200 OK
Cache-Control: max-age=0, private, must-revalidate
Content-Type: application/json; charset=utf-8
Date: Wed, 18 Mar 2015 00:59:31 GMT
ETag: "86f277ea63295460d4f3bed9a073eaa2"
Server: nginx/1.6.2
Status: 200 OK
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Request-Id: dd36f139-1986-4da6-9645-4438d41e74b0
X-Runtime: 0.123865
X-XSS-Protection: 1; mode=block
transfer-encoding: chunked
Connection: keep-alive
Request #2
HTTP/1.1 200 OK
Cache-Control: max-age=0, private, must-revalidate
Content-Type: application/json; charset=utf-8
Date: Wed, 18 Mar 2015 00:59:36 GMT
ETag: "86f277ea63295460d4f3bed9a073eaa2"
Server: nginx/1.6.2
Status: 200 OK
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Request-Id: 0cfd7705-157b-41b5-aa36-739bc6f8302e
X-Runtime: 0.092672
X-XSS-Protection: 1; mode=block
Content-Length: 2234
Connection: keep-alive
Both responses are valid according to HTTP 1.1, so you need to fix your client code that it can handle both. It is a bad idea to try to fix the server so that that it behave in a way that it does not trigger a bug in the client.
The next version of nginx may behave differently, you users may even have proxies that change the transfer, maybe only when they do roaming and use a different provider.
If you want to do some finger-printing on the header, the ETag-header may help you, as the ETag should stay constant when the content of the response is not changed, regardless of the transfer.
The server typically sends in chunks when it calls a dynamic page, because it then does not need to create a buffer for the whole page and wait till all of the page is generated.
The server often send the response in one go if it has the buffer already, for example because it is in cache or the content is on a file and is not to big. Sending in one go is more efficient, on the other hand, an extra copy of the data to buffer the output needs more memory and is less efficient. So the server may even decide this according to the available memory.

Rails 4 redirects to 'data:,' in Chrome

There is a weird behavior in Google Chrome, which is also described in this question: rails redirects to 'data:,'
When a new resource is being created and my controller redirects to the show action, chrome initiates loading of a blank page with 'data:,' in the address bar. The reply of the author who asked the above mentioned question is the following:
This is a security feature, the HTML content of the new page matches the HTML content of the submitted form, which Chrome blocks.
However no explanation of how to fix it followed. The behavior is only present in Chrome browser.
I've been googling it and found that editing posts with an iframe in Rails 4.0 causes a redirect to "data:,"
Rails 4 now sets the X-XSS-Protection header for all requests, so the iframe trips up the XSS protection in Chrome after a form submit.
(https://github.com/elektronaut/sugar/issues/41#issuecomment-25987368)
Solution, add it to your controller:
before_filter :disable_xss_protection
protected
def disable_xss_protection
# Disabling this is probably not a good idea,
# but the header causes Chrome to choke when being
# redirected back after a submit and the page contains an iframe.
response.headers['X-XSS-Protection'] = "0"
end
Ok I think I know what this is. You can specify images and text inside a data: protocol, and I believe Chrome is seeing escaped HTML and thinking it is data. Since the mime type is not specified, it leaves the mime type blank after the colon, and just prints the comma.
http://guides.rubyonrails.org/security.html#redirection
Rails 4 automatically escapes HTML, so if you are trying to render HTML, you have to indicate not to escape it. Look at the options for render:
http://guides.rubyonrails.org/security.html#redirection
You can use raw() to render direct HTML.
http://www.webbydude.com/posts/9-the-h-helper-in-rails-3
I'm not convinced it is related to a mimetype issue. I have this issue when a user posts a blog entry that has iframes in its content. When the entry is saved it redirects to the "show" action which will have the user's content (raw/html_safe). Chrome will display the page for a split second and then for some reason re-direct again to the blank "data:," page (in history it will only leave the data:, and the submit page).
here are the response headers i registered:
Ruby 2.0.0 / Rails 4 migrated app with incorrect behavior (staging server)
Cache-Control:max-age=0, no-cache, no-store
Cache-Control:max-age=0, private, must-revalidate
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:25359
Content-Type:text/html; charset=utf-8
Date:Thu, 23 Jan 2014 16:37:11 GMT
ETag:"6d9d4961ea2df12de67f8a92c43579fb"
Server:Apache
Set-Cookie: _**********_session_dev=1774518c571bf4e65189d607b276e65e; domain=*********.com; path=/; expires=Thu, 23 Jan 2014 18:37:11 -0000; HttpOnly
Status:200 OK
Vary:Accept-Encoding
X-Content-Type-Options:nosniff
X-Frame-Options:SAMEORIGIN
X-Mod-Pagespeed:1.6.29.7-3566
X-Request-Id:9f5314a5-ad01-4aec-bd0f-04e8afd9bdac
X-UA-Compatible:chrome=1
X-XSS-Protection:1; mode=block
Ruby 1.8.7 / Rails 2 app with correct behavior (prod server)
HTTP/1.1 200 OK
Date: Thu, 23 Jan 2014 16:32:53 GMT
Server: Apache
ETag: "f12135ddd373205352f9754328368217"
Cache-Control: private, max-age=0, must-revalidate
Status: 200
X-Mod-Pagespeed: 1.4.26.5-3533
Cache-Control: max-age=0, no-cache, no-store
Vary: Accept-Encoding
Content-Length: 27167
X-Cnection: close
Content-Type: text/html; charset=utf-8
Connection: Keep-Alive
Content-Encoding: gzip
also tried having this as the initial html:
<!DOCTYPE html>
<html>
<head>...
and also just (as random tests to detect what could be wrong)
<!DOCTYPE html>
<head>...
All I know is that if the submitted content has iframes, when redirecting to the blog "display" page chrome's weird behavior kicks in.

Why aren't my nimbus didImageLoad methods being called?

I'm new to Nimbus. Right now my app is trying to retrieve 4 images via this code:
for (int i=minFoto; i<=maxFoto; i++) {
NINetworkImageView* networkImageView = [self networkImageView];
NSString *resourceURL = [NSString stringWithFormat:#"%#registration/rest/users/account_get_foto/%#?fotoId=%d", baseURL, ssid, i];
NSLog(resourceURL);
[networkImageView setPathToNetworkImage:resourceURL
forDisplaySize: CGSizeMake(50, 50)
contentMode: networkImageView.contentMode];
I know my loop is working because I see all four NSLog's come out correctly. However, I am only getting the first image. networkImageViewDidStartLoad is only being called once and neither didLoadImage or networkImageViewDidFailLoad is being called. I think it is odd that didLoadImage is never being call. Never. I know I have the data because I'm using CharlieProxy (great app BTW, well worth the $50) and it shows the image data in the response packets.
So I commented this out of my delegate:
[[Nimbus networkOperationQueue] setMaxConcurrentOperationCount:1];
And as you might expect, I'm getting 4 calls to networkImageViewDidStartLoad, and still zero to didLoadImage or networkImageViewDidFailLoad.
Here are my request headers (from CharlieProxy)
GET /registration/rest/users/account_get_foto/fdbc2222-7b75-4ff4-b111-623e951e5b00?fotoId=134 HTTP/1.1
Host: -------------:8080
User-Agent: Ferret/1.0 CFNetwork/548.0.3 Darwin/11.2.0
Accept: */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: keep-alive
and here's the response headers, showing a "200 OK"
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Powered-By: Servlet/3.0; JBossAS-6
Content-Type: image/*
Content-Length: 461109
Date: Tue, 31 Jan 2012 21:12:33 GMT
âPNG (png data deleted...)
I'm a little puzzled now. My server is clearly returning the image data, but my app just isn't getting it. Any ideas?
Well, I found it. Might as well put the answer in for future googlers.
My networkImageView had gone out of scope and was ARC'd. Funny, I thought I had saved it away, but that code was partially commented out!

Resources