{"id":16423514,"url":"https://github.com/piotrpolak/android-http-server","last_synced_at":"2025-04-05T21:07:46.050Z","repository":{"id":41353786,"uuid":"42557838","full_name":"piotrpolak/android-http-server","owner":"piotrpolak","description":"A complete zero-dependency implementation of a web server and a servlet container in Java with a sample Android application.","archived":false,"fork":false,"pushed_at":"2024-06-08T07:34:51.000Z","size":2119,"stargazers_count":351,"open_issues_count":6,"forks_count":84,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-03-29T19:08:39.761Z","etag":null,"topics":["android","http","java","server","servlet","sms","standalone-server","web","webserver","zero","zero-dependency"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/piotrpolak.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-09-16T01:46:34.000Z","updated_at":"2025-03-26T01:35:09.000Z","dependencies_parsed_at":"2024-11-16T19:01:48.159Z","dependency_job_id":"6e6311ae-6e05-435f-afdf-9630ff57dfc4","html_url":"https://github.com/piotrpolak/android-http-server","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrpolak%2Fandroid-http-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrpolak%2Fandroid-http-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrpolak%2Fandroid-http-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrpolak%2Fandroid-http-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/piotrpolak","download_url":"https://codeload.github.com/piotrpolak/android-http-server/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247399877,"owners_count":20932876,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","http","java","server","servlet","sms","standalone-server","web","webserver","zero","zero-dependency"],"created_at":"2024-10-11T07:40:01.713Z","updated_at":"2025-04-05T21:07:46.026Z","avatar_url":"https://github.com/piotrpolak.png","language":"Java","readme":"# Android HTTP Server\n\nSmall but powerful multithreaded web server written completely in Java SE and then ported to Android.\n\n[![codecov](https://codecov.io/gh/piotrpolak/android-http-server/branch/master/graph/badge.svg)](https://codecov.io/gh/piotrpolak/android-http-server)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/ce45bf2bf46f46fe94e48b22c17dcd2a)](https://www.codacy.com/app/piotrpolak/android-http-server)\n\n\nThe server implements most of the HTTP 1.1 specification and provides custom servlet API that can be\nused to handle dynamic pages. The servlet API is designed after the official `javax.servlet` API\nyet it is not compatible. Dynamic pages support cookies, sessions, file uploads and anything else\nto build a common web application.\n\n* [Key features](#key-features)\n* [Building the application](#building-the-application)\n    * [Installing Android SDK from command line](#installing-android-sdk-from-command-line)\n* [The http subproject and the idea behind it](#the-http-subproject-and-the-idea-behind-it)\n* [Mutation testing](#mutation-testing)\n* [Running standalone server (CLI)](#running-standalone-server-cli)\n* [Demo](#demo)\n* [Sample code](#sample-code)\n    * [Hello World servlet](#hello-world-servlet)\n    * [Request logging filter](#request-logging-filter)\n* [Building a deployment descriptor](#building-a-deployment-descriptor)\n* [Templating support](#templating-support)\n* [Screens](#screens)\n* [500 error stack trace in browser](#500-error-page-trace-in-browser)\n* [Sample script to send SMS using wget command line utility](#sample-script-to-send-sms-using-wget-command-line-utility)\n* [License](#license)\n\n## Key features\n\n* Small footprint, requires no external libraries\n* Handles HTTP requests in separate threads\n* Provides custom servlets API for generating dynamic content\n* Supports GET, POST, HEAD methods (or more, depending on the configuration)\n* Supports chunked transfer type\n* Provides full support for mime types (uses Apache like mime.type)\n* Supports buffered file upload (multipart requests), cookies, persisted sessions\n* Supports serving partial body (ranges)\n* Can serve static content both from file system and APK resources\n\n## Building the application\n\nThe provided Gradle wrapper should be used to build the application:\n\n```bash\n./gradlew clean build\n```\n\n## Installing Android SDK from command line\n\nWhen running the full build for the first time you must first install the Android SDK. You might either install it manually\nor use the following script that downloads and installs all required dependencies to `~/android-sdk`.\n\n```bash\n./installsdk.sh\n```\n\nTo make things work after you logout and login back, configure the `ANDROID_HOME` environment variable:\n\n```bash\necho \"export ANDROID_HOME=~/android-sdk\" \u003e\u003e ~/.bashrc \u0026\u0026 source ~/.bashrc\n```\n\n## The http subproject and the idea behind it\n\nThe [http](../../tree/master/http/) subproject is the heart of the application and it is\nindependent on Android platform.\n\nIn fact the Android app was just an attempt to find a more practical use of the experimental HTTP\nprotocol implementation.\n\nOne of the design goals was to keep the resulting artifact **small in size** and minimalistic\nin terms of dependency on other libraries - **it does not require any third party component**,\nall HTTP protocol implementation is based on parsing data read from raw TCP sockets.\n\nOnce the [ro.polak.http](../../tree/master/http/src/main/java/) package is mature enough it will be\nreleased as an independent artifact.\n\nThe subproject can be tested in the following way:\n\n```bash\n./gradlew :http:clean :http:check -PskipAndroidBuild\n```\n\nThe original package code has been refactored and covered with unit and integration tests.\nCode coverage should be kept above 90%.\n\n[![codecov](https://codecov.io/gh/piotrpolak/android-http-server/branch/master/graphs/icicle.svg\n)](https://codecov.io/gh/piotrpolak/android-http-server)\n\n### Android SDK compatibility issues\n\nAll application code is targeted to Java 7. It also compiles for the Android SDK versions \u003c 19\n(try with resources is not supported, use\n[IOUtilities.closeSilently(closeable)](../../tree/master/http/src/main/java/ro/polak/http/utilities/IOUtilities.java)\nin a `finally` block as an alternative when closing streams).\n\nAnother compatibility constraint is that `Random` instead of `ThreadLocalRandom` is used for\ngenerating random sequences in [StringUtilities](../../tree/master/http/src/main/java/ro/polak/http/utilities/StringUtilities.java)\n\n## Mutation testing\n\nMutation tests can be run by executing the following command:\n\n```bash\n./gradlew :http:clean :http:pitest -PskipAndroidBuild\n```\n\nThe results can then be found under `http/build/reports/pitest/ro.polak.http/index.html` and\n`http/build/reports/pitest/ro.polak.http/mutation.xml`.\n\n## Running standalone server (CLI)\n\nStandalone server can be used to bundle the `http` subproject into a runnable server implementation.\nThe CLI subproject is also independent on the Android platform, it is not bundled with the main APK.\n\n```bash\n./gradlew :cli:bootRun -PskipAndroidBuild\n```\n\nIt is also possible to build one \"uber-jar\" and to use it as a standalone application:\n\n```bash\n./gradlew :cli:fatJar -PskipAndroidBuild\n```\n\nThe resulting artifact can then be grabbed from `./cli/build/libs/cli-all.jar`.\n\nThe standalone server jar can be run on any machine with the following command:\n\n```bash\njava -jar ./cli/build/libs/cli-all.jar\n```\n\n### Overwriting configuration values from command line\n\n```bash\njava -jar ./cli/build/libs/cli-all.jar \\\n    -Dserver.port=8888 \\\n    -Dserver.static.path=/www/public_html\n```\n\nFor a complete list of available parameters refer to\n[httpd.properties](../../tree/master//app/src/main/assets/conf/httpd.properties).\n\n![Command line interface0](screens/command-line-interface.png)\n\n## Demo\n\nA demo application is automatically deployed to Heroku and can be reached at:\n\n* [https://android-http-server-demo.herokuapp.com/example/](https://android-http-server-demo.herokuapp.com/example/).\n\nPlease note the deployed application **does not contain** the *admin* application since that is only\navailable for Android. See [Procfile](../../tree/master/Procfile) for the deployment description.\n\n## Sample code\n\n### Hello World servlet\n\n```java\npackage example;\n\nimport ro.polak.http.servlet.HttpServletRequest;\nimport ro.polak.http.servlet.HttpServletResponse;\nimport ro.polak.http.servlet.HttpServlet;\n\npublic class HelloWorld extends HttpServlet {\n\n    @Override\n    public void service(HttpServletRequest request, HttpServletResponse response) {\n        response.getWriter().print(\"Hello World!\");\n    }\n}\n```\n\n### Request logging filter\n\n```java\npackage example;\n\nimport java.io.IOException;\n\nimport ro.polak.http.exception.ServletException;\nimport ro.polak.http.servlet.Filter;\nimport ro.polak.http.servlet.FilterChain;\nimport ro.polak.http.servlet.FilterConfig;\nimport ro.polak.http.servlet.HttpServletRequest;\nimport ro.polak.http.servlet.HttpServletResponse;\n\npublic class RequestLoggingFilter implements Filter {\n    \n    private static final Logger LOGGER = Logger.getLogger(RequestLoggingFilter.class.getName());\n\n    @Override\n    public void init(FilterConfig filterConfig) throws ServletException {\n        // Do nothing\n    }\n\n    @Override\n    public void doFilter(HttpServletRequest request, HttpServletResponse response,\n                         FilterChain filterChain) throws IOException, ServletException {\n        \n        LOGGER.fine(\"Handling incoming request \" + request.getRequestURL());\n        \n        filterChain.doFilter(request, response);\n    }\n}\n```\n\nExample servlets can be found in [http/src/main/java/example](../../tree/master/http/src/main/java/example).\n\nA practical use of filters can be checked at\n[SecurityFilter.java](../../tree/master/app/src/main/java/admin/filter/SecurityFilter.java) and \n[LogoutFilter.java](../../tree/master/app/src/main/java/admin/filter/LogoutFilter.java) of the\nadmin application.\n\n## Building a deployment descriptor\n\n[DeploymentDescriptorBuilder](../../tree/master/http/src/main/java/ro/polak/http/configuration/DeploymentDescriptorBuilder.java)\nis an API alternative to traditional `web.xml` approach that aims to make servlet mapping building\nand filter registration easy. See example code below.\n\n```java\npackage example;\n\nimport java.util.List;\nimport java.util.regex.Pattern;\n\nimport ro.polak.http.configuration.DeploymentDescriptorBuilder;\nimport ro.polak.http.configuration.ServerConfig;\nimport ro.polak.http.session.storage.SessionStorage;\n\nclass DeploymentDescriptorFactory {\n    public List\u003cServletContextWrapper\u003e buildDeploymentDescriptor(SessionStorage sessionStorage,\n                                                    ServerConfig serverConfig) {\n        \n        return DeploymentDescriptorBuilder.create()\n                   .withSessionStorage(sessionStorage)\n                   .withServerConfig(serverConfig)\n                   .addServletContext()\n                       .withContextPath(\"/example\")\n                       .addFilter()\n                           .withUrlPattern(Pattern.compile(\"^.*$\"))\n                           .withUrlExcludedPattern(Pattern.compile(\"^/(?:Login|Logout)\"))\n                           .withFilterClass(SecurityFilter.class)\n                       .end()\n                       .addFilter()\n                           .withUrlPattern(Pattern.compile(\"^/Logout$\"))\n                           .withFilterClass(LogoutFilter.class)\n                       .end()\n                       .addServlet()\n                           .withUrlPattern(Pattern.compile(\"^/Index$\"))\n                           .withServletClass(Index.class)\n                       .end()\n                       .addServlet()\n                           .withUrlPattern(Pattern.compile(\"^/$\"))\n                           .withServletClass(Index.class)\n                       .end()\n                   .end()\n                   .build();\n    }\n}\n```\n\n## Serving static contents\n\nServing static resources is implemented using [DefaultServlet](../../tree/master/http/src/main/java/ro/polak/http/DefaultServlet.java)\n- the servlet is automatically registered as the very last element of [DeploymentDescriptorBuilder](../../tree/master/http/src/main/java/ro/polak/http/configuration/DeploymentDescriptorBuilder.java)\nacting as a fallback resource.\n\nThe actual resource loading is implemented by registering an instance [ResourceProvider](../../tree/master/http/src/main/java/ro/polak/http/resource/provider/ResourceProvider.java)\nin the server config.\n\nCurrently there are two resource providers implemented \n\n* File system resources [FileSystemResourceProvider](../../tree/master/http/src/main/java/ro/polak/http/resource/provider/FileSystemResourceProvider.java)\n* Android APK resources [AssetResourceProvider](../../tree/master/base/src/main/java/ro/polak/webserver/base/AssetResourceProvider.java)\n \n### Sample dummy implementation of a ResourceProvider\n\n```java\npackage ro.polak.http.resource.provider;\n\nimport ro.polak.http.servlet.impl.HttpServletRequestImpl;\nimport ro.polak.http.servlet.impl.HttpServletResponseImpl;\n\nimport java.io.IOException;\n\npublic class DummyResourceProvider implements ResourceProvider {\n\n    /**\n     * Tells whether this resource provider can load resource for given path.\n     */\n    @Override\n    public boolean canLoad(final String path) {\n        return false; // TODO Add some logic\n    }\n\n    /**\n     * Loads the resource for the given path by copying the stream to the response.getOutputStream().\n     */\n    @Override\n    public void load(final String path,\n                     final HttpServletRequestImpl request,\n                     final HttpServletResponseImpl response) throws IOException {\n\n        // TODO Load the stream to response.getOutputStream();\n    }\n\n    /**\n     * Shuts down the resource provider if necessary, usually closes all open resources.\n     */\n    @Override\n    public void shutdown() {\n    }\n}\n```\n\n\n## Templating support\n\n\nThe following example presents how to integrate [Jtwig](http://jtwig.org/) templating engine.\n\nFirst you need to add Jtwig dependency in your gradle file:\n\n```groovy\n// ...\ndependencies {\n    // ...\n    compile 'org.jtwig:jtwig-core:5.87.0.RELEASE'\n}\n// ...\n```\n\nThen it works out of the box:\n\n```java\npackage example;\n\nimport org.jtwig.JtwigModel;\nimport org.jtwig.JtwigTemplate;\n\nimport ro.polak.http.exception.ServletException;\nimport ro.polak.http.servlet.HttpServlet;\nimport ro.polak.http.servlet.HttpServletRequest;\nimport ro.polak.http.servlet.HttpServletResponse;\n\npublic class Templating extends HttpServlet {\n\n    @Override\n    public void service(HttpServletRequest request, HttpServletResponse response)\n    throws ServletException {\n        JtwigTemplate template = JtwigTemplate.inlineTemplate(\"Hello {{ var }}\");\n        JtwigModel model = JtwigModel.newModel().with(\"var\", \"World\");\n\n        template.render(model, response.getOutputStream());\n    }\n}\n```\n\n## Screens\n\n![Admin main activity](screens/main.png)\n![HTTP back-office login](screens/admin-login.png)\n![HTTP back-office menu](screens/admin-menu.png)\n\n![HTTP back-office drive access](screens/admin-drive-access.png)\n![HTTP back-office server statistics](screens/admin-server-statistics.png)\n![HTTP back-office SMS inbox](screens/admin-sms-inbox.png)\n\n### 500 error page trace in browser\n\n![Servlet error 500](screens/error-500-stacktrace.png)\n\n## Sample script to send SMS using wget command line utility\n\nIf you want to send a real SMS please remove \"\u0026test=1\" from the POST params.\n\n```bash\nSERVER_IP=192.168.1.1; SERVER_PORT=8080; \\\n    echo \"Phone number:\"; read TO; echo \"Message:\"; read MESSAGE; \\\n    wget -qO- --post-data \"to=$TO\u0026message=$MESSAGE\u0026test=1\" \\\n    http://$SERVER_IP:$SERVER_PORT/api/1.0/sms/send\n```\n\n# Icons\n\nAndroid HTTP server uses icons from the beautifully designed \"Farm-Fresh Web Icons\" pack by\nFatCow Web Hosting! These icon sets are licensed under a\n[Creative Commons Attribution 3.0 License](https://creativecommons.org/licenses/by/3.0/).\n\n# License\n\nThe project is shared upon [GNU GPLv3 license](LICENSE.txt).\n\nIf you are interested in a dedicated commercial license please drop me a line at\n`piotr [at] polak [dot] ro`","funding_links":[],"categories":["网络编程"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpiotrpolak%2Fandroid-http-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpiotrpolak%2Fandroid-http-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpiotrpolak%2Fandroid-http-server/lists"}