forked from nikhilk/scriptsharp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHttpMessage.cs
More file actions
258 lines (203 loc) · 7.63 KB
/
Copy pathHttpMessage.cs
File metadata and controls
258 lines (203 loc) · 7.63 KB
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
// HttpMessage.cs
// Script#/Tools/Testing
// This source code is subject to terms and conditions of the Apache License, Version 2.0.
//
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace ScriptSharp.Testing.WebServer {
internal sealed class HttpMessage {
private const int MaximumPostSize = 10 * 1024 * 1024; // 10MB
private const int BufferSize = 4096;
private HttpServer _server;
private TcpClient _client;
private Action<HttpMessage> _getHandler;
private Action<HttpMessage> _postHandler;
private string _method;
private string _path;
private Dictionary<string, string> _headers;
private Stream _requestStream;
private Stream _responseStream;
internal HttpMessage(HttpServer server, Action<HttpMessage> getHandler, Action<HttpMessage> postHandler) {
_server = server;
_getHandler = getHandler;
_postHandler = postHandler;
}
public string Method {
get {
return _method;
}
}
public string Path {
get {
return _path;
}
}
public Stream RequestStream {
get {
return _requestStream;
}
}
public Stream ResponseStream {
get {
return _responseStream;
}
}
private void ParseRequest(Stream inputStream) {
string request = ReadLine(inputStream);
string[] tokens = request.Split(' ');
if (tokens.Length != 3) {
throw new Exception("Invalid HTTP request");
}
_method = tokens[0].ToUpper();
_path = tokens[1];
}
private void ParseRequestHeaders(Stream inputStream) {
_headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
string line;
while ((line = ReadLine(inputStream)) != null) {
if (line.Equals("")) {
// Finished reading headers
return;
}
int separator = line.IndexOf(':');
if (separator == -1) {
throw new Exception("Invalid HTTP header: " + line);
}
string name = line.Substring(0, separator);
int pos = separator + 1;
while ((pos < line.Length) && (line[pos] == ' ')) {
// Strip any spaces
pos++;
}
string value = line.Substring(pos, line.Length - pos);
if (String.IsNullOrEmpty(value)) {
throw new Exception("Invalid HTTP header: " + line);
}
_headers[name] = value;
}
}
private void ParseRequestBody(Stream inputStream) {
MemoryStream ms = new MemoryStream();
int contentLength;
string contentLengthValue;
if (_headers.TryGetValue("Content-Length", out contentLengthValue) &&
Int32.TryParse(contentLengthValue, out contentLength)) {
if (contentLength > MaximumPostSize) {
throw new Exception("Request is too large");
}
int bytesToRead = contentLength;
byte[] buffer = new byte[BufferSize];
while (bytesToRead > 0) {
int bytesRead = inputStream.Read(buffer, 0, Math.Min(BufferSize, bytesToRead));
if (bytesRead == 0) {
if (bytesToRead == 0) {
break;
}
else {
throw new Exception("Client disconnected");
}
}
bytesToRead -= bytesRead;
ms.Write(buffer, 0, bytesRead);
}
ms.Seek(0, SeekOrigin.Begin);
}
_requestStream = ms;
}
public void ProcessClient(TcpClient client) {
_client = client;
Thread messageThread = new Thread(Run);
messageThread.Start();
}
private string ReadLine(Stream inputStream) {
int nextByte;
string data = "";
while (true) {
nextByte = inputStream.ReadByte();
if (nextByte == '\n') {
break;
}
if (nextByte == '\r') {
continue;
}
if (nextByte == -1) {
Thread.Sleep(1);
continue;
};
data += Convert.ToChar(nextByte);
}
return data;
}
private void Run() {
NetworkStream tcpStream = _client.GetStream();
BufferedStream tcpReadStream = new BufferedStream(tcpStream);
BufferedStream tcpSendStream = new BufferedStream(tcpStream);
_responseStream = tcpSendStream;
try {
ParseRequest(tcpReadStream);
ParseRequestHeaders(tcpReadStream);
bool processed = false;
if (_method.Equals("GET")) {
if (_getHandler != null) {
_getHandler(this);
processed = true;
}
}
else if (_method.Equals("POST")) {
if (_postHandler != null) {
ParseRequestBody(tcpReadStream);
_postHandler(this);
processed = true;
}
}
if (processed == false) {
WriteStatus(HttpStatusCode.MethodNotAllowed);
}
}
catch (Exception) {
WriteStatus(HttpStatusCode.InternalServerError);
}
_responseStream.Flush();
_client.Close();
}
public void WriteContent(string content, string contentType) {
StreamWriter writer = new StreamWriter(_responseStream);
writer.WriteLine("HTTP/1.0 200 OK");
writer.WriteLine("Content-Type: " + contentType);
writer.WriteLine("Connection: close");
writer.WriteLine("");
writer.Write(content);
writer.Flush();
}
public void WriteFile(string path, string contentType) {
StreamWriter writer = new StreamWriter(_responseStream);
writer.WriteLine("HTTP/1.0 200");
writer.WriteLine("Content-Type: " + contentType);
writer.WriteLine("Connection: close");
writer.WriteLine("");
writer.Flush();
using (Stream fileStream = File.OpenRead(path)) {
byte[] buffer = new byte[BufferSize];
int bytesRead = 0;
do {
bytesRead = fileStream.Read(buffer, 0, BufferSize);
if (bytesRead != 0) {
_responseStream.Write(buffer, 0, bytesRead);
}
}
while (bytesRead != 0);
}
}
public void WriteStatus(HttpStatusCode statusCode) {
StreamWriter writer = new StreamWriter(_responseStream);
writer.WriteLine("HTTP/1.0 " + (int)statusCode);
writer.WriteLine("Connection: close");
writer.WriteLine("");
writer.Flush();
}
}
}