Show status information
[louyapi.git] / src / main / java / de / cweiske / ouya / louyapi / HttpServer.java
1 package de.cweiske.ouya.louyapi;
2
3 import static de.cweiske.ouya.louyapi.HttpService.TAG;
4
5 import android.content.res.AssetManager;
6 import android.util.Log;
7
8 import java.io.IOException;
9 import java.io.InputStream;
10
11 import fi.iki.elonen.NanoHTTPD;
12
13 public class HttpServer extends NanoHTTPD {
14
15     private final AssetManager assetManager;
16     protected String prefix = "stouyapi-www";
17
18     public HttpServer(int port, AssetManager assetManager) {
19         super(port);
20         this.assetManager = assetManager;
21     }
22
23     /**
24      * This is basically a re-implementation of the stouyapi .htaccess file
25      *
26      * @param session The HTTP session
27      * @return HTTP response
28      */
29     public Response serve(IHTTPSession session) {
30         String path = session.getUri();
31         InputStream content;
32
33         if (path.equals("/api/v1/status") || path.equals("/generate_204")) {
34             //usage: check if internet connection is working
35             return newFixedLengthResponse(Response.Status.NO_CONTENT, null, "");
36
37         } else if (path.equals("/api/v1/details") && session.getParameters().containsKey("app")) {
38             //usage: detail page for installed games
39             if (null != (content = loadFileContent("/api/v1/details-data/" + session.getParameters().get("app").get(0) + ".json"))) {
40                 return newFixedLengthResponse(Response.Status.OK, "application/json", content);
41             } else {
42                 content = loadFileContent("/api/v1/details-dummy/error-unknown-app-details.json");
43                 return newFixedLengthResponse(Response.Status.OK, "application/json", content);
44             }
45
46         } else if (path.startsWith("/api/v1/apps/") && path.endsWith("/download")) {
47             String appid = path.substring(13, path.length() - 9);
48             if (null != (content = loadFileContent("/api/v1/apps/" + appid + "-download.json"))) {
49
50                 return newFixedLengthResponse(Response.Status.OK, "application/json", content);
51             }
52
53         } else if (path.startsWith("/api/v1/apps/")) {
54             String appid = path.substring(13);
55             if (null != (content = loadFileContent("/api/v1/apps/" + appid + ".json"))) {
56                 return newFixedLengthResponse(Response.Status.OK, "application/json", content);
57             }
58
59         } else if (path.startsWith("/api/v1/developers") && path.endsWith("/products/") && session.getParameters().containsKey("only")) {
60             //usage: product details for a single product
61             if (null != (content = loadFileContent(path + session.getParameters().get("only").get(0) + ".json"))) {
62                 return newFixedLengthResponse(Response.Status.OK, "application/json", content);
63             }
64
65         } else if (path.equals("/api/v1/discover")) {
66             //usage: main store page
67             if (null != (content = loadFileContent("/api/v1/discover-data/discover.json"))) {
68                 return newFixedLengthResponse(Response.Status.OK, "application/json", content);
69             }
70
71         } else if (path.startsWith("/api/v1/discover/")) {
72             //usage: store category
73             if (null != (content = loadFileContent("/api/v1/discover-data/" + path.substring(17) + ".json"))) {
74                 return newFixedLengthResponse(Response.Status.OK, "application/json", content);
75             }
76
77         } else if (path.equals("/api/v1/gamers")) {
78             //usage: register a user
79             if (null != (content = loadFileContent("/api/v1/gamers/register-error.json"))) {
80                 return newFixedLengthResponse(Response.Status.BAD_REQUEST, "application/json", content);
81             }
82
83         } else if (path.equals("/api/v1/gamers/me")) {
84             //usage: fetch user data
85             if (null != (content = loadFileContent("/api/v1/gamers/me.json"))) {
86                 return newFixedLengthResponse(Response.Status.OK, "application/json", content);
87             }
88
89         } else if (path.equals("/api/v1/search") && session.getParameters().containsKey("q")) {
90             //usage: search for games
91             String query = session.getParameters().get("q").get(0);
92             if (null != (content = loadFileContent("/api/v1/search-data/" + query.charAt(0) + ".json"))) {
93                 return newFixedLengthResponse(Response.Status.OK, "application/json", content);
94             }
95
96         } else if (!path.endsWith("/") && null != (content = loadFileContent(path))) {
97             //try if the path exists in the assets/stouyapi-www/ dir
98             return newFixedLengthResponse(Response.Status.OK, mimeTypeFromPath(path), content);
99
100         } else if (null != (content = loadFileContent(path.replaceAll("/$", "") + "/index.htm"))) {
101             //usage: /api/v1/developers/*/products
102             return newFixedLengthResponse(Response.Status.OK, "text/html", content);
103
104         } else if (!path.endsWith("/") && null != (content = loadAssetsFileContent(path))) {
105             //try if the path exists in the assets/ dir (without stouyapi-www)
106             return newFixedLengthResponse(Response.Status.OK, mimeTypeFromPath(path), content);
107
108         } else if (null != (content = loadAssetsFileContent(path.replaceAll("/$", "") + "/index.htm"))) {
109             //try if the path + index.htm exists in the assets/ dir (without stouyapi-www)
110             return newFixedLengthResponse(Response.Status.OK, "text/html", content);
111
112         } else if (path.startsWith("/api/v1/games/") && path.endsWith("/purchases")) {
113             //usage: purchases for non-existing game
114             if (null != (content = loadFileContent("/api/v1/games/purchases-empty.json"))) {
115                 return newFixedLengthResponse(Response.Status.OK, "application/json", content);
116             }
117         }
118
119         return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "File not found");
120     }
121
122     /**
123      * Automatically determine the length of the input stream
124      */
125     protected Response newFixedLengthResponse(Response.IStatus status, String mimeType, InputStream data) {
126         int length = 0;
127         try {
128             length = streamLength(data);
129             data.reset();
130         } catch (IOException e) {
131             Log.e(TAG, e.getMessage());
132             return newFixedLengthResponse(
133                 Response.Status.INTERNAL_ERROR, "text/plain",
134                 "Error: " + e.getMessage()
135             );
136         }
137         return newFixedLengthResponse(status, mimeType, data, length);
138     }
139
140     protected InputStream loadFileContent(String path)
141     {
142         return loadAssetsFileContent(prefix + path);
143     }
144
145     protected InputStream loadAssetsFileContent(String path)
146     {
147         path = path.replaceAll("^/", "");
148         try {
149             Log.d(TAG, "loadFileContent: " + path);
150             return assetManager.open(path, AssetManager.ACCESS_BUFFER);
151         } catch (IOException e) {
152             //file does not exist
153             Log.d(TAG, "loadFileContent: fail");
154             return null;
155         }
156     }
157
158     protected static int streamLength(InputStream inputStream) throws IOException {
159         byte[] buffer = new byte[4096];
160         int chunkBytesRead = 0;
161         int length = 0;
162         while((chunkBytesRead = inputStream.read(buffer)) != -1) {
163             length += chunkBytesRead;
164         }
165         return length;
166     }
167
168     protected String mimeTypeFromPath(String path) {
169         if (path.endsWith(".htm")) {
170             return "text/html";
171         } else if (path.endsWith(".ico")) {
172             return "image/vnd.microsoft.icon";
173         } else if (path.endsWith(".jpg")) {
174             return "image/jpeg";
175         } else if (path.endsWith(".png")) {
176             return "image/png";
177         }
178         return "text/plain";
179     }
180
181     protected boolean isBinary(String path) {
182         return path.endsWith(".ico")
183             || path.endsWith(".jpg")
184             || path.endsWith(".png");
185     }
186 }