Search code examples
rubyajaxd3.jssinatrahttp-status-code-403

Using d3.json to query a sinatra app works fine on localhost, but fails with forbidden when deployed


I have a simple Sinatra app. One end point serves a page including a chart rendered with d3.js. d3 loads the data from a second end point "\tasks.json" using a call to d3.json.

  d3.json('/tasks.json', function(json) { ... }

This works on my windows PC, but when pushed to production (a linux box), I get no data shown. Using chrome to view the network traffic, I can see the "tasks.json" call is failing with the following header:

HTTP/1.0 403 Forbidden
Server: openresty
Date: Fri, 20 Mar 2015 02:39:06 GMT
Content-Type: text/plain
Content-Length: 9
X-Content-Type-Options: nosniff
X-Cache: MISS from proxy.xx.xx.xx
X-Cache-Lookup: MISS from proxy.xx.xx.xx:3128
Via: 1.0 proxy.xx.xx.xx (squid/3.1.6)
Proxy-Connection: keep-alive

However.... if I open the same 'tasks.json' endpoint directly in the browser it loads fine.

HTTP/1.0 200 OK
Server: openresty
Date: Fri, 20 Mar 2015 02:46:53 GMT
Content-Type: application/json
Content-Length: 3524
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: OPTIONS
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept
X-Frame-Options: ALLOW-FROM *
X-Content-Type-Options: nosniff
X-Cache: MISS from proxy.xx.xx.xx
X-Cache-Lookup: MISS from proxy.xx.xx.xx:3128
Via: 1.0 proxy.xx.xx.xx (squid/3.1.6)
Proxy-Connection: keep-alive

Within my sinatra app I also have protection turned off.

  set :protection, false

Any thoughts?

The failing call has the following request header

GET http://project_dash.locallan.link/tasks.json HTTP/1.1
Host: project_dash.locallan.link
Proxy-Connection: keep-alive
accept: application/json,*/*
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36
DNT: 1
Referer: http://project_dash.locallan.link/gantt
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-AU,en;q=0.8,en-US;q=0.6
Cookie: __utma=10890537.1709214504.1411711051.1411711051.1411711051.1; __utmz=10890537.1411711051.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _sio=30220c08-f9ff-4584-8218-9145a2a1cde1----; ajs_user_id=null; ajs_group_id=null

The working call has the following header

GET http://project_dash.locallan.link/tasks.json HTTP/1.1
Host: project_dash.locallan.link
Proxy-Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36
DNT: 1
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-AU,en;q=0.8,en-US;q=0.6
Cookie: __utma=10890537.1709214504.1411711051.1411711051.1411711051.1; __utmz=10890537.1411711051.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _sio=30220c08-f9ff-4584-8218-9145a2a1cde1----; ajs_user_id=null; ajs_group_id=null

Note: If it replace the d3.json call with a jquery ajax call, then it starts working. I therefore think that d3 must be setting a property in it's request that the Sinatra/Rack pipeline is blocking with a 403.

Is there any way to work out what part of the stack is returning the 403?

I'm running the server as a docker container, and the docker log shows the request coming in but doesn't explain the 403.

22:58:11 web.1  | 10.10.254.12 - - [22/Mar/2015:22:58:11 +0000] "GET / HTTP/1.1" 200 1675 0.0025
22:59:01 web.1  | 10.10.240.18 - - [22/Mar/2015:22:59:01 +0000] "GET /gantt HTTP/1.0" 200 2123 0.0283
22:59:02 web.1  | 10.10.240.18 - - [22/Mar/2015:22:59:02 +0000] "GET /js/gantt-chart-d3.js HTTP/1.0" 200 6325 0.1259
22:59:02 web.1  | 10.10.240.18 - - [22/Mar/2015:22:59:02 +0000] "GET /js/gantt.js HTTP/1.0" 200 1367 0.0015
22:59:02 web.1  | 10.10.240.18 - - [22/Mar/2015:22:59:02 +0000] "GET /css/style.css HTTP/1.0" 200 30 0.0257
22:59:04 web.1  | 10.10.240.18 - - [22/Mar/2015:22:59:04 +0000] "GET /tasks.json HTTP/1.0" 403 9 0.4286
22:59:11 web.1  | 10.10.254.12 - - [22/Mar/2015:22:59:11 +0000] "GET / HTTP/1.1" 200 1675 0.0138
22:59:11 web.1  | 10.10.240.18 - - [22/Mar/2015:22:59:11 +0000] "GET /tasks.json HTTP/1.0" 200 3524 0.0873

Solution

  • I don't understand why this works, but in the end I did the following.

      get '/:file.json' do |file|
        #content_type :json
        content_type  = 'text/json'
        File.read("#{file}.json")
      end
    

    I changed the "content_type" line to set the content to "text/json". This allowed the D3 library to load the data correctly.

    I assumed this was equivalent to the line above it.