Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MaxContentLength modification at runtime #5

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ ext/*.log
ext/*.o
ext/*.dll
ext/*.bundle
ext/*.so
ext/*.so
.idea
7 changes: 6 additions & 1 deletion README
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@

def post_init
super
no_environment_strings

# faster if you don't need evironment variables (CGI methods)
self.no_environment_strings

# max length of the POST content
self.max_content_length = 10_000_000
end

def process_http_request
Expand Down
3 changes: 2 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ namespace :build do
end
CLEAN.include('ext/Makefile')
CLEAN.include('ext/*.log')

CLEAN.include('ext/*.o')

libfile = "ext/eventmachine_httpserver.#{Config::CONFIG['DLEXT']}"
file libfile => ['ext/Makefile', *sources] do
Dir.chdir 'ext' do
Expand Down
7 changes: 3 additions & 4 deletions eventmachine_httpserver.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

Gem::Specification.new do |s|
s.name = %q{eventmachine_httpserver}
s.version = "0.2.1"
s.version = "0.2.2"

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Francis Cianfrocca"]
s.cert_chain = nil
s.cert_chain = []
s.date = %q{2007-03-16}
s.description = %q{}
s.email = %q{[email protected]}
Expand All @@ -17,11 +17,10 @@ Gem::Specification.new do |s|
s.rdoc_options = ["--title", "EventMachine_HttpServer", "--main", "docs/README", "--line-numbers"]
s.require_paths = ["lib"]
s.required_ruby_version = Gem::Requirement.new("> 0.0.0")
s.rubygems_version = %q{1.3.7}
s.rubygems_version = %q{1.6.2}
s.summary = %q{EventMachine HTTP Server}

if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 1

if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
Expand Down
2 changes: 1 addition & 1 deletion eventmachine_httpserver.gemspec.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = %q{eventmachine_httpserver}
s.version = "0.2.1"
s.version = "0.2.2"

s.specification_version = 1 if s.respond_to? :specification_version=

Expand Down
63 changes: 49 additions & 14 deletions ext/http.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ HttpConnection_t::HttpConnection_t()
// instead of buffering it here. To get the latter behavior, user code must call
// dont_accumulate_post.
bAccumulatePost = true;

// By default this limit is initialized to 20 MiB, it could be changed at runtime
// by the user if needed
ContentLengthLimit = MaxContentLength;
}


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

/*********************************
HttpConnection_t::Get/SetMaxContentLength
*********************************/

int HttpConnection_t::GetMaxContentLength ()
{
return ContentLengthLimit;
}

void HttpConnection_t::SetMaxContentLength (int len)
{
ContentLengthLimit = len;
}

