Thin post request not working - ruby

I have a Thin server that runs a Sinatra app. In one file, I send a POST request, but it never arrives to its route. The same code works if I run it with bundle exec ruby myapp.rb but when it's run by Thin using bundle exec thin start -p 3000, it simply does not match the post route.
post '/save/:website' do |website|
website_data = JSON.parse(request.body.read)
puts "Saving #{website} plugin data" #never reaches here
persister = Persister.new
persister.update_or_persist(website_data)
body "success"
end
And this is how I declare the POST request
def send_result (path, result)
request = Net::HTTP::Post.new('/save/something', initheader = {'Content-Type' => 'application/json'})
request.body = result.to_json
response = Net::HTTP.new('localhost', '3000').start { |http| http.request(request) }
end
Here is the config.ru
# config.ru
require "./myapp"
run Sinatra::Application
I never reach the puts in my post route, but I do reach it when it's deployed without thin. Why does this request not work?
Also, I had already opened another question about this, but this time I really need the Thin server because I will use Apache to proxy to Thin.

Related

Ruby/Sinatra - How can I call post in lambda class?

I'm make a little program in sinatra and I'm wanted to perfom some dynamic call of post, with diynamic uri, so I make a Connexion class like this:
class Connexion
def initialize(path)
#path = path
end
def sinatraPost
post "/#{#path}" do
# some code
end
end
end
But when I'm launch sinatraPost, I've got this error:
undefined method `post' for #<Connexion:0x000000026206b8> (NoMethodError)
How can I call the sinatra post method in my class ?
EDIT: Okay ! So, I change my strategy, I have this following code:
class Webhook < Sinatra::Base
get '/:name' do
# compare with names array
end
end
Webhook.run!
Thank's to everyone !
It looks like you're going about this the wrong way. If you want to set up your app to receive a POST request, you'll need routing logic in your controller. Sinatra controllers normally look like this:
require 'sinatra'
get '/route1' do
# do stuff
end
post '/route2' do
# do stuff
end
If you're using a modular app, you'll want to have your app inherit from Sinatra::Base. See the Sinatra docs for more.
Making a post request is different, and doesn't rely on Sinatra methods.
require 'net/http'
uri = URI("http://google.com")
headers = {}
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri, headers)
response = http.request(request)
Or something like that. Good luck!

Responding to HTTP Requests in Eventmachine

I have a very simple server for use in integration tests, built using eventmachine:
EM.run do
EM::start_server(server, port, HttpRecipient)
end
I can receive HTTP requests and parse them like so:
class HttpRecipient < EM::Connection
def initialize
##stored = ''
end
# Data is received in chunks, so here we wait until we've got a full web request before
# calling spool.
def receive_data(data)
##stored << data
begin
spool(##stored)
EM.stop
rescue WEBrick::HTTPStatus::BadRequest
#Not received a complete request yet
end
end
def spool(data)
#Parse the request
req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
req.parse(StringIO.new(##stored))
#Send a response, e.g. HTTP OK
end
end
The question is, how do I send a response? Eventmachine provides a send_data for sending responses, but that doesn't understand http. Similarly there is the em-http-request
module for sending requests, but it's not obvious that this is capable of generating responses.
I can generate HTTP messages manually and then send them using send_data, but I wonder if there is a clean way to use an existing http library, or the functionality built in to eventmachine?
If you want something easy then use Thin or Rainbows. It uses Eventmachine inside and provides Rack interface support.
# config.ru
http_server = proc do |env|
response = "Hello World!"
[200, {"Connection" => "close", "Content-Length" => response.bytesize.to_s}, [response]]
end
run http_server
And then
>> thin start -R config.ru
UPD.
If you need server to run in parallel you could run it in a Thread
require 'thin'
class ThreadedServer
def initialize(*args)
#server = Thin::Server.new(*args)
end
def start
#thread = Thread.start do
#server.start
end
end
def stop
#server.stop
if #thread
#thread.join
#thread = nil
end
end
end
http_server = proc do |env|
response = "Hello World!"
[200, {"Connection" => "close", "Content-Length" => response.bytesize.to_s}, [response]]
end
server = ThreadedServer.new http_server
server.start
# Some job with server
server.stop
# Server is down

Eventmachine calls callback twice

I tried to launch eventmachine httpserver example, but I've added simple puts in the process_http_request method. To my surprise, when I access localhost:8080 from browser, I see puts output in terminal twice.
Why is it printed twice? Is it a bug? Maybe I misunderstand something in eventmachine.
You can see my example below.
require 'eventmachine'
require 'evma_httpserver'
class MyHttpServer < EM::Connection
include EM::HttpServer
def post_init
super
no_environment_strings
end
def process_http_request
response = EM::DelegatedHttpResponse.new(self)
response.status = 200
response.content_type 'text/html'
response.content = '<center><h1>Hi there</h1></center>'
puts 'my_test_string'
response.send_response
end
end
EM.run do
EM.start_server '0.0.0.0', 8080, MyHttpServer
end
The first one is a request for the favicon. The second one is a request for the page body. If you want to call it a bug, it is your bug, not the library's.

How to stream response rails app on heroku

I have a rails 3.1 app running on heroku.
I need to provide the user with the ability to download csv data.
I'm trying to stream the data, but it is all sent in one go.
Which for larger requests will timeout.
There is much talk on the heroku site about streaming and chunking
but as far as I can tell thin collects all the data and sends it in one go.
How do I get it to work?
Do I have to add some middleware? e.g. unicorn
The code streams fine running with mongrel.
I'm pretty sure you just need to add
stream
to the top of your controller.
More info on HTTP streaming can be found on RailsCasts: http://railscasts.com/episodes/266-http-streaming
This question is really old but the issue is still very common because of the 30'' limit in Heroku responses so I will add some code on how I achieved it. Works with Rails 5.2 & 6.1 on Heroku with Puma server.
I'm using #send_stream method (present only in edge rails, future rails 7) so I just copied it + set the Last-Modified header manually. Added all in a rails concern to reuse it.
module Streameable
extend ActiveSupport::Concern
include ActionController::Live
def send_stream(filename:, disposition: 'attachment', type: nil)
response.headers['Content-Type'] =
(type.is_a?(Symbol) ? Mime[type].to_s : type) ||
Mime::Type.lookup_by_extension(File.extname(filename).downcase.delete('.')) ||
'application/octet-stream'
response.headers['Content-Disposition'] =
ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename) # for Rails 5, use content_disposition gem
# extra: needed for streaming correctly
response.headers['Last-Modified'] = Time.now.httpdate
yield response.stream
ensure
response.stream.close
end
end
class ExporterController < ApplicationController
include Streameable
def index
respond_to do |format|
format.html # index.html
format.js # index.js
format.csv do
send_stream(attachment_opts) do |stream|
stream.write "email_address,updated_at\n"
50.times.each do |i|
line = "user_#{i}#acme.com,#{Time.zone.now}\n"
stream.write line
puts line
sleep 1 # force slow response for testing respose > 30''
end
end
end
end
end
private
def attachment_opts
{
filename: "data_#{Time.zone.now.to_i}.csv",
disposition: 'attachment',
type: 'text/csv'
}
end
end
Then, if you use something like curl you will see the output generated second by second.
$ curl -i http://localhost:3000/exporter.csv
An important thing is to write your code to iterate the data with #each, by using the Enumerable module. Oh, a tip with ActiveRecord, use #find_each so the DB fetch is in batches.

View Savon Request XML without Sending to Server

I'm using the Savon gem to make a SOAP request using code similar to what's below. It's working, but I would like to view/capture the request XML without actually making a call to their server. I can view it now after a request is made by sticking a debugger line after the request and inspecting the client variable.
Does anyone know of a way to view the request XML without actually making a request? I want to be able to validate the XML against a schema using Cucumber or Rspec.
client = Savon::Client.new do |wsdl, http|
wsdl.document = "http://fakesite.org/fake.asmx?wsdl"
end
client.request(:testpostdata, :xmlns => "http://fakesite.org/") do
soap.header = { :cAuthentication => {"UserName" => "MyName", "Password" => "MyPassword" } }
soap.body = { :xml_data => to_xml }
end
Using Savon 2 I do it this way, write a method that return the request body from the client.
client = Savon::Client.new(....)
this is not mentioned in the documentation
def get_request
# list of operations can be found using client.operations
ops = client.operation(:action_name_here)
# build the body of the xml inside the message here
ops.build(message: { id: 42, name: "Test User", age: 20 }).to_s
end
You can directly via the Savon::Client#build_request method.
Example:
request = client.build_request(:some_operation, some_payload)
request.body # Get the request body
request.headers # Get the request headers
Take a peak # https://github.com/savonrb/savon/blob/master/lib/savon/request.rb for the full doc.
I am using Savon 2.11 and I can accomplish it with globals in the client:
def client
#client ||= Savon.client(soap_version: 2,
wsdl: config.wsdl,
logger: Rails.logger,
log: true)
end
More info on the globals here.
Then the logger spits out the host, the http verb and the complete xml ("headers" and body) for both request and response.
While I'm sure there's a better way to do this, I just overrode response.
class Savon::SOAP::Request
def response
pp self.request.headers
puts
puts self.request.body
exit
end
end
They've updated the API since the last post. Set this setting in Savon.client: :pretty_print_xml => true. After your call, search the logs for SOAP request:. The output is put to stdout. Check the console console history if you're testing your connection from the console.
Savon uses HTTPI to execute SOAP requests. HTTPI is a common interface on top of various Ruby HTTP clients. You could probably mock/stub the HTTP request executed by Savon via:
HTTPI.expects(:post).with do |http|
SchemaValidation.validate(:get_user, http.body)
end
Please note that I used Mocha for mocking the SOAP request, getting the HTTP body and validating it against some validation method (pseudo-code).
Currently, Savon does not support building up requests without executing them. So the only way to validate the request would be to intercept it.
If you would need Savon to support this feature, please let me know and open a ticket over at GitHub.
EDIT: There's also savon_spec, which is a little helper for basic fixture-based testing with Savon.
I had the same issue and patched Savon as follows:
module Savon
class Client
def get_request_xml operation_name, locals
Savon::Builder.new(operation_name, #wsdl, #globals, locals).pretty
end
end
end
This builds the XML and returns it as a string without sending it to the API endpoint. It doesn't accept a block argument in the same way client.call does, so it won't be able to reproduce every type of request you're making, but it meets my needs for now.

Resources