OSDN Git Service

閲覧サーバーの処理をプロキシサーバーに代行させる
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / LogServer.cs
1 // Copyright (C) 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 //\r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 using System;\r
16 using System.Collections.Generic;\r
17 using System.Globalization;\r
18 using System.IO;\r
19 using System.Linq;\r
20 using System.Net.Sockets;\r
21 using System.Text;\r
22 \r
23 namespace KancolleSniffer\r
24 {\r
25     public class LogServer\r
26     {\r
27         private static readonly string IndexDir = AppDomain.CurrentDomain.BaseDirectory;\r
28         private static string _outputDir = AppDomain.CurrentDomain.BaseDirectory;\r
29 \r
30         public static string OutputDir\r
31         {\r
32             set { _outputDir = value; }\r
33         }\r
34 \r
35         public static void Process(Socket client, string requestLine)\r
36         {\r
37             var from = DateTime.MinValue;\r
38             var to = DateTime.MaxValue;\r
39             var request = requestLine.Split(' ');\r
40             if (request.Length != 3)\r
41             {\r
42                 SendError(client, "400 Bad Request");\r
43                 return;\r
44             }\r
45             if (!request[0].StartsWith("GET", StringComparison.OrdinalIgnoreCase))\r
46             {\r
47                 SendError(client, "501 Not Implemented");\r
48                 return;\r
49             }\r
50             var tmp = request[1].Split('?');\r
51             var path = HttpUtility.UrlDecode(tmp[0]);\r
52             if (path == null || !path.StartsWith("/"))\r
53             {\r
54                 SendError(client, "400 Bad Request");\r
55                 return;\r
56             }\r
57             if (tmp.Length == 2)\r
58             {\r
59                 var query = HttpUtility.ParseQueryString(tmp[1]);\r
60                 if (query["from"] != null)\r
61                 {\r
62                     double tick;\r
63                     double.TryParse(query["from"], out tick);\r
64                     from = new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(tick / 1000);\r
65                 }\r
66                 if (query["to"] != null)\r
67                 {\r
68                     double tick;\r
69                     double.TryParse(query["to"], out tick);\r
70                     to = new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(tick / 1000);\r
71                 }\r
72             }\r
73 \r
74             path = path == "/" ? "index.html" : path.Substring(1);\r
75             var full = Path.Combine(IndexDir, path);\r
76             var csv = Path.Combine(_outputDir, path);\r
77             if (path.EndsWith(".html", StringComparison.OrdinalIgnoreCase) && File.Exists(full))\r
78             {\r
79                 SendFile(client, full, "text/html");\r
80                 return;\r
81             }\r
82             if (path.EndsWith(".csv", StringComparison.OrdinalIgnoreCase) && File.Exists(csv))\r
83             {\r
84                 SendFile(client, csv, "text/csv; charset=Shift_JIS");\r
85                 return;\r
86             }\r
87             if (path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))\r
88             {\r
89                 SendJsonData(client, csv, from, to);\r
90                 return;\r
91             }\r
92             if (path.EndsWith(".js", StringComparison.OrdinalIgnoreCase) && File.Exists(full))\r
93             {\r
94                 SendFile(client, full, "application/javascript");\r
95                 return;\r
96             }\r
97             if (path.EndsWith("proxy.pac"))\r
98             {\r
99                 SendProxyPac(client, HttpProxy.LocalPort);\r
100                 return;\r
101             }\r
102             SendError(client, "404 Not Found");\r
103         }\r
104 \r
105         private static void SendError(Socket client, string error)\r
106         {\r
107             using (var writer = new StreamWriter(new MemoryStream(), Encoding.ASCII))\r
108             {\r
109                 writer.Write("HTTP/1.1 {0}\r\n", error);\r
110                 writer.Write("Server: KancolleSniffer\r\n");\r
111                 writer.Write("Date: {0:R}\r\n", DateTime.Now);\r
112                 writer.Write("Connection: close\r\n\r\n");\r
113                 writer.Write("<html><head><title>{0}</title></head>\r\n", error);\r
114                 writer.Write("<body><h4>{0}</h4></body></html>\r\n\r\n", error);\r
115                 writer.Flush();\r
116                 client.Send(((MemoryStream)writer.BaseStream).ToArray());\r
117             }\r
118         }\r
119 \r
120         private static void SendJsonData(Socket client, string path, DateTime from, DateTime to)\r
121         {\r
122             using (var header = new StreamWriter(new MemoryStream(), Encoding.ASCII))\r
123             {\r
124                 header.Write("HTTP/1.1 200 OK\r\n");\r
125                 header.Write("Server: KancolleSniffer\r\n");\r
126                 header.Write("Date: {0:R}\r\n", DateTime.Now);\r
127                 header.Write("Content-Type: {0}\r\n", "application/json; charset=Shift_JIS");\r
128                 header.Write("Connection: close\r\n\r\n");\r
129                 header.Flush();\r
130                 client.Send(((MemoryStream)header.BaseStream).ToArray());\r
131             }\r
132             var csv = path.Replace(".json", ".csv");\r
133             var encoding = Encoding.GetEncoding("Shift_JIS");\r
134             client.Send(encoding.GetBytes("{ \"data\": [\n"));\r
135             try\r
136             {\r
137                 if (File.Exists(csv))\r
138                 {\r
139                     var delimiter = "";\r
140                     var material = path.EndsWith("資材ログ.json"); // 末尾の空データを削除する必要がある\r
141                     var battle = path.EndsWith("海戦・ドロップ報告書.json"); // データを40個にそろえる必要がある\r
142                     foreach (var line in File.ReadLines(csv, encoding).Skip(1))\r
143                     {\r
144                         var data = line.Split(',');\r
145                         DateTime date;\r
146                         if (!DateTime.TryParseExact(data[0], Logger.DateTimeFormat, CultureInfo.InvariantCulture,\r
147                             DateTimeStyles.AssumeLocal, out date) &&\r
148                             DateTime.TryParse(data[0], CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal, out date))\r
149                         {\r
150                             data[0] = date.ToString(Logger.DateTimeFormat);\r
151                         }\r
152                         if (date < from || to < date)\r
153                             continue;\r
154                         IEnumerable<string> entries = data;\r
155                         if (material)\r
156                             entries = data.Take(9);\r
157                         if (battle)\r
158                             entries = data.Concat(Enumerable.Repeat("", 3)).Take(38);\r
159                         client.Send(encoding.GetBytes(delimiter + "[\"" +\r
160                                                       string.Join("\",\"", entries) + "\"]"));\r
161                         delimiter = ",\n";\r
162                     }\r
163                 }\r
164             }\r
165             finally\r
166             {\r
167                 client.Send(encoding.GetBytes("]}\n"));\r
168             }\r
169         }\r
170 \r
171         private static void SendFile(Socket client, string path, string mime)\r
172         {\r
173             using (var header = new StreamWriter(new MemoryStream(), Encoding.ASCII))\r
174             {\r
175                 header.Write("HTTP/1.1 200 OK\r\n");\r
176                 header.Write("Server: KancolleSniffer\r\n");\r
177                 header.Write("Date: {0:R}\r\n", DateTime.Now);\r
178                 header.Write("Content-Length: {0}\r\n", new FileInfo(path).Length);\r
179                 header.Write("Content-Type: {0}\r\n", mime);\r
180                 header.Write("Connection: close\r\n\r\n");\r
181                 header.Flush();\r
182                 client.SendFile(path, ((MemoryStream)header.BaseStream).ToArray(), null,\r
183                     TransmitFileOptions.UseDefaultWorkerThread);\r
184             }\r
185         }\r
186 \r
187         private static void SendProxyPac(Socket client, int port)\r
188         {\r
189             using (var header = new StreamWriter(new MemoryStream(), Encoding.ASCII))\r
190             {\r
191                 header.Write("HTTP/1.1 200 OK\r\n");\r
192                 header.Write("Server: KancolleSniffer\r\n");\r
193                 header.Write("Date: {0:R}\r\n", DateTime.Now);\r
194                 header.Write("Content-Type: application/x-ns-proxy-autoconfig\r\n");\r
195                 header.Write("Connection: close\r\n\r\n");\r
196                 header.Flush();\r
197                 client.Send(((MemoryStream)header.BaseStream).ToArray());\r
198             }\r
199             var pacFile = @"\r
200 function FindProxyForURL(url, host) {\r
201   if(isInNet(host, ""203.104.209.71"", ""255.255.255.255"") ||\r
202      isInNet(host, ""125.6.184.15"", ""255.255.255.255"") ||\r
203      isInNet(host, ""125.6.184.16"", ""255.255.255.255"") ||\r
204      isInNet(host, ""125.6.187.205"", ""255.255.255.255"") ||\r
205      isInNet(host, ""125.6.187.229"", ""255.255.255.255"") ||\r
206      isInNet(host, ""125.6.187.253"", ""255.255.255.255"") ||\r
207      isInNet(host, ""125.6.188.25"", ""255.255.255.255"") ||\r
208      isInNet(host, ""203.104.248.135"", ""255.255.255.255"") ||\r
209      isInNet(host, ""125.6.189.7"", ""255.255.255.255"") ||\r
210      isInNet(host, ""125.6.189.39"", ""255.255.255.255"") ||\r
211      isInNet(host, ""125.6.189.71"", ""255.255.255.255"") ||\r
212      isInNet(host, ""125.6.189.103"", ""255.255.255.255"") ||\r
213      isInNet(host, ""125.6.189.135"", ""255.255.255.255"") ||\r
214      isInNet(host, ""125.6.189.167"", ""255.255.255.255"") ||\r
215      isInNet(host, ""125.6.189.215"", ""255.255.255.255"") ||\r
216      isInNet(host, ""125.6.189.247"", ""255.255.255.255"") ||\r
217      isInNet(host, ""203.104.209.23"", ""255.255.255.255"") ||\r
218      isInNet(host, ""203.104.209.39"", ""255.255.255.255"") ||\r
219      isInNet(host, ""203.104.209.55"", ""255.255.255.255"") ||\r
220      isInNet(host, ""203.104.209.102"", ""255.255.255.255"") ||\r
221      isInNet(host, ""203.104.209.87"", ""255.255.255.255"")) {\r
222        return ""PROXY 127.0.0.1:8080"";\r
223     }\r
224   else {\r
225     return ""DIRECT"";\r
226   }\r
227 }".Replace("8080", port.ToString());\r
228             client.Send(Encoding.ASCII.GetBytes(pacFile));\r
229         }\r
230     }\r
231 }