/*****************************
HttpConnection_t::ConsumeData
*****************************/
Expand Down Expand Up @@ -254,7 +272,7 @@ void HttpConnection_t::ConsumeData (const char *data, int length)
}
else {
const char *nl = strpbrk (data, "\r\n");
int len = nl ? (nl - data) : length;
int len = nl ? (int)(nl - data) : length;
if ((size_t)(HeaderLinePos + len) >= sizeof(HeaderLine)) {
// TODO, log this
goto fail_connection;
Expand Down Expand Up @@ -358,17 +376,17 @@ bool HttpConnection_t::_InterpretHeaderLine (const char *header)
if (bContentLengthSeen) {
// TODO, log this. There are some attacks that depend
// on sending more than one content-length header.
_SendError (406);
_SendError (400, "Bad Request");
return false;
}
bContentLengthSeen = true;
const char *s = header + 15;
while (*s && ((*s==' ') || (*s=='\t')))
s++;
ContentLength = atoi (s);
if (ContentLength > MaxContentLength) {
if (ContentLength > ContentLengthLimit) {
// TODO, log this.
_SendError (406);
_SendError (413, "Request Entity Too Large");
return false;
}
}
Expand Down Expand Up @@ -400,7 +418,7 @@ bool HttpConnection_t::_InterpretHeaderLine (const char *header)

// Copy the incoming header into a block
if ((HeaderBlockPos + strlen(header) + 1) < HeaderBlockSize) {
int len = strlen(header);
int len = (int)strlen(header);
memcpy (HeaderBlock+HeaderBlockPos, header, len);
HeaderBlockPos += len;
HeaderBlock [HeaderBlockPos++] = 0;
Expand Down Expand Up @@ -439,26 +457,37 @@ bool HttpConnection_t::_InterpretRequest (const char *header)

const char *blank = strchr (header, ' ');
if (!blank) {
_SendError (406);
_SendError (400, "Bad Request");
return false;
}

if (!_DetectVerbAndSetEnvString (header, blank - header))
if (!_DetectVerbAndSetEnvString (header, (int)(blank - header)))
return false;

blank++;
// according to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1
// HTTP 1.1 complient servers MUST also accept absolute URIs in the request line
// e.g. GET http://foo/bar
if (strncasecmp (blank, "http://", 7) == 0) {
blank += 7; // skip over scheme part
while (*blank && *blank != '/') { // skip to next slash
blank++;
}
}

if (*blank != '/') {
_SendError (406);
_SendError (400, "Bad Request");
return false;
}

const char *blank2 = strchr (blank, ' ');
if (!blank2) {
_SendError (406);
_SendError (400, "Bad Request");
return false;
}

if (strcasecmp (blank2 + 1, "HTTP/1.0") && strcasecmp (blank2 + 1, "HTTP/1.1")) {
_SendError (505);
_SendError (505, "HTTP Version Not Supported");
return false;
}

Expand Down Expand Up @@ -543,7 +572,8 @@ bool HttpConnection_t::_DetectVerbAndSetEnvString (const char *request, int verb
"POST",
"PUT",
"DELETE",
"HEAD"
"HEAD",
"OPTIONS"
};

int n_verbs = sizeof(verbs) / sizeof(const char*);
Expand Down Expand Up @@ -573,13 +603,18 @@ HttpConnection_t::_SendError
****************************/

void HttpConnection_t::_SendError (int code)
{
_SendError(code, "...");
}

void HttpConnection_t::_SendError (int code, const char *desc)
{
stringstream ss;
ss << "HTTP/1.1 " << code << " ...\r\n";
ss << "HTTP/1.1 " << code << " " << desc << "\r\n";
ss << "Connection: close\r\n";
ss << "Content-type: text/plain\r\n";
ss << "\r\n";
ss << "Detected error: HTTP code " << code;

SendData (ss.str().c_str(), ss.str().length());
}
SendData (ss.str().c_str(), (int)ss.str().length());
}
4 changes: 4 additions & 0 deletions ext/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class HttpConnection_t
virtual void ReceivePostData(const char *data, int len);
virtual void SetNoEnvironmentStrings() {bSetEnvironmentStrings = false;}
virtual void SetDontAccumulatePost() {bAccumulatePost = false;}
virtual int GetMaxContentLength();
virtual void SetMaxContentLength(int len);

private:

Expand Down Expand Up @@ -85,6 +87,7 @@ class HttpConnection_t
int HeaderBlockPos;

int ContentLength;
int ContentLengthLimit;
int ContentPos;
char *_Content;

Expand All @@ -107,6 +110,7 @@ class HttpConnection_t
bool _InterpretRequest (const char*);
bool _DetectVerbAndSetEnvString (const char*, int);
void _SendError (int);
void _SendError (int, const char*);
};

#endif // __HttpPersonality__H_
Expand Down
69 changes: 62 additions & 7 deletions ext/rubyhttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,30 @@ Statics

VALUE Intern_http_conn;

/********************
t_get_http_connection
********************/

static RubyHttpConnection_t *t_get_http_connection(VALUE self)
{
RubyHttpConnection_t *hc;
VALUE ivar = rb_ivar_get (self, Intern_http_conn);
if (ivar != Qnil)
Data_Get_Struct (ivar, RubyHttpConnection_t, hc);
else
hc = NULL;
return hc;
}

/********************
t_delete_http_connection
********************/

void t_delete_http_connection(RubyHttpConnection_t *hc)
{
delete hc;
}

/***********
t_post_init
***********/
Expand All @@ -182,7 +206,12 @@ static VALUE t_post_init (VALUE self)
if (!hc)
throw std::runtime_error ("no http-connection object");

rb_ivar_set (self, Intern_http_conn, LONG2NUM ((long)hc));
/*
HACK: http_connection should be given an actual type. No one should
be touching it from inside ruby, but still
*/
VALUE http_connection = Data_Wrap_Struct(CLASS_OF(self), 0, t_delete_http_connection, hc);
rb_ivar_set (self, Intern_http_conn, http_connection);
return Qnil;
}

