OSDN Git Service

エラーメッセージのword wrapを止める
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / LogServer.cs
1 // Copyright (C) 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 //\r
3 // This program is part of KancolleSniffer.\r
4 //\r
5 // KancolleSniffer is free software: you can redistribute it and/or modify\r
6 // it under the terms of the GNU General Public License as published by\r
7 // the Free Software Foundation, either version 3 of the License, or\r
8 // (at your option) any later version.\r
9 //\r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 //\r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, see <http://www.gnu.org/licenses/>.\r
17 \r
18 using System;\r
19 using System.Globalization;\r
20 using System.IO;\r
21 using System.Linq;\r
22 using System.Net;\r
23 using System.Net.Sockets;\r
24 using System.Text;\r
25 using System.Threading;\r
26 using System.Web;\r
27 \r
28 namespace KancolleSniffer\r
29 {\r
30     public class LogServer\r
31     {\r
32         private readonly TcpListener _listener;\r
33         private readonly string _indexDir = AppDomain.CurrentDomain.BaseDirectory;\r
34         private string _outputDir = AppDomain.CurrentDomain.BaseDirectory;\r
35 \r
36         public int Port { get; private set; }\r
37 \r
38         public bool IsListening { get; private set; }\r
39 \r
40         public string OutputDir\r
41         {\r
42             set { _outputDir = value; }\r
43         }\r
44 \r
45         public LogServer(int port)\r
46         {\r
47             _listener = new TcpListener(IPAddress.Loopback, port);\r
48         }\r
49 \r
50         public void Start()\r
51         {\r
52             _listener.Start();\r
53             Port = ((IPEndPoint)_listener.LocalEndpoint).Port;\r
54             IsListening = true;\r
55             new Thread(Listen).Start();\r
56         }\r
57 \r
58         private void Listen()\r
59         {\r
60             try\r
61             {\r
62                 while (true)\r
63                 {\r
64                     var socket = _listener.AcceptSocket();\r
65                     new Thread(Process).Start(socket);\r
66                 }\r
67             }\r
68             catch (SocketException)\r
69             {\r
70             }\r
71             finally\r
72             {\r
73                 _listener.Stop();\r
74             }\r
75         }\r
76 \r
77         private void Process(Object obj)\r
78         {\r
79             var client = (Socket)obj;\r
80             var data = new byte[4096];\r
81             var from = DateTime.MinValue;\r
82             var to = DateTime.MaxValue;\r
83             try\r
84             {\r
85                 if (client.Receive(data) == 0)\r
86                     return;\r
87                 var request = Encoding.UTF8.GetString(data).Split('\r')[0].Split(' ');\r
88                 if (request.Length != 3)\r
89                 {\r
90                     SendError(client, "400 Bad Request");\r
91                     return;\r
92                 }\r
93                 if (!request[0].StartsWith("GET", StringComparison.OrdinalIgnoreCase))\r
94                 {\r
95                     SendError(client, "501 Not Implemented");\r
96                     return;\r
97                 }\r
98                 var tmp = request[1].Split('?');\r
99                 var path = HttpUtility.UrlDecode(tmp[0]);\r
100                 if (path == null || !path.StartsWith("/"))\r
101                 {\r
102                     SendError(client, "400 Bad Request");\r
103                     return;\r
104                 }\r
105                 if (tmp.Length == 2)\r
106                 {\r
107                     var query = HttpUtility.ParseQueryString(tmp[1]);\r
108                     if (query["from"] != null)\r
109                     {\r
110                         double tick;\r
111                         double.TryParse(query["from"], out tick);\r
112                         from = new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(tick / 1000);\r
113                     }\r
114                     if (query["to"] != null)\r
115                     {\r
116                         double tick;\r
117                         double.TryParse(query["to"], out tick);\r
118                         to = new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(tick / 1000);\r
119                     }\r
120                 }\r
121 \r
122                 path = path == "/" ? "index.html" : path.Substring(1);\r
123                 var full = Path.Combine(_indexDir, path);\r
124                 var csv = Path.Combine(_outputDir, path);\r
125                 if (path.EndsWith(".html", StringComparison.OrdinalIgnoreCase) && File.Exists(full))\r
126                 {\r
127                     SendFile(client, full, "text/html");\r
128                     return;\r
129                 }\r
130                 if (path.EndsWith(".csv", StringComparison.OrdinalIgnoreCase) && File.Exists(csv))\r
131                 {\r
132                     SendFile(client, csv, "text/csv; charset=Shift_JIS");\r
133                     return;\r
134                 }\r
135                 if (path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))\r
136                 {\r
137                     SendJsonData(client, csv, from, to);\r
138                     return;\r
139                 }\r
140                 if (path.EndsWith(".js", StringComparison.OrdinalIgnoreCase) && File.Exists(full))\r
141                 {\r
142                     SendFile(client, full, "application/javascript");\r
143                     return;\r
144                 }\r
145                 SendError(client, "404 Not Found");\r
146             }\r
147             catch (IOException)\r
148             {\r
149             }\r
150             catch (SocketException)\r
151             {\r
152             }\r
153             finally\r
154             {\r
155                 client.Close();\r
156             }\r
157         }\r
158 \r
159         private void SendError(Socket client, string error)\r
160         {\r
161             using (var writer = new StreamWriter(new MemoryStream(), Encoding.ASCII))\r
162             {\r
163                 writer.Write("HTTP/1.1 {0}\r\n", error);\r
164                 writer.Write("Server: KancolleSniffer\r\n");\r
165                 writer.Write("Date: {0:R}\r\n", DateTime.Now);\r
166                 writer.Write("Connection: close\r\n\r\n");\r
167                 writer.Write("<html><head><title>{0}</title></head>\r\n", error);\r
168                 writer.Write("<body><h4>{0}</h4></body></html>\r\n\r\n", error);\r
169                 writer.Flush();\r
170                 client.Send(((MemoryStream)writer.BaseStream).ToArray());\r
171             }\r
172         }\r
173 \r
174         private void SendJsonData(Socket client, string path, DateTime from, DateTime to)\r
175         {\r
176             var header = new StreamWriter(new MemoryStream(), Encoding.ASCII);\r
177             header.Write("HTTP/1.1 200 OK\r\n");\r
178             header.Write("Server: KancolleSniffer\r\n");\r
179             header.Write("Date: {0:R}\r\n", DateTime.Now);\r
180             header.Write("Content-Type: {0}\r\n", "application/json; charset=Shift_JIS");\r
181             header.Write("Connection: close\r\n\r\n");\r
182             header.Flush();\r
183             client.Send(((MemoryStream)header.BaseStream).ToArray());\r
184 \r
185             var csv = path.Replace(".json", ".csv");\r
186             var encoding = Encoding.GetEncoding("Shift_JIS");\r
187             client.Send(encoding.GetBytes("{ \"data\": [\n"));\r
188             try\r
189             {\r
190                 if (File.Exists(csv))\r
191                 {\r
192                     var delimiter = "";\r
193                     var material = path.EndsWith("資材ログ.json"); // 末尾の空データを削除する必要がある\r
194                     foreach (var line in File.ReadLines(csv, encoding).Skip(1))\r
195                     {\r
196                         var data = line.Split(',');\r
197                         DateTime date;\r
198                         if (!DateTime.TryParseExact(data[0], Logger.DateTimeFormat, CultureInfo.InvariantCulture,\r
199                             DateTimeStyles.AssumeLocal, out date) &&\r
200                             DateTime.TryParse(data[0], CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal, out date))\r
201                         {\r
202                             data[0] = date.ToString(Logger.DateTimeFormat);\r
203                         }\r
204                         if (date < from || to < date)\r
205                             continue;\r
206                         client.Send(encoding.GetBytes(delimiter + "[\"" +\r
207                                                       string.Join("\",\"", (material ? data.Take(9) : data)) + "\"]"));\r
208                         delimiter = ",\n";\r
209                     }\r
210                 }\r
211             }\r
212             finally\r
213             {\r
214                 client.Send(encoding.GetBytes("]}\n"));\r
215             }\r
216         }\r
217 \r
218         private void SendFile(Socket client, string path, string mime)\r
219         {\r
220             using (var writer = new StreamWriter(new MemoryStream(), Encoding.ASCII))\r
221             {\r
222                 writer.Write("HTTP/1.1 200 OK\r\n");\r
223                 writer.Write("Server: KancolleSniffer\r\n");\r
224                 writer.Write("Date: {0:R}\r\n", DateTime.Now);\r
225                 writer.Write("Content-Length: {0}\r\n", new FileInfo(path).Length);\r
226                 writer.Write("Content-Type: {0}\r\n", mime);\r
227                 writer.Write("Connection: close\r\n\r\n");\r
228                 writer.Flush();\r
229                 client.SendFile(path, ((MemoryStream)writer.BaseStream).ToArray(), null,\r
230                     TransmitFileOptions.UseDefaultWorkerThread);\r
231             }\r
232         }\r
233 \r
234         public void Stop()\r
235         {\r
236             IsListening = false;\r
237             _listener.Server.Close();\r
238         }\r
239     }\r
240 }