MalformedStreamException when posting from Tcl script - tomcat6

I've made a servlet which uses the org.apache.commons.fileupload api to upload a CSV file which it should then load into a MySQL table. This works perfectly when posting to the servlet from a form in a browser. However, it fails when trying to post a form via a Tcl script http://www.tcl.tk/man/tcl/TclCmd/http.htm#M20.
The servlet throws the following MalformedStreamException:
org.apache.commons.fileupload.MultipartStream$MalformedStreamException:
Stream ended unexpectedly
The servlet is hosted on Tomcat version 6.0.16.
The connection is made successfully by the Tcl script as it receives an HTTP/1.1 200 OK response and the servlet does return some print statements back to the client Tcl script. However it fails when trying to read the input stream.
Tcl script:
package require http
proc upload { url csv } {
set boundary "-----WebKitFormBoundary[clock seconds][pid]"
set fid [open $csv r]
if {[catch {read $fid [file size $csv]} data]} {
return -code error $data
}
close $fid
set content {}
append content "--${boundary}\n"
append content "Content-Disposition: form-data; name=\"db\"\n\n"
append content "test\n"
append content "--${boundary}\n"
append content "Content-Disposition: form-data; name=\"table\"\n\n"
append content "testing\n"
append content "--${boundary}\n"
append content "Content-Disposition: form-data; name=\"file\"; filename=\"$csv\"\n"
append content "Content-Type: text/csv\n\n"
append content "$data\n"
append content "${boundary}--"
set headers "connection keep-alive"
set token [::http::geturl $url -keepalive 1 -headers $headers -query $content -type "multipart/form-data; boundary=$boundary"]
upvar 0 $token state
if {$state(http) == "HTTP/1.1 200 OK"} {
# no error reported in http headers
puts stdout $state(http)
puts stdout $state(body)
return 1
} else {
# error reported in http headers
puts stdout $state(http)
puts stdout $state(body)
return 0
}
}
set csv "data.csv"
set url "http://ecat:8080/MySqlImport/MySqlImportServlet"
set retVal [upload $url $csv]

Generating a proper multipart message can be highly aggravating. The mime package in Tcllib can help. The top example on this page looks highly relevant.

While Donal Fellows most probably got it right, I want to point out one (typical) mistake with the Daniel's approach: when one uses a high-level language which has the concept of a "string of characters"—as opposed to a "string of bytes"—one is oblidged to take special care when preparing the data being processed to be placed on "the wire".
In this case "the wire" is the HTTP protocol and so the data prepared should most probably go through something like [encoding convertto utf-8 ...] or something like this depending on the intent. (By the way, Tcl has rather extensive support for binary strings so that they can be appended to, concatenated etc without losing their property of being binary.)
The next thing which can be recommended is to get accustomed with a network sniffer (or a specific HTTP sniffer). A great cross-platform solution is Wireshark, on Windows platforms there's Network Monitor (a generic sniffer) and Fiddler (HTTP-specific). The idea is to first capture and study closely the "reference" session performed by a browser then to capture and analyze the misbehaving session performed by your program. So you just compare the sessions and see where the generated traffic differ when it comes to HTTP payload.

Thank you for the help on this one but I have found my specific issue and it was a simple one in the end. It seems as though the ServletFileUpload.parseRequest(request) method in apache.commons.fileupload requires the line endings to be in windows format.
As you can see in the question the $content line endings was \n changing this to \r\n corrected the problem.
Using just \r meant that the error
org.apache.commons.fileupload.MultipartStream$MalformedStreamException:
Stream ended unexpectedly
no longer occured, however it failed to recognise the different items or parts in the request. The combination of \r\n line endings and it works correctly.

Related

apache camel use simple to read and print out file content does not work

I want to use simple to read and print out file content. but it reads file and only print out file name instead of content.
Why? Did I did something wrong?
from(SOAP_ENDPOINT + "&dataFormat=CXF_MESSAGE")
.log(LoggingLevel.INFO, "##### SOAP REQUEST ##### ${body}")
.to("language:simple:wsdl/dummyResponse.xml").convertBodyTo(String.class)
.log(LoggingLevel.INFO, "##### SOAP RESPONSE ##### ${body}");
Any suggestion and ideas are more than welcome!
As noted in the Camel docs the pattern you use executes a script in the declared language.
language:simple:wsdl/dummyResponse.xml
means: execute the script "wsdl/dummyResponse.xml" that contains Simple expressions. I assume that the filename is the result of trying to execute the XML (some fallback result perhaps).
To read the dummyResponse from filesystem you can for example use a Java bean or the Content enricher EIP

Binary file corruption over http using Apache Camel

