forked from CharlZKP/CPE433-62
-
Notifications
You must be signed in to change notification settings - Fork 34
/
Program.cs
308 lines (283 loc) · 9.93 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Threading;
using Microsoft.Extensions.Configuration;
namespace DNWS
{
// Main class
public class Program
{
static public IConfigurationRoot Configuration { get; set; }
// Log to console
public void Log(String msg)
{
Console.WriteLine(msg);
}
// Start the server, Singleton here
public void Start()
{
// Start server
var builder = new ConfigurationBuilder();
builder.SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("config.json");
Configuration = builder.Build();
DotNetWebServer ws = DotNetWebServer.GetInstance(this);
ws.Start();
}
static void Main(string[] args)
{
Program p = new Program();
p.Start();
}
}
/// <summary>
/// HTTP processor will process each http request
/// </summary>
public class HTTPProcessor
{
protected class PluginInfo
{
protected string _path;
protected string _type;
protected bool _preprocessing;
protected bool _postprocessing;
protected IPlugin _reference;
public string path
{
get { return _path;}
set {_path = value;}
}
public string type
{
get { return _type;}
set {_type = value;}
}
public bool preprocessing
{
get { return _preprocessing;}
set {_preprocessing = value;}
}
public bool postprocessing
{
get { return _postprocessing;}
set {_postprocessing = value;}
}
public IPlugin reference
{
get { return _reference;}
set {_reference = value;}
}
}
// Get config from config manager, e.g., document root and port
protected string ROOT = Program.Configuration["DocumentRoot"];
protected Socket _client;
protected Program _parent;
protected Dictionary<string, PluginInfo> plugins;
/// <summary>
/// Constructor, set the client socket and parent ref, also init stat hash
/// </summary>
/// <param name="client">Client socket</param>
/// <param name="parent">Parent ref</param>
public HTTPProcessor(Socket client, Program parent)
{
_client = client;
_parent = parent;
plugins = new Dictionary<string, PluginInfo>();
// load plugins
var sections = Program.Configuration.GetSection("Plugins").GetChildren();
foreach(ConfigurationSection section in sections) {
PluginInfo pi = new PluginInfo();
pi.path = section["Path"];
pi.type = section["Class"];
pi.preprocessing = section["Preprocessing"].ToLower().Equals("true");
pi.postprocessing = section["Postprocessing"].ToLower().Equals("true");
pi.reference = (IPlugin) Activator.CreateInstance(Type.GetType(pi.type));
plugins[section["Path"]] = pi;
}
}
/// <summary>
/// Get a file from local harddisk based on path
/// </summary>
/// <param name="path">Absolute path to the file</param>
/// <returns></returns>
protected HTTPResponse getFile(String path)
{
HTTPResponse response = null;
// Guess the content type from file extension
string fileType = "text/html";
if (path.ToLower().EndsWith("jpg") || path.ToLower().EndsWith("jpeg"))
{
fileType = "image/jpeg";
}
if (path.ToLower().EndsWith("png"))
{
fileType = "image/png";
}
// Try to read the file, if not found then 404, otherwise, 500.
try
{
response = new HTTPResponse(200);
response.type = fileType;
response.body = System.IO.File.ReadAllBytes(path);
}
catch (FileNotFoundException ex)
{
response = new HTTPResponse(404);
response.body = Encoding.UTF8.GetBytes("<h1>404 Not found</h1>" + ex.Message);
}
catch (Exception ex)
{
response = new HTTPResponse(500);
response.body = Encoding.UTF8.GetBytes("<h1>500 Internal Server Error</h1>" + ex.Message);
}
return response;
}
/// <summary>
/// Get a request from client, process it, then return response to client
/// </summary>
public void Process()
{
NetworkStream ns = new NetworkStream(_client);
string requestStr = "";
HTTPRequest request = null;
HTTPResponse response = null;
byte[] bytes = new byte[1024];
int bytesRead;
// Read all request
do
{
bytesRead = ns.Read(bytes, 0, bytes.Length);
requestStr += Encoding.UTF8.GetString(bytes, 0, bytesRead);
} while (ns.DataAvailable);
request = new HTTPRequest(requestStr);
request.addProperty("RemoteEndPoint", _client.RemoteEndPoint.ToString());
// We can handle only GET now
if(request.Status != 200) {
response = new HTTPResponse(request.Status);
}
else
{
bool processed = false;
// pre processing
foreach(KeyValuePair<string, PluginInfo> plugininfo in plugins) {
if(plugininfo.Value.preprocessing) {
plugininfo.Value.reference.PreProcessing(request);
}
}
// plugins
foreach(KeyValuePair<string, PluginInfo> plugininfo in plugins) {
if(request.Filename.StartsWith(plugininfo.Key)) {
response = plugininfo.Value.reference.GetResponse(request);
processed = true;
}
}
// local file
if(!processed) {
if (request.Filename.Equals(""))
{
response = getFile(ROOT + "/index.html");
}
else
{
response = getFile(ROOT + "/" + request.Filename);
}
}
// post processing pipe
foreach(KeyValuePair<string, PluginInfo> plugininfo in plugins) {
if(plugininfo.Value.postprocessing) {
response = plugininfo.Value.reference.PostProcessing(response);
}
}
}
// Generate response
ns.Write(Encoding.UTF8.GetBytes(response.header), 0, response.header.Length);
if(response.body != null) {
ns.Write(response.body, 0, response.body.Length);
}
// Shuting down
//ns.Close();
_client.Shutdown(SocketShutdown.Both);
//_client.Close();
}
}
public class TaskInfo
{
private HTTPProcessor _hp;
public HTTPProcessor hp
{
get {return _hp;}
set {_hp = value;}
}
public TaskInfo(HTTPProcessor hp)
{
this.hp = hp;
}
}
/// <summary>
/// Main server class, open the socket and wait for client
/// </summary>
public class DotNetWebServer
{
protected int _port;
protected Program _parent;
protected Socket serverSocket;
protected Socket clientSocket;
private static DotNetWebServer _instance = null;
protected int id;
private DotNetWebServer(Program parent)
{
_parent = parent;
id = 0;
}
/// <summary>
/// Singleton here
/// </summary>
/// <param name="parent">parent ref</param>
/// <returns></returns>
public static DotNetWebServer GetInstance(Program parent)
{
if (_instance == null)
{
_instance = new DotNetWebServer(parent);
}
return _instance;
}
public void ThreadProc(Object stateinfo)
{
TaskInfo ti = stateinfo as TaskInfo;
ti.hp.Process();
}
/// <summary>
/// Server starting point
/// </summary>
public void Start()
{
_port = Convert.ToInt32(Program.Configuration["Port"]);
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, _port);
// Create listening socket, queue size is 5 now.
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(localEndPoint);
serverSocket.Listen(5);
_parent.Log("Server started at port " + _port + ".");
while (true)
{
try
{
// Wait for client
clientSocket = serverSocket.Accept();
// Get one, show some info
_parent.Log("Client accepted:" + clientSocket.RemoteEndPoint.ToString());
HTTPProcessor hp = new HTTPProcessor(clientSocket, _parent);
hp.Process();
}
catch (Exception ex)
{
_parent.Log("Server starting error: " + ex.Message + "\n" + ex.StackTrace);
}
}
}
}
}