Roxen.git/server/plugins/protocols/http.pike:1:
// This is a ChiliMoon protocol module.
// Modified by Francesco Chemolli to add throttling capabilities.
// Copyright © 1996 - 2001, Roxen IS.
- constant cvs_version = "$Id: http.pike,v 1.414 2004/07/06 09:06:56 _cvs_stephen Exp $";
+ constant cvs_version = "$Id: http.pike,v 1.415 2005/04/14 23:06:59 _cvs_dirix Exp $";
//#define REQUEST_DEBUG
//#define CONNECTION_DEBUG
#define MAGIC_ERROR
#define HTTPTIMEOUT 90
// HTTP protocol module.
#include <config.h>
#define TIMER_PREFIX "http:"
#include <timers.h>
#include <stat.h>
inherit RequestID;
#ifdef PROFILE
#define HRTIME() gethrtime()
int req_time = HRTIME();
#endif
- #ifdef ID_OBJ_DEBUG
- RoxenDebug.ObjectMarker __marker = RoxenDebug.ObjectMarker(this);
- #endif
-
+
#ifdef REQUEST_DEBUG
int footime, bartime;
#define REQUEST_WERR(X) do {bartime = gethrtime()-footime; werror("%s (%d)\n", (X), bartime);footime=gethrtime();} while (0)
#else
#define REQUEST_WERR(X) do {} while (0)
#endif
#ifdef FD_DEBUG
-
+ #ifdef REQUEST_DEBUG
+ #define FD_WERR(X) REQUEST_WERR(X)
+ #else
+ #define FD_WERR(X) werror("%s\n", (X))
+ #endif
#define MARK_FD(X) do { \
int _fd = my_fd && my_fd->query_fd ? my_fd->query_fd() : -1; \
REQUEST_WERR("FD " + (_fd == -1 ? sprintf ("%O", my_fd) : _fd) + ": " + (X)); \
-
+ FD_WERR("FD " + (_fd == -1 ? sprintf ("%O", my_fd) : _fd) + ": " + (X)); \
mark_fd(_fd, (X)+" "+remoteaddr); \
} while (0)
#else
#define MARK_FD(X) do {} while (0)
#endif
#ifdef THROTTLING_DEBUG
#undef THROTTLING_DEBUG
#define THROTTLING_DEBUG(X) werror("Throttling: "+X+"\n")
#else
Roxen.git/server/plugins/protocols/http.pike:61:
private static multiset(string) none_match;
int kept_alive;
#ifdef DEBUG
#define CHECK_FD_SAFE_USE do { \
if (this_thread() != core->backend_thread && \
(my_fd->query_read_callback() || my_fd->query_write_callback() || \
my_fd->query_close_callback() || \
!zero_type (find_call_out (do_timeout)))) \
- error ("Got callbacks but not called from backend thread.\n"); \
+ error ("Got callbacks but not called from backend thread.\n" \
+ "rcb:%O\n" \
+ "wcb:%O\n" \
+ "ccb:%O\n" \
+ "timeout:%O\n", \
+ my_fd->query_read_callback(), \
+ my_fd->query_write_callback(), \
+ my_fd->query_close_callback(), \
+ find_call_out (do_timeout)); \
} while (0)
#else
#define CHECK_FD_SAFE_USE do {} while (0)
#endif
#include <roxen.h>
#include <module.h>
#include <variables.h>
#include <request_trace.h>
Roxen.git/server/plugins/protocols/http.pike:282:
else
rest_query = http_decode_string( v );
rest_query=replace(rest_query, "+", "\000"); /* IDIOTIC STUPID STANDARD */
}
return f;
}
private static mixed f, line;
private static int hstart;
- //! Parse a cookie string.
+ //! Parse a cookie strings.
//!
//! @param contents
- //! HTTP transport-encoded cookie header value.
+ //! HTTP transport-encoded cookie header value or array with values.
//!
//! @returns
//! Returns the resulting current cookie mapping.
- mapping(string:string) parse_cookies( string contents )
+ mapping(string:string) parse_cookies( array|string contents )
{
if(!contents)
return cookies;
// misc->cookies += ({contents});
- foreach(((contents/";") - ({""})), string c)
+
+ array tmp = arrayp(contents) ? contents : ({ contents});
+
+ foreach(tmp, string cookieheader) {
+
+ foreach(((cookieheader/";") - ({""})), string c)
{
string name, value;
while(sizeof(c) && c[0]==' ') c=c[1..];
if(sscanf(c, "%s=%s", name, value) == 2)
{
value=http_decode_string(value);
name=http_decode_string(name);
cookies[ name ]=value;
}
}
-
+ }
return cookies;
}
int things_to_do_when_not_sending_from_cache( )
{
array|string contents;
misc->pref_languages=PrefLanguages();
misc->cachekey = CacheKey();
misc->_cachecallbacks = ({});
Roxen.git/server/plugins/protocols/http.pike:401:
{
// The Proxy-authorization header should be removed... So there.
mixed tmp1,tmp2;
foreach(tmp2 = (raw / "\n"), tmp1) {
if(has_prefix(lower_case(tmp1), "proxy-authorization:"))
tmp2 -= ({tmp1});
}
raw = tmp2 * "\n";
}
- if(!supports->cookies)
+ if(!supports->cookies && !sizeof(config))
config = prestate;
else
if( port_obj->set_cookie
&& !cookies->ChiliMoonUserID && sizeof(not_query)
&& not_query[0]=='/' && method!="PUT")
{
if (!(port_obj->set_cookie_only_once &&
cache_lookup("hosts_for_cookie",remoteaddr)))
misc->moreheads = ([ "Set-Cookie":Roxen.http_roxen_id_cookie(), ]);
if (port_obj->set_cookie_only_once)
Roxen.git/server/plugins/protocols/http.pike:685:
real_variables[part->disp_params->name] += ({part->getdata()});
if(part->headers["content-type"])
real_variables[part->disp_params->name+".mimetype"] +=
({ part->headers["content-type"] });
}
stash_body_parts = real_variables + ([ ]);
}
break;
}
}
+ } else {
+ leftovers = data;
}
TIMER_END(parse_got_2_more_data);
if (!(< "HTTP/1.0", "HTTP/0.9" >)[prot]) {
if (!misc->host) {
// RFC 2616 requires this behaviour.
REQUEST_WERR("HTTP: HTTP/1.1 request without a host header.");
my_fd->write((prot||"HTTP/1.1") +
" 400 Bad request (missing host header).\r\n"
"Content-Length: 0\r\n"
"Date: "+Roxen.http_date(predef::time())+"\r\n"
Roxen.git/server/plugins/protocols/http.pike:722:
misc->cacheable = t;
return ot;
}
void disconnect()
{
file = 0;
conf && conf->connection_drop( this );
if (my_fd) {
MARK_FD("HTTP my_fd in HTTP disconnected?");
- catch(my_fd->close());
+ if (mixed err = catch (my_fd->close())) {
+ #ifdef DEBUG
+ report_debug ("Failed to close http(s) connection: " +
+ describe_error (err));
+ #endif
+ }
my_fd = 0;
}
MERGE_TIMERS(conf);
- if(do_not_disconnect) return;
+
destruct();
}
static void cleanup_request_object()
{
if( conf )
conf->connection_drop( this );
-
+ xml_data = 0;
}
void end(int|void keepit)
{
CHECK_FD_SAFE_USE;
remove_call_out(do_timeout);
cleanup_request_object();
if(keepit
Roxen.git/server/plugins/protocols/http.pike:776:
pipe = 0;
disconnect();
return;
}
data_buffer = 0;
pipe = 0;
disconnect();
}
+ static void close_cb()
+ {
+ #ifdef CONNECTION_DEBUG
+ werror ("HTTP: Client close ---------------------------------------------\n")
+ ;
+ #else
+ REQUEST_WERR ("HTTP: Got remote close.");
+ #endif
+
+ CHECK_FD_SAFE_USE;
+
+ cleanup_request_object();
+
+ data_buffer = 0;
+ pipe = 0;
+
+ // Avoid that the fd is closed by disconnect() - the write direction
+ // might still want to use it. We rely on refcount garbing instead.
+ my_fd = 0;
+
+ disconnect();
+ }
+
static void do_timeout()
{
int elapsed = predef::time(1)-time;
if(time && elapsed >= HTTPTIMEOUT/3)
{
- REQUEST_WERR("HTTP: Connection timed out. Closing.");
+ #ifdef CONNECTION_DEBUG
+ werror("HTTP: Connection timed out. Closing.\n"
+ "rcb:%O\n"
+ "wcb:%O\n"
+ "ccb:%O\n",
+ my_fd->query_read_callback(),
+ my_fd->query_write_callback(),
+ my_fd->query_close_callback());
+ #else
+ REQUEST_WERR(sprintf("HTTP: Connection timed out. Closing.\n"
+ "rcb:%O\n"
+ "wcb:%O\n"
+ "ccb:%O\n",
+ my_fd->query_read_callback(),
+ my_fd->query_write_callback(),
+ my_fd->query_close_callback()));
+ #endif
MARK_FD("HTTP timeout");
end();
} else {
#ifdef DEBUG
error ("This shouldn't happen.\n");
#endif
// premature call_out... *¤#!"
call_out(do_timeout, 10);
MARK_FD("HTTP premature timeout");
}
}
string link_to(string file, int line, string fun, int eid, int qq)
{
if (!file || !line) return "<a>";
- if(file[0]!='/') file = combine_path(getcwd(), file);
+
return ("<a href=\"/(old_error,find_file)/error/?"+
"file="+Roxen.http_encode_url(file)+
(fun ? "&fun="+Roxen.http_encode_url(fun) : "") +
"&off="+qq+
"&error="+eid+
"&error_md5="+get_err_md5(get_err_info(eid))+
(line ? "&line="+line+"#here" : "") +
"\">");
}
- static string error_page_header (string title)
+ static string error_page(string line1, string title, string body)
{
- title = Roxen.html_encode_string (title);
- return #"<html><head><title>" + title + #"</title></head>
- <body bgcolor='white' text='black' link='#ce5c00' vlink='#ce5c00'>
- <table width='100%'><tr>
- <td><a href='http://www.roxen.com/'><imgs border='0' src='/$/roxen-small' /></a></td>
- <td><b><font size='+1'>" + title + #"</font></b></td>
- <td align='right'><font size='+1'>ChiliMoon " + Roxen.html_encode_string (roxen_version()) + #"</font></td>
- </tr></table>
-
- ";
+ return #"\
+ <html><head>
+ <title>Internal Server Error</title>
+ <style>
+ .msg { font-family: verdana, helvetica, arial, sans-serif;
+ font-size: 12px;
+ line-height: 160% }
+ .big { font-family: georgia, times, serif;
+ font-size: 18px;
+ padding-top: 6px;
+ padding-bottom: 20px }
+ .info { font-family: verdana, helvetica, arial, sans-serif;
+ font-size: 10px;
+ color: #999999 }
+ .list { padding-left: 20px;
+ list-style-type:square; }
+ .code { font-family: monaco, courier, monospace;
+ font-size: 10px;
+ color: #404070; }
+ </style>
+ </head>
+ <body text='#000000' style='margin: 0; padding: 0' vlink='#2331d1'
+ rightmargin='0' leftmargin='0' alink='#f6f6ff' link='#0000ee'
+ bgcolor='#f2f1eb' bottommargin='0' topmargin='0'>
+ <table border='0' cellspacing='0' cellpadding='0' height='99%'>
+ <tr>
+ <td><img src='/internal-roxen-unit' height='30' /></td>
+ </tr><tr>
+ <td></td>
+ <td><img src='/$/chilimoon-black' /></td>
+ <td><img src='/$/unit' width='30' /></td>
+ <td valign='bottom'><img src='/$/internal-roxen-server-error' /></td>
+ </tr><tr>
+ <td><img src='/$/unit' height='30' /></td>
+ </tr><tr>
+ <td colspan='3'></td>
+ <td>
+ <div class='msg'>" + line1 + #"</div>
+ <div class='big'>" + title + #"</div>
+ </td>
+ </tr><tr>
+ <td></td>
+ <td colspan='3'>
+ <div class='msg'>" + body + #"</div>
+ </td>
+ </tr><tr valign='bottom' height='100%'>
+ <td colspan='4' align='right'>
+ <img src='/$/unit' height='20' />
+ <table border='0' cellspacing='0' cellpadding='0'>
+ <tr>
+ <td><img src='/$/chili-small-black.gif' /></td>
+ <td class='info'>
+ <b>" + roxen_product_name + #"</b>
+ <font color='#ffbe00'>|</font> " + roxen_dist_version + #"
+ </td>
+ </tr>
+ </table>
+ <img src='/$/unit' height='15' />
+ </td>
+ </tr>
+ </table>
+ </body></html>";
}
static string get_err_md5(array(string|array(string)|array(array)) err_info)
{
if (err_info) {
return String.string2hex(Crypto.MD5.hash(err_info[3]));
}
return "NONE";
}
Roxen.git/server/plugins/protocols/http.pike:850:
}
return err_info;
}
string format_backtrace(int eid, string|void md5)
{
array(string|array(string)|array(array)) err_info = get_err_info(eid, md5);
if (!err_info) {
- return error_page_header("Unregistred Error");
+ return error_page("Unregistered error", "", "");
}
[string msg, array(string) rxml_bt, array(array) bt,
string raw_bt_descr, string raw_url, string raw] = err_info;
- string res = error_page_header ("Internal Server Error") +
- "<h1>" + replace (Roxen.html_encode_string (msg), "\n", "<br />\n") + "</h1>\n";
+ string title = replace(Roxen.html_encode_string(msg), "\n", "<br />\n");
+ string body = "";
if (rxml_bt && sizeof (rxml_bt)) {
- res += "<h3>RXML frame backtrace</h3>\n<ul>\n";
- foreach (rxml_bt, string line)
- res += "<li>" + Roxen.html_encode_string (line) + "</li>\n";
- res += "</ul>\n\n";
+ body +=
+ "RXML frame backtrace"
+ "<ul class='list'>";
+ foreach(rxml_bt, string line)
+ body += "<li class='code'>" + Roxen.html_encode_string(line) + "</li>\n";
+ body += "</ul>\n";
}
if (bt && sizeof (bt)) {
- res += "<h3>Pike backtrace</h3>\n<ul>\n";
+ body +=
+ "Pike backtrace"
+ "<ul class='list'>";
int q = sizeof (bt);
foreach(bt, [string file, int line, string func, string descr])
{
#if constant(PIKE_MODULE_RELOC)
file = file && master()->relocate_module(file);
#endif
- res += "<li value="+(q--)+">" +
- link_to (file, line, func, eid, q) +
- (file ? Roxen.html_encode_string (file) : "<i>Unknown program</i>") +
+ q--;
+ body +=
+ "<li>" +
+ link_to(file, line, func, eid, q) + // inserts <a>
+ (file ? Roxen.html_encode_string(file) : "<i>Unknown program</i>") +
(line ? ":" + line : "") +
"</a>" + (file ? Roxen.html_encode_string (get_cvs_id (file)) : "") + ":<br />\n" +
- replace (Roxen.html_encode_string (descr),
- ({"(", ")", " "}), ({"<b>(</b>", "<b>)</b>", " "})) +
- "</li>\n";
+ "</a>" +
+ (file ? Roxen.html_encode_string(get_cvs_id(file)) : "") +
+ "<br /><span class='code'>" +
+ replace(Roxen.html_encode_string(descr), " ", " ") +
+ "</span></li>\n";
}
- res += "</ul>\n\n";
+ body += "</ul>\n\n";
}
- res += ("<p><b><a href=\"/(old_error,plain)/error/?"
- "error="+eid+
- "&error_md5="+get_err_md5(get_err_info(eid))+
+ body +=
+ "<p>Generate "
+ "<a href=\"/(old_error,plain)/error/?"
+ "error=" + eid +
+ "&error_md5=" + get_err_md5(get_err_info(eid)) +
"\">"
- "Generate text only version of this error message, for bug reports"+
- "</a></b></p>\n\n");
- return res+"</body></html>";
+ "text-only version</a> of this error message for bug reports.</p>";
+ return error_page("The server failed to fulfill your query.", title, body);
}
string generate_bugreport(string msg, array(string) rxml_bt, array(string) bt,
string raw_bt_descr, string raw_url, string raw)
{
- return ("ChiliMoon version: "+version()+
- (core.real_version != version()?
+ return ("ChiliMoon version: "+core.version()+
+ (core.real_version != core.version()?
" ("+core.real_version+")":"")+
"\nPike version: " + predef::version() +
"\nRequested URL: "+raw_url+"\n"
"\nError: " + raw_bt_descr +
"\nRequest data:\n"+raw);
}
string censor(string what)
{
string a, b, c;
Roxen.git/server/plugins/protocols/http.pike:1026:
array err2;
if(port_obj && port_obj->query("show_internals"))
{
err2 = catch {
file = Roxen.http_low_answer(500, format_backtrace(store_error(err)));
};
if(err2) {
werror("Internal server error in internal_error():\n" +
describe_backtrace(err2)+"\n while processing \n"+
describe_backtrace(err));
- file = Roxen.http_low_answer(500, "<h1>Error: The server failed to "
- "fulfill your query, due to an "
- "internal error in the internal error routine.</h1>");
+ file =
+ Roxen.http_low_answer(500, error_page("The server failed to fulfill "
+ "your query due to an internal "
+ "error in the internal error "
+ "routine.", "", ""));
}
} else {
- file = Roxen.http_low_answer(500, "<h1>Error: The server failed to "
- "fulfill your query, due to an internal error.</h1>");
+ file =
+ Roxen.http_low_answer(500, error_page("The server failed to fulfill "
+ "your query.", "", ""));
}
report_error("Internal server error: " +
describe_backtrace(err) + "\n");
#ifdef INTERNAL_ERROR_DEBUG
report_error("Raw backtrace:%O\n", err);
#endif /* INTERNAL_ERROR_DEBUG */
}
// This macro ensures that something gets reported even when the very
// call to internal_error() fails. That happens eg when "this" has been
Roxen.git/server/plugins/protocols/http.pike:1055:
if (mixed __eRr = catch (internal_error (err))) \
report_error("Internal server error: " + describe_backtrace(err) + \
"internal_error() also failed: " + describe_backtrace(__eRr)); \
} while (0)
int wants_more()
{
return !!cache;
}
+ static object(this_program) chained_to;
+
+ // Paranoia.
+ static void destroy()
+ {
+ if (chained_to) {
+ werror("HTTP: Still chained in destroy!\n");
+ call_out(chained_to->my_fd_released, 0);
+ chained_to = 0;
+ }
+ }
+
void do_log(int fsent)
{
-
+ #ifdef CONNECTION_DEBUG
+ werror ("HTTP: Response sent ============================================\n")
+ ;
+ #endif
MARK_FD("HTTP logging"); // fd can be closed here
-
+ if (chained_to) {
+ // Release the other sender.
+ call_out(chained_to->my_fd_released, 0);
+ chained_to = 0;
+ }
TIMER_START(do_log);
if(conf)
{
conf->sent+=fsent;
file->len += misc->_log_cheat_addition;
conf->log(file, this);
}
if( !port_obj )
{
Roxen.git/server/plugins/protocols/http.pike:1108: Inside #if defined(FD_DEBUG)
} else {
MARK_FD("HTTP piping, but no pipe for "+not_query);
}
call_out(timer, 30, start);
}
#endif
string handle_error_file_request (string msg, array(string) rxml_bt, array(array) bt,
string raw_bt_descr, string raw_url, string raw)
{
- string data = Stdio.read_bytes(variables->file);
-
+ // Check that the file is valid and is present in the backtrace.
+ string data;
+ foreach(bt, array frame) {
+ if (frame[0] == variables->file) {
+ data = Stdio.read_bytes(variables->file);
+ break;
+ }
+ }
if(!data)
- return error_page_header (variables->file) +
- "<h3><i>Source file could not be read</i></h3>\n"
- "</body></html>";
+ return error_page("Source file could not be read:", variables->file, "");
string down;
int next = (int) variables->off + 1;
if(next < sizeof (bt)) {
[string file, int line, string func, string descr] = bt[next];
down = link_to (file, line, func, (int) variables->error, next);
}
else
down = "<a>";
Roxen.git/server/plugins/protocols/http.pike:1140:
off += start;
start = 0;
}
int end = (int)variables->line+50;
lines = map (lines[start..end], Roxen.html_encode_string);
if(sizeof(lines)>off) {
sscanf (lines[off], "%[ \t]%s", string indent, string code);
if (!sizeof (code)) code = " ";
- lines[off] = indent + "<font size='+1'><b>"+down+code+"</a></b></font></a>";
+ lines[off] = indent + "<font size='+1'><b>"+down+code+"</a></b></font>";
}
lines[max(off-20,0)] = "<a name=here>"+lines[max(off-20,0)]+"</a>";
-
- return error_page_header (variables->file) +
- "<font size='-1'><pre>" + lines*"\n" + "</pre></font>\n"
- "</body></html>";
+ return error_page("Source code for", variables->file,
+ "<span class='code'><pre>" +
+ (lines * "\n") +
+ "</pre></span>");
}
// The wrapper for multiple ranges (send a multipart/byteranges reply).
#define BOUND "Byte_Me_Now_Chily"
class MultiRangeWrapper
{
object file;
function rcb;
int current_pos, len, separator;
Roxen.git/server/plugins/protocols/http.pike:1368:
file = Roxen.http_status(misc->error_code, errors[misc->error]);
else if(err = catch {
file = conf->error_file( this );
})
INTERNAL_ERROR(err);
}
else
{
if((file->file == -1) || file->leave_me)
{
- TIMER_END(send_result);
+ TIMER_END(send_result)
file = 0;
pipe = 0;
if(do_not_disconnect)
return;
my_fd = 0;
return;
}
if(file->type == "raw") file->raw = 1;
}
Roxen.git/server/plugins/protocols/http.pike:1514:
// Reply with a 416 Requested Range not satisfiable.
file->error = 416;
heads["Content-Range"] = "*/"+file->len;
if(method == "GET") {
file->file = file->data = file->type = file->len = 0;
}
}
}
}
}
-
+
head_string = sprintf("%s %d %s\r\n", prot, file->error,
head_status || errors[file->error] || "");
// Must update the content length after the modifications of the
// data to send that might have been done above for 206 or 304.
heads["Content-Length"] = (string)file->len;
// Some browsers, e.g. Netscape 4.7, don't trust a zero
// content length when using keep-alive. So let's force a
// close in that case.
if( file->error/100 == 2 && file->len <= 0 )
{
heads->Connection = "close";
misc->connection = "close";
}
-
+ // Check for wide headers.
+ // FIXME: Assumes no header names are wide.
+ foreach(heads; string header_name; string content) {
+ if (String.width(content) > 8) {
+ array(array(string)|int) tokenized =
+ MIME.decode_words_tokenized(string_to_utf8(content));
+ foreach(tokenized, array(string)|int token) {
+ if (arrayp(token)) {
+ string raw = utf8_to_string(token[0]);
+ if (String.width(raw) > 8) {
+ if (token[1]) {
+ // Should not happen in normal circumstances.
+ catch {
+ // Attempt to recode in UTF-8.
+ token[0] = string_to_utf8(Locale.Charset.
+ decoder(token[1])->
+ feed(raw)->drain());
+ };
+ }
+ token[1] = "utf-8";
+ } else {
+ token[0] = raw;
+ }
+ }
+ }
+ string q = MIME.encode_words_quoted(tokenized, "q");
+ string b = MIME.encode_words_quoted(tokenized, "b");
+ // Prefer QP to BASE-64 if they are the same length.
+ heads[header_name] = (sizeof(b)<sizeof(q))?b:q;
+ }
+ }
if( mixed err = catch( head_string += Roxen.make_http_headers( heads ) ) )
{
#ifdef DEBUG
report_debug ("Roxen.make_http_headers failed: " +
describe_error (err));
#endif
foreach(heads; string x; string|array(string) val) {
if (stringp(val))
head_string += x+": "+val+"\r\n";
Roxen.git/server/plugins/protocols/http.pike:1551:
foreach( val, string xx )
head_string += x+": "+xx+"\r\n";
else if( catch {
head_string += x+": "+(string)val+"\r\n";
} )
error("Illegal value in headers array! "
"Expected string or array(string)\n");
}
head_string += "\r\n";
}
-
- if (sscanf (heads["Content-Type"], "; charset=%s", string charset) ||
- String.width( head_string ) > 8 )
- head_string = output_encode( head_string, 0, charset )[1];
+
conf->hsent += sizeof(head_string);
}
else
if(!file->type) file->type="text/plain";
#if 0
REQUEST_WERR(sprintf("HTTP: Sending result for prot:%O, method:%O, file:%O",
prot, method, file));
#endif
MARK_FD("HTTP handled");
if( (method!="HEAD") && (file->error!=204) )
// No data for these two...
{
#ifdef RAM_CACHE
if( (misc->cacheable > 0) && (file->data || file->file) &&
(prot != "HTTP/0.9") && !misc->no_proto_cache)
{
if( file->len>0 && // known length.
((file->len + sizeof( head_string )) <
conf->datacache->max_file_size)
-
+ // vvv Relying on the interpreter lock from here.
&& misc->cachekey )
{
-
+ misc->cachekey->activate();
+ // ^^^ Relying on the interpreter lock to here.
string data = "";
if( file->file ) data += file->file->read();
if( file->data ) data += file->data;
MY_TRACE_ENTER (sprintf ("Storing in ram cache, entry: %O", raw_url), 0);
MY_TRACE_LEAVE ("");
conf->datacache->set( raw_url, data,
([
// We have to handle the date header.
"hs":head_string,
"key":misc->cachekey,
Roxen.git/server/plugins/protocols/http.pike:1636:
TIMER_END(blocking_write);
do_log(s);
return;
}
if(sizeof(head_string)) send(head_string);
if(file->data && sizeof(file->data)) send(file->data, file->len);
if(file->file) send(file->file, file->len);
}
else
{
- if( sizeof( head_string ) < (HTTP_BLOCKING_SIZE_THRESHOLD))
+ if( !kept_alive &&
+ (strlen(head_string) < (HTTP_BLOCKING_SIZE_THRESHOLD)))
{
#ifdef CONNECTION_DEBUG
werror ("HTTP: Response =================================================\n"
"%s\n",
replace (sprintf ("%O", head_string),
({"\\r\\n", "\\n", "\\t"}),
({"\n", "\n", "\t"})));
#else
REQUEST_WERR (sprintf ("HTTP: Send headers blocking %O", head_string));
#endif
Roxen.git/server/plugins/protocols/http.pike:1772:
if (string p = misc->site_prefix_path) cached_url_base += p;
cached_url_base += "/";
}
return cached_url_base;
}
/* We got some data on a socket.
* =================================================
*/
- int processed;
+
// array ccd = ({});
- void got_data(mixed fooid, string s)
+ void got_data(mixed fooid, string s, void|int chained)
{
#ifdef CONNECTION_DEBUG
werror ("HTTP: Request --------------------------------------------------\n"
"%s\n",
replace (sprintf ("%O", s),
({"\\r\\n", "\\n", "\\t"}),
({"\n", "\n", "\t"})));
#else
REQUEST_WERR(sprintf("HTTP: Got %O", s));
#endif
Roxen.git/server/plugins/protocols/http.pike:1801:
if(!data_buffer) {
// The 16384 is some reasonable extra padding to
// avoid having to realloc.
data_buffer = String.Buffer(wanted_data + 16384);
data_buffer->add(data);
data = "";
}
data_buffer->add(s);
have_data += sizeof(s);
+ REQUEST_WERR("HTTP: We want more data.");
+
// Reset timeout.
remove_call_out(do_timeout);
call_out(do_timeout, HTTPTIMEOUT);
- REQUEST_WERR("HTTP: We want more data.");
+
+ if (chained)
+ my_fd->set_nonblocking(got_data, 0, close_cb);
return;
}
if(data_buffer) {
data_buffer->add(s);
data = (string)data_buffer;
data_buffer = 0;
}
else
data += s;
}
Roxen.git/server/plugins/protocols/http.pike:1835:
conf->connection_drop( this );
MARK_FD ("HTTP: Port closed.");
call_out (disconnect, 0);
return;
}
switch( parse_got( s ) )
{
case 0:
REQUEST_WERR("HTTP: Request needs more data.");
+ if (chained)
+ my_fd->set_nonblocking(got_data, 0, close_cb);
return;
case 1:
REQUEST_WERR("HTTP: Stupid Client Error.");
my_fd->write((prot||"HTTP/1.0")+" 500 Illegal request\r\n"
"Content-Length: 0\r\n"+
"Date: "+Roxen.http_date(predef::time())+"\r\n"
"\r\n");
end();
return; // Stupid request.
Roxen.git/server/plugins/protocols/http.pike:1859:
return;
}
#ifdef CONNECTION_DEBUG
werror ("HTTP: Request received -----------------------------------------\n");
#endif
if( method == "GET" || method == "HEAD" ) {
misc->cacheable = INITIAL_CACHEABLE; // FIXME: Make configurable.
#ifdef DEBUG_CACHEABLE
- report_debug("===> Request for %s initiated cacheable to %d.\n", raw_url,
- misc->cacheable);
+ report_debug("<=== Request for %s returned cacheable %d (proto cache %s).\n",
+ raw_url, misc->cacheable,
+ misc->no_proto_cache ? "disabled" : "enabled");
#endif
}
TIMER_START(find_conf);
string path;
if( !conf || !(path = port_obj->path )
|| (sizeof( path )
&& raw_url[..sizeof(path) - 1] != path) )
{
Roxen.git/server/plugins/protocols/http.pike:1946:
if( conf )
{
conf->connection_add( this, ([]) );
conf->received += sizeof(raw);
conf->requests++;
}
CHECK_FD_SAFE_USE;
my_fd->set_close_callback(0);
my_fd->set_read_callback(0);
if (my_fd->set_accept_callback) my_fd->set_accept_callback(0);
- processed=1;
+
remove_call_out(do_timeout);
#ifdef RAM_CACHE
TIMER_START(cache_lookup);
array cv;
if( prot != "HTTP/0.9" &&
misc->cacheable &&
!misc->no_proto_cache &&
!since &&
(cv = conf->datacache->get( raw_url )) )
Roxen.git/server/plugins/protocols/http.pike:2061:
REQUEST_WERR(sprintf("HTTP: cooked headers %O", request_headers));
REQUEST_WERR(sprintf("HTTP: cooked variables %O", real_variables));
REQUEST_WERR(sprintf("HTTP: cooked cookies %O", cookies));
TIMER_END(parse_request);
REQUEST_WERR("HTTP: Calling core.handle().");
core.handle(handle_request);
})
{
report_error("Internal server error: " + describe_backtrace(err));
- my_fd->set_blocking();
- my_fd->close();
- my_fd = 0;
+
disconnect();
}
}
/* Get a somewhat identical copy of this object, used when doing
* 'simulated' requests. */
this_program clone_me()
{
this_program c=this_program(0, port_obj, conf);
Roxen.git/server/plugins/protocols/http.pike:2136:
end();
else if((predef::time(1) - time) > 4800)
end();
}
static void create(object f, object c, object cc)
{
if(f)
{
f->set_nonblocking(got_data, f->query_write_callback(), end);
+ f->set_nonblocking(got_data, f->query_write_callback(), close_cb);
my_fd = f;
CHECK_FD_SAFE_USE;
MARK_FD("HTTP connection");
if( c ) port_obj = c;
if( cc ) conf = cc;
time = predef::time(1);
if(f->sslfile)
f->sslfile->set_close_callback(end);
call_out(do_timeout, HTTPTIMEOUT);
}
root_id = this;
}
void chain( object f, object c, string le )
{
my_fd = f;
-
+
+ #ifdef DEBUG
+ if (this_thread() != roxen->backend_thread)
+ error ("Not called from backend\n");
+ #endif
+
+ CHECK_FD_SAFE_USE;
+
f->set_nonblocking(0, f->query_write_callback(), end);
port_obj = c;
- processed = 0;
- do_not_disconnect=-1; // Block destruction until we return.
+
MARK_FD("HTTP kept alive");
time = predef::time();
- if ( le && sizeof( le ) )
- got_data( 0,le );
+ if ( le && sizeof( le ) ) {
+ #ifdef CONNECTION_DEBUG
+ werror("HTTP: Leftovers: %O\n", le);
+ #else
+ REQUEST_WERR(sprintf("HTTP: %d bytes left over.\n", sizeof(le)));
+ #endif
+ got_data( 0,le, 1);
+ }
else
{
// If no pipelined data is available, call out...
remove_call_out(do_timeout);
call_out(do_timeout, HTTPTIMEOUT);
-
+ my_fd->set_nonblocking(got_data, 0, close_cb);
}
-
- if(!my_fd)
- {
- if(do_not_disconnect == -1)
- {
- do_not_disconnect=0;
- disconnect();
+
}
- }
- else
- {
- if(do_not_disconnect == -1)
- do_not_disconnect = 0;
- f->set_nonblocking(!processed && got_data, f->query_write_callback(), end);
- }
- }
+
- string _sprintf(int t)
- {
- if(t!='O') return 0;
- return "RequestID(" + (raw_url||"") + ")"
- #ifdef ID_OBJ_DEBUG
- + (__marker ? "[" + __marker->count + "]" : "")
- #endif
- ;
- }
-
+
Stdio.File connection( )
{
return my_fd;
}
Configuration configuration()
{
return conf;
}