From 105696774f1aaf007213404c7bf3dbd08b6ee248 Mon Sep 17 00:00:00 2001
From: =?utf8?q?Stefan=20K=C3=B6gl?=
Date: Mon, 24 Jan 2011 17:28:58 +0200
Subject: [PATCH] Provide HTML representation of responses
---
feedservice/app.yaml | 3 +++
feedservice/index.html | 31 ++++++++++++-------------------
feedservice/main.py | 35 ++++++++++++++++++++++++++---------
feedservice/static/prettify.css | 1 +
feedservice/static/prettify.js | 33 +++++++++++++++++++++++++++++++++
feedservice/static/screen.css | 7 +++++++
6 files changed, 82 insertions(+), 28 deletions(-)
create mode 100644 feedservice/static/prettify.css
create mode 100644 feedservice/static/prettify.js
create mode 100644 feedservice/static/screen.css
diff --git a/feedservice/app.yaml b/feedservice/app.yaml
index 81f6f07..b769dcd 100644
--- a/feedservice/app.yaml
+++ b/feedservice/app.yaml
@@ -8,6 +8,9 @@ handlers:
static_files: index.html
upload: index.html
+- url: /static
+ static_dir: static
+
- url: /.*
script: main.py
diff --git a/feedservice/index.html b/feedservice/index.html
index 17ecf6d..7181434 100644
--- a/feedservice/index.html
+++ b/feedservice/index.html
@@ -1,19 +1,11 @@
+
+
+
gpodder.net Feed-Service
-
-
@@ -25,12 +17,12 @@ a, a:visited {
Usage
Examples
-
- curl http://mygpo-feedservice.appspot.com/parse?url =http://feeds.feedburner.com/linuxoutlaws&inline_logo =1&scale_logo =30
- curl http://mygpo-feedservice.appspot.com/parse?url =http://leo.am/podcasts/floss&url =http://feeds.twit.tv/floss_video_large
- curl http://mygpo-feedservice.appspot.com/parse?url =http://www.dancarlin.com/cswdc.xml
- curl -d url=http://feeds.feedburner.com/linuxoutlaws http://mygpo-feedservice.appspot.com/parse
-
+
+ SERVER=http://mygpo-feedservice.appspot.com/parse
+ curl --header "Accept: application/json" "$SERVER?url =http://feeds.feedburner.com/linuxoutlaws&inline_logo =1&scale_logo =30" #^
+ curl --header "Accept: application/json" "$SERVER?url =http://leo.am/podcasts/floss&url =http://feeds.twit.tv/floss_video_large" #^
+ curl --header "Accept: application/json" "$SERVER?url =http://www.dancarlin.com/cswdc.xml&strip_html =1" #^
+ curl --header "Accept: application/json" -d "url=http://feeds.feedburner.com/linuxoutlaws" $SERVER #^
Requests
Parameters to /parse (either GET or POST as application/x-www-form-urlencoded)
@@ -45,7 +37,7 @@ a, a:visited {
If-Modified-Since : Time when all requested feeds have been accessed the last time. The response will only contain podcasts that have been modified in the meantime.
User-Agent : Clients should send a descriptive User-Agent string. In case of abuse of the service, misbehaving and/or generic user-agents might be blocked.
- Accept : Clients should send Accept: application/json to indicate that they are prepared to receive JSON data. Currently this is the only supported format, but in the future, this header might be used to distinguish between different formats.
+ Accept : Clients should send Accept: application/json to indicate that they are prepared to receive JSON data. If you send a different Accept header, you will receive a HTML formatted response.
@@ -84,7 +76,8 @@ a, a:visited {
Headers
- Last-Modified : The timestamp of the latest change to any of the requested feeds. This value can be used in the If-Modified-Since parameter to subsequent requests.
+ Last-Modified : The timestamp of the latest change to any of the requested feeds. This value can be used in the If-Modified-Since parameter to subsequent requests. This header is not sent for the HTML formatted response.
+ Content-Type : application/json if your request contains Accept: application/json , otherwise the response will contain the HTML representation with text/html .
diff --git a/feedservice/main.py b/feedservice/main.py
index fb82ba8..2689dec 100644
--- a/feedservice/main.py
+++ b/feedservice/main.py
@@ -16,30 +16,47 @@ class Parse(webapp.RequestHandler):
return self.get()
def get(self):
- self.response.headers['Content-Type'] = 'text/plain'
urls = self.request.get_all('url')
urls = map(urllib.unquote, urls)
inline_logo = self.request.get_range('inline_logo', 0, 1, default=0)
scale_to = self.request.get_range('scale_logo', 0, 1, default=0)
strip_html = self.request.get_range('strip_html', 0, 1, default=0)
modified = self.request.headers.get('If-Modified-Since', None)
+ accept = self.request.headers.get('Accept', 'application/json')
if urls:
podcasts, last_modified = feeddownloader.parse_feeds(urls, inline_logo, scale_to, strip_html, modified)
- pretty = json.dumps(podcasts, sort_keys=True, indent=4)
-
- if last_modified:
- from email import utils
- import time
- self.response.headers.add_header('Last-Modified', utils.formatdate(time.mktime(last_modified)))
-
- self.response.out.write(pretty)
+ self.send_response(podcasts, last_modified, accept)
else:
self.response.set_status(400)
self.response.out.write('parameter url missing')
+ def send_response(self, podcasts, last_modified, format):
+ if 'json' in format:
+ content_type = 'application/json'
+ content = json.dumps(podcasts, sort_keys=True, indent=None, separators=(',', ':'))
+ from email import utils
+ import time
+ self.response.headers.add_header('Last-Modified', utils.formatdate(time.mktime(last_modified)))
+
+
+ else:
+ import cgi
+ content_type = 'text/html'
+ pretty_json = json.dumps(podcasts, sort_keys=True, indent=4)
+ pretty_json = cgi.escape(pretty_json)
+ content = """
+
+
+
+HTML Response This response is HTML formatted. To get just the JSON data for processing in your client, send the HTTP Header Accept: application/json . Back to the Documentation
%s """ % pretty_json
+
+ self.response.headers['Content-Type'] = content_type
+ self.response.out.write(content)
+
+
application = webapp.WSGIApplication([
('/', MainPage),
('/parse', Parse)
diff --git a/feedservice/static/prettify.css b/feedservice/static/prettify.css
new file mode 100644
index 0000000..2925d13
--- /dev/null
+++ b/feedservice/static/prettify.css
@@ -0,0 +1 @@
+.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun{color:#660}.pln{color:#000}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec{color:#606}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}@media print{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun{color:#440}.pln{color:#000}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}
\ No newline at end of file
diff --git a/feedservice/static/prettify.js b/feedservice/static/prettify.js
new file mode 100644
index 0000000..c9161da
--- /dev/null
+++ b/feedservice/static/prettify.js
@@ -0,0 +1,33 @@
+window.PR_SHOULD_USE_CONTINUATION=true;window.PR_TAB_WIDTH=8;window.PR_normalizedHtml=window.PR=window.prettyPrintOne=window.prettyPrint=void 0;window._pr_isIE6=function(){var y=navigator&&navigator.userAgent&&navigator.userAgent.match(/\bMSIE ([678])\./);y=y?+y[1]:false;window._pr_isIE6=function(){return y};return y};
+(function(){function y(b){return b.replace(L,"&").replace(M,"<").replace(N,">")}function H(b,f,i){switch(b.nodeType){case 1:var o=b.tagName.toLowerCase();f.push("<",o);var l=b.attributes,n=l.length;if(n){if(i){for(var r=[],j=n;--j>=0;)r[j]=l[j];r.sort(function(q,m){return q.name");
+for(l=b.firstChild;l;l=l.nextSibling)H(l,f,i);if(b.firstChild||!/^(?:br|link|img)$/.test(o))f.push("",o,">");break;case 3:case 4:f.push(y(b.nodeValue));break}}function O(b){function f(c){if(c.charAt(0)!=="\\")return c.charCodeAt(0);switch(c.charAt(1)){case "b":return 8;case "t":return 9;case "n":return 10;case "v":return 11;case "f":return 12;case "r":return 13;case "u":case "x":return parseInt(c.substring(2),16)||c.charCodeAt(1);case "0":case "1":case "2":case "3":case "4":case "5":case "6":case "7":return parseInt(c.substring(1),
+8);default:return c.charCodeAt(1)}}function i(c){if(c<32)return(c<16?"\\x0":"\\x")+c.toString(16);c=String.fromCharCode(c);if(c==="\\"||c==="-"||c==="["||c==="]")c="\\"+c;return c}function o(c){var d=c.substring(1,c.length-1).match(RegExp("\\\\u[0-9A-Fa-f]{4}|\\\\x[0-9A-Fa-f]{2}|\\\\[0-3][0-7]{0,2}|\\\\[0-7]{1,2}|\\\\[\\s\\S]|-|[^-\\\\]","g"));c=[];for(var a=[],k=d[0]==="^",e=k?1:0,h=d.length;e122)){s<65||g>90||a.push([Math.max(65,g)|32,Math.min(s,90)|32]);s<97||g>122||a.push([Math.max(97,g)&-33,Math.min(s,122)&-33])}}a.sort(function(v,w){return v[0]-w[0]||w[1]-v[1]});d=[];g=[NaN,NaN];for(e=0;eh[0]){h[1]+1>h[0]&&a.push("-");
+a.push(i(h[1]))}}a.push("]");return a.join("")}function l(c){for(var d=c.source.match(RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g")),a=d.length,k=[],e=0,h=0;e=2&&c==="[")d[e]=o(g);else if(c!=="\\")d[e]=g.replace(/[a-zA-Z]/g,function(s){s=s.charCodeAt(0);return"["+String.fromCharCode(s&-33,s|32)+"]"})}return d.join("")}for(var n=0,r=false,j=false,q=0,m=b.length;q=0;l-=16)o.push(" ".substring(0,l));l=n+1;break;case "\n":f=0;break;default:++f}if(!o)return i;o.push(i.substring(l));return o.join("")}}function I(b,
+f,i,o){if(f){b={source:f,c:b};i(b);o.push.apply(o,b.d)}}function B(b,f){var i={},o;(function(){for(var r=b.concat(f),j=[],q={},m=0,t=r.length;m=0;)i[c.charAt(d)]=p;p=p[1];c=""+p;if(!q.hasOwnProperty(c)){j.push(p);q[c]=null}}j.push(/[\0-\uffff]/);o=O(j)})();var l=f.length;function n(r){for(var j=r.c,q=[j,z],m=0,t=r.source.match(o)||[],p={},c=0,d=t.length;c=5&&"lang-"===k.substring(0,5))&&!(e&&typeof e[1]==="string")){h=false;k=P}h||(p[a]=k)}g=m;m+=a.length;if(h){h=e[1];var s=a.indexOf(h),v=s+h.length;if(e[2]){v=a.length-e[2].length;s=v-h.length}k=k.substring(5);I(j+g,a.substring(0,s),n,q);I(j+g+s,h,Q(k,h),q);I(j+g+v,a.substring(v),n,q)}else q.push(j+g,k)}r.d=q}return n}function x(b){var f=[],i=[];if(b.tripleQuotedStrings)f.push([A,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
+null,"'\""]);else b.multiLineStrings?f.push([A,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"]):f.push([A,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"]);b.verbatimStrings&&i.push([A,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null]);if(b.hashComments)if(b.cStyleComments){f.push([C,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"]);i.push([A,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,
+null])}else f.push([C,/^#[^\r\n]*/,null,"#"]);if(b.cStyleComments){i.push([C,/^\/\/[^\r\n]*/,null]);i.push([C,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}b.regexLiterals&&i.push(["lang-regex",RegExp("^"+Z+"(/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/)")]);b=b.keywords.replace(/^\s+|\s+$/g,"");b.length&&i.push([R,RegExp("^(?:"+b.replace(/\s+/g,"|")+")\\b"),null]);f.push([z,/^\s+/,null," \r\n\t\u00a0"]);i.push([J,/^@[a-z_$][a-z_$@0-9]*/i,null],[S,/^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/,
+null],[z,/^[a-z_$][a-z_$@0-9]*/i,null],[J,/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,null,"0123456789"],[E,/^.[^\s\w\.$@\'\"\`\/\#]*/,null]);return B(f,i)}function $(b){function f(D){if(D>r){if(j&&j!==q){n.push("");j=null}if(!j&&q){j=q;n.push('')}var T=y(p(i.substring(r,D))).replace(e?d:c,"$1 ");e=k.test(T);n.push(T.replace(a,s));r=D}}var i=b.source,o=b.g,l=b.d,n=[],r=0,j=null,q=null,m=0,t=0,p=Y(window.PR_TAB_WIDTH),c=/([\r\n ]) /g,
+d=/(^| ) /gm,a=/\r\n?|\n/g,k=/[ \r\n]$/,e=true,h=window._pr_isIE6();h=h?b.b.tagName==="PRE"?h===6?" \r\n":h===7?" \r":" \r":" ":" ";var g=b.b.className.match(/\blinenums\b(?::(\d+))?/),s;if(g){for(var v=[],w=0;w<10;++w)v[w]=h+'';var F=g[1]&&g[1].length?g[1]-1:0;n.push('");s=function(){var D=v[++F%10];return j?" "+D+'':D}}else s=h;
+for(;;)if(m");j=null}n.push(o[m+1]);m+=2}else if(t");g&&n.push("");b.a=n.join("")}function u(b,f){for(var i=f.length;--i>=0;){var o=f[i];if(G.hasOwnProperty(o))"console"in window&&console.warn("cannot override language handler %s",o);else G[o]=b}}function Q(b,f){b&&G.hasOwnProperty(b)||(b=/^\s*1&&m.charAt(0)==="<"){if(!ba.test(m))if(ca.test(m)){f.push(m.substring(9,m.length-3));n+=m.length-12}else if(da.test(m)){f.push("\n");++n}else if(m.indexOf(V)>=0&&m.replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,' $1="$2$3$4"').match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/)){var t=m.match(W)[2],p=1,c;c=j+1;a:for(;c=0;){var e=p.indexOf(";",k);if(e>=0){var h=p.substring(k+3,e),g=10;if(h&&h.charAt(0)==="x"){h=h.substring(1);g=16}var s=parseInt(h,g);isNaN(s)||(p=p.substring(0,k)+String.fromCharCode(s)+p.substring(e+1))}}a=p.replace(ea,"<").replace(fa,">").replace(ga,"'").replace(ha,'"').replace(ia," ").replace(ja,
+"&")}f.push(a);n+=a.length}}o={source:f.join(""),h:r};var v=o.source;b.source=v;b.c=0;b.g=o.h;Q(i,v)(b);$(b)}catch(w){if("console"in window)console.log(w&&w.stack?w.stack:w)}}var A="str",R="kwd",C="com",S="typ",J="lit",E="pun",z="pln",P="src",V="nocode",Z=function(){for(var b=["!","!=","!==","#","%","%=","&","&&","&&=","&=","(","*","*=","+=",",","-=","->","/","/=",":","::",";","<","<<","<<=","<=","=","==","===",">",">=",">>",">>=",">>>",">>>=","?","@","[","^","^=","^^","^^=","{","|","|=","||","||=",
+"~","break","case","continue","delete","do","else","finally","instanceof","return","throw","try","typeof"],f="(?:^^|[+-]",i=0;i:&a-z])/g,"\\$1");f+=")\\s*";return f}(),L=/&/g,M=//g,X=/\"/g,ea=/</g,fa=/>/g,ga=/'/g,ha=/"/g,ja=/&/g,ia=/ /g,ka=/[\r\n]/g,K=null,aa=RegExp("[^<]+|