Learning tiny HTTP server NanoHTTPD
NanoHTTPD is a light-weight HTTP server designed for embedding in other applications, released under a Modified BSD license.
The latest release version is "nanohttpd-2.3.1", seems this version has a tiny bug: after an update from version 2.1.1 to the last version 2.3.0 of the library (with some code modifications), every request to the server (loading simple HTML for example) is executed very slowly and load up to ~5 seconds each. (inetAddress.getHostName() consumes too much time.)
The master branch fixes that but there's no new release, so make sure to download the master version.
I'm still learning how to use that and didn't test NanoWebsocket, maybe it works, or maybe not.
So, here's what I got right now:
private NanoWebsocketServer nanowebsocketserver;
private NanoHttpServer nanohttpserver;
@Override
public void onCreate() {
super.onCreate();
NanoServer("WEB SERVER PORT","SOCKET SERVER PORT");
}
@Override
public void onDestroy() {
if(nanohttpserver!=null){nanohttpserver.stop();nanohttpserver=null;}
if(nanowebsocketserver!=null){nanowebsocketserver.stop();nanowebsocketserver=null;}
super.onDestroy();
}
private void NanoServer(String http,String websocket){
if(nanohttpserver!=null){nanohttpserver.stop();nanohttpserver=null;}
if(nanowebsocketserver!=null){nanowebsocketserver.stop();nanowebsocketserver=null;}
int p=0;try{p=Integer.parseInt(http.trim());if(p<1)p=0;if(p>65535)p=0;}catch(NumberFormatException e){p=0;}
if(p>0)try{nanohttpserver=new NanoHttpServer(p);nanohttpserver.start(2000,false);}catch(Exception e){}
p=0;try{p=Integer.parseInt(websocket.trim());if(p<1)p=0;if(p>65535)p=0;}catch(NumberFormatException e){p=0;}
if(p>0)try{nanowebsocketserver=new NanoWebsocketServer(p);nanowebsocketserver.start(2000,false);}catch(Exception e){}
}
private class NanoHttpServer extends org.nanohttpd.protocols.http.NanoHTTPD{
public NanoHttpServer(int port){super(port);}
public org.nanohttpd.protocols.http.response.Response aoa(){return org.nanohttpd.protocols.http.response.Response.newFixedLengthResponse(org.nanohttpd.protocols.http.response.Status.NOT_FOUND,org.nanohttpd.protocols.http.NanoHTTPD.MIME_HTML,"404");}
public org.nanohttpd.protocols.http.response.Response http(org.nanohttpd.protocols.http.IHTTPSession s){java.util.Map<String,String> map=query(s);String refer=query(map,"refer",query(map,"REFER",query(map,"referer",query(map,"REFERER"))));String mime=query(map,"mime",query(map,"MIME"));String url=query(map,"url",query(map,"URL"));String ua=query(map,"ua",query(map,"UA"));if((url.isEmpty())||(!url.contains("://"))){String text=query(map,"text");return html(mime.isEmpty()?org.nanohttpd.protocols.http.NanoHTTPD.MIME_HTML:mime,text.trim().isEmpty()?"[OK]":text);}try{okhttp3.OkHttpClient client=new okhttp3.OkHttpClient();okhttp3.Request.Builder builder=new okhttp3.Request.Builder().url(url).removeHeader("User-Agent").addHeader("User-Agent",ua.isEmpty()?"Mozilla/5.0 (iPhone; CPU iPhone OS 16_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Mobile/15E148 Safari/604.1":ua);if(!refer.isEmpty())builder.addHeader("Referer",refer);if(org.nanohttpd.protocols.http.request.Method.POST.equals(s.getMethod())){map=param(s);okhttp3.MultipartBody.Builder form=new okhttp3.MultipartBody.Builder().setType(okhttp3.MultipartBody.FORM);for(String key:map.keySet())if(!((key.equals("UA"))||(key.equals("URL"))||(key.equals("MIME"))||(key.equals("REFER"))||(key.equals("REFERER"))))form.addFormDataPart(key,map.get(key));builder.post(form.build());}else{builder.get();}okhttp3.Request clientrequest=builder.build();okhttp3.Response clientresponse=client.newCall(clientrequest).execute();String contentType=clientresponse.body().contentType().toString();if((mime.isEmpty())&&(!contentType.isEmpty()))mime=contentType;return cors(code(stream(mime.isEmpty()?org.nanohttpd.protocols.http.NanoHTTPD.MIME_HTML:mime,clientresponse.body().byteStream()),clientresponse.code()));}catch(Exception e){}return aoa();}
public org.nanohttpd.protocols.http.response.Response assets(String mime,String file){try{return stream(mime,new java.io.BufferedInputStream(getResources().getAssets().open(file)));}catch(Exception e){}return aoa();}
public org.nanohttpd.protocols.http.response.Response stream(String mime,java.io.InputStream stream,long size){org.nanohttpd.protocols.http.response.Response r;try{r=org.nanohttpd.protocols.http.response.Response.newFixedLengthResponse(org.nanohttpd.protocols.http.response.Status.OK,mime,stream,size);r.addHeader("Accept-Ranges","bytes");}catch(Exception e){r=aoa();}return r;}
public org.nanohttpd.protocols.http.response.Response stream(String mime,java.io.InputStream stream){org.nanohttpd.protocols.http.response.Response r;try{r=org.nanohttpd.protocols.http.response.Response.newFixedLengthResponse(org.nanohttpd.protocols.http.response.Status.OK,mime,stream,stream.available());r.addHeader("Accept-Ranges","bytes");}catch(Exception e){r=aoa();}return r;}
public org.nanohttpd.protocols.http.response.Response bytes(String mime,byte[] data){return org.nanohttpd.protocols.http.response.Response.newFixedLengthResponse(org.nanohttpd.protocols.http.response.Status.OK,mime,data);}
public org.nanohttpd.protocols.http.response.Response file(String mime,java.io.File file){org.nanohttpd.protocols.http.response.Response r;try{r=org.nanohttpd.protocols.http.response.Response.newFixedLengthResponse(org.nanohttpd.protocols.http.response.Status.OK,mime,new java.io.FileInputStream(file),(int)file.length());r.addHeader("Accept-Ranges","bytes");}catch(java.io.IOException e){r=aoa();}return r;}
public org.nanohttpd.protocols.http.response.Response file(String mime,String file){try{return file(mime,new java.io.File(file));}catch(Exception e){}return aoa();}
public org.nanohttpd.protocols.http.response.Response html(String mime,String s){return org.nanohttpd.protocols.http.response.Response.newFixedLengthResponse(org.nanohttpd.protocols.http.response.Status.OK,mime,s);}
public org.nanohttpd.protocols.http.response.Response html(String s){return org.nanohttpd.protocols.http.response.Response.newFixedLengthResponse(s);}
public org.nanohttpd.protocols.http.response.Response mime(org.nanohttpd.protocols.http.response.Response r,String s){r.addHeader("Content-Type",s);return r;};
public String mime(String url,String def){int i=url.indexOf("#");if(i>-1)url=url.substring(0,i);i=url.indexOf("?");if(i>-1)url=url.substring(0,i);i=url.lastIndexOf(".");if(i<0)return def;url=url.substring(i+1).toLowerCase().trim();android.webkit.MimeTypeMap map=android.webkit.MimeTypeMap.getSingleton();if(map.hasExtension(url))return map.getMimeTypeFromExtension(url);return def;}
public String mime(String url){return mime(url,"application/octet-stream");}
public org.nanohttpd.protocols.http.response.Response code(org.nanohttpd.protocols.http.response.Response r,int code){org.nanohttpd.protocols.http.response.Status s=org.nanohttpd.protocols.http.response.Status.lookup(code);if(s!=null)r.setStatus(s);return r;};
public org.nanohttpd.protocols.http.response.Response cors(org.nanohttpd.protocols.http.response.Response r){r.addHeader("Access-Control-Allow-Origin","*");return r;};
public String query(java.util.Map<String,String> m,String k,String v){String s=m.get(k);return (s==null)?v:s;}
public String query(java.util.Map<String,String> m,String k){return query(m,k,"");}
public String query(org.nanohttpd.protocols.http.IHTTPSession s,String k,String v){return query(s.getParms(),k,v);}
public String query(org.nanohttpd.protocols.http.IHTTPSession s,String k){return query(s,k,"");}
public java.util.Map<String,String> query(org.nanohttpd.protocols.http.IHTTPSession s){return s.getParms();}
public java.util.Map<String,String> param(org.nanohttpd.protocols.http.IHTTPSession s){java.util.Map<String,String> map=new java.util.HashMap<String,String>();if(org.nanohttpd.protocols.http.request.Method.POST.equals(s.getMethod()))try{String mime=s.getHeaders().get("content-type");if(mime.contains(";"))mime=mime.substring(0,mime.indexOf(";"));s.parseBody(map);if((mime.startsWith("application/x-www-form-urlencoded"))||(mime.startsWith("multipart/form-data")))map=s.getParms();}catch(Exception e){}return map;}
public java.util.Map<String,String> qaram(org.nanohttpd.protocols.http.IHTTPSession s){if(!org.nanohttpd.protocols.http.request.Method.POST.equals(s.getMethod()))return s.getParms();java.util.Map<String,String> map=new java.util.HashMap<String,String>();try{String mime=s.getHeaders().get("content-type");if(mime.contains(";"))mime=mime.substring(0,mime.indexOf(";"));s.parseBody(map);if((mime.startsWith("application/x-www-form-urlencoded"))||(mime.startsWith("multipart/form-data")))map=s.getParms();}catch(Exception e){}return map;}
@Override
public org.nanohttpd.protocols.http.response.Response serve(org.nanohttpd.protocols.http.IHTTPSession session){
String method=session.getMethod().toString();
String uri=session.getUri();
if(uri.equals("/favicon.ico"))return assets("image/x-icon","www/favicon.ico");
if(uri.equals("/stamp"))return cors(html(Long.toString(System.currentTimeMillis())));
if(uri.equals("/http"))return http(session);
if(uri.startsWith("/assets/www/"))return cors(assets(mime(uri),uri.substring(8)));
if(uri.startsWith("/sdcard/"))return cors(file(mime(uri),uri));
if(uri.equals("/")){java.util.Map<String,String> map=qaram(session);String html=query(map,"html");if(!html.isEmpty())return cors(html(html));html=query(map,"text");if(html.isEmpty())html="index";return cors(html(html));}
return aoa();
}
}
private class NanoWebsocketServer extends org.nanohttpd.protocols.websockets.NanoWSD{
public NanoWebsocketServer(int port){super(port);}
@Override
protected org.nanohttpd.protocols.websockets.WebSocket openWebSocket(org.nanohttpd.protocols.http.IHTTPSession handshake){return new NanoWebsocket(this, handshake);}
private class NanoWebsocket extends org.nanohttpd.protocols.websockets.WebSocket{
private final NanoWebsocketServer server;
public NanoWebsocket(NanoWebsocketServer server,org.nanohttpd.protocols.http.IHTTPSession handshakeRequest){super(handshakeRequest);this.server=server;}
@Override
protected void onOpen(){}
@Override
protected void onClose(org.nanohttpd.protocols.websockets.CloseCode code,String reason,boolean initiatedByRemote){
/*if (server.debug) {
System.out.println("C [" + (initiatedByRemote ? "Remote" : "Self") + "] " + (code != null ? code : "UnknownCloseCode[" + code + "]")
+ (reason != null && !reason.isEmpty() ? ": " + reason : ""));
}*/
}
@Override
protected void onMessage(org.nanohttpd.protocols.websockets.WebSocketFrame message){
try{
message.setUnmasked();
sendFrame(message);
}catch(java.io.IOException e){
throw new RuntimeException(e);
}
}
@Override
protected void onPong(org.nanohttpd.protocols.websockets.WebSocketFrame pong){}
@Override
protected void onException(java.io.IOException exception){}
}
}
okay that's it, hope it is useful~