Skip to content

Commit 381dbd9

Browse files
committed
max content length for POST requests can be modified at runtime
1 parent 89bca67 commit 381dbd9

File tree

5 files changed

+78
-15
lines changed

5 files changed

+78
-15
lines changed

README

+6-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@
1919

2020
def post_init
2121
super
22-
no_environment_strings
22+
23+
# faster if you don't need evironment variables (CGI methods)
24+
self.no_environment_strings
25+
26+
# max length of the POST content
27+
self.max_content_length = 10_000_000
2328
end
2429

2530
def process_http_request

Rakefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ namespace :build do
2020
end
2121
CLEAN.include('ext/Makefile')
2222
CLEAN.include('ext/*.log')
23-
23+
CLEAN.include('ext/*.o')
24+
2425
libfile = "ext/eventmachine_httpserver.#{Config::CONFIG['DLEXT']}"
2526
file libfile => ['ext/Makefile', *sources] do
2627
Dir.chdir 'ext' do

ext/http.cpp

+37-13
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ HttpConnection_t::HttpConnection_t()
7575
// instead of buffering it here. To get the latter behavior, user code must call
7676
// dont_accumulate_post.
7777
bAccumulatePost = true;
78+
79+
// By default this limit is initialized to 20 MiB, it could be changed at runtime
80+
// by the user if needed
81+
ContentLengthLimit = MaxContentLength;
7882
}
7983

8084

@@ -140,6 +144,20 @@ void HttpConnection_t::ReceivePostData (const char *data, int len)
140144
cerr << "UNIMPLEMENTED ReceivePostData" << endl;
141145
}
142146

147+
/*********************************
148+
HttpConnection_t::Get/SetMaxContentLength
149+
*********************************/
150+
151+
int HttpConnection_t::GetMaxContentLength ()
152+
{
153+
return ContentLengthLimit;
154+
}
155+
156+
void HttpConnection_t::SetMaxContentLength (int len)
157+
{
158+
ContentLengthLimit = len;
159+
}
160+
143161
/*****************************
144162
HttpConnection_t::ConsumeData
145163
*****************************/
@@ -254,7 +272,7 @@ void HttpConnection_t::ConsumeData (const char *data, int length)
254272
}
255273
else {
256274
const char *nl = strpbrk (data, "\r\n");
257-
int len = nl ? (nl - data) : length;
275+
int len = nl ? (int)(nl - data) : length;
258276
if ((size_t)(HeaderLinePos + len) >= sizeof(HeaderLine)) {
259277
// TODO, log this
260278
goto fail_connection;
@@ -358,17 +376,17 @@ bool HttpConnection_t::_InterpretHeaderLine (const char *header)
358376
if (bContentLengthSeen) {
359377
// TODO, log this. There are some attacks that depend
360378
// on sending more than one content-length header.
361-
_SendError (406);
379+
_SendError (400, "Bad Request");
362380
return false;
363381
}
364382
bContentLengthSeen = true;
365383
const char *s = header + 15;
366384
while (*s && ((*s==' ') || (*s=='\t')))
367385
s++;
368386
ContentLength = atoi (s);
369-
if (ContentLength > MaxContentLength) {
387+
if (ContentLength > ContentLengthLimit) {
370388
// TODO, log this.
371-
_SendError (406);
389+
_SendError (413, "Request Entity Too Large");
372390
return false;
373391
}
374392
}
@@ -400,7 +418,7 @@ bool HttpConnection_t::_InterpretHeaderLine (const char *header)
400418

401419
// Copy the incoming header into a block
402420
if ((HeaderBlockPos + strlen(header) + 1) < HeaderBlockSize) {
403-
int len = strlen(header);
421+
int len = (int)strlen(header);
404422
memcpy (HeaderBlock+HeaderBlockPos, header, len);
405423
HeaderBlockPos += len;
406424
HeaderBlock [HeaderBlockPos++] = 0;
@@ -439,26 +457,27 @@ bool HttpConnection_t::_InterpretRequest (const char *header)
439457

440458
const char *blank = strchr (header, ' ');
441459
if (!blank) {
442-
_SendError (406);
460+
_SendError (400, "Bad Request");
443461
return false;
444462
}
445463

446-
if (!_DetectVerbAndSetEnvString (header, blank - header))
464+
if (!_DetectVerbAndSetEnvString (header, (int)(blank - header)))
447465
return false;
448466

449467
blank++;
450468
if (*blank != '/') {
451-
_SendError (406);
469+
_SendError (400, "Bad Request");
452470
return false;
453471
}
454472

455473
const char *blank2 = strchr (blank, ' ');
456474
if (!blank2) {
457-
_SendError (406);
475+
_SendError (400, "Bad Request");
458476
return false;
459477
}
478+
460479
if (strcasecmp (blank2 + 1, "HTTP/1.0") && strcasecmp (blank2 + 1, "HTTP/1.1")) {
461-
_SendError (505);
480+
_SendError (505, "HTTP Version Not Supported");
462481
return false;
463482
}
464483

@@ -573,13 +592,18 @@ HttpConnection_t::_SendError
573592
****************************/
574593

575594
void HttpConnection_t::_SendError (int code)
595+
{
596+
_SendError(code, "...");
597+
}
598+
599+
void HttpConnection_t::_SendError (int code, const char *desc)
576600
{
577601
stringstream ss;
578-
ss << "HTTP/1.1 " << code << " ...\r\n";
602+
ss << "HTTP/1.1 " << code << " " << desc << "\r\n";
579603
ss << "Connection: close\r\n";
580604
ss << "Content-type: text/plain\r\n";
581605
ss << "\r\n";
582606
ss << "Detected error: HTTP code " << code;
583607

584-
SendData (ss.str().c_str(), ss.str().length());
585-
}
608+
SendData (ss.str().c_str(), (int)ss.str().length());
609+
}

ext/http.h

+4
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ class HttpConnection_t
5858
virtual void ReceivePostData(const char *data, int len);
5959
virtual void SetNoEnvironmentStrings() {bSetEnvironmentStrings = false;}
6060
virtual void SetDontAccumulatePost() {bAccumulatePost = false;}
61+
virtual int GetMaxContentLength();
62+
virtual void SetMaxContentLength(int len);
6163

6264
private:
6365

@@ -85,6 +87,7 @@ class HttpConnection_t
8587
int HeaderBlockPos;
8688

8789
int ContentLength;
90+
int ContentLengthLimit;
8891
int ContentPos;
8992
char *_Content;
9093

@@ -107,6 +110,7 @@ class HttpConnection_t
107110
bool _InterpretRequest (const char*);
108111
bool _DetectVerbAndSetEnvString (const char*, int);
109112
void _SendError (int);
113+
void _SendError (int, const char*);
110114
};
111115

112116
#endif // __HttpPersonality__H_

ext/rubyhttp.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,33 @@ static VALUE t_dont_accumulate_post (VALUE self)
258258
return Qnil;
259259
}
260260

