eventmachine_httpserver 0.1.1-x86-mswin32-60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/extconf.rb ADDED
@@ -0,0 +1,133 @@
1
+ #----------------------------------------------------------------------------
2
+ #
3
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
4
+ #
5
+ # Gmail: garbagecat10
6
+ #
7
+ # This program is free software; you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation; either version 2 of the License, or
10
+ # (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program; if not, write to the Free Software
19
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+ #
21
+ #---------------------------------------------------------------------------
22
+ #
23
+ # extconf.rb for Ruby/EventMachine
24
+ # We have to munge LDSHARED because this code needs a C++ link.
25
+ #
26
+
27
+ require 'mkmf'
28
+
29
+ flags = []
30
+
31
+ case RUBY_PLATFORM.split('-',2)[1]
32
+ when 'mswin32', 'mingw32', 'bccwin32'
33
+ unless have_header('windows.h') and
34
+ have_header('winsock.h') and
35
+ have_library('kernel32') and
36
+ have_library('rpcrt4') and
37
+ have_library('gdi32')
38
+ exit
39
+ end
40
+
41
+ flags << "-D OS_WIN32"
42
+ flags << '-D BUILD_FOR_RUBY'
43
+ flags << "-EHs"
44
+ flags << "-GR"
45
+
46
+ dir_config('ssl')
47
+ if have_library('ssleay32') and
48
+ have_library('libeay32') and
49
+ have_header('openssl/ssl.h') and
50
+ have_header('openssl/err.h')
51
+ flags << '-D WITH_SSL'
52
+ else
53
+ flags << '-D WITHOUT_SSL'
54
+ end
55
+
56
+ when /solaris/
57
+ unless have_library('pthread') and
58
+ have_library('nsl') and
59
+ have_library('socket')
60
+ exit
61
+ end
62
+
63
+ flags << '-D OS_UNIX'
64
+ flags << '-D OS_SOLARIS8'
65
+ flags << '-D BUILD_FOR_RUBY'
66
+
67
+ dir_config('ssl')
68
+ if have_library('ssl') and
69
+ have_library('crypto') and
70
+ have_header('openssl/ssl.h') and
71
+ have_header('openssl/err.h')
72
+ flags << '-D WITH_SSL'
73
+ else
74
+ flags << '-D WITHOUT_SSL'
75
+ end
76
+
77
+ # on Unix we need a g++ link, not gcc.
78
+ CONFIG['LDSHARED'] = "$(CXX) -shared"
79
+
80
+ when /darwin/
81
+ flags << '-DOS_UNIX'
82
+ flags << '-DBUILD_FOR_RUBY'
83
+
84
+ dir_config('ssl')
85
+ if have_library('ssl') and
86
+ have_library('crypto') and
87
+ have_library('C') and
88
+ have_header('openssl/ssl.h') and
89
+ have_header('openssl/err.h')
90
+ flags << '-DWITH_SSL'
91
+ else
92
+ flags << '-DWITHOUT_SSL'
93
+ end
94
+ # on Unix we need a g++ link, not gcc.
95
+ # Ff line contributed by Daniel Harple.
96
+ CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
97
+
98
+ else
99
+ unless have_library('pthread')
100
+ exit
101
+ end
102
+
103
+ flags << '-DOS_UNIX'
104
+ flags << '-DBUILD_FOR_RUBY'
105
+
106
+ dir_config('ssl')
107
+ if have_library('ssl') and
108
+ have_library('crypto') and
109
+ have_header('openssl/ssl.h') and
110
+ have_header('openssl/err.h')
111
+ flags << '-DWITH_SSL'
112
+ else
113
+ flags << '-DWITHOUT_SSL'
114
+ end
115
+ # on Unix we need a g++ link, not gcc.
116
+ CONFIG['LDSHARED'] = "$(CXX) -shared"
117
+
118
+ # Modify the mkmf constant LINK_SO so the generated shared object is stripped.
119
+ # You might think modifying CONFIG['LINK_SO'] would be a better way to do this,
120
+ # but it doesn't work because mkmf doesn't look at CONFIG['LINK_SO'] again after
121
+ # it initializes.
122
+ # linkso = Object.send :remove_const, "LINK_SO"
123
+ # LINK_SO = linkso + "; strip $@"
124
+ end
125
+
126
+ if $CPPFLAGS
127
+ $CPPFLAGS += ' ' + flags.join(' ')
128
+ else
129
+ $CFLAGS += ' ' + flags.join(' ')
130
+ end
131
+
132
+
133
+ create_makefile "eventmachine_httpserver"
data/ext/http.cpp ADDED
@@ -0,0 +1,584 @@
1
+ /*****************************************************************************
2
+
3
+ File: http.cpp
4
+ Date: 21Apr06
5
+
6
+ Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
7
+ Gmail: garbagecat10
8
+
9
+ This program is free software; you can redistribute it and/or modify
10
+ it under the terms of the GNU General Public License as published by
11
+ the Free Software Foundation; either version 2 of the License, or
12
+ (at your option) any later version.
13
+
14
+ This program is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ GNU General Public License for more details.
18
+
19
+ You should have received a copy of the GNU General Public License
20
+ along with this program; if not, write to the Free Software
21
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
+
23
+ *****************************************************************************/
24
+
25
+
26
+ #include <iostream>
27
+ #include <string>
28
+ #include <cstdlib>
29
+ #include <cstring>
30
+ #include <sstream>
31
+ #include <stdexcept>
32
+ #include <stdio.h>
33
+
34
+ #ifdef OS_WIN32
35
+ #include <windows.h>
36
+ #endif
37
+
38
+ using namespace std;
39
+
40
+ #include "http.h"
41
+
42
+
43
+ #ifdef OS_WIN32
44
+ #define strncasecmp _strnicmp
45
+ #define strcasecmp _stricmp
46
+ void setenv (const char *str, const char *value, bool replace)
47
+ {
48
+ SetEnvironmentVariable (str, value);
49
+ }
50
+ void unsetenv (const char *str)
51
+ {
52
+ SetEnvironmentVariable (str, NULL);
53
+ }
54
+ #endif
55
+
56
+
57
+ /**********************************
58
+ HttpConnection_t::HttpConnection_t
59
+ **********************************/
60
+
61
+ HttpConnection_t::HttpConnection_t()
62
+ {
63
+ ProtocolState = BaseState;
64
+ _Content = NULL;
65
+
66
+ // By default, we set the standard CGI environment strings.
67
+ // (This is primarily beneficial because it lets the caller use Ruby's CGI classes.)
68
+ // The caller can switch this off in Ruby code, which greatly improves performance.
69
+ bSetEnvironmentStrings = true;
70
+
71
+ // This flag was added by Kirk Haines (thanks, Kirk). It preserves the original
72
+ // behavior with respect to POST content, which was to accumulate it in a buffer
73
+ // allocated and managed in this class. Kirk's mods allow callers to specify that
74
+ // POST content be submitted directly to user code piece by piece as we receive it,
75
+ // instead of buffering it here. To get the latter behavior, user code must call
76
+ // dont_accumulate_post.
77
+ bAccumulatePost = true;
78
+ }
79
+
80
+
81
+ /***********************************
82
+ HttpConnection_t::~HttpConnection_t
83
+ ***********************************/
84
+
85
+ HttpConnection_t::~HttpConnection_t()
86
+ {
87
+ if (_Content)
88
+ free (_Content);
89
+ }
90
+
91
+
92
+
93
+ /**************************
94
+ HttpConnection_t::SendData
95
+ **************************/
96
+
97
+ void HttpConnection_t::SendData (const char *data, int length)
98
+ {
99
+ cerr << "UNIMPLEMENTED SendData" << endl;
100
+ }
101
+
102
+
103
+ /*********************************
104
+ HttpConnection_t::CloseConnection
105
+ *********************************/
106
+
107
+ void HttpConnection_t::CloseConnection (bool after_writing)
108
+ {
109
+ cerr << "UNIMPLEMENTED CloseConnection" << endl;
110
+ }
111
+
112
+
113
+ /********************************
114
+ HttpConnection_t::ProcessRequest
115
+ ********************************/
116
+
117
+ void HttpConnection_t::ProcessRequest (const char *method,
118
+ const char *cookie,
119
+ const char *ifnonematch,
120
+ const char *contenttype,
121
+ const char *query_string,
122
+ const char *path_info,
123
+ const char *request_uri,
124
+ const char *protocol,
125
+ int post_length,
126
+ const char *post_content,
127
+ const char *hdrblock,
128
+ int hdrblocksize)
129
+ {
130
+ cerr << "UNIMPLEMENTED ProcessRequest" << endl;
131
+ }
132
+
133
+
134
+ /*********************************
135
+ HttpConnection_t::ReceivePostData
136
+ *********************************/
137
+
138
+ void HttpConnection_t::ReceivePostData (const char *data, int len)
139
+ {
140
+ cerr << "UNIMPLEMENTED ReceivePostData" << endl;
141
+ }
142
+
143
+ /*****************************
144
+ HttpConnection_t::ConsumeData
145
+ *****************************/
146
+
147
+ void HttpConnection_t::ConsumeData (const char *data, int length)
148
+ {
149
+ if (ProtocolState == EndState)
150
+ return;
151
+
152
+ if ((length > 0) && !data)
153
+ throw std::runtime_error ("bad args consuming http data");
154
+
155
+ while (length > 0) {
156
+ //----------------------------------- BaseState
157
+ // Initialize for a new request. Don't consume any data.
158
+ // For anal-retentive security we may want to bzero the header block.
159
+ if (ProtocolState == BaseState) {
160
+ ProtocolState = PreheaderState;
161
+ nLeadingBlanks = 0;
162
+ HeaderLinePos = 0;
163
+ HeaderBlockPos = 0;
164
+ ContentLength = 0;
165
+ ContentPos = 0;
166
+ bRequestSeen = false;
167
+ bContentLengthSeen = false;
168
+ if (_Content) {
169
+ free ((void*)_Content);
170
+ _Content = NULL;
171
+ }
172
+ RequestMethod = NULL;
173
+ #ifdef OS_WIN32
174
+ Cookie.erase(Cookie.begin(),Cookie.end());
175
+ IfNoneMatch.erase(IfNoneMatch.begin(),IfNoneMatch.end());
176
+ ContentType.erase(ContentType.begin(),ContentType.end());
177
+ PathInfo.erase(PathInfo.begin(),PathInfo.end());
178
+ RequestUri.erase(RequestUri.begin(),RequestUri.end());
179
+ QueryString.erase(QueryString.begin(),QueryString.end());
180
+ Protocol.erase(Protocol.begin(),Protocol.end());
181
+ #else
182
+ Cookie.clear();
183
+ IfNoneMatch.clear();
184
+ ContentType.clear();
185
+ PathInfo.clear();
186
+ RequestUri.clear();
187
+ QueryString.clear();
188
+ Protocol.clear();
189
+ #endif
190
+
191
+ if (bSetEnvironmentStrings) {
192
+ unsetenv ("REQUEST_METHOD");
193
+ unsetenv ("HTTP_COOKIE");
194
+ unsetenv ("IF_NONE_MATCH");
195
+ unsetenv ("CONTENT_TYPE");
196
+ unsetenv ("PATH_INFO");
197
+ unsetenv ("REQUEST_URI");
198
+ unsetenv ("QUERY_STRING");
199
+ unsetenv ("PROTOCOL");
200
+ }
201
+ }
202
+
203
+ //----------------------------------- PreheaderState
204
+ // Consume blank lines (but not too many of them)
205
+ while ((ProtocolState == PreheaderState) && (length > 0)) {
206
+ if ((*data == '\r') || (*data == '\n')) {
207
+ data++;
208
+ length--;
209
+ nLeadingBlanks++;
210
+ if (nLeadingBlanks > MaxLeadingBlanks) {
211
+ // TODO, log this.
212
+ goto fail_connection;
213
+ }
214
+ }
215
+ else
216
+ ProtocolState = HeaderState;
217
+ }
218
+
219
+ //----------------------------------- HeaderState
220
+ // Read HTTP headers.
221
+ // This processing depends on the fact that the end
222
+ // of the data buffer we receive will have a null terminator
223
+ // just after the last byte indicated by the length parameter.
224
+ // Cf notes in ConnectionDescriptor::Read.
225
+ while ((ProtocolState == HeaderState) && (length > 0)) {
226
+ if (*data == '\n') {
227
+ HeaderLine [HeaderLinePos] = 0;
228
+ if (!_InterpretHeaderLine (HeaderLine))
229
+ goto send_error;
230
+ if (HeaderLinePos == 0) {
231
+ if (ContentLength > 0) {
232
+ if (_Content)
233
+ free (_Content);
234
+ _Content = NULL;
235
+ if (bAccumulatePost) {
236
+ _Content = (char*) malloc (ContentLength + 1);
237
+ if (!_Content)
238
+ throw std::runtime_error ("resource exhaustion");
239
+ }
240
+ ContentPos = 0;
241
+ ProtocolState = ReadingContentState;
242
+ }
243
+ else
244
+ ProtocolState = DispatchState;
245
+ }
246
+ HeaderLinePos = 0;
247
+ data++;
248
+ length--;
249
+ }
250
+ else if (*data == '\r') {
251
+ // ignore \r
252
+ data++;
253
+ length--;
254
+ }
255
+ else {
256
+ const char *nl = strpbrk (data, "\r\n");
257
+ int len = nl ? (nl - data) : length;
258
+ if ((size_t)(HeaderLinePos + len) >= sizeof(HeaderLine)) {
259
+ // TODO, log this
260
+ goto fail_connection;
261
+ }
262
+ memcpy (HeaderLine + HeaderLinePos, data, len);
263
+ data += len;
264
+ length -= len;
265
+ HeaderLinePos += len;
266
+ }
267
+ }
268
+
269
+
270
+ //----------------------------------- ReadingContentState
271
+ // Read POST content.
272
+ while ((ProtocolState == ReadingContentState) && (length > 0)) {
273
+ int len = ContentLength - ContentPos;
274
+ if (len > length)
275
+ len = length;
276
+
277
+ if (bAccumulatePost)
278
+ memcpy (_Content + ContentPos, data, len);
279
+ else
280
+ ReceivePostData (data, len);
281
+
282
+ data += len;
283
+ length -= len;
284
+ ContentPos += len;
285
+ if (ContentPos == ContentLength) {
286
+ if (bAccumulatePost)
287
+ _Content[ContentPos] = 0;
288
+ ProtocolState = DispatchState;
289
+ }
290
+ }
291
+
292
+
293
+ //----------------------------------- DispatchState
294
+ if (ProtocolState == DispatchState) {
295
+ ProcessRequest (RequestMethod, Cookie.c_str(), IfNoneMatch.c_str(), ContentType.c_str(), QueryString.c_str(), PathInfo.c_str(), RequestUri.c_str(), Protocol.c_str(), ContentLength, _Content, HeaderBlock, HeaderBlockPos);
296
+ ProtocolState = BaseState;
297
+ }
298
+ }
299
+
300
+ return;
301
+
302
+ fail_connection:
303
+ // For protocol errors or security violations- kill the connection dead.
304
+ CloseConnection (false);
305
+ ProtocolState = EndState;
306
+ return;
307
+
308
+ send_error:
309
+ // for HTTP-level errors that will send back a response to the client.
310
+ CloseConnection (true);
311
+ ProtocolState = EndState;
312
+ return;
313
+
314
+ }
315
+
316
+
317
+ /**************************************
318
+ HttpConnection_t::_InterpretHeaderLine
319
+ **************************************/
320
+
321
+ bool HttpConnection_t::_InterpretHeaderLine (const char *header)
322
+ {
323
+ /* Return T/F to indicate whether we should continue processing
324
+ * this request. Return false to indicate that we detected a fatal
325
+ * error or other condition which should cause us to drop the
326
+ * connection.
327
+ * BY DEFINITION, this doesn't define any immediate fatal errors.
328
+ * That may need to change, in which case we'll have to return
329
+ * an error code rather than T/F, so the caller will know whether
330
+ * to drop the connection gracefully or not.
331
+ *
332
+ * There's something odd and possibly undesirable about how we're
333
+ * doing this. We fully process each header (including the request)
334
+ * _as we see it,_ and not at the end when all the headers have
335
+ * been seen. This saves us the trouble of keeping them all around
336
+ * and possibly parsing them twice, but it also means that when
337
+ * we emit errors from here (that generate HTTP responses other than
338
+ * 200 and therefore close the connection), we do so _immediately_
339
+ * and before looking at the rest of the headers. That might surprise
340
+ * and confuse some clients.
341
+ *
342
+ * Revised 27Sep06, we now store all the headers in one place, on a
343
+ * per-request basis, for the purpose of making them available to
344
+ * downstream users. At present this involves an undesirable extra
345
+ * memory copy. Eventually should rework the main header processing
346
+ * so it can be done in place.
347
+ */
348
+
349
+ if (!header) // an assert, really.
350
+ throw std::runtime_error ("bad arg interpreting headers");
351
+
352
+ if (!bRequestSeen) {
353
+ bRequestSeen = true;
354
+ return _InterpretRequest (header);
355
+ }
356
+
357
+ if (!strncasecmp (header, "content-length:", 15)) {
358
+ if (bContentLengthSeen) {
359
+ // TODO, log this. There are some attacks that depend
360
+ // on sending more than one content-length header.
361
+ _SendError (406);
362
+ return false;
363
+ }
364
+ bContentLengthSeen = true;
365
+ const char *s = header + 15;
366
+ while (*s && ((*s==' ') || (*s=='\t')))
367
+ s++;
368
+ ContentLength = atoi (s);
369
+ if (ContentLength > MaxContentLength) {
370
+ // TODO, log this.
371
+ _SendError (406);
372
+ return false;
373
+ }
374
+ }
375
+ else if (!strncasecmp (header, "cookie:", 7)) {
376
+ const char *s = header + 7;
377
+ while (*s && ((*s==' ') || (*s=='\t')))
378
+ s++;
379
+ Cookie = s;
380
+ if (bSetEnvironmentStrings)
381
+ setenv ("HTTP_COOKIE", s, true);
382
+ }
383
+ else if (!strncasecmp (header, "If-none-match:", 14)) {
384
+ const char *s = header + 14;
385
+ while (*s && ((*s==' ') || (*s=='\t')))
386
+ s++;
387
+ IfNoneMatch = s;
388
+ if (bSetEnvironmentStrings)
389
+ setenv ("IF_NONE_MATCH", s, true);
390
+ }
391
+ else if (!strncasecmp (header, "Content-type:", 13)) {
392
+ const char *s = header + 13;
393
+ while (*s && ((*s==' ') || (*s=='\t')))
394
+ s++;
395
+ ContentType = s;
396
+ if (bSetEnvironmentStrings)
397
+ setenv ("CONTENT_TYPE", s, true);
398
+ }
399
+
400
+
401
+ // Copy the incoming header into a block
402
+ if ((HeaderBlockPos + strlen(header) + 1) < HeaderBlockSize) {
403
+ int len = strlen(header);
404
+ memcpy (HeaderBlock+HeaderBlockPos, header, len);
405
+ HeaderBlockPos += len;
406
+ HeaderBlock [HeaderBlockPos++] = 0;
407
+ }
408
+ else {
409
+ // TODO, log this.
410
+ _SendError (406);
411
+ return false;
412
+ }
413
+
414
+ return true;
415
+ }
416
+
417
+
418
+ /***********************************
419
+ HttpConnection_t::_InterpretRequest
420
+ ***********************************/
421
+
422
+ bool HttpConnection_t::_InterpretRequest (const char *header)
423
+ {
424
+ /* Return T/F to indicate whether we should continue processing
425
+ * this request. Return false to indicate that we detected a fatal
426
+ * error or other condition which should cause us to drop the
427
+ * connection.
428
+ * Interpret the contents of the given line as an HTTP request string.
429
+ * WE ASSUME the passed-in header is not null.
430
+ *
431
+ * In preparation for a CGI-style call, we set the following
432
+ * environment strings here (other code will DEPEND ON ALL OF
433
+ * THESE BEING SET HERE in case there are no errors):
434
+ * REQUEST_METHOD, PATH_INFO, QUERY_STRING.
435
+ *
436
+ * Oh and by the way, this code sucks. It's reasonably fast
437
+ * but not terribly fast, and it's ugly. Refactor someday.
438
+ */
439
+
440
+ const char *blank = strchr (header, ' ');
441
+ if (!blank) {
442
+ _SendError (406);
443
+ return false;
444
+ }
445
+
446
+ if (!_DetectVerbAndSetEnvString (header, blank - header))
447
+ return false;
448
+
449
+ blank++;
450
+ if (*blank != '/') {
451
+ _SendError (406);
452
+ return false;
453
+ }
454
+
455
+ const char *blank2 = strchr (blank, ' ');
456
+ if (!blank2) {
457
+ _SendError (406);
458
+ return false;
459
+ }
460
+ if (strcasecmp (blank2 + 1, "HTTP/1.0") && strcasecmp (blank2 + 1, "HTTP/1.1")) {
461
+ _SendError (505);
462
+ return false;
463
+ }
464
+
465
+ string prot (blank2+1);
466
+ Protocol = prot.c_str();
467
+
468
+ // Here, the request starts at blank and ends just before blank2.
469
+ // Find the query-string (?) and/or fragment (#,;), if either are present.
470
+ const char *questionmark = strchr (blank, '?');
471
+ if (questionmark && (questionmark >= blank2))
472
+ questionmark = NULL;
473
+ const char *fragment = strpbrk ((questionmark ? (questionmark+1) : blank), "#;");
474
+ if (fragment && (fragment >= blank2))
475
+ fragment = NULL;
476
+
477
+ if (questionmark) {
478
+ string req (blank, questionmark - blank);
479
+ PathInfo = req.c_str();
480
+ RequestUri = req.c_str();
481
+ string qs (questionmark+1, fragment ? (fragment - (questionmark+1)) : (blank2 - (questionmark+1)));
482
+ QueryString = qs.c_str();
483
+
484
+ if (bSetEnvironmentStrings) {
485
+ setenv ("PATH_INFO", req.c_str(), true);
486
+ setenv ("REQUEST_URI", req.c_str(), true);
487
+ setenv ("QUERY_STRING", qs.c_str(), true);
488
+ setenv ("PROTOCOL", prot.c_str(), true);
489
+ }
490
+ }
491
+ else if (fragment) {
492
+ string req (blank, fragment - blank);
493
+ PathInfo = req.c_str();
494
+ RequestUri = req.c_str();
495
+ #ifdef OS_WIN32
496
+ QueryString.erase(QueryString.begin(),QueryString.end());
497
+ #else
498
+ QueryString.clear();
499
+ #endif
500
+ if (bSetEnvironmentStrings) {
501
+ setenv ("PATH_INFO", req.c_str(), true);
502
+ setenv ("REQUEST_URI", req.c_str(), true);
503
+ setenv ("QUERY_STRING", "", true);
504
+ setenv ("PROTOCOL", prot.c_str(), true);
505
+ }
506
+ }
507
+ else {
508
+ string req (blank, blank2 - blank);
509
+ PathInfo = req.c_str();
510
+ RequestUri = req.c_str();
511
+ #ifdef OS_WIN32
512
+ QueryString.erase(QueryString.begin(),QueryString.end());
513
+ #else
514
+ QueryString.clear();
515
+ #endif
516
+ if (bSetEnvironmentStrings) {
517
+ setenv ("PATH_INFO", req.c_str(), true);
518
+ setenv ("REQUEST_URI", req.c_str(), true);
519
+ setenv ("QUERY_STRING", "", true);
520
+ setenv ("PROTOCOL", prot.c_str(), true);
521
+ }
522
+ }
523
+
524
+ return true;
525
+ }
526
+
527
+
528
+ /********************************************
529
+ HttpConnection_t::_DetectVerbAndSetEnvString
530
+ ********************************************/
531
+
532
+ bool HttpConnection_t::_DetectVerbAndSetEnvString (const char *request, int verblength)
533
+ {
534
+ /* Helper method for _InterpretRequest.
535
+ * WE MUST SET THE ENV STRING "REQUEST_METHOD" HERE
536
+ * unless there is an error.
537
+ * The hardcoded verbs MUST be static, as we'll carry around pointers to them.
538
+ */
539
+
540
+ static const char *verbs[] = {
541
+ "GET",
542
+ "POST",
543
+ "PUT",
544
+ "DELETE",
545
+ "HEAD"
546
+ };
547
+
548
+ int n_verbs = sizeof(verbs) / sizeof(const char*);
549
+
550
+ // Warning, this algorithm is vulnerable to head-matches,
551
+ // so compare the longer head-matching strings first.
552
+ // We could fix this if we included the blank in the search
553
+ // string but then we'd have to lop it off in the env string.
554
+ // ALSO NOTICE the early return on success.
555
+ for (int i=0; i < n_verbs; i++) {
556
+ if (!strncasecmp (request, verbs[i], verblength) && (strlen(verbs[i]) == (size_t)verblength)) {
557
+ RequestMethod = verbs[i];
558
+ if (bSetEnvironmentStrings)
559
+ setenv ("REQUEST_METHOD", verbs[i], 1);
560
+ return true;
561
+ }
562
+ }
563
+
564
+ _SendError (405);
565
+ return false;
566
+ }
567
+
568
+
569
+
570
+ /****************************
571
+ HttpConnection_t::_SendError
572
+ ****************************/
573
+
574
+ void HttpConnection_t::_SendError (int code)
575
+ {
576
+ stringstream ss;
577
+ ss << "HTTP/1.1 " << code << " ...\r\n";
578
+ ss << "Connection: close\r\n";
579
+ ss << "Content-type: text/plain\r\n";
580
+ ss << "\r\n";
581
+ ss << "Detected error: HTTP code " << code;
582
+
583
+ SendData (ss.str().c_str(), ss.str().length());
584
+ }