| 1 | using System;
|
|---|
| 2 | using System.Collections.Generic;
|
|---|
| 3 | using System.Linq;
|
|---|
| 4 | using System.Text;
|
|---|
| 5 | using System.Text.RegularExpressions;
|
|---|
| 6 | using System.Net;
|
|---|
| 7 | using System.Web;
|
|---|
| 8 | using System.Diagnostics;
|
|---|
| 9 | using System.IO;
|
|---|
| 10 |
|
|---|
| 11 | namespace SoyText
|
|---|
| 12 | {
|
|---|
| 13 | public class DocumentWebServer
|
|---|
| 14 | {
|
|---|
| 15 | int port;
|
|---|
| 16 | DocumentSet documentSet;
|
|---|
| 17 | public static string detectContentType(Document d)
|
|---|
| 18 | {
|
|---|
| 19 | var c = d.str["Content-Type"];
|
|---|
| 20 | if (c != null) return c;
|
|---|
| 21 | var n = d.str["name"];
|
|---|
| 22 | c = "text/plain; charset=utf-8";
|
|---|
| 23 | if (n != null)
|
|---|
| 24 | {
|
|---|
| 25 | if (n.EndsWith(".js"))
|
|---|
| 26 | {
|
|---|
| 27 | c = "text/javascript; charset=utf-8";
|
|---|
| 28 | }
|
|---|
| 29 | if (n.EndsWith(".html"))
|
|---|
| 30 | {
|
|---|
| 31 | c = "text/html; charset=utf-8";
|
|---|
| 32 | }
|
|---|
| 33 | }
|
|---|
| 34 | return c;
|
|---|
| 35 | }
|
|---|
| 36 | void feedDocumentBody(Document d,HttpListenerResponse res) {
|
|---|
| 37 | res.ContentType = detectContentType(d);
|
|---|
| 38 | var lastup = lastModifiedField(d);
|
|---|
| 39 | res.Headers["Last-Modified"] = lastup;
|
|---|
| 40 | Debug.syslog("last up :" + lastup);
|
|---|
| 41 | if (d.str["file"] != null)
|
|---|
| 42 | {
|
|---|
| 43 | try
|
|---|
| 44 | {
|
|---|
| 45 | Httpd.respondByFile(res, d.str["file"]);
|
|---|
| 46 | }
|
|---|
| 47 | catch (IOException e)
|
|---|
| 48 | {
|
|---|
| 49 | Httpd.respondByString(res, e.ToString());
|
|---|
| 50 | }
|
|---|
| 51 | }
|
|---|
| 52 | else
|
|---|
| 53 | {
|
|---|
| 54 | Httpd.respondByString(res, d.body);
|
|---|
| 55 | }
|
|---|
| 56 | }
|
|---|
| 57 | string lastModifiedField(Document d)
|
|---|
| 58 | {
|
|---|
| 59 | return TimeFormat.toRFC2822(TimeFormat.toUtcTicks(d.lastUpdate, TimeZone.CurrentTimeZone));
|
|---|
| 60 | }
|
|---|
| 61 | void feedDocumentHead(Document d, HttpListenerResponse res)
|
|---|
| 62 | {
|
|---|
| 63 | var c = "text/plain; charset=utf-8";
|
|---|
| 64 | res.ContentType = c;
|
|---|
| 65 | res.Headers["Last-Modified"] = lastModifiedField(d);
|
|---|
| 66 | Httpd.respondByString(res, d.head);
|
|---|
| 67 | }
|
|---|
| 68 | void feedDocumentHeadBody(Document d, HttpListenerResponse res)
|
|---|
| 69 | {
|
|---|
| 70 | var c = "text/plain; charset=utf-8";
|
|---|
| 71 | res.ContentType = c;
|
|---|
| 72 | res.Headers["Last-Modified"] = lastModifiedField(d);
|
|---|
| 73 | Httpd.respondByString(res, d.content);
|
|---|
| 74 | }
|
|---|
| 75 |
|
|---|
| 76 | const string bodyAttr = "_body";
|
|---|
| 77 | void procDocument(Document d, string query, HttpListenerRequest req, HttpListenerResponse res)
|
|---|
| 78 | {
|
|---|
| 79 | if (req.HttpMethod == "GET")
|
|---|
| 80 | {
|
|---|
| 81 | var lm = req.Headers["If-Modified-Since"];
|
|---|
| 82 | if (lm != null)
|
|---|
| 83 | {
|
|---|
| 84 | var oneSec = 10000000;
|
|---|
| 85 | // 10^-9 = 0.000000001(10億分の1)
|
|---|
| 86 | // 100nano = 10^-7
|
|---|
| 87 | // 1sec/100nano sec = 1 sec / (10^-7) sec = 10^7
|
|---|
| 88 | var ifModifiedSince = TimeFormat.fromRFC2822(lm) + oneSec;
|
|---|
| 89 | var lastModified = TimeFormat.toUtcTicks(d.lastUpdate, TimeZone.CurrentTimeZone);
|
|---|
| 90 | var now = TimeFormat.utcnow();
|
|---|
| 91 | Debug.syslog(req.Url +
|
|---|
| 92 | " lm: " + TimeFormat.toRFC2822(lastModified)+
|
|---|
| 93 | " ifm : " + TimeFormat.toRFC2822(ifModifiedSince) +
|
|---|
| 94 | " now: " + TimeFormat.toRFC2822(now)
|
|---|
| 95 | );
|
|---|
| 96 | if (lastModified <= ifModifiedSince && ifModifiedSince<now )
|
|---|
| 97 | {
|
|---|
| 98 | res.StatusCode = 304;
|
|---|
| 99 | Debug.syslog(req.Url + " not modified");
|
|---|
| 100 | return;
|
|---|
| 101 | }
|
|---|
| 102 | }
|
|---|
| 103 | if (query == "meta")
|
|---|
| 104 | {
|
|---|
| 105 | feedDocumentHead(d, res);
|
|---|
| 106 | }
|
|---|
| 107 | else if (query == "full")
|
|---|
| 108 | {
|
|---|
| 109 | feedDocumentHeadBody(d, res);
|
|---|
| 110 | }
|
|---|
| 111 | else
|
|---|
| 112 | {
|
|---|
| 113 | feedDocumentBody(d, res);
|
|---|
| 114 | }
|
|---|
| 115 | }
|
|---|
| 116 | else if (req.HttpMethod == "POST")
|
|---|
| 117 | {
|
|---|
| 118 | /*var b = new byte[req.ContentLength64];
|
|---|
| 119 | req.InputStream.Read(b, 0, b.Length);
|
|---|
| 120 | char[] cs=System.Text.Encoding.UTF8.GetChars(b);
|
|---|
| 121 | var str = new String(cs);
|
|---|
| 122 | */
|
|---|
| 123 | var str = getContentStr(req);
|
|---|
| 124 |
|
|---|
| 125 | //str=HttpUtility.UrlDecode(str);
|
|---|
| 126 | var keys=HttpUtility.ParseQueryString(str);
|
|---|
| 127 | var debug = "";
|
|---|
| 128 | foreach (var k in keys.AllKeys)
|
|---|
| 129 | {
|
|---|
| 130 | debug+=(k + ": " + keys[k]+"\n");
|
|---|
| 131 | if (k == bodyAttr)
|
|---|
| 132 | {
|
|---|
| 133 | d.body = keys[k];
|
|---|
| 134 | }
|
|---|
| 135 | else
|
|---|
| 136 | {
|
|---|
| 137 | d.str[k] = keys[k];
|
|---|
| 138 | }
|
|---|
| 139 |
|
|---|
| 140 | }
|
|---|
| 141 | d.save();
|
|---|
| 142 | Httpd.respondByString(res, "id: "+d.id+"\n"+debug);
|
|---|
| 143 | }
|
|---|
| 144 | }
|
|---|
| 145 | static string getContentStr(HttpListenerRequest req)
|
|---|
| 146 | {
|
|---|
| 147 | var b = new byte[req.ContentLength64];
|
|---|
| 148 | req.InputStream.Read(b, 0, b.Length);
|
|---|
| 149 | char[] cs = System.Text.Encoding.UTF8.GetChars(b);
|
|---|
| 150 | var str = new String(cs);
|
|---|
| 151 | return str;
|
|---|
| 152 | }
|
|---|
| 153 | public void openInBrowser(Document d) {
|
|---|
| 154 | if (d == null) return;
|
|---|
| 155 | Process.Start("http://localhost:"+port+"/byid/" + d.id);
|
|---|
| 156 | }
|
|---|
| 157 | public DocumentWebServer(DocumentSet documentSet,int port)
|
|---|
| 158 | {
|
|---|
| 159 | this.port = port;
|
|---|
| 160 | this.documentSet = documentSet;
|
|---|
| 161 | new Httpd(port, (req, res) =>
|
|---|
| 162 | {
|
|---|
| 163 | var u = req.Url;
|
|---|
| 164 | var str = u.PathAndQuery;
|
|---|
| 165 | str = HttpUtility.UrlDecode(str,Encoding.UTF8);
|
|---|
| 166 | var pathQuery = Regex.Split(str,@"\?");
|
|---|
| 167 | str = pathQuery[0];
|
|---|
| 168 | var query = "";
|
|---|
| 169 | if (pathQuery.Length > 1)
|
|---|
| 170 | {
|
|---|
| 171 | query = pathQuery[1];
|
|---|
| 172 | }
|
|---|
| 173 | Debug.syslog("Query = " + str+ "?" +query);
|
|---|
| 174 | //var m=Regex.Match(str,@"byId/");
|
|---|
| 175 | Debug.syslog(str);
|
|---|
| 176 | var s=Regex.Split(str, @"/");
|
|---|
| 177 | if (req.RemoteEndPoint.Address.ToString()!="127.0.0.1" &&
|
|---|
| 178 | req.RemoteEndPoint.Address.ToString()!="::1")
|
|---|
| 179 | {
|
|---|
| 180 | res.StatusCode = 403;
|
|---|
| 181 | Httpd.respondByString(res, "isLocal "+req.IsLocal+" / "+req.RemoteEndPoint.Address);
|
|---|
| 182 | return;
|
|---|
| 183 | }
|
|---|
| 184 | if (s.Length>=2 && s[1].ToLower()=="byid" )
|
|---|
| 185 | {
|
|---|
| 186 | var id = s[2];
|
|---|
| 187 | Debug.print("ID=" + id);
|
|---|
| 188 | Document d = documentSet.byId(id);
|
|---|
| 189 | if (d != null)
|
|---|
| 190 | {
|
|---|
| 191 | procDocument(d, query, req, res);
|
|---|
| 192 | }
|
|---|
| 193 | else
|
|---|
| 194 | {
|
|---|
| 195 | res.StatusCode = 404;
|
|---|
| 196 | Httpd.respondByString(res,id+" Not found");
|
|---|
| 197 | }
|
|---|
| 198 | }
|
|---|
| 199 | else if (s.Length >= 3 && s[1].ToLower() == "asyncsearch")
|
|---|
| 200 | {
|
|---|
| 201 | var se = documentSet.createLoggedDocumentSearcher(s[2]);
|
|---|
| 202 | se.timeOut = 10000000;
|
|---|
| 203 | foreach (var dd in se.search()){}
|
|---|
| 204 | var d=se.searchLog;
|
|---|
| 205 | feedDocumentHeadBody(d, res);
|
|---|
| 206 | }
|
|---|
| 207 | else if (req.HttpMethod == "POST" && s.Length >= 2 && s[1].ToLower() == "new")
|
|---|
| 208 | {
|
|---|
| 209 | var d = documentSet.createDocument();
|
|---|
| 210 | procDocument(d, query, req, res);
|
|---|
| 211 | }
|
|---|
| 212 | else if (req.HttpMethod == "POST" && s.Length >= 2 && s[1].ToLower() == "getcontents")
|
|---|
| 213 | {
|
|---|
| 214 | getContents(query, req, res);
|
|---|
| 215 | }
|
|---|
| 216 | else
|
|---|
| 217 | {
|
|---|
| 218 | //var c = new AndCondition();
|
|---|
| 219 | var cstr = "";
|
|---|
| 220 | for (var i = 1; i < s.Length - 1; i++)
|
|---|
| 221 | {
|
|---|
| 222 | if (i > 1) cstr += " ";
|
|---|
| 223 | cstr += "tag:" + s[i];
|
|---|
| 224 | // c.add(new AttributeCondition("tag",s[i]));
|
|---|
| 225 | }
|
|---|
| 226 | var name = s[s.Length - 1];
|
|---|
| 227 | if (name == "")
|
|---|
| 228 | {
|
|---|
| 229 | search(cstr, res);
|
|---|
| 230 | }
|
|---|
| 231 | else
|
|---|
| 232 | {
|
|---|
| 233 | var nc = new AttributeCondition("name", name);
|
|---|
| 234 | //c.add();
|
|---|
| 235 | //var src=documentSet.createDocumentSearcher(c);
|
|---|
| 236 | Document found = null;
|
|---|
| 237 | foreach (var d in documentSet.search(cstr, true))
|
|---|
| 238 | {
|
|---|
| 239 | if (nc.matches(d))
|
|---|
| 240 | {
|
|---|
| 241 | found = d;
|
|---|
| 242 | break;
|
|---|
| 243 | }
|
|---|
| 244 | }
|
|---|
| 245 | if (found != null)
|
|---|
| 246 | {
|
|---|
| 247 | procDocument(found, query, req, res);
|
|---|
| 248 | }
|
|---|
| 249 | else
|
|---|
| 250 | {
|
|---|
| 251 | res.StatusCode = 404;
|
|---|
| 252 | Httpd.respondByString(res, "Not found : " + str);
|
|---|
| 253 | }
|
|---|
| 254 | }
|
|---|
| 255 | }
|
|---|
| 256 | });
|
|---|
| 257 | }
|
|---|
| 258 |
|
|---|
| 259 | private void getContents(string query, HttpListenerRequest req, HttpListenerResponse res)
|
|---|
| 260 | {
|
|---|
| 261 | var str = getContentStr(req);
|
|---|
| 262 | var keys = HttpUtility.ParseQueryString(str);
|
|---|
| 263 | var ids = keys["ids"];
|
|---|
| 264 | //content=HttpUtility.UrlDecode(content);
|
|---|
| 265 | var r=new Random();
|
|---|
| 266 | var sep = "---" + r.Next(1000000) + "----" + r.Next(1000000)+"\n";
|
|---|
| 267 | var buf = "";
|
|---|
| 268 | foreach (var line in Str.lines(ids)) {
|
|---|
| 269 | Debug.syslog("getContents " + line);
|
|---|
| 270 | var d=documentSet.byId(line);
|
|---|
| 271 | if (d==null) continue;
|
|---|
| 272 | buf += sep;
|
|---|
| 273 | if (d.str["id"]!=d.id)
|
|---|
| 274 | {
|
|---|
| 275 | buf += "id :" + d.id + "\n";
|
|---|
| 276 | }
|
|---|
| 277 | buf += "lastUpdate :" + TimeFormat.format(d.lastUpdate) + "\n";
|
|---|
| 278 | //buf += "\n";
|
|---|
| 279 | buf += d.content;
|
|---|
| 280 | buf += "\n";
|
|---|
| 281 | }
|
|---|
| 282 | buf += sep;
|
|---|
| 283 | Debug.syslog("getContents respond");
|
|---|
| 284 |
|
|---|
| 285 | Httpd.respondByString(res, buf);
|
|---|
| 286 | Debug.syslog("getContents done");
|
|---|
| 287 | }
|
|---|
| 288 |
|
|---|
| 289 | private void search(string cstr, HttpListenerResponse res)
|
|---|
| 290 | {
|
|---|
| 291 | var buf = "";
|
|---|
| 292 | foreach (var d in documentSet.search(cstr, true))
|
|---|
| 293 | {
|
|---|
| 294 | buf += "<a href=\"/byid/" + d.id + "\">" + d.ToString() + "</a><br/>\n";
|
|---|
| 295 |
|
|---|
| 296 | }
|
|---|
| 297 | res.ContentType = "text/html; charset=utf-8";
|
|---|
| 298 | Httpd.respondByString(res, buf);
|
|---|
| 299 | }
|
|---|
| 300 | }
|
|---|
| 301 | }
|
|---|