Expand All @@ -194,7 +223,7 @@ t_receive_data
static VALUE t_receive_data (VALUE self, VALUE data)
{
int length = NUM2INT (rb_funcall (data, rb_intern ("length"), 0));
RubyHttpConnection_t *hc = (RubyHttpConnection_t*)(NUM2LONG (rb_ivar_get (self, Intern_http_conn)));
RubyHttpConnection_t *hc = t_get_http_connection (self);
if (hc)
hc->ConsumeData (StringValuePtr (data), length);
return Qnil;
Expand All @@ -216,9 +245,6 @@ t_unbind

static VALUE t_unbind (VALUE self)
{
RubyHttpConnection_t *hc = (RubyHttpConnection_t*)(NUM2LONG (rb_ivar_get (self, Intern_http_conn)));
if (hc)
delete hc;
return Qnil;
}

Expand All @@ -240,7 +266,7 @@ t_no_environment_strings

static VALUE t_no_environment_strings (VALUE self)
{
RubyHttpConnection_t *hc = (RubyHttpConnection_t*)(NUM2LONG (rb_ivar_get (self, Intern_http_conn)));
RubyHttpConnection_t *hc = t_get_http_connection (self);
if (hc)
hc->SetNoEnvironmentStrings();
return Qnil;
Expand All @@ -252,12 +278,39 @@ t_dont_accumulate_post

static VALUE t_dont_accumulate_post (VALUE self)
{
RubyHttpConnection_t *hc = (RubyHttpConnection_t*)(NUM2LONG (rb_ivar_get (self, Intern_http_conn)));
RubyHttpConnection_t *hc = t_get_http_connection (self);
if (hc)
hc->SetDontAccumulatePost();
return Qnil;
}

/**********************
t_get_max_content_length
**********************/

static VALUE t_get_max_content_length (VALUE self)
{
RubyHttpConnection_t *hc = t_get_http_connection (self);
if (hc)
return INT2FIX (hc->GetMaxContentLength());

return Qnil;
}

/**********************
t_set_max_content_length
**********************/

static VALUE t_set_max_content_length (VALUE self, VALUE data)
{
RubyHttpConnection_t *hc = t_get_http_connection (self);
if (hc) {
hc->SetMaxContentLength(FIX2INT(data));
return INT2FIX (hc->GetMaxContentLength());
}

return Qnil;
}

/****************************
Init_eventmachine_httpserver
Expand All @@ -276,4 +329,6 @@ extern "C" void Init_eventmachine_httpserver()
rb_define_method (HttpServer, "process_http_request", (VALUE(*)(...))t_process_http_request, 0);
rb_define_method (HttpServer, "no_environment_strings", (VALUE(*)(...))t_no_environment_strings, 0);
rb_define_method (HttpServer, "dont_accumulate_post", (VALUE(*)(...))t_dont_accumulate_post, 0);
rb_define_method (HttpServer, "max_content_length", (VALUE(*)(...))t_get_max_content_length, 0);
rb_define_method (HttpServer, "max_content_length=", (VALUE(*)(...))t_set_max_content_length, 1);
}
7 changes: 4 additions & 3 deletions lib/evma_httpserver/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ module EventMachine
# EventMachine::Connection.
#
class HttpResponse
attr_accessor :status, :content, :headers, :chunks, :multiparts
attr_accessor :status, :status_string, :content, :headers, :chunks, :multiparts

def initialize
@headers = {}
Expand Down Expand Up @@ -98,7 +98,7 @@ def send_response
send_headers
send_body
send_trailer
close_connection_after_writing unless (@keep_connection_open and (@status || 200) == 200)
close_connection_after_writing unless (@keep_connection_open and (@status || 200) < 500)
end

# Send the headers out in alpha-sorted order. This will degrade performance to some
Expand All @@ -111,7 +111,7 @@ def send_headers
fixup_headers

ary = []
ary << "HTTP/1.1 #{@status || 200} ...\r\n"
ary << "HTTP/1.1 #{@status || 200} #{@status_string || '...'}\r\n"
ary += generate_header_lines(@headers)
ary << "\r\n"

Expand Down Expand Up @@ -286,6 +286,7 @@ def self.concoct_multipart_boundary

def send_redirect location
@status = 302 # TODO, make 301 available by parameter
@status_string = "Moved Temporarily"
@headers["Location"] = location
send_response
end
Expand Down
Loading