forked from PJayB/HTTP
-
Notifications
You must be signed in to change notification settings - Fork 1
/
HTTP.h
471 lines (402 loc) · 11 KB
/
HTTP.h
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
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
#pragma once
#include <string>
#include <map>
#include <functional>
#ifdef _WIN32
# include <SDKDDKVer.h>
# include <Windows.h>
#else
# error Good luck.
#endif
namespace HTTP
{
enum METHOD
{
METHOD_GET,
METHOD_PUT,
METHOD_POST,
METHOD_DELETE,
METHOD_HEAD,
METHOD_OPTIONS
};
enum PROTOCOL
{
PROTOCOL_HTTP_1_0,
PROTOCOL_HTTP_1_1
};
enum AUTH_MODE
{
AUTH_NONE,
AUTH_BASIC
};
enum RESPONSE_CODE
{
// Informational
RESPONSE_CONTINUE = 100, // Continue (please send body)
RESPONSE_SWITCHING_PROTOCOLS = 101, // Switching Protocols
RESPONSE_PROCESSING = 102, // Processing WebDAV operations
// Success codes
RESPONSE_OK = 200, // OK
RESPONSE_CREATED = 201, // Created
RESPONSE_ACCEPTED = 202, // Request Accepted
RESPONSE_NON_AUTHORITATIVE = 203, // Returning information from untrusted source
RESPONSE_NO_CONTENT = 204, // No content returned by the server
RESPONSE_RESET = 205, // Like 204, but client must reset the view
RESPONSE_PARTIAL = 206, // Partial content being returned
// Redirection codes
RESPONSE_MOVED = 301, // Response should be "URI: url comment endl"
RESPONSE_FOUND = 302, // Treated the same as 301
RESPONSE_METHOD = 303, // Reponse should be "Method: method url endl body"
RESPONSE_NOTMODIFIED = 304, // Like anyone cares about this.
// Client error
RESPONSE_BADREQUEST = 400, // The client sent a malformed request
RESPONSE_UNAUTHORISED = 401, // The client isn't authorised for this resource
RESPONSE_PAYMENTREQUIRED = 402, // The client must pay to access this resource
RESPONSE_FORBIDDEN = 403, // The client can't access this
RESPONSE_NOTFOUND = 404, // It doesn't exist.
// Server error
RESPONSE_NOTIMPL = 500, // The function requested isn't implemented.
RESPONSE_TOOBUSY = 501, // The server is too busy.
RESPONSE_GATEWAYTIMEOUT = 502 // The gateway timed out.
};
enum REQUEST_PARSE_RESULT
{
REQUEST_PARSE_OK,
REQUEST_PARSE_MALFORMED, // Unknown parsing error
REQUEST_PARSE_UNKNOWN_METHOD, // Expected GET or POST
REQUEST_PARSE_UNKNOWN_PROTOCOL, // Expected HTTP/1.1
REQUEST_PARSE_MALFORMED_AUTH, // Authorization data was corrupt
REQUEST_PARSE_MALFORMED_CONTENT, // Content attached to the header was corrupt
};
enum RESPONSE_HEADER_RESULT
{
RESPONSE_HEADER_OK,
RESPONSE_HEADER_NEED_REDIRECT_URI,
RESPONSE_HEADER_NEED_AUTH_MODE,
RESPONSE_HEADER_NEED_AUTH_REALM,
RESPONSE_HEADER_NEED_CONTENT_MIME
};
enum SAFE_URI_ENCODE
{
SAFE_URI_ENCODE_RFC_3986, // !*'();:@&=+$,/\?%#[]
SAFE_URI_ENCODE_RFC_3986_PATH, // !*'();:@&=+$,?%#[] (leaves / and \)
SAFE_URI_ENCODE_ALL_NON_ALPHANUMERIC // All characters that aren't A-Z/a-z/0-9
};
//
// These convert some of the various enums above into
// ASCII strings.
//
LPCSTR MethodToString(_In_ METHOD m);
LPCSTR ProtocolToString(_In_ PROTOCOL p);
LPCSTR AuthModeToString(_In_ AUTH_MODE a);
LPCSTR ResponseCodeToString(_In_ RESPONSE_CODE rc);
typedef std::string String;
typedef const std::string& StringRef;
typedef std::map<std::string, std::string> StringTable;
typedef const StringTable& StringTableRef;
//
// When we receive data from browsers, they come prefixed
// with a header. Pass your data to RequestHeader.Parse
// to decode it.
//
// Explanation of some of the fields:
//
// Method: This is either GET or POST. If it is POST,
// you should use pPostDataOffsetOut to decode
// post data that begins after the header.
//
// AuthMode: If this is 'None', you can ignore it. In
// all other cases, this means that User and
// Password will contain the user's inputted
// credentials.
// NOTE: This HTTP server only supports Basic.
//
// ResourceURI: The URL that the user requested.
// e.g. /my/path/my_page.txt?param1=foo¶m2=bar
// You can use the URI class to parse this.
//
// Header: This contains extra fields provided from
// the browser, indexable by name. A common
// one is "User-Agent".
//
class RequestHeader
{
public:
RequestHeader();
~RequestHeader();
METHOD Method() const;
PROTOCOL Protocol() const;
// This is 'None' if no credentials were supplied.
AUTH_MODE AuthMode() const;
StringRef AuthUser() const;
StringRef AuthPassword() const;
StringRef ResourceURI() const;
// You can access other header information here.
// e.g. Header()["User-Agent"]
StringTableRef Header() const;
//
// Parses a header stream received from a browser.
//
// If there's any post data, it begins 'PostDataOffset'
// bytes into the pRequestData stream.
//
REQUEST_PARSE_RESULT Parse(
_In_ const char* pRequestData,
_Out_opt_ SIZE_T* pPostDataOffsetOut);
private:
struct REQUEST_DATA* m_pData;
};
//
// This is used to build a stream for sending back data
// to the browser.
//
class ResponseHeaderBuilder
{
public:
ResponseHeaderBuilder();
ResponseHeaderBuilder&
AddKey(
_In_z_ StringRef Key,
_In_z_ StringRef Value);
RESPONSE_HEADER_RESULT
Build(
_Out_ String& Output) const;
//
// Tools for constructing default web responses
//
RESPONSE_HEADER_RESULT
AddBinaryHeaders(
_In_ SIZE_T ContentLength,
_In_z_ LPCSTR MimeType);
RESPONSE_HEADER_RESULT
AddTextHeaders(
_In_ SIZE_T ContentLength,
_In_z_ LPCSTR MimeType,
_In_z_ LPCSTR Encoding);
PROTOCOL Protocol; // The HTTP protocol to use
RESPONSE_CODE Code; // The response code
METHOD Method; // For RESPONSE_METHOD only.
String RedirectURI; // Must specify redirect code to use this
AUTH_MODE AuthMode; // Set to non-None to request credentials
String AuthRealm; // Description of the authorization realm
private:
StringTable m_ExtraLines;
};
//
// Utility to generate timestamps
//
String TimeStampString();
//
// Utility function for splitting HTTP-style parameter
// lists into key-value pairs.
//
// Example Input: sourceid=chrome&ie=UTF-8&q=foo+%26+bar
// Example Output:
// Key Value
// ---------- ----------
// ie UTF-8
// q foo+%26+bar
// sourceid chrome
void
ParseParameterList(
_In_z_ LPCSTR ParameterString,
_Out_ StringTable& Output);
//
// Builds a parameter string from a StringTable. Make sure
// that the entries in the string table are URI safe.
//
String
BuildParameterList(
_In_ StringTableRef Parameters);
//
// Utility function for decoding URI-safe strings
//
// Example Input: foo%20%26%20bar
// Example Output: foo & bar
//
String
DecodeURISafeString(
_In_reads_(Count) LPCSTR URIString,
_In_ SIZE_T Count);
String
DecodeURISafeString(
_In_z_ LPCSTR URIString);
//
// Encode a string into a URI-safe string.
// Use SAFE_URI_ENCODE_RFC_3986 for most data.
// Use SAFE_URI_ENCODE_RFC_3986_PATH for URI paths.
// Use SAFE_URI_ENCODE_ALL_NON_ALPHANUMERIC for ALL symbols to be encoded.
//
String
EncodeURISafeString(
_In_reads_(Count) LPCSTR UnsafeString,
_In_ SIZE_T Count,
_In_ SAFE_URI_ENCODE EncodeType);
String
EncodeURISafeString(
_In_z_ LPCSTR UnsafeString,
_In_ SAFE_URI_ENCODE EncodeType);
//
// This decodes a URI. This will perform all encoding
// and parsing processes.
//
// For example, calling Parse on a URL such as:
// /resource/path%20with%20spaces.ext?param=lolcats%20%26%20cake#anchor
// Will output:
// Resource = "/resource/path with spaces.ext"
// Anchor = "anchor"
// Parameters = { "param", "lolcats & caek" }
//
// Likewise, calling ToString() with those members will
// construct the example string listed above.
//
class URI
{
public:
String Resource;
String Anchor;
StringTable Parameters;
//
// This takes the resource and parameters and constructs
// a string like this:
// /some/resource/path.ext?param1=foo¶m2=bar#anchor
//
String ToString(
_In_ bool MakeURISafe = true
) const;
//
// This takes a string like:
// /some/resource/path.ext?param1=foo¶m2=bar#anchor
// And splits the path into 'Resource' and the
// GET parameters into 'Parameters'.
//
// Existing Parameters will be erased.
//
void
ParseURIString(
_In_z_ LPCSTR URIString);
};
//
// Base64 decoding/encoding
//
String
Base64Encode(
_In_reads_(DataSize) LPCVOID pData,
_In_ SIZE_T DataSize);
String
Base64Encode(
_In_ StringRef In);
String
Base64Decode(
_In_reads_(DataSize) LPCVOID pData,
_In_ SIZE_T DataSize);
String
Base64Decode(
_In_ StringRef In);
//
// WebSockets API
//
enum WS_RESPONSE_RESULT
{
WS_RESPONSE_OK,
WS_RESPONSE_MISSING_KEY
};
// Returns true if the header looks like a Websocket request
bool IsWebsocketRequest(
_In_ const RequestHeader& req);
// This takes in a string and generates a 160-bit SHA1 hash, returned through
// 5 UINTs.
typedef std::function<void (LPCSTR, LPUINT)> HashFunc;
WS_RESPONSE_RESULT
BuildWebsocketRequestResponse(
_In_ const HTTP::RequestHeader& request,
_In_ HashFunc HashFunction,
_Out_ ResponseHeaderBuilder* responseBuilder);
enum WS_FRAME_RESULT
{
WS_FRAME_OK,
WS_FRAME_ERROR,
// If you get this, it means you specified/received an OpCode
// without FinalPacket set.
WS_FRAME_FRAGMENTED_OPCODE
};
enum WS_FRAME_OPCODE
{
WS_FRAME_OPCODE_CONTINUATION = 0x0,
WS_FRAME_OPCODE_TEXT = 0x1,
WS_FRAME_OPCODE_BINARY = 0x2,
WS_FRAME_OPCODE_CONNECTION_CLOSE = 0x8,
WS_FRAME_OPCODE_PING = 0x9,
WS_FRAME_OPCODE_PONG = 0xA
};
enum WS_CLOSE_REASON
{
WS_CLOSE_NORMAL = 1000,
WS_CLOSE_GOING_AWAY = 1001,
WS_CLOSE_PROTOCOL_ERROR = 1002,
WS_CLOSE_NO_DATA_HANDLER = 1003,
// 1004 Reserved
// 1005 Reserved
// 1006 Reserved
WS_CLOSE_INCONSISTENT_DATA = 1007,
WS_CLOSE_VOILATES_POLICY = 1008,
WS_CLOSE_MESSAGE_TOO_LARGE = 1009,
WS_CLOSE_REQUIRES_EXTENSION = 1010,
WS_CLOSE_CANNOT_FULFILL_REQUEST = 1011
// 1012-2999 Reserved
};
struct WS_FRAME_INFO
{
BYTE FinalPacket : 1;
BYTE RSV1 : 1;
BYTE RSV2 : 1;
BYTE RSV3 : 1;
BYTE Masked : 1;
WS_FRAME_OPCODE OpCode : 7;
DWORD MaskingKey;
ULONGLONG PayloadLength;
};
struct WS_PACKED_FRAME_HEADER
{
BYTE Length;
BYTE Data[15];
};
WS_FRAME_RESULT
ParseWebsocketFrame(
_In_reads_(DataLength) LPCVOID pData,
_In_ ULONGLONG DataLength,
_Out_ WS_FRAME_INFO* pFrameInfo,
_Out_ LPCVOID* pFramePayload);
WS_FRAME_RESULT
SetWebsocketFrame(
_In_ const WS_FRAME_INFO* pFrameInfo,
_Out_ WS_PACKED_FRAME_HEADER* pFrameHeader);
// Utility function for constructing a control frame
WS_FRAME_RESULT
SetWebsocketControlFrame(
_In_ WS_FRAME_OPCODE OpCode,
_In_ ULONGLONG PayloadLength,
_Out_ WS_PACKED_FRAME_HEADER* pFrameHeader);
// Utility function for constructing a close frame
WS_FRAME_RESULT
SetWebsocketCloseFrame(
_In_ WS_CLOSE_REASON Reason,
_In_ ULONGLONG PayloadLength,
_Out_ WS_PACKED_FRAME_HEADER* pFrameHeader);
// This is synonymous with UnmaskWebsocketFrame, but
// is aliased for clarity.
void
MaskWebsocketPayload(
_In_reads_(DataLength) LPCVOID pData,
_In_ ULONGLONG DataLength,
_In_ DWORD MaskingKey,
_Out_writes_(DataLength) LPVOID pOutData);
// This is synonymous with MaskWebsocketFrame, but
// is aliased for clarity.
void
UnmaskWebsocketPayload(
_In_reads_(DataLength) LPCVOID pData,
_In_ ULONGLONG DataLength,
_In_ DWORD MaskingKey,
_Out_writes_(DataLength) LPVOID pOutData);
}