261+
/**********************
262+
t_get_max_content_length
263+
**********************/
264+
265+
static VALUE t_get_max_content_length (VALUE self)
266+
{
267+
RubyHttpConnection_t *hc = (RubyHttpConnection_t*)(NUM2LONG (rb_ivar_get (self, Intern_http_conn)));
268+
if (hc)
269+
return INT2FIX (hc->GetMaxContentLength());
270+
271+
return Qnil;
272+
}
273+
274+
/**********************
275+
t_set_max_content_length
276+
**********************/
277+
278+
static VALUE t_set_max_content_length (VALUE self, VALUE data)
279+
{
280+
RubyHttpConnection_t *hc = (RubyHttpConnection_t*)(NUM2LONG (rb_ivar_get (self, Intern_http_conn)));
281+
if (hc) {
282+
hc->SetMaxContentLength(FIX2INT(data));
283+
return INT2FIX (hc->GetMaxContentLength());
284+
}
285+
286+
return Qnil;
287+
}
261288

262289
/****************************
263290
Init_eventmachine_httpserver
@@ -276,4 +303,6 @@ extern "C" void Init_eventmachine_httpserver()
276303
rb_define_method (HttpServer, "process_http_request", (VALUE(*)(...))t_process_http_request, 0);
277304
rb_define_method (HttpServer, "no_environment_strings", (VALUE(*)(...))t_no_environment_strings, 0);
278305
rb_define_method (HttpServer, "dont_accumulate_post", (VALUE(*)(...))t_dont_accumulate_post, 0);
306+
rb_define_method (HttpServer, "max_content_length", (VALUE(*)(...))t_get_max_content_length, 0);
307+
rb_define_method (HttpServer, "max_content_length=", (VALUE(*)(...))t_set_max_content_length, 1);
279308
}

0 commit comments

Comments
 (0)