Blobstore Issues with GAE Cloud Tools and Local Development Server - java

I'm trying to debug some GAE code on my local development server but have hit a wall. The code uses Google's Blobstore service to facilitate file uploads. The code works just fine on production but not on my local development server.
I'm using the standard Google pattern of including as my form action in my jsp blobstoreService.createUploadUrl("/uploadSurvey") and then calling blobstoreService.getUploads(request) in my servlet.
The file is correctly uploaded (I can see it using the local admin console) but the call to getUploads() throws the exception: java.lang.IllegalStateException: Must be called from a blob upload callback request.
Looking at the request in the debugger, the required blobkey attribute isn't found, nor are any of the other input parameters in the form. Looking at the raw request (the one dispatched to /_ah/upload/...), the form parameters are present.
I use the google cloud tools app-engine-plugin, which uses the gcloud python dev server to run the generated war.
I realize blobstore is an older GAE feature, but as this code is "working" on prod, I'd prefer to not have to switch to the newer subsystem.
Anyone able to give me a clue where to look to get this all working on my dev server?
Thanks,
-- Dave
p.s. below is the request forwarded on to my servlet after the uploaded file has been stripped out:
POST /uploadSurvey HTTP/1.1
Accept-Encoding: identity
X-APPENGINE-BACKEND-ID: 8
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
X-APPENGINE-SERVER-NAME: localhost
Cookie: JSESSIONID=5773y31x3eut
X-Appengine-User-Email:
X-APPENGINE-DEFAULT-VERSION-HOSTNAME: localhost:8888
X-APPENGINE-SERVER-PROTOCOL: HTTP/1.1
X-Appengine-User-Organization:
X-APPENGINE-DEV-SCRIPT: unused
ORIGIN: http://localhost:8888
X-Appengine-User-Id:
Accept-Language: en-us
X-APPENGINE-SERVER-SOFTWARE: Development/2.0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/602.4.8 (KHTML, like Gecko) Version/10.0.3 Safari/602.4.8
X-Appengine-User-Nickname:
Host: localhost:8888
X-Appengine-Dev-Request-Id: wCTAonUKrB
Content-Type: multipart/form-data; boundary="===============1477989950756010976=="
Content-Length: 1372
X-APPENGINE-REQUEST-LOG-ID: 5e8eaef5aff4add89b774badea1fd3a30da8be
X-Appengine-User-Is-Admin: 0
UPGRADE-INSECURE-REQUESTS: 1
X-APPENGINE-SERVER-PORT: 8888
Referer: http://localhost:8888/settings
X-AppEngine-Country: ZZ
X-APPENGINE-REQUEST-ID-HASH: BFD4FDDA
X-APPENGINE-REMOTE-ADDR: ::1
Update:
I added some debugging to http_proxy.py under the gcloud devserver2 directory and observed this content type being forwarded. I'm even more confused now, as it looks like the content is present...
--===============5516630363169856841==
Content-Type: message/external-body; blob-key="XOQvaKc1cdczcwkIHfRFOw=="; access-type="X-AppEngine-BlobKey"
Content-Disposition: form-data; name="myFile"; filename="Naro Group - SNHU - Sales Readiness Assessment - Dec 2016.xls"
Content-Type: application/vnd.ms-excel
Content-Length: 164352
Content-MD5: NjBiNjI0N2M3MjZiMzc3NWMxZDQxYmM5YTU2YmM5YmM=
content-disposition: form-data; name="myFile"; filename="Naro Group - SNHU - Sales Readiness Assessment - Dec 2016.xls"
X-AppEngine-Upload-Creation: 2017-02-16 20:17:05.729401
--===============5516630363169856841==
Content-Type: text/plain
Content-Disposition: form-data; name="newSurveyId"
10001
--===============5516630363169856841==
Content-Type: text/plain
Content-Disposition: form-data; name="newSurveyName"
N
--===============5516630363169856841==
Content-Type: text/plain
Content-Disposition: form-data; name="newUserMessage"
--===============5516630363169856841==
Content-Type: text/plain
Content-Disposition: form-data; name="selectedClient"
6
--===============5516630363169856841==
Content-Type: text/plain
Content-Disposition: form-data; name="selectedPsf"
3
--===============5516630363169856841==
Content-Type: text/plain
Content-Disposition: form-data; name="selectedSection"
1
--===============5516630363169856841==--