I'm trying to read a binary file from a local filesystem, send it over HTTP, then in a different application I need to receive the file and write it out to the local file system, all using Apache Camel.
My (simplified) client code looks like this:
from("file:<path_to_local_directory>")
.setHeader(Exchange.HTTP_PATH, header("CamelFileNameOnly"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/octet-stream"))
.to("http4:localhost:9095");
And my server code is:
restConfiguration()
.component("spark-rest")
.port(9095);
rest("/{fileName}")
.post()
.consumes("application/octet-stream")
.to("file:<path_to_output_dir>?fileName=${header.fileName}");
As you can see, I'm using the Camel HTTP4 Component to send the file and the Spark-Rest component to receive it.
When I run this, and drop a file into the local directory, both the client and server applications work and the file is transmitted, received and written out again. The problem I'm seeing is that the original file is 5860kb, but the received file is 9932kb. As it's a binary file it's not really readable, but when I open it in a text editor I can easily see that it has changed and many characters are different.
It feels like it's being treated as a text file and it's being received and written out in a different character set to that in which it is written. As a binary file, I don't want it to be treated as a text file which is why I'm handling it as application/octet-stream, but this doesn't seem to be honoured. Or maybe it's not a character set problem and could be something else? Plain text files are transmitted and received correctly, with no corruption, which leads me to think that it is the special characters in the binary file that are causing the problem.
I'd like to resolve this so that the received file is identical to the sent file, so any help would be appreciated.
I got the same issue. By default, Camel will serialize it as a String when producing to the http endoint.
You should explicitly convert the GenericFile to byte[] by doing a simple : .convertBodyTo(byte[].class) before your .to("http4:..")

Paw File behaves differently when an Environment Variable in multipart form-data

I'm trying to use Environments in Paw to help with testing a web API that uses a multipart/form-data submission of an image file. Paw will do this fine if I have a Body part name with a Value of the "File" type and drag my image file in.
file in multipart form
This produces a request part like:
--Emm5HQuI6PlP2Jo1k3KW284fY8yeluRO
Content-Disposition: form-data; name="persfront"; filename="sample_front.jpg"
Content-Type: image/jpeg
ÿØÿá ExifMM*i&;...
But when I try to abstract that file out of the normal Multipart Body window and into an Environment variable and then use that, it still puts the file contents in the request, but leaves the file information out of the part header.
This:
environment variables for file in form
produces this request:
--2gEAMPgSTNfB0dsLImFDHYMRLjqag7Hu
Content-Disposition: form-data; name="persfront"
ÿØÿá ExifMM*i&;...
The lack of file header, normally supplied in there, causes my request to fail.
It's as if the environment variable is being evaluated prior to it being used and so the multipart form is just seeing a bunch of binary instead of a file. Is this a bug or am I doing something incorrectly?
Is there any other way to use the utility of the Environments in this situation?
Thanks!
Probably very late for an answer, but yes, Paw has a special case where if the content of a multipart request is a file, it will include the filename in the multpart headers. Though, if you abstract the file in an environment variable, this special case isn't honoured anymore...

C: simple HTTP server. Read a local file and download it from browser

I'm working on a simple server that have to work with a browser. When I give it some command, it has to reply me with some html code so to reply my answer. For example I can see the list of the files of a folder etc. I can access my server using localhost:port/somecommand
Now I'm working on donwloading a file from the local hard-disk. What I want to do is enter an url like localhost:port/download/filepath and make the browser download it. When I create the reply I put all things html need to understand that there is a file to download, and infact I have the classical pop-up window that ask me to download the file, but what I receive is bigger than the original file located to the hard-disk, and infact the file is corrupted.
Here the code:
This is the html I send back
HTTP/1.0 200 OK
Date: Tue Apr 10 16:23:55 2012
Content-Type: application/octet-stream
Content-Disposition: attachment; filename=mypic.jpg
Content-Length: 2574359
//empty line here
(I followed The Content-Disposition Header Field text )
then I read from file and I send back html first and what I read from file then:
int file=open(name,O_RDONLY);
void * data=malloc(buf.st_size+1); //buf.st_size is the correct file size
size_t readed=read(file,data,buf.st_size);
send(sd_current, html, DIRSIZE, 0); //html is a string containing what you I showed you
send(sd_current, data, readed);
This result the file I can download using localhost:port/download/filepath to be bigger than original then corrupted, but I can't get rid of why. Can someone help me?
Things to check:
is DIRSIZE really the size of the http header? (Since the size should vary, and capitals normally means constant).
Is the read successful?
How is the file corrupted?
Are the line endings on the http header correct? They should be \r\n
EDIT:
If DIRSIZE is not the size of the header, then the rest of the buffer (containing NULLs or junk) will be sent to the other size.
So the other side sees the HTTP header, then starts receiving data - starting with the rest of the html buffer, then the contents of the file.
Then depending on the receiver, it either stops at the Content-Length header size, or carries on while the stream is still delivering data.
Does that match your result contents: Some junk at the beginning, followed by the expected file contents?
Content-Length: 2574359, 2574359 should be the size of your jpg file, but I see nothing in your code to put that in the html portion. buf.st_size == 2574359 ?

CURLOPT_POSTFIELDS CURL post image file, C language

Wonder if anyone has any experience posting image files with CURL in C..?
I am writing a program to post to a facebook Type web service, and everything is going fine, except when I attempt to post image files...
There's a special format that the server needs or it will not accept the post...
something like this:
---webformkitXXXXXXXX\r\n
filename"somefile.jpg"\r\n
JPEG or IMAGE FILE HERE (in binary)
---webformkitXXXXXXXX\r\n
END----
So when I am finally able to to memcpy together the different pieces I need,
I can save it to file, and it looks just fine, but I can see from the packet captures, that CURL doesn't like taking the binary, it appears that it's truncating the buffer at the first sign of a '\0' because, it only sends like 300 bytes, when it should be sending 80K...
I've been using this: curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
Thank You!
You need to use CURLOPT_WRITEDATA and plug in a function you have written to write the POST data.

Resources