Mercurial Hosting > luan
changeset 1607:fa066aaa068c
nginx caching
line wrap: on
line diff
--- a/src/goodjava/util/CacheMap.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/goodjava/util/CacheMap.java Fri Apr 30 20:23:28 2021 -0600 @@ -34,36 +34,36 @@ } } - public int size() { + @Override public int size() { return cache.size(); } - public boolean isEmpty() { + @Override public boolean isEmpty() { return cache.isEmpty(); } - public boolean containsKey(Object key) { + @Override public boolean containsKey(Object key) { return cache.containsKey(key); } - public V get(Object key) { + @Override public V get(Object key) { MyReference<K,V> ref = cache.get(key); return ref==null ? null : ref.get(); } - public V put(K key,V value) { + @Override public V put(K key,V value) { sweep(); MyReference<K,V> ref = cache.put( key, newReference(key,value,queue) ); return ref==null ? null : ref.get(); } - public V remove(Object key) { + @Override public V remove(Object key) { sweep(); MyReference<K,V> ref = cache.remove(key); return ref==null ? null : ref.get(); } - public void clear() { + @Override public void clear() { sweep(); cache.clear(); } @@ -75,43 +75,39 @@ return map; } */ - public Set<K> keySet() { + @Override public Set<K> keySet() { return cache.keySet(); } - public Set<Map.Entry<K,V>> entrySet() { + @Override public Set<Map.Entry<K,V>> entrySet() { return new MySet(); } private class MySet extends AbstractSet<Map.Entry<K,V>> { - public int size() { + @Override public int size() { return CacheMap.this.size(); } - public Iterator<Map.Entry<K,V>> iterator() { - return new MyIterator(cache.entrySet().iterator()); + @Override public Iterator<Map.Entry<K,V>> iterator() { + return new MyIterator(); } } private class MyIterator implements Iterator<Map.Entry<K,V>> { - Iterator<Map.Entry<K,MyReference<K,V>>> iter; + final Iterator<Map.Entry<K,MyReference<K,V>>> iter = cache.entrySet().iterator(); - MyIterator(Iterator<Map.Entry<K,MyReference<K,V>>> iter) { - this.iter = iter; - } - - public boolean hasNext() { + @Override public boolean hasNext() { return iter.hasNext(); } - public void remove() { + @Override public void remove() { iter.remove(); } - public Map.Entry<K,V> next() { + @Override public Map.Entry<K,V> next() { return new MyEntry( iter.next() ); } } @@ -123,21 +119,21 @@ this.entry = entry; } - public K getKey() { + @Override public K getKey() { return entry.getKey(); } - public V getValue() { + @Override public V getValue() { MyReference<K,V> ref = entry.getValue(); return ref.get(); } - public V setValue(V value) { + @Override public V setValue(V value) { MyReference<K,V> ref = entry.setValue( newReference(getKey(),value,queue) ); return ref.get(); } - public boolean equals(Object o) { + @Override public boolean equals(Object o) { if( o==null || !(o instanceof CacheMap.MyEntry) ) return false; @SuppressWarnings("unchecked") @@ -145,7 +141,7 @@ return entry.equals(m.entry); } - public int hashCode() { + @Override public int hashCode() { K key = getKey(); V value = getValue(); return (key==null ? 0 : key.hashCode()) ^
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/goodjava/util/CaseInsensitiveMap.java Fri Apr 30 20:23:28 2021 -0600 @@ -0,0 +1,99 @@ +package goodjava.util; + +import java.util.Map; +import java.util.AbstractMap; +import java.util.Set; +import java.util.AbstractSet; +import java.util.Iterator; + + +public final class CaseInsensitiveMap<V> extends AbstractMap<String,V> { + + public static final class Value<V> { + private final String s; + private final V v; + + private Value(String s,V v) { + this.s = s; + this.v = v; + } + } + + private final Map<String,Value<V>> map; + + public CaseInsensitiveMap(Map<String,Value<V>> map) { + this.map = map; + } + + @Override public int size() { + return map.size(); + } + + @Override public boolean isEmpty() { + return map.isEmpty(); + } + + @Override public boolean containsKey(Object key) { + if( !(key instanceof String) ) + return false; + String s = (String)key; + return map.containsKey(s.toLowerCase()); + } + + @Override public V get(Object key) { + if( !(key instanceof String) ) + return null; + String s = (String)key; + Value<V> val = map.get(s.toLowerCase()); + return val==null ? null : val.v; + } + + @Override public V put(String key,V value) { + Value<V> val = map.put( key.toLowerCase(), new Value<V>(key,value) ); + return val==null ? null : val.v; + } + + @Override public V remove(Object key) { + if( !(key instanceof String) ) + return null; + String s = (String)key; + Value<V> val = map.remove(s.toLowerCase()); + return val==null ? null : val.v; + } + + @Override public void clear() { + map.clear(); + } + + @Override public Set<Map.Entry<String,V>> entrySet() { + return new MySet(); + } + + private class MySet extends AbstractSet<Map.Entry<String,V>> { + + @Override public int size() { + return CaseInsensitiveMap.this.size(); + } + + @Override public Iterator<Map.Entry<String,V>> iterator() { + return new MyIterator(); + } + } + + private class MyIterator implements Iterator<Map.Entry<String,V>> { + final Iterator<Map.Entry<String,Value<V>>> iter = map.entrySet().iterator(); + + @Override public Map.Entry<String,V> next() { + Value<V> val = iter.next().getValue(); + return new AbstractMap.SimpleImmutableEntry<String,V>( val.s, val.v ); + } + + @Override public boolean hasNext() { + return iter.hasNext(); + } + + @Override public void remove() { + iter.remove(); + } + } +}
--- a/src/goodjava/webserver/Connection.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/goodjava/webserver/Connection.java Fri Apr 30 20:23:28 2021 -0600 @@ -114,8 +114,8 @@ msg = "invalid content for content-type " + contentType + "\n" + msg; response = Response.errorResponse(Status.BAD_REQUEST,msg); } - response.headers.put("connection","close"); - response.headers.put("content-length",Long.toString(response.body.length)); + response.headers.put("Connection","close"); + response.headers.put("Content-Length",Long.toString(response.body.length)); byte[] header = response.toHeaderString().getBytes(); OutputStream out = socket.getOutputStream();
--- a/src/goodjava/webserver/Request.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/goodjava/webserver/Request.java Fri Apr 30 20:23:28 2021 -0600 @@ -3,6 +3,7 @@ import java.util.Map; import java.util.LinkedHashMap; import java.util.Collections; +import goodjava.util.CaseInsensitiveMap; public class Request { @@ -13,7 +14,7 @@ public volatile String path; public volatile String protocol; // only HTTP/1.1 is accepted public volatile String scheme; - public final Map<String,Object> headers = Collections.synchronizedMap(new LinkedHashMap<String,Object>()); + public final Map<String,Object> headers = Collections.synchronizedMap(new CaseInsensitiveMap<Object>(new LinkedHashMap<String,CaseInsensitiveMap.Value<Object>>())); public final Map<String,Object> parameters = Collections.synchronizedMap(new LinkedHashMap<String,Object>()); public final Map<String,String> cookies = Collections.synchronizedMap(new LinkedHashMap<String,String>()); public volatile byte[] body;
--- a/src/goodjava/webserver/RequestParser.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/goodjava/webserver/RequestParser.java Fri Apr 30 20:23:28 2021 -0600 @@ -127,7 +127,7 @@ int start = parser.currentIndex(); require( tokenChar() ); while( tokenChar() ); - return parser.textFrom(start).toLowerCase(); + return parser.textFrom(start); } private String parseValue() throws ParseException {
--- a/src/goodjava/webserver/Response.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/goodjava/webserver/Response.java Fri Apr 30 20:23:28 2021 -0600 @@ -6,14 +6,15 @@ import java.util.LinkedHashMap; import java.util.Collections; import java.util.List; +import goodjava.util.CaseInsensitiveMap; public class Response { public final String protocol = "HTTP/1.1"; public volatile Status status = Status.OK; - public final Map<String,Object> headers = Collections.synchronizedMap(new LinkedHashMap<String,Object>()); + public final Map<String,Object> headers = Collections.synchronizedMap(new CaseInsensitiveMap<Object>(new LinkedHashMap<String,CaseInsensitiveMap.Value<Object>>())); { - headers.put("server","goodjava"); + headers.put("Server","goodjava"); } private static final Body empty = new Body(0,new InputStream(){ public int read() { return -1; } @@ -76,7 +77,7 @@ public static Response errorResponse(Status status,String text) { Response response = new Response(); response.status = status; - response.headers.put( "content-type", "text/plain; charset=utf-8" ); + response.headers.put( "Content-Type", "text/plain; charset=utf-8" ); PrintWriter writer = new PrintWriter( new ResponseOutputStream(response) ); writer.write( text ); writer.close();
--- a/src/goodjava/webserver/examples/Cookies.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/goodjava/webserver/examples/Cookies.java Fri Apr 30 20:23:28 2021 -0600 @@ -26,7 +26,7 @@ response.setCookie(name,"delete",attributes); } } - response.headers.put( "content-type", "text/plain; charset=utf-8" ); + response.headers.put( "Content-Type", "text/plain; charset=utf-8" ); try { Writer writer = new OutputStreamWriter( new ResponseOutputStream(response) ); for( Map.Entry<String,String> entry : request.cookies.entrySet() ) {
--- a/src/goodjava/webserver/examples/Example.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/goodjava/webserver/examples/Example.java Fri Apr 30 20:23:28 2021 -0600 @@ -23,7 +23,7 @@ public Response handle(Request request) { Response response = new Response(); - response.headers.put( "content-type", "text/plain; charset=utf-8" ); + response.headers.put( "Content-Type", "text/plain; charset=utf-8" ); try { Writer writer = new OutputStreamWriter( new ResponseOutputStream(response) ); writer.write("Hello World\n");
--- a/src/goodjava/webserver/examples/Headers.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/goodjava/webserver/examples/Headers.java Fri Apr 30 20:23:28 2021 -0600 @@ -14,7 +14,7 @@ public Response handle(Request request) { Response response = new Response(); - response.headers.put( "content-type", "text/plain; charset=utf-8" ); + response.headers.put( "Content-Type", "text/plain; charset=utf-8" ); try { Writer writer = new OutputStreamWriter( new ResponseOutputStream(response) ); for( Map.Entry<String,Object> entry : request.headers.entrySet() ) {
--- a/src/goodjava/webserver/examples/Params.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/goodjava/webserver/examples/Params.java Fri Apr 30 20:23:28 2021 -0600 @@ -14,7 +14,7 @@ public Response handle(Request request) { Response response = new Response(); - response.headers.put( "content-type", "text/plain; charset=utf-8" ); + response.headers.put( "Content-Type", "text/plain; charset=utf-8" ); try { Writer writer = new OutputStreamWriter( new ResponseOutputStream(response) ); for( Map.Entry<String,Object> entry : request.parameters.entrySet() ) {
--- a/src/goodjava/webserver/handlers/ContentTypeHandler.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/goodjava/webserver/handlers/ContentTypeHandler.java Fri Apr 30 20:23:28 2021 -0600 @@ -2,56 +2,59 @@ import java.util.Map; import java.util.HashMap; +import goodjava.logging.Logger; +import goodjava.logging.LoggerFactory; import goodjava.webserver.Handler; import goodjava.webserver.Request; import goodjava.webserver.Response; public class ContentTypeHandler implements Handler { - private final Handler handler; + private static final Logger logger = LoggerFactory.getLogger(ContentTypeHandler.class); // maps extension to content-type // key must be lower case - public final Map<String,String> map = new HashMap<String,String>(); - - // set to null for none - public String contentTypeForNoExtension; - - public ContentTypeHandler(Handler handler) { - this(handler,"utf-8"); + public static final Map<String,String> map = new HashMap<String,String>(); + static { + String textType = "text/plain; charset=utf-8"; + map.put( "txt", textType ); + map.put( "luan", textType ); + map.put( "luano", textType ); + map.put( "log", textType ); + map.put( "html", "text/html; charset=utf-8" ); + map.put( "css", "text/css; charset=utf-8" ); + map.put( "js", "application/javascript; charset=utf-8" ); + map.put( "json", "application/json; charset=utf-8" ); + map.put( "mp4", "video/mp4" ); + map.put( "jpg", "image/jpeg" ); + map.put( "jpeg", "image/jpeg" ); + map.put( "png", "image/png" ); + // add more as need } - public ContentTypeHandler(Handler handler,String charset) { + public static String getExtension(String path) { + int iSlash = path.lastIndexOf('/'); + int iDot = path.lastIndexOf('.'); + return iDot > iSlash ? path.substring(iDot+1).toLowerCase() : null; + } + + private final Handler handler; + + public ContentTypeHandler(Handler handler) { this.handler = handler; - String attrs = charset== null ? "" : "; charset="+charset; - String htmlType = "text/html" + attrs; - String textType = "text/plain" + attrs; - contentTypeForNoExtension = htmlType; - map.put( "html", htmlType ); - map.put( "txt", textType ); - map.put( "luan", textType ); - map.put( "css", "text/css" ); - map.put( "js", "application/javascript" ); - map.put( "json", "application/json" + attrs ); - map.put( "mp4", "video/mp4" ); - // add more as need } public Response handle(Request request) { Response response = handler.handle(request); - if( response!=null && !response.headers.containsKey("content-type") ) { - String path = request.path; - int iSlash = path.lastIndexOf('/'); - int iDot = path.lastIndexOf('.'); - String type; - if( iDot < iSlash ) { // no extension - type = contentTypeForNoExtension; - } else { // extension - String extension = path.substring(iDot+1); - type = map.get( extension.toLowerCase() ); + if( response!=null && response.status.code==200 && !response.headers.containsKey("Content-Type") ) { + String extension = getExtension(request.path); + if( extension != null ) { + String type = map.get(extension); + if( type != null ) + response.headers.put("Content-Type",type); + else + logger.info("no type defined for extension: "+extension); } - if( type != null ) - response.headers.put("content-type",type); } return response; }
--- a/src/goodjava/webserver/handlers/DirHandler.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/goodjava/webserver/handlers/DirHandler.java Fri Apr 30 20:23:28 2021 -0600 @@ -42,7 +42,7 @@ if( path.endsWith("/") && file.isDirectory() ) { DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzz"); Response response = new Response(); - response.headers.put( "content-type", "text/html; charset=utf-8" ); + response.headers.put( "Content-Type", "text/html; charset=utf-8" ); Writer writer = new OutputStreamWriter( new ResponseOutputStream(response) ); writer.write( "<!doctype html>\n<html>\n" ); writer.write( "\t<head>\n" );
--- a/src/goodjava/webserver/handlers/FileHandler.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/goodjava/webserver/handlers/FileHandler.java Fri Apr 30 20:23:28 2021 -0600 @@ -44,7 +44,7 @@ DateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z"); fmt.setTimeZone(TimeZone.getTimeZone("GMT")); String lastModified = fmt.format(new Date(file.lastModified())); - String ifMod = (String)request.headers.get("if-modified-since"); + String ifMod = (String)request.headers.get("If-Modified-Since"); if( ifMod != null ) { try { Date ifModDate = fmt.parse(ifMod); @@ -55,7 +55,7 @@ } catch(ParseException e) {} } - response.headers.put("last-modified",lastModified); + response.headers.put("Last-Modified",lastModified); response.body = new Response.Body( file.length(), new FileInputStream(file) ); return response;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/goodjava/webserver/handlers/HeadersHandler.java Fri Apr 30 20:23:28 2021 -0600 @@ -0,0 +1,35 @@ +package goodjava.webserver.handlers; + +import goodjava.webserver.Handler; +import goodjava.webserver.Request; +import goodjava.webserver.Response; + + +public class HeadersHandler implements Handler { + private final Handler handler; + + public HeadersHandler(Handler handler) { + this.handler = handler; + } + + public Response handle(Request request) { + Response response = handler.handle(request); + if( response!=null ) { + if( response.headers.get("Last-Modified")!=null + && response.headers.get("Cache-Control")==null + ) { + String contentType = (String)response.headers.get("Content-Type"); + if( contentType!=null + && !contentType.startsWith("image/") + && !contentType.startsWith("video/") + ) + response.headers.put("Cache-Control","max-age=3600"); + } + if( response.headers.get("Last-Modified")!=null + && response.headers.get("X-Accel-Expires")==null + ) + response.headers.put("X-Accel-Expires","1"); + } + return response; + } +}
--- a/src/goodjava/webserver/handlers/SafeHandler.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/goodjava/webserver/handlers/SafeHandler.java Fri Apr 30 20:23:28 2021 -0600 @@ -31,7 +31,7 @@ logger.error("",e); Response response = new Response(); response.status = Status.INTERNAL_SERVER_ERROR; - response.headers.put( "content-type", "text/plain; charset=utf-8" ); + response.headers.put( "Content-Type", "text/plain; charset=utf-8" ); PrintWriter writer = new PrintWriter( new ResponseOutputStream(response) ); writer.write( "Internel Server Error\n\n" ); e.printStackTrace(writer);
--- a/src/luan/LuanTable.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/luan/LuanTable.java Fri Apr 30 20:23:28 2021 -0600 @@ -105,10 +105,9 @@ } public String toStringLuan(Luan luan) throws LuanException { - Object h = getHandler(luan,"__to_string"); - if( h == null ) + LuanFunction fn = luan.getHandlerFunction("__to_string",this); + if( fn == null ) return rawToString(); - LuanFunction fn = Luan.checkFunction(h); return Luan.checkString( Luan.first( fn.call(luan,this) ) ); } @@ -268,9 +267,8 @@ } public int length(Luan luan) throws LuanException { - Object h = getHandler(luan,"__len"); - if( h != null ) { - LuanFunction fn = Luan.checkFunction(h); + LuanFunction fn = luan.getHandlerFunction("__len",this); + if( fn != null ) { return (Integer)Luan.first(fn.call(luan,this)); } return rawLength(); @@ -299,9 +297,10 @@ } public Iterator<Map.Entry> iterator(final Luan luan) throws LuanException { - if( getHandler(luan,"__pairs") == null ) + LuanFunction h = luan.getHandlerFunction("__pairs",this); + if( h == null ) return rawIterator(); - final LuanFunction fn = pairs(luan); + final LuanFunction fn = pairs(luan,h); return new Iterator<Map.Entry>() { private Map.Entry<Object,Object> next = getNext(); @@ -336,18 +335,16 @@ } public LuanFunction pairs(Luan luan) throws LuanException { - Object h = getHandler(luan,"__pairs"); - if( h != null ) { - if( h instanceof LuanFunction ) { - LuanFunction fn = (LuanFunction)h; - Object obj = Luan.first(fn.call(luan,this)); - if( !(obj instanceof LuanFunction) ) - throw new LuanException( "metamethod __pairs should return function but returned " + Luan.type(obj) ); - return (LuanFunction)obj; - } - throw new LuanException( "invalid type of metamethod __pairs: " + Luan.type(h) ); - } - return rawPairs(); + return pairs( luan, luan.getHandlerFunction("__pairs",this) ); + } + + private LuanFunction pairs(Luan luan,LuanFunction fn) throws LuanException { + if( fn==null ) + return rawPairs(); + Object obj = Luan.first(fn.call(luan,this)); + if( !(obj instanceof LuanFunction) ) + throw new LuanException( "metamethod __pairs should return function but returned " + Luan.type(obj) ); + return (LuanFunction)obj; } private LuanFunction rawPairs() {
--- a/src/luan/host/WebHandler.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/luan/host/WebHandler.java Fri Apr 30 20:23:28 2021 -0600 @@ -16,6 +16,7 @@ import goodjava.webserver.handlers.LogHandler; import goodjava.webserver.handlers.FileHandler; import goodjava.webserver.handlers.DirHandler; +import goodjava.webserver.handlers.HeadersHandler; import luan.Luan; import luan.LuanException; import luan.LuanTable; @@ -65,11 +66,13 @@ FileHandler fileHandler = new FileHandler(dirStr+"/site/"); Handler handler = new ListHandler( luanHandler, fileHandler ); + handler = new ContentTypeHandler(handler); handler = new IndexHandler(handler); DirHandler dirHandler = new DirHandler(fileHandler); Handler notFoundHander = new NotFound(luanHandler); + notFoundHander = new ContentTypeHandler(notFoundHander); handler = new ListHandler( handler, dirHandler, notFoundHander ); - handler = new ContentTypeHandler(handler); + handler = new HeadersHandler(handler); handler = new SafeHandler(handler); handler = new LogHandler(handler,LogHandler.dirLogger(new File(logDir),days30));
--- a/src/luan/modules/BasicLuan.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/luan/modules/BasicLuan.java Fri Apr 30 20:23:28 2021 -0600 @@ -211,8 +211,9 @@ } } - public static String stringify(Object obj,LuanTable options,LuanTable subOptions) throws LuanException { + public static String stringify(Luan luan,Object obj,LuanTable options,LuanTable subOptions) throws LuanException { LuanToString lts = new LuanToString(options,subOptions); + lts.luan = luan; return lts.toString(obj); }
--- a/src/luan/modules/Table.luan Tue Apr 20 18:06:50 2021 -0600 +++ b/src/luan/modules/Table.luan Fri Apr 30 20:23:28 2021 -0600 @@ -20,6 +20,7 @@ local error = Luan.error local type = Luan.type or error() local pairs = Luan.pairs or error() +local set_metatable = Luan.set_metatable or error() local toTable = TableLuan.toTable or error() local copy = Table.copy or error() @@ -56,4 +57,41 @@ end +function Table.case_insensitive(src) + local String = require "luan:String.luan" + local lower = String.lower or error() + + local map = {} + local mt = {} + function mt.__new_index(tbl,key,value) + if value==nil then + map[lower(key)] = nil + else + map[lower(key)] = { s=key, v=value } + end + end + function mt.__index(tbl,key) + local val = map[lower(key)] + return val and val.v + end + function mt.__pairs(tbl) + local fn = pairs(map) + return function() + local _, val = fn() + if val == nil then + return nil + end + return val.s, val.v + end + end + + local t = {} + set_metatable(t,mt) + for k,v in pairs(src or {}) do + t[k] = v + end + return t +end + + return Table
--- a/src/luan/modules/http/Http.luan Tue Apr 20 18:06:50 2021 -0600 +++ b/src/luan/modules/http/Http.luan Fri Apr 30 20:23:28 2021 -0600 @@ -15,6 +15,7 @@ local Table = require "luan:Table.luan" local clear = Table.clear or error() local java_to_table_deep = Table.java_to_table_deep or error() +local case_insensitive = Table.case_insensitive or error() local Package = require "luan:Package.luan" local String = require "luan:String.luan" local lower = String.lower or error() @@ -75,7 +76,7 @@ if java == nil then this.method = "GET" this.scheme = "http" - this.headers = {} + this.headers = case_insensitive{} this.parameters = {} this.cookies = {} else @@ -88,13 +89,13 @@ this.path = java.path or error() this.protocol = java.protocol or error() this.scheme = java.scheme or error() - this.headers = java_to_table_deep(java.headers) + this.headers = case_insensitive(java_to_table_deep(java.headers)) this.parameters = java_to_table_deep(java.parameters,java_to_table_shallow) this.cookies = java_to_table_deep(java.cookies) end function this.url() - return this.scheme.."://"..this.headers["host"]..this.raw_path + return this.scheme.."://"..this.headers["Host"]..this.raw_path end return this @@ -115,7 +116,7 @@ function this.reset() this.java = Response.new() - this.headers = {} + this.headers = case_insensitive{} this.status = STATUS.OK this.writer = nil end @@ -124,14 +125,14 @@ function this.send_redirect(location) this.status = STATUS.FOUND - this.headers["location"] = location + this.headers["Location"] = location end function this.send_error(status,msg) this.reset() this.status = status if msg ~= nil then - this.headers["content-type"] = "text/plain; charset=utf-8" + this.headers["Content-Type"] = "text/plain; charset=utf-8" local writer = this.text_writer() writer.write(msg) end @@ -183,7 +184,6 @@ java.status = Status.getStatus(response.status) for name, value in pairs(response.headers) do type(name)=="string" or "header name must be string" - name = lower(name) value = LuanJava.toJava(value) java.headers.put(name,value) end @@ -209,7 +209,7 @@ Http.is_serving = false function Http.format_date(date) - return time_format(date,"EEE, dd MMM yyyy HH:mm:ss 'GMT'","GMT") + return time_format(date,"EEE, dd MMM yyyy HH:mm:ss z","GMT") end return Http
--- a/src/luan/modules/http/Server.luan Tue Apr 20 18:06:50 2021 -0600 +++ b/src/luan/modules/http/Server.luan Fri Apr 30 20:23:28 2021 -0600 @@ -22,6 +22,7 @@ local SafeHandler = require "java:goodjava.webserver.handlers.SafeHandler" local LogHandler = require "java:goodjava.webserver.handlers.LogHandler" local ListHandler = require "java:goodjava.webserver.handlers.ListHandler" +local HeadersHandler = require "java:goodjava.webserver.handlers.HeadersHandler" local LuanHandler = require "java:luan.modules.http.LuanHandler" local System = require "java:java.lang.System" local NotFound = require "java:luan.modules.http.NotFound" @@ -67,11 +68,13 @@ local file_handler = FileHandler.new(dir_path) local luan_handler = LuanHandler.new() local handler = ListHandler.new( luan_handler, file_handler ) + handler = ContentTypeHandler.new(handler) handler = IndexHandler.new(handler) local dir_handler = DirHandler.new(file_handler) local not_found_hander = NotFound.new(luan_handler) + not_found_hander = ContentTypeHandler.new(not_found_hander) handler = ListHandler.new( handler, dir_handler, not_found_hander ) - handler = ContentTypeHandler.new(handler) + handler = HeadersHandler.new(handler) handler = SafeHandler.new(handler) handler = LogHandler.new(handler) local server = JavaServer.new(port,handler)
--- a/src/luan/modules/http/tools/Run.luan Tue Apr 20 18:06:50 2021 -0600 +++ b/src/luan/modules/http/tools/Run.luan Fri Apr 30 20:23:28 2021 -0600 @@ -26,7 +26,9 @@ end end -local function form() %> +local function form() + Http.response.headers["Content-Type"] = "text/html; charset=utf-8" +%> <!doctype html> <html> <head> @@ -54,7 +56,6 @@ <body> <h2>Run Luan Code</h2> <form method="post"> - <input type="hidden" name="content_type" value="text/plain; charset=utf-8" /> <div> <textarea name="code" rows="20" cols="90" autofocus></textarea> </div> @@ -64,16 +65,18 @@ </form> </body> </html> -<% end +<% +end function Run.run(code,source_name) try + Http.response.headers["Content-Type"] = "text/plain; charset=utf-8" local run = load(code,source_name) run() return true catch e Http.response.reset() - Http.response.headers["content-type"] = "text/plain; charset=utf-8" + Http.response.headers["Content-Type"] = "text/plain; charset=utf-8" Io.stdout = Http.response.text_writer() print(e) print() @@ -84,10 +87,6 @@ end function Run.respond() - local content_type = Http.request.parameters.content_type - if content_type ~= nil then - Http.response.headers["content-type"] = content_type - end Io.stdout = Http.response.text_writer() local code = Http.request.parameters.code if code == nil then
--- a/src/luan/modules/parsers/LuanToString.java Tue Apr 20 18:06:50 2021 -0600 +++ b/src/luan/modules/parsers/LuanToString.java Fri Apr 30 20:23:28 2021 -0600 @@ -7,6 +7,7 @@ import java.util.Collections; import luan.Luan; import luan.LuanTable; +import luan.LuanFunction; import luan.LuanException; import luan.LuanRuntimeException; @@ -62,6 +63,7 @@ } public final Settings settingsInit = new Settings(); + public Luan luan = null; private final LuanTable subOptions; public LuanToString(LuanTable options,LuanTable subOptions) throws LuanException { @@ -119,6 +121,46 @@ } private void toString(LuanTable tbl,StringBuilder sb,int indented,Settings settings) throws LuanException { + if( tbl.getMetatable()!=null ) { + if( settings.strict ) + throw new LuanException("can't handle metatables when strict"); + if( luan==null ) + throw new LuanException("can't handle metatables when luan isn't set"); + } + LuanFunction pairs = luan.getHandlerFunction("__pairs",tbl); + if( pairs != null ) { + sb.append( '{' ); + boolean first = true; + for( Object obj : tbl.iterable(luan) ) { + Map.Entry entry = (Map.Entry)obj; + if( settings.compressed ) { + if( first ) + first = false; + else + sb.append( ',' ); + } else if( settings.inline ) { + if( first ) { + first = false; + sb.append( ' ' ); + } else + sb.append( ", " ); + } else { + first = false; + indent(sb,indented+1); + } + toString(entry,sb,indented+1,settings); + } + if( !first ) { + if( settings.compressed ) { + } else if( settings.inline ) { + sb.append( ' ' ); + } else { + indent(sb,indented); + } + } + sb.append( '}' ); + return; + } List list = tbl.asList(); Map map = tbl.rawMap(); sb.append( '{' );