I figured out a workaround, but I do think there is a bug here the Google folks need to look at. The Cloud Tools python dev server wasn't putting the all important X-AppEngine-BlobUpload header element into the rewritten request header. I modified blob_upload.py and http_proxy.py to do this (in tools/devserver2 under google-cloud-sdk). I then had to steel some code from Google's own production server code base, ParseBlobUploadFilter.java, to process the non-standard request payload and build the missing request attributes and make accessible the original request parameters. Of course, this code path should only be taken when running on a local dev server; this code is correctly called by the Google App Engine.

Related

file could not be downloaded in IE 11 when a compressed(gzipped) response is sent to the client

I am trying to download a file from an application through two frameworks. one with struts 1(older framework) and the other with Spring MVC(migrated from old).In Spring migrated application it is showing file could not be downloaded in IE 11 when a compressed(gzipped) response is sent to the client. It is showing "File could not be downloaded". It works fine in chrome as well as in older struts framework. Also if the response is not compressed, file gets downloaded successfully on IE as well on Spring MVC. I cannot really identify the cause here. Requesting some guidance and help to identify this problem.
Request headers is
<code>
Request URL: //edited
Request Method: POST
Status Code: 200 / OK
- Request Headers
Accept: text/html, application/xhtml+xml, image/jxr, */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US
Authorization: Basic YW3Rt2aW46dG4V3zdD5EyMz6Q=
Cache-Control: no-cache
Connection: Keep-Alive
Content-Length: 3521
Content-Type: application/x-www-form-urlencoded
Cookie: JSESSIONID=urVBPpjD3QrP6KhkqCK4r8KSAuvKFSVPdp-UXyz-FYSz4W0cQmV9sh!4524586920
Host: localhost:7001
Referer: //edited
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
</code>
Below is the response headers
<code>
Response Headers
Cache-Control: private, no-cache, no-store
Content-Disposition: attachment; filename="Closed DSP01 CRD0037_2019-12-26_133924.csv"
Content-Encoding: gzip
Content-Type: text/csv; charset=UTF-8
Date: Thu, 26 Dec 2019 08:09:23 GMT
Expires: 0
Pragma: no-cache
Transfer-Encoding: chunked
</code>
particular code where compression is done
<code>
if (canUseGzip) {
response.setHeader("Content-Encoding", "gzip");
GZIPOutputStream out = new GZIPOutputStream(response.getOutputStream());
pw = new OutputStreamWriter(out, "UTF-8");
} else {
pw = response.getWriter();
}
</code>
I tried different possibilities and when I tried to explicitly set content-length header ,say,
response.setHeader("Content-Length", String.valueOf(1024));
the file got downloaded successfully in IE. When I googled, I found transfer-encoding and content-length are mutually exclusive and former is already on the response. I don't know why adding content-length worked here and it was required only on the Spring migrated code. File gets downloaded successfully on older struts framework without mentioning the content-length header.
Is there anything specific I am missing here? Is there any other permanent solution as well?
Also how to correctly set the content length? I randomly tried with 1024 and it may fail for some other data.
Any help is appreciated

Spark java Serving mp3 file (dynamic with seek)

I'm trying to create a simple REST service to serve audio files (with seek support).
I'm using this example which is based on Jersey:
https://github.com/aruld/jersey-streaming/tree/jersey2
This is a quite simple example, it listens to GET and HEAD requests used by the browsers, look for the Range header and respond with 206 plus the archive slice requested (with byte ranges).
The catch here is that I'm re-writing this on spark java (a tiny framework with an embedded jetty server).
Every thing seems to be OK. The browser sends the GET and the server crates the response accordingly... though the player never loads nor plays anything. The request is made and the response header is perfect:
Request:
Host: localhost:4567
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0
Accept: audio/webm,audio/ogg,audio/wav,audio/*;q=0.9,application/ogg;q=0.7,video/*;q=0.6,*/*;q=0.5
Accept-Language: en-US,en;q=0.5
Range: bytes=0-
Connection: keep-alive
Response:
Accept-Ranges: bytes
Content-Length: 1048577
Content-Range: bytes 0-1048576/5563904
Content-Type: audio/mp3
Date: Sat, 20 Aug 2016 05:41:23 GMT
Last-Modified: Sat Aug 20 01:12:37 BRT 2016
Server: Jetty(9.3.6.v20151106)
sasd
One thing I noticed is that seems the transfer for this request never ends. When I close the server, the transfer ends at 0,03KB (always).
My proof of concept app code:
http://pastebin.com/xjkLne7E
Found an answer!
I did some more research and found that it is a Spark feature to be implemented:
https://github.com/perwendel/spark/issues/397
User tchoulihan already tried to implement such feature with success here:
https://github.com/tchoulihan/torrenttunes-client
Here is a sample of the spark get request that handles the upload:
https://github.com/tchoulihan/torrenttunes-client/blob/master/src/main/java/com/torrenttunes/client/webservice/Platform.java#L555
I can't paste a blob here since GPLv3 would clash with cc-wiki license. Hes work is inspired on the same resource I first found. Based on that I have coded a version of my own that works on android, mozilla and chrome.
TL;DR The problem was that mozilla doesn't understand 206 request properly and I wasn't closing and flushing the ByteOutputStream. Also I tried to use the StreamingOutput from JAX as a response instead of sending a simple raw http response.

