Google App Engine not generating 304, instead generating 200 always - google-app-engine

Google App engine always generates 200 for the url /test.js and
test.js is not a static resource, but a url pattern for dynamically generated content. The content will expire after N hours and a fresh content will be generated.
I've tried with Last-Modified, ETag and Cache-Control. None seems to work.
Request
Request URL:http://localhost:8081/test.js
Request Method:GET
Status Code: 200 OK
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Host:localhost:8081
If-Modified-Since:Fri, 18 Oct 2013 14:10:39 GMT
If-None-Match:"1B2M2Y8AsgTpgAmY7PhCfg"
Referer:http://localhost:8080/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
Response Headers
cache-control:public, max-age=360000
Content-Length:2
content-type:application/script; charset=utf-8
Date:Fri, 18 Oct 2013 14:10:40 GMT
etag:"1B2M2Y8AsgTpgAmY7PhCfg"
expires:Tue, 22 Oct 2013 18:10:40 GMT
last-modified:Fri, 18 Oct 2013 14:10:40 GMT
Server:Development/2.0

Your request has Cache-Control:max-age=0, so any intermediate caches (incl. the browser-cache) won't serve cached content. This is likely a result of a setting in your browser.
For requests with revalidate headers (If-X), you need to have the logic in place to act properly. To save bandwidth, this is pretty simple with
webob (which is used by webapp2 and other frameworks) and the conditional-response setting. Avoiding computation as well depends a little more on what you're doing, but webob helps here too.
Redbot is a really useful tool for checking HTTP cache behaviour.

Refer to this for HTTP status:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
200 is just the correct HTTP OK status, that doesn't have any interpretation on whether the resource is static or not. (Try any dynamic web page out there like e.g. facebook) and you will notice it's 200. Having a response of 200 is perfectly normal
for 304 it's "Not Modified" - As mentioned in w3 "The 304 response MUST NOT contain a message-body". This is not what you want.
In your case your concern should be to set the correct expiry time for these http header (do it within your program code), so that the browser always request for a fresh copy of content after the expiry time (e.g. after 1 hour):
cache-control:public, max-age=3600
expires:Tue, 20 Oct 2013 18:10:40 GMT

Related

Why is POST Response Data Not Received in Internet Explorer?

