andyjeffries-eventmachine_httpserver 0.1.201

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{eventmachine_httpserver}
3
+ s.version = "0.1.201"
4
+
5
+ s.specification_version = 1 if s.respond_to? :specification_version=
6
+
7
+ s.required_rubygems_version = nil if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Francis Cianfrocca"]
9
+ s.cert_chain = nil
10
+ s.date = %q{2007-03-16}
11
+ s.description = %q{}
12
+ s.email = %q{garbagecat10@gmail.com}
13
+ s.extensions = ["ext/extconf.rb"]
14
+ s.extra_rdoc_files = `git ls-files docs`.split
15
+ s.files = `git ls-files | grep -v .gitignore`.split
16
+ s.has_rdoc = true
17
+ s.homepage = %q{http://rubyeventmachine.com}
18
+ s.rdoc_options = ["--title", "EventMachine_HttpServer", "--main", "docs/README", "--line-numbers"]
19
+ s.require_paths = ["lib"]
20
+ s.required_ruby_version = Gem::Requirement.new("> 0.0.0")
21
+ s.rubygems_version = %q{1.3.1}
22
+ s.summary = %q{EventMachine HTTP Server}
23
+ end
@@ -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"
@@ -0,0 +1,585 @@
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
+ const char *fragment = strpbrk ((questionmark ? (questionmark+1) : blank), "#");
475
+ if (fragment && (fragment >= blank2))
476
+ fragment = NULL;
477
+
478
+ if (questionmark) {
479
+ string req (blank, questionmark - blank);
480
+ PathInfo = req.c_str();
481
+ RequestUri = req.c_str();
482
+ string qs (questionmark+1, fragment ? (fragment - (questionmark+1)) : (blank2 - (questionmark+1)));
483
+ QueryString = qs.c_str();
484
+
485
+ if (bSetEnvironmentStrings) {
486
+ setenv ("PATH_INFO", req.c_str(), true);
487
+ setenv ("REQUEST_URI", req.c_str(), true);
488
+ setenv ("QUERY_STRING", qs.c_str(), true);
489
+ setenv ("PROTOCOL", prot.c_str(), true);
490
+ }
491
+ }
492
+ else if (fragment) {
493
+ string req (blank, fragment - blank);
494
+ PathInfo = req.c_str();
495
+ RequestUri = req.c_str();
496
+ #ifdef OS_WIN32
497
+ QueryString.erase(QueryString.begin(),QueryString.end());
498
+ #else
499
+ QueryString.clear();
500
+ #endif
501
+ if (bSetEnvironmentStrings) {
502
+ setenv ("PATH_INFO", req.c_str(), true);
503
+ setenv ("REQUEST_URI", req.c_str(), true);
504
+ setenv ("QUERY_STRING", "", true);
505
+ setenv ("PROTOCOL", prot.c_str(), true);
506
+ }
507
+ }
508
+ else {
509
+ string req (blank, blank2 - blank);
510
+ PathInfo = req.c_str();
511
+ RequestUri = req.c_str();
512
+ #ifdef OS_WIN32
513
+ QueryString.erase(QueryString.begin(),QueryString.end());
514
+ #else
515
+ QueryString.clear();
516
+ #endif
517
+ if (bSetEnvironmentStrings) {
518
+ setenv ("PATH_INFO", req.c_str(), true);
519
+ setenv ("REQUEST_URI", req.c_str(), true);
520
+ setenv ("QUERY_STRING", "", true);
521
+ setenv ("PROTOCOL", prot.c_str(), true);
522
+ }
523
+ }
524
+
525
+ return true;
526
+ }
527
+
528
+
529
+ /********************************************
530
+ HttpConnection_t::_DetectVerbAndSetEnvString
531
+ ********************************************/
532
+
533
+ bool HttpConnection_t::_DetectVerbAndSetEnvString (const char *request, int verblength)
534
+ {
535
+ /* Helper method for _InterpretRequest.
536
+ * WE MUST SET THE ENV STRING "REQUEST_METHOD" HERE
537
+ * unless there is an error.
538
+ * The hardcoded verbs MUST be static, as we'll carry around pointers to them.
539
+ */
540
+
541
+ static const char *verbs[] = {
542
+ "GET",
543
+ "POST",
544
+ "PUT",
545
+ "DELETE",
546
+ "HEAD"
547
+ };
548
+
549
+ int n_verbs = sizeof(verbs) / sizeof(const char*);
550
+
551
+ // Warning, this algorithm is vulnerable to head-matches,
552
+ // so compare the longer head-matching strings first.
553
+ // We could fix this if we included the blank in the search
554
+ // string but then we'd have to lop it off in the env string.
555
+ // ALSO NOTICE the early return on success.
556
+ for (int i=0; i < n_verbs; i++) {
557
+ if (!strncasecmp (request, verbs[i], verblength) && (strlen(verbs[i]) == (size_t)verblength)) {
558
+ RequestMethod = verbs[i];
559
+ if (bSetEnvironmentStrings)
560
+ setenv ("REQUEST_METHOD", verbs[i], 1);
561
+ return true;
562
+ }
563
+ }
564
+
565
+ _SendError (405);
566
+ return false;
567
+ }
568
+
569
+
570
+
571
+ /****************************
572
+ HttpConnection_t::_SendError
573
+ ****************************/
574
+
575
+ void HttpConnection_t::_SendError (int code)
576
+ {
577
+ stringstream ss;
578
+ ss << "HTTP/1.1 " << code << " ...\r\n";
579
+ ss << "Connection: close\r\n";
580
+ ss << "Content-type: text/plain\r\n";
581
+ ss << "\r\n";
582
+ ss << "Detected error: HTTP code " << code;
583
+
584
+ SendData (ss.str().c_str(), ss.str().length());
585
+ }