Java http request for django rest framework service throws http 406 exception

I hava a django rest framework web service that works fine with httpie and firefox: when I request with httpie I have a json formatted answer and when I request with firefox an html formatted one (httpie is a http client).
Now I'm building java API to communicate with services. I'm using URL class to perform requests.
I can receive html-formatted answers from the server if I don't override the content-type property. So I looked how httpie overrides this property and did the same:
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
connection.setRequestProperty("Accept", "*\\*");
Now the communication end with Http 406 error, which means that client can't accept the answer.
If I use only the content-type property I have no error but still the html-formatted answer
Does anyone know how to solve it?
EDIT (adding requests' header):
httpie:
GET /match/39.3280114/16.241917599999965/0/5/ HTTP/1.1
Host: 127.0.0.1:8001
Connection: keep-alive
Accept-Encoding: gzip, deflate
Accept: /
User-Agent: HTTPie/0.9.3
java-API
GET /match/39.3280114/16.241917599999965/0/5/ HTTP/1.1
Host: 127.0.0.1:8001
Accept-Encoding: gzip, deflate
Accept: **
User-Agent: Java-API
Solved: I was using the wrong slash for Accept property
Your Accept header is malformed. It should be:
Accept: */*
See RFC 7231 § 5.3.2.
However, */* means “any media type.” If you actually want a specific media type (JSON), you should request it:
Accept: application/json

HTTP gzip encoding with multipart message

I am trying to send a gziped multipart POST to a Tomcat server from a Java application using Jersey. When the multipart request is not compressed, it works perfectly fine. Other types of compressed POSTS work fine, such as sending a single entity XML. I (believe) posting compressed data isn't an HTTP standard, but it does seem Tomcat supports it to some degree.
a working uncompressed multipart post:
POST /myApp/rest/data HTTP/1.1
Content-Type: multipart/mixed; boundary=Boundary_1_23237284_1331130438482
Cookie: JSESSIONID=XXXXXXXXXXXXXXXXXXXXXXXXX;Version=1;Path=/myApp/
MIME-Version: 1.0
User-Agent: Java/1.6.0_26
Host: localhost:8080
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Transfer-Encoding: chunked
d3
--Boundary_1_23237284_1331130438482
Content-Type: application/octet-stream
Content-Disposition: form-data; filename="uploadFile.war"; modification-date="Wed, 29 Feb 2012 18:01:38 GMT"; size=25343899; name="file"
{binary data here}
--Boundary_1_25179713_1331128929019
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><myXMLEntity>stuff</myXMLEntity>
--Boundary_1_25179713_1331128929019--
When I compress it using the Jersey GZIPContentEncodingFilter() the following headers are sent, and I get back an HTTP 400 with a description of "incorrect syntax"
POST /myApp/rest/data HTTP/1.1
Content-Type: multipart/mixed
Cookie: JSESSIONID=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX;Version=1;Path=/myApp/
Accept-Encoding: gzip
Content-Encoding: gzip
User-Agent: Java/1.6.0_26
Host: localhost:8080
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Transfer-Encoding: chunked
{binary data here}
Is what I'm trying to do possible? Should the Content-Type actually read multipart/x-gzip? I notice that when it gets compressed, the boundary text gets left off of the Content-Type header - is this also a problem?
I ran into this same issue (or something very similar) and tracked it down to the Content-Type header missing the boundary parameter when using GZIPContentEncodingFilter. I was able to work around it by using MultiPartMediaTypes.createFormData() when setting the type of the entity I was POSTing from the Jersey client. Doing so makes sure the boundary parameter is set earlier than Jersey would automatically set it, which seems to be too late when using the GZIPContentEncodingFilter for compressing the request entity. There is an equivalent method for multipart/mixed.
I don't have an IDE handy but something similar to this:
// client is a Jersey Client object
client.resource(uri).entity(multipartFormData, MultiPartMediaTypes.createFormData()).post(ClientResponse.class);
All that said, this will still only work if your server is able to handle GZIP compressed requests.
IMO you can't do this that way, because the server and the client need to agree on how to communicate (e.g. zip compression). HTTP is designed as request/response and server can return what the client can support.
The client sends request to the saying, "Hey server, I need this resource and I support gzip, so you can return gzip if you can". :)
Imagine a situation that your client sends to the server a few megabytes in gzip, but the server doesn't support this.