I have an AngularJS web app that accesses a .NET WebAPI server end. Authentication is implemented through the AngularJS-OAuth2 library. I have the app and the WebAPI hosted in localhost under two different port numbers. I have also enabled Microsoft.Owin.Cors package on the server end to handle cross-domain requests.
In Chrome, GET and POST requests return data to the front-end. By inspecting the traffic through Fiddler I could see that a pair of requests/responses are sent (preflight/OPTIONS + actual) and also the relevant CORS headers (including origin and Access-Control-* headers) in both the requests and the responses. All as expected.
However, in Internet Explorer, my GET requests return data through the $http service but the POST does not. I could inspect that there are no preflight requests or CORS headers (I think IE treats different ports as the same origin). In checking the POST request/response in IE through Fiddler I could observe that it returns HTTP status 200 but state of Aborted (with X-ABORTED-WHEN: SendingResponse flag set). I could also inspect the JSON response with the correct data returned.
I have also tried setting a high timeout to no avail. The $http call looks like this:
return $http.post(apiUrl + "/search", service.getParameters(), { timeout: 600000 })
.success(function (data) {...
Fiddler shows something like this for the IE POST request:
Also (only) in IE, an unintentional page refresh is also triggered with the same button click as this POST operation.
Why does Internet Explorer abort only the POST requests when the correct data is also returned to the client and when Chrome does not have any issues at all?
Additional Information
Request:
POST https://localhost:44321/api//search HTTP/1.1
Content-Type: application/json;charset=utf-8
Accept: application/json, text/plain, */*
Authorization: Bearer <token>
Referer: https://localhost:44322/search
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: localhost:44321
Content-Length: 202
DNT: 1
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: .ASPXANONYMOUS=<cookie>
Reponse:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: <file>
X-Powered-By: ASP.NET
Date: Wed, 10 Feb 2016 13:43:45 GMT
Content-Length: 2284
Fiddler session properties:
SESSION STATE: Aborted.
Request Entity Size: 202 bytes.
Response Entity Size: 2284 bytes.
== FLAGS ==================
BitFlags: [IsHTTPS, ClientPipeReused, ServerPipeReused] 0x19
X-ABORTED-WHEN: SendingResponse
X-CLIENTIP: 127.0.0.1
X-CLIENTPORT: 41889
X-EGRESSPORT: 41890
X-HOSTIP: ::1
X-PROCESSINFO: avp:3584
X-RESPONSEBODYTRANSFERLENGTH: 2,284
X-SERVERSOCKET: REUSE ServerPipe#168
== TIMING INFO ============
ClientConnected: 19:13:42.408
ClientBeginRequest: 19:13:42.444
GotRequestHeaders: 19:13:42.444
ClientDoneRequest: 19:13:42.772
Determine Gateway: 0ms
DNS Lookup: 0ms
TCP/IP Connect: 0ms
HTTPS Handshake: 0ms
ServerConnected: 19:13:42.413
FiddlerBeginRequest: 19:13:42.772
ServerGotRequest: 19:13:42.772
ServerBeginResponse: 19:13:45.360
GotResponseHeaders: 19:13:45.360
ServerDoneResponse: 19:13:45.360
ClientBeginResponse: 19:13:45.360
ClientDoneResponse: 19:13:45.360
Overall Elapsed: 0:00:02.915
The response was buffered before delivery to the client.
== WININET CACHE INFO ============
This URL is not present in the WinINET cache. [Code: 2]
* Note: Data above shows WinINET's current cache state, not the state at the time of the request.
* Note: Data above shows WinINET's Medium Integrity (non-Protected Mode) cache only.
I believe you get bitten by the P3P policy requirement of IE here:
Internet Explorer supports a cookie-restricting privacy feature called P3P. Web developers often get tripped up by it because no other browser implements the P3P standard.
It seems similar to those QAs:
CORS request with IE11
CORS doesn't work with cookies in IE10
Internet Explorer 10 is ignoring XMLHttpRequest 'xhr.withCredentials = true'
Here's a blog post with an example how to send P3P information. Here's a document from Microsoft about P3P configuration

Google Cloud Storage JSON API: batch delete returns Unknown Error (500 Internal Server Error)

I'm trying to delete several object in batch using the documentation: Sending Batch Requests. Here is my request (<my_api_key> is the valid API key and other methods like list contents of a bucket work good, <my_bucket> is the placeholder for the exact bucket name):
POST /batch?key=<my_api_key>
host: www.googleapis.com
content-type:multipart/mixed; boundary="===============7330845974216740156=="
--===============7330845974216740156==
Content-Type: application/http
Content-Transfer-Encoding:binary
DELETE /storage/v1/b/<my_bucket>/o/James-Hetfield-happy.jpg
--===============7330845974216740156==
Here is the response:
access-control-allow-credentials:true
access-control-allow-origin:chrome-extension://fdmmgilgnpjigdojojpjoooidkmcomcm
access-control-expose-headers:Cache-Control,Content-Encoding,Content-Length,Content-Type,Date,Expires,Pragma,Server,Vary
alternate-protocol:443:quic,p=0.02
cache-control:no-cache, no-store, max-age=0, must-revalidate
content-encoding:gzip
content-length:33
content-type:text/html; charset=UTF-8
date:Tue, 10 Feb 2015 16:55:14 GMT
expires:Fri, 01 Jan 1990 00:00:00 GMT
pragma:no-cache
server:GSE
status:500 Internal Server Error
vary:Origin
vary:X-Origin
version:HTTP/1.1
x-content-type-options:nosniff
x-frame-options:SAMEORIGIN
x-xss-protection:1; mode=block
Unknown Error
When I send the following body:
--===============7330845974216740156==
DELETE /storage/v1/b/<my_bucket>/o/James-Hetfield-happy.jpg
--===============7330845974216740156==
I got 200 OK response with the body --batch_rTJhZwR1jHM_AAh2WtGp7ik-- but the file still exists.
Please advise proper format for sending batch delete requests using Google Cloud Storage JSON API.
My bad. When copy-pasting from examples, a spare space character appeared right after the header Content-Type: application/http. When I removed all spare characters in request's headers the batch delete worked good.
Conclusions: validate syntax of requests manually after copy-pasting.

Breeze always 404

I built a oData-Service which works well.
Now I want to query them with Breeze but I always get an 404 error.
In Fiddler I see first a 200 with empty Body and then follows the 404 with "No HTTP resource was found that matches the request URI".
But running in a normal browser the odata Service delivers the right data.
I debug it and set a breakpoint on the Server to see if the service is called correctly. After sending the request the breakpoint shows that the Service is called. But parallel to the break breeze calls the fail-method of
manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
and e.message is empty.
Any idea??? :-/
Next entry...
I deactivate CORS and now I get the 404 immediately.
Here is the request:
OPTIONS xxx:8080/myDataService/odata/Person?$select=Name HTTP/1.1
Host: xxx:8080
Connection: keep-alive
Access-Control-Request-Method: GET
Origin:localhost:61244
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36
Access-Control-Request-Headers: dataserviceversion, accept
Accept: /
Referer: http://localhost:61244/index.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
and here the answer:
HTTP/1.1 404 Not Found
Content-Length: 178
Content-Type: application/json; odata.metadata=minimal
Server: Microsoft-HTTPAPI/2.0
OData-Version: 4.0
Date: Mon, 19 Jan 2015 12:11:48 GMT
{
"error":{
"code":"","message":"No HTTP resource was found that matches the request URI xxx:8080/myDataService/odata/Person?$select=Name'."
}
}
No surprise that the OPTION request passes; you've probably got the server set to say "OK" to every request ... and that's just fine for now.
The rejection has to do with the address of your GET request: xxx:8080/myDataService/odata/Person. Are you sure that's the right endpoint? The path is usually plural (Persons).
It's easy enough to just paste that url into a browser address bar and see what it does.

Google Webmaster Tools gets 301 Redirect incorrectly

In Google Webmaster Tools, I tell Google to crawl and render my site:
Inlcuding:
http://smartnavi-app.com/download
Google shows me its HTTP Response:
HTTP/1.1 301 Moved Permanently
Date: Mon, 11 Aug 2014 07:24:56 GMT
Server: nginx/1.4.2
Connection: Keep-Alive
Content-Type: text/html;charset=UTF-8
Location: http://smartnavi-app.com/index.html
Content-Length: 0
Keep-Alive: timeout=5, max=100
But there should be NO redirect for that URL!
If I open this URL I am correctly not redirected. So why is Google?
I got it. I am using prerender.io to cache my AJAX Website for Crawler-Bots.
In the .htaccess my Domain in the prerender.io part was an old one, so there was kind of a redirect loop.
So if you change redirects etc. never forget your prerender.io stuff!

GAE: Enabling Edge Cache with webapp2 (Python)

There has been this new video on youtube demonstrating the strength of EdgeCaching in the GAE architecture, and at this particular point in the video they demonstrate how easy it is to leverage:
http://www.youtube.com/watch?v=QJp6hmASstQ#t=11m12
Unfortunately it's not that easy...
I'm looking to enable edge caching using the webapp2 framework provided by Google.
I'm calling:
self.response.pragma = 'Public'
self.response.cache_expires(300)
but it seems overridden by something else.
The header I get is:
HTTP/1.1 200 OK
Pragma: Public
Cache-Control: max-age=300, no-cache
Expires: Sat, 23 Feb 2013 19:15:11 GMT
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
X-AppEngine-Estimated-CPM-US-Dollars: $0.000085
X-AppEngine-Resource-Usage: ms=39 cpu_ms=64
Date: Sat, 23 Feb 2013 19:10:11 GMT
Pragma: no-cache
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Cache-Control: no-cache, must-revalidate
Vary: Accept-Encoding
Server: Google Frontend
Content-Length: 600
I'm using ndb top level:
app = ndb.toplevel(webapp2.WSGIApplication(...
I tried the technics explained here, but they don't seem to apply to webapp2:
http://code.google.com/p/googleappengine/issues/detail?id=2258#c14
I also looked at this post too:
https://groups.google.com/d/topic/webapp2/NmHXoZZSVvo/discussion
I tried to set everything manually with no success. Something is overriding my cache settings.
Is there a way to make it work with webapp2? Any other option is welcome.
EDIT: I'm using an url with version prefix: http://version.appname.appspot.com and it's probably the cause of my problem.
This should be all you need:
self.response.cache_control = 'public'
self.response.cache_control.max_age = 300
Check Caching Details for more information, may be you broke some rules. Next the best part:
A response can be stored in Cloud CDN caches only if all of the following are true:
It was served by a backend service with caching enabled.
It was a response to a GET request.
The status code was 200, 203, 300, 301, 302, 307, or 410.
It has a Cache-Control: public directive.
It has a Cache-Control: s-maxage, Cache-Control: max-age, or Expires
header.
It has either a Content-Length header or a Transfer-Encoding header.
Additionally, there are checks that will block caching of responses. A response will not be cached if any of the following are true:
It has a Set-Cookie header.
Its body exceeds 4 MB.
It has a Vary header with a value other than Accept, Accept-Encoding, or - Origin.
It has a Cache-Control: no-store, no-cache, or private directive.
The corresponding request had a Cache-Control: no-store directive.
I'm guessing that you're mixing up two related but distinct ideas.
The first idea, which the video you link to talks about, is arranging to have certain files in your app served by a pool of App Engine servers that specialize in serving static content. This is faster than having your app serve these files, since there won't be a delay to start up a new instance of your app to serve a static file. (Strongly consider serving up your .js and .css this way.) This static serving facility is controlled entirely at app update (upload) time, via declarations you make in app.yaml (or appengine-web.xml for Java apps).
The second idea is arranging, via HTTP response headers, for pages that your app emits to be cacheable by caches outside of app engine.
If you declare files as static, you have some control over addition HTTP response headers that get served along with the file. See the documentation on configuring static files.

Resources