cURL: Malformed encoding found in chunked-encoding, why? - ruby

I am experimenting with CGI and the chunked encoding ("Transfer-Encoding: chunked" HTTP header field.) This way files can be sent without a content-length header. I wrote a minimalistic CGI application in Ruby, to try it out. My code is the following (chunked.rb):
#!/usr/bin/ruby
puts "Date: Fri, 28 Nov 2015 09:59:59 GMT"
puts "Content-Type: application/octet-stream; charset=\"ASCII-8BIT\""
puts "Content-Disposition: attachment; filename=image.jpg"
puts "Transfer-Encoding: chunked"
puts
File.open("image.jpg","rb"){|f|
while data=f.read(32)
STDOUT.puts data.size.to_s(16)
STDOUT.puts data
end
STDOUT.puts "0"
STDOUT.puts
}
I took the idea and chunked format example from here: https://www.jmarshall.com/easy/http/
HTTP/1.1 200 OK
Date: Fri, 31 Dec 1999 23:59:59 GMT
Content-Type: text/plain
Transfer-Encoding: chunked
1a; ignore-stuff-here
abcdefghijklmnopqrstuvwxyz
10
1234567890abcdef
0
some-footer: some-value
another-footer: another-value
[blank line here]
As my CGI app resides in Apache cgi-bin directory, I can issue cURL:
curl http://example.com/cgi-bin/chunked.rb -O -J
cURL should reassamble the original image.jpg file from the chunks, but unfortunately the saved file isn't complete, it is smaller than the original, and I get an error message too from cURL:
curl: (56) Malformed encoding found in chunked-encoding
However when I change line data=f.read(32) to something like data=f.read(1024*50), then file is saved correctly. Using another, bigger file from the server make the CGI app useless again, I got the same error message again. What can I do to make my CGI app working, and to send the file correctly?

So the working example:
puts "Date: Fri, 28 Nov 2015 09:59:59 GMT"
puts "Content-Type: application/octet-stream; charset=\"ASCII-8BIT\""
puts "Content-Disposition: attachment; filename=image.jpg"
puts "Transfer-Encoding: chunked"
puts
File.open("image.jpg","rb"){|f|
while data=f.read(32)
STDOUT.puts data.size.to_s(16)
STDOUT.print data
STDOUT.puts
end
STDOUT.puts "0"
STDOUT.puts
}

Related

Get Json body and Response Status from Bash Script POST