Need to submit forms twice to be processed?

Recently I notice that all action for submitting forms need to be executed twice to actually submit the values. Before this worked fine when I hard coded the resulting action path from my backing beans, but since I started to resolve all action path via the faces-config strange things has started to happen. One thing is that I need to press a submit button twice to fire the action, the browser fires the POST. I can see in firebug that two POST is fired and only the last one is processed by Glassfish.
Don know if this is side effect of the problem I experience. But many times when I do a forward the page losses CSS classes and things start to look ugly and some components (Richfaces - datatable) start to render badly. Actually I have to do redirect most of the time for things to render properly.
What can be the reason that I need to do two POST/submit before the Glassfish server will process the request ?
Why do I constantly need to redirect to make components (Richfaces) to render correctly ?
I use:
Glassfish 3.1
NetBeans 7.0.1
Richfaces 4.0-final
Sample of my Faces-config;
<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
<from-outcome>CreateActivity</from-outcome>
<to-view-id>/app_user/activity/Create.xhtml</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
<from-outcome>CreateActivityDetail</from-outcome>
<to-view-id>/app_user/activity/UpdateDetail.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
Firebug log --
First POST (doesn't work when submitted)
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:6.0) Gecko/20100101 Firefox/6.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Faces-Request: partial/ajax
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:8080/humis/faces/app_user/activity/List.xhtml
Content-Length: 430
Cookie: JSESSIONID=cd70a069ad528b3923bd52f12b1c
JSESSIONID=cbe0c977cf1c550f2a57cb4b334f;
treeForm_tree-hi=treeForm:tree:resources:jmsResources:jmsDestinationResources
Pragma: no-cache
Cache-Control: no-cache
First Response
X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 3.1.1 Java/Sun Microsystems Inc./1.6), JSF/2.0
Server: GlassFish Server Open Source Edition 3.1.1
Cache-Control: no-cache
Content-Type: text/xml;charset=ISO-8859-1
Content-Length: 667
Date: Thu, 18 Aug 2011 13:30:22 GMT
Second POST (the one that works)
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:6.0) Gecko/20100101 Firefox/6.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Faces-Request: partial/ajax
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:8080/humis/faces/app_user/activity/List.xhtml
Content-Length: 494
Cookie: JSESSIONID=cd70a069ad528b3923bd52f12b1c;
JSESSIONID=cbe0c977cf1c550f2a57cb4b334f;
treeForm_tree-hi=treeForm:tree:resources:jmsResources:jmsDestinationResources
Pragma: no-cache
Cache-Control: no-cache
Second Response
X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 3.1.1 Java/Sun Microsystems Inc./1.6), JSF/2.0
Server: GlassFish Server Open Source Edition 3.1.1
Cache-Control: no-cache
Content-Type: text/xml;charset=UTF-8
Content-Length: 145
Date: Thu, 18 Aug 2011 13:30:44 GMT
It sounds like that you've mapped the FacesServlet on multiple URL-patterns and that in some way all the autogenerated URLs to CSS and JS resources become invalid. Doing some basic debugging and investigation of the <link> and <script> URLs in the JSF-generated HTML source and the HTTP traffic with for example Firebug should give a lot of insights as to what exactly broke. You should have seen 404's for those CSS/JS resources.
As this trivial debugging information is missing in your question, I can't answer anything else than suggesting to trying to explicitly map the FacesServlet on a single URL pattern, e.g. *.xhtml.

Categories

Resources