Currently I am using:
#!/bin/bash
PROCESS=$(curl --location --request -v -X POST 'https://jsonplaceholder.typicode.com/posts' \
--header 'Content-Type: application/json' \
--data-raw '{"title": "foo","body": "bar","userId": "1"}')
echo "$PROCESS"
And getting:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 111 100 67 100 44 208 137 --:--:-- --:--:-- --:--:-- 344
{
"title": "foo",
"body": "bar",
"userId": "1",
"id": 101
}
But I also want the response status e.g. 201 or like this.
HTTP/2 200
date: Mon, 30 Nov 2020 14:00:56 GMT
content-type: application/json; charset=utf-8
set-cookie: __cfduid=dfda1e85d5738eb18115dc0a07311a4dd1606744856; expires=Wed, 30-Dec-20 14:00:56 GMT; path=/; domain=.typicode.com; HttpOnly; SameSite=Lax
x-powered-by: Express
x-ratelimit-limit: 1000
x-ratelimit-remaining: 999
x-ratelimit-reset: 1606702897
vary: Origin, Accept-Encoding
access-control-allow-credentials: true
cache-control: max-age=43200
pragma: no-cache
expires: -1
x-content-type-options: nosniff
etag: W/"6b80-Ybsq/K6GwwqrYkAsFxqDXGC7DoM"
via: 1.1 vegur
cf-cache-status: HIT
age: 13185
cf-request-id: 06bb0df15c0000edfbfb9b8000000001
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report?s=ABBCY6aKAHfezboFKgcq%2FlsWKQZDAORup49fKMArhm%2BYl3Kb99pMLrZpLtbXsfz%2BQ6RxnutmzE0mCX5AcIVGRjmq%2FIrIja5MeNFFnmpO7WBT1725PWdN1J0KFhcqNxvNP8He2TBjfd3N"}],"group":"cf-nel","max_age":604800}
nel: {"report_to":"cf-nel","max_age":604800}
server: cloudflare
cf-ray: 5fa518fbcbdfedfb-CDG
I want to do the post and the echo out body and response code in a nice way.
Response code is sent in HTTP headers.
You may redirect headers to STDERR e.g. as described here: Report HTTP Response Headers to stderr?
So you may do this:
out=$(curl -s -D /dev/stderr http://boardreader.com 2>/tmp/headers)
# parse /tmp/headers
If you don't want to mess with temp file, you may try more complex solutions like
Capture stdout and stderr into different variables
You can only issue either a post or header request in one call and so you will need to do this in two separate calls read into the same variable and so:
PROCESS=$(curl -I 'https://jsonplaceholder.typicode.com/posts' && curl -X POST 'https://jsonplaceholder.typicode.com/posts' --header 'Content-Type: application/json' --data '{"title": "foo","body": "bar","userId": "1"}')
To me it makes sense to check the headers first and if this command is successful, get the json response with both being read into the PROCESS variable. You can of course change the order if you wish.

OpenURI fails to follow URLs that have %20 [duplicate]

This question already has answers here:
Does a `+` in a URL scheme/host/path represent a space?
(6 answers)
Closed 3 years ago.
I am having some issues with Ruby's OpenURI follow redirect functionality.
When going to a URL that contains %20 in it, and that redirects with a 30x, Ruby's OpenURI fails.
The exact same URL, with a + instead of %20 works.
Both the %20 and + versions work properly with curl -L (follow).
Code
require 'open-uri'
base = "http://software-engineering-handbook.com/Handbook"
puts "===> PASS: URI Open +"
result = open "#{base}/Video+Series"
p result.status
puts "===> PASS: Curl +"
puts `curl -LIsS "#{base}/Video+Series" | grep HTTP`
puts "===> PASS: Curl %20"
puts `curl -LIsS "#{base}/Video%20Series" | grep HTTP`
puts "===> FAIL: URI Open %20"
begin
result = open "#{base}/Video%20Series"
p result.status
rescue => e
puts "#{e.class} #{e.message}"
end
Output
===> PASS: URI Open +
["200", "OK"]
===> PASS: Curl +
HTTP/1.1 200 OK
===> PASS: Curl %20
HTTP/1.1 303 See Other
HTTP/1.1 200 OK
===> FAIL: URI Open %20
OpenURI::HTTPError 302 Found (Invalid Location URI)
I am not sure what is going on here. Tried HTTParty (although I know it is just a wrapper), hoping to see a different behavior, but it also fails.
The server is responding with an redirect to an invalid URI. curl is being lax about it, but Ruby is being strict.
If we print out the e.cause we get more information.
#<URI::InvalidURIError: bad URI(is not URI?): "http://software-engineering-handbook.com/Handbook/Video Series/">
And also by looking at the headers from curl -I 'http://software-engineering-handbook.com/Handbook/Video%20Series'...
HTTP/1.1 303 See Other
Server: Cowboy
Date: Sat, 28 Dec 2019 21:41:28 GMT
Connection: keep-alive
Content-Type: text/html;charset=utf-8
Location: http://software-engineering-handbook.com/Handbook/Video Series/
And, indeed, the server is returning an invalid URI. Spaces are not allowed in a URI path. Ruby's URI class will not parse it.
> URI("http://software-engineering-handbook.com/Handbook/Video Series/")
URI::InvalidURIError: bad URI(is not URI?): "http://software-engineering-handbook.com/Handbook/Video Series/"
from /Users/schwern/.rvm/rubies/ruby-2.6.5/lib/ruby/2.6.0/uri/rfc3986_parser.rb:67:in `split'

Cannot extract data from an HTTP PUT request in Ruby

I am trying to implement a simple server in Ruby, but somehow I can't get the data from a put request.
curl request that I am making:
curl -v -X PUT localhost:2016/api/kill -d {"connId" : 1}
The server seems to be reading the request alright.
The code:
while line = socket.gets
puts line.chomp
request << line.chomp
break if line =~ /^\s*$/
end
produces the output:
PUT /api/kill HTTP/1.1
User-Agent: curl/7.35.0
Host: localhost:2016
Accept: */*
Content-Length: 7
Content-Type: application/x-www-form-urlencoded
But I don't see the data anywhere?
Am I supposed to see it?
Is something wrong with the curl request?
You need single quotes around the body.
curl -v -X PUT localhost:2016/api/kill -d '{"connId" : 1}'

MongoDB with Ruby driver how to create fields with curl?

I am sending the following curl request to my Ruby Driver
curl -i -X POST -H "Content-Type:application/json" -d '{ "firstName" : "Frodo", "lastName" : "Baggins" }' http://localhost:4567/new_document/?
This is the code for the POST operation in ruby.
post '/new_document/?' do
content_type :json
db = settings.mongo_db
result = db.insert_one params
db.find(:_id => result.inserted_id).to_a.first.to_json
end
I get the following response in the console.
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 43
X-Content-Type-Options: nosniff
Server: WEBrick/1.3.1 (Ruby/2.0.0/2013-11-22)
Date: Tue, 10 Nov 2015 18:38:59 GMT
Connection: Keep-Alive
{"_id":{"$oid":"564239c3e89bde194d000007"}}
As you can see the fields first and last name never get created. What am I doing wrong?
Figured it out! This creates the correct fields and their values.
curl -d 'name=adam&last=hoffman' http://localhost:4567/new_document/?

Simple TCPSocket server in Ruby exhibits a HTTP header issue

I'm benchmarking some simple HTTP server implementations in Ruby (no threads, threaded, fibers and eventmachine) but this simple piece of code fails using threads:
#!/usr/bin/env ruby
require 'socket'
server = TCPServer.new("127.0.0.1", 8080)
puts "Listening on 127.0.0.1:8080"
while true
Thread.new(server.accept) do |client|
msg = client.readline
headers = [
"",
"HTTP/1.1 200 OK",
"Date: Fri, 30 Sep 2011 08:11:27 GMT",
"Server: TCP socket test",
"Content-Type: text/html; charset=iso-8859-1",
"Content-Length: #{msg.length}\r\n\r\n"].join("\r\n")
client.write headers
client.write ">>> Data sent:\n #{msg}"
client.close
end
end
A simple curl http://localhost:8080/ works fine, when the first element in the array is "" or other String, but not the "HTTP/1.1 200 OK" response directly. Why is this?

Resources