Roxen.git / server / plugins / protocols / http.pike

version» Context lines:

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.398 2004/05/23 02:35:26 _cvs_stephen Exp $"; + constant cvs_version = "$Id: http.pike,v 1.399 2004/05/24 13:40:49 _cvs_stephen Exp $";   // #define REQUEST_DEBUG   #define MAGIC_ERROR      // HTTP protocol module.   #include <config.h>   #define TIMER_PREFIX "http:"   #include <timers.h>      inherit RequestID;      #ifdef PROFILE   #define HRTIME() gethrtime() - #define HRSEC(X) ((int)((X)*1000000)) - #define SECHR(X) ((X)/(float)1000000) +    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) bartime = gethrtime()-footime; werror("%s (%d)\n", (X), bartime);footime=gethrtime() + #define REQUEST_WERR(X) do {bartime = gethrtime()-footime; werror("%s (%d)\n", (X), bartime);footime=gethrtime();} while (0)   #else - #define REQUEST_WERR(X) + #define REQUEST_WERR(X) do {} while (0)   #endif      #ifdef FD_DEBUG - #define MARK_FD(X) \ -  catch{ \ -  REQUEST_WERR("FD " + my_fd->query_fd() + ": " + (X)); \ -  mark_fd(my_fd->query_fd(), (X)+" "+remoteaddr); \ -  } + #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)); \ +  mark_fd(_fd, (X)+" "+remoteaddr); \ +  } while (0)   #else - #define MARK_FD(X) + #define MARK_FD(X) do {} while (0)   #endif      #ifdef THROTTLING_DEBUG   #undef THROTTLING_DEBUG   #define THROTTLING_DEBUG(X) werror("Throttling: "+X+"\n")   #else   #define THROTTLING_DEBUG(X)   #endif      constant decode = MIME.decode_base64;   constant find_supports_and_vars = core.find_supports_and_vars;   constant version = core.version;   constant _query = core.query;      private static array(string) cache;   private static int wanted_data, have_data;   private static String.Buffer data_buffer;    -  + private static multiset(string) none_match; +  + int kept_alive; +  + #ifdef DEBUG + #define CHECK_FD_SAFE_USE do { \ +  if (this_thread() != roxen->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"); \ +  } 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>      #define MY_TRACE_ENTER(A, B) \    do {RequestID id = this; TRACE_ENTER (A, B);} while (0)   #define MY_TRACE_LEAVE(A) \    do {RequestID id = this; TRACE_LEAVE (A);} while (0)   
Roxen.git/server/plugins/protocols/http.pike:75: Inside #if defined(REQUEST_DEBUG)
  ([   #ifdef REQUEST_DEBUG    "trace_enter":lambda(mixed ...args) {    REQUEST_WERR(sprintf("TRACE_ENTER(%{%O,%})", args));    },    "trace_leave":lambda(mixed ...args) {    REQUEST_WERR(sprintf("TRACE_LEAVE(%{%O,%})", args));    }   #endif // REQUEST_DEBUG   ]); + mapping (string:mixed) connection_misc = ([ ]);   mapping (string:string) cookies = ([ ]);   mapping (string:string) request_headers = ([ ]);   mapping (string:string) client_var = ([ ]);      multiset (string) prestate = (< >);   multiset (string) config = (< >);   multiset (string) pragma = (< >);      mapping file;   
Roxen.git/server/plugins/protocols/http.pike:98:      class AuthEmulator   // Emulate the old (rather cumbersome) authentication API   {    mixed `[]( int i )    {    User u;    switch( i )    {    case 0: -  return conf->authenticate( this ); +  return conf->authenticate( this_object() );    case 1: -  if( u = conf->authenticate( this ) ) +  if( u = conf->authenticate( this_object() ) )    return u->name();    if( realauth )    return (realauth/":")[0];       case 2: -  if( u = conf->authenticate( this ) ) +  if( u = conf->authenticate( this_object() ) )    return 0;    if( realauth )    return ((realauth/":")[1..])*":";    }    }    int `!( )    {    return !realauth;    }   }    - AuthEmulator auth; + array|AuthEmulator auth;    - string charset_name( function|string what ) - { -  switch( f ) -  { -  case string_to_unicode: return "ISO10646-1"; -  case string_to_utf8: return "UTF-8"; -  default: return upper_case((string)what); -  } - } -  - function charset_function( function|string what, int allow_entities ) - { -  switch( what ) -  { -  case "ISO-10646-1": -  case "ISO10646-1": -  case string_to_unicode: -  return string_to_unicode; -  case "UTF-8": -  case string_to_utf8: -  return string_to_utf8; -  default: -  catch { -  // If current file is "text/html" or "text/xml" we'll use an entity -  // encoding fallback instead of empty string subsitution. -  function fallback_func = -  allow_entities && -  (file->type[0..8] == "text/html" || file->type[0..7] == "text/xml") && -  lambda(string char) { -  return sprintf("&#x%x;", char[0]); -  }; -  return -  Roxen._charset_decoder( Locale.Charset.encoder( (string) what, -  "", fallback_func ) ) -  ->decode; -  }; -  } -  return lambda(string what){return what;}; - } -  - static array(string) join_charset( string old, -  function|string add, -  function oldcodec, -  int allow_entities ) - { -  switch( old&&upper_case(old) ) -  { -  case 0: -  return ({ charset_name( add ), charset_function( add, allow_entities ) }); -  case "ISO10646-1": -  case "UTF-8": -  return ({ old, oldcodec }); // Everything goes here. :-) -  case "ISO-2022": -  return ({ old, oldcodec }); // Not really true, but how to know this? -  default: -  // Not true, but there is no easy way to add charsets yet... -  return ({ charset_name( add ), charset_function( add, allow_entities ) }); -  } - } -  - static array(string) output_encode( string what, int|void allow_entities, -  string|void force_charset ) - { -  if( !force_charset ) -  { -  string charset; -  function encoder; -  -  foreach( output_charset, string|function f ) -  [charset,encoder] = join_charset( charset, f, encoder, allow_entities ); -  -  -  if( !encoder ) -  if( String.width( what ) > 8 ) -  { -  charset = "UTF-8"; -  encoder = string_to_utf8; -  } -  if( encoder ) -  what = encoder( what ); -  return ({ charset, what }); -  } -  else -  return ({ -  0, -  Locale.Charset.encoder( (force_charset/"=")[-1] )->feed( what )->drain() -  }); - } -  +    void decode_map( mapping what, function decoder )   {    foreach( what; mixed q; mixed val )    {    string ni;    if( stringp( q ) )    catch { ni = decoder( q ); };    if( stringp( val ) )    catch { val = decoder( val ); };    else if( arrayp( val ) )
Roxen.git/server/plugins/protocols/http.pike:240:    return q;    } ));    what[ni] = val;    if( q != ni )    m_delete( what, q );    }   }      void decode_charset_encoding( string|function(string:string) decoder )   { +  if( misc->request_charset_decoded ) +  return;    if(stringp(decoder))    decoder = Roxen._charset_decoder(Locale.Charset.decoder(decoder))->decode; -  -  if( misc->request_charset_decoded ) +  if( !decoder )    return;       misc->request_charset_decoded = 1;    -  if( !decoder ) -  return; -  +     string safe_decoder(string s) {    catch { return decoder(s); };    return s;    };       if( prot ) prot = safe_decoder( prot );    if( clientprot ) clientprot = safe_decoder( clientprot );    if( method ) method = safe_decoder( method );    if( rest_query ) rest_query = safe_decoder( rest_query );    if( query ) query = safe_decoder( query );
Roxen.git/server/plugins/protocols/http.pike:377: Inside #if defined(OLD_RXML_CONFIG)
   if(!mod_config) return config;    foreach(mod_config, string m)    if(m[0]=='-')    config[m[1..]]=0;    else    config[m]=1;    return config;    };       void do_send_reply( string what, string url ) { +  CHECK_FD_SAFE_USE;    url = url_base() + url[1..];    my_fd->set_blocking();    my_fd->write( prot + " 302 ChiliMoon config coming up\r\n"+    (what?what+"\r\n":"")+"Location: "+url+"\r\n"    "Connection: close\r\nDate: "+    Roxen.http_date(predef::time(1))+    "\r\nContent-Type: text/html\r\n"    "Content-Length: 1\r\n\r\nx" );    my_fd->close();    my_fd = 0;
Roxen.git/server/plugins/protocols/http.pike:408: Inside #if defined(OLD_RXML_CONFIG)
   "/(%*s)/%s", url) == 2)    url = "/" + url;       do_send_reply(0,Roxen.add_pre_state( url, do_mod_config( prestate ) ));   }   #endif      private static mixed f, line;   private static int hstart;    - class CacheKey { - #if ID_CACHEKEY_DEBUG -  constant __num = ({ 0 }); -  int _num; -  string _sprintf(int t) { -  return t=='O' && sprintf("%O(#%d)", this_program, _num); -  } -  void create() { _num = ++__num[0]; } -  void destroy() { werror("CacheKey(#" + _num + "): --DESTROY--\n" -  "%s\n\n", "" || describe_backtrace(backtrace())); } - #endif - } -  +    //! Parse a cookie string.   //!   //! @param contents   //! HTTP transport-encoded cookie header value.   //!   //! @returns   //! Returns the resulting current cookie mapping.   mapping(string:string) parse_cookies( string contents )   {    if(!contents)    return cookies;    -  + // misc->cookies += ({contents});    foreach(((contents/";") - ({""})), 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;   #ifdef OLD_RXML_CONFIG
Roxen.git/server/plugins/protocols/http.pike:614:    }    if( !res )    {    TIMER_END(parse_got);    return 0; // Not enough data    }    data = res[0];    line = res[1];    request_headers = res[2];    } +  TIMER_END(parse_got);    return parse_got_2();   }      private final int parse_got_2( )   { -  +  TIMER_START(parse_got_2); +  TIMER_START(parse_got_2_parse_line);    string trailer, trailer_trailer;    multiset (string) sup;    string a, b, s="", linename, contents;    array(string) sl = line / " ";    switch( sizeof( sl ) )    {    default:    sl = ({ sl[0], sl[1..sizeof(sl)-2]*" ", sl[-1] });    /* FALL_THROUGH */   
Roxen.git/server/plugins/protocols/http.pike:660:    f = sl[-1];    if( sizeof( sl ) == 1 )    sscanf( method, "%s%*[\r\n]", method );       clientprot = prot = "HTTP/0.9";    if(method != "PING")    method = "GET"; // 0.9 only supports get.    else    {    my_fd->write("PONG\r\n"); +  TIMER_END(parse_got_2);    return 2;    }    s = data = ""; // no headers or extra data...    sscanf( f, "%s%*[\r\n]", f );    misc->no_proto_cache = 1;    break;       case 0:    /* Not reached */    break;    } -  +  TIMER_END(parse_got_2_parse_line);    REQUEST_WERR(sprintf("HTTP: request line %O", line));    REQUEST_WERR(sprintf("HTTP: headers %O", request_headers));    REQUEST_WERR(sprintf("HTTP: data (length %d) %O", strlen(data),data));    raw_url = f;    time = predef::time(1);    // if(!data) data = "";    //REQUEST_WERR(sprintf("HTTP: raw_url %O", raw_url));       if(!remoteaddr)    {    if(my_fd) {    remoteaddr = my_fd->query_address();    if(remoteaddr)    sscanf(remoteaddr, "%s %*s", remoteaddr);    }    if(!remoteaddr) {    REQUEST_WERR("HTTP: No remote address."); -  TIMER_END(parse_got); +  TIMER_END(parse_got_2);    return 2;    }    }    -  +  TIMER_START(parse_got_2_parse_headers);    foreach( (array)request_headers, [string linename, array|string contents] )    {    if( arrayp(contents) ) contents = contents[0];    switch (linename)    {    case "cache-control": // Opera sends "no-cache" here.    case "pragma": pragma|=(multiset)((contents-" ")/","); break;       case "content-length": misc->len = (int)contents; break;    case "authorization": rawauth = contents; break;    case "referer": referer = ({contents}); break;    case "if-modified-since": since=contents; break; -  +  case "if-match": break; // Not supported yet. +  case "if-none-match": +  none_match = (multiset)((contents-" ")/","); +  break;       case "proxy-authorization":    array y;    y = contents / " ";    if(sizeof(y) < 2)    break;    y[1] = decode(y[1]);    misc->proxyauth=y;    break;   
Roxen.git/server/plugins/protocols/http.pike:741:    case "range":    contents = lower_case(contents-" ");    if(!misc->range && has_prefix(contents, "bytes"))    // Only care about "byte" ranges. Also the Request-Range header    // has precedence since Stupid Netscape (TM) sends both but can't    // handle multipart/byteranges but only multipart/x-byteranges.    // Duh!!!    misc->range = contents[6..];    break;    -  -  case "host": +     case "connection": -  +  misc->client_connection = (<@(lower_case(contents)/" " - ({""}))>); +  if (misc->client_connection->close) { +  misc->connection = "close"; +  } else if (misc->client_connection["keep-alive"]) { +  misc->connection = "keep-alive"; +  } +  break; +  case "host":    misc[linename] = lower_case(contents);    break;    case "content-type":    misc[linename] = contents;    break; -  +  case "destination": +  misc["new-uri"] = contents; +  if (mixed err = catch { +  misc["new-uri"] = Standards.URI(contents)->path; +  }) { + #ifdef DEBUG +  report_debug(sprintf("Destination header contained a bad URI: %O\n" +  "%s", contents, describe_error(err))); + #endif /* DEBUG */    } -  +  break;    } -  +  } +  TIMER_END(parse_got_2_parse_headers); +  TIMER_START(parse_got_2_more_data);    if(misc->len)    {    if(!data) data="";    int l = misc->len;    wanted_data=l;    have_data=strlen(data);       if(strlen(data) < l)    {    REQUEST_WERR(sprintf("HTTP: More data needed in %s.", method)); -  TIMER_END(parse_got); +  ready_to_receive(); +  TIMER_END(parse_got_2);    return 0;    }    leftovers = data[l+2..];    data = data[..l+1];    -  if (method == "POST") { +  switch(method) { +  case "POST":    switch(lower_case((((misc["content-type"]||"")+";")/";")[0]-" "))    {    default:    // Normal form data.    string v;       // Ok.. This might seem somewhat odd, but IE seems to add a    // (spurious) \r\n to the end of the data, and some versions of    // opera seem to add (spurious) \r\n to the start of the data.    //
Roxen.git/server/plugins/protocols/http.pike:824:    if(part->headers["content-type"])    real_variables[part->disp_params->name+".mimetype"] +=    ({ part->headers["content-type"] });    }    stash_body_parts = real_variables + ([ ]);    }    break;    }    }    } +  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"    "\r\n"); -  +  TIMER_END(parse_got_2);    return 2;    }    } -  TIMER_END(parse_got); +  TIMER_END(parse_got_2);    return 3; // Done.   }      int get_max_cache()   {    return misc->cacheable;   }      int set_max_cache( int t )   {    int ot = misc->cacheable;    misc->cacheable = t;    return ot;   }      void disconnect()   {    file = 0;    conf && conf->connection_drop( this ); - #ifdef REQUEST_DEBUG -  if (my_fd) +  if (my_fd) {    MARK_FD("HTTP my_fd in HTTP disconnected?"); - #endif +  my_fd->close(); +  my_fd = 0; +  }    MERGE_TIMERS(conf);    if(do_not_disconnect) return;    destruct();   }    - void end(int|void keepit) + static void cleanup_request_object()   {    if( conf )    conf->connection_drop( this ); -  + } +  + void end(int|void keepit) + { +  CHECK_FD_SAFE_USE; +  +  cleanup_request_object(); +     if(keepit    && !file->raw -  && (misc->connection == "keep-alive" || -  (prot == "HTTP/1.1" && misc->connection != "close")) +  && misc->connection != "close" +  && ((prot == "HTTP/1.1") || (misc->connection == "keep-alive"))    && my_fd -  +  // Is this necessary now when this function no longer is called +  // from the close callback? /mast    && !catch(my_fd->query_address()) )    {    // Now.. Transfer control to a new http-object. Reset all variables etc.. -  this_program o = this_program(0, 0, 0); +  object o = object_program(this)(0, 0, 0);    o->remoteaddr = remoteaddr;    o->client = client;    o->supports = supports;    o->client_var = client_var;    o->host = host;    o->conf = conf;    o->pipe = pipe; -  MARK_FD("HTTP kept alive"); +  o->connection_misc = connection_misc; +  o->kept_alive = kept_alive+1;    object fd = my_fd;    my_fd=0;    o->chain(fd,port_obj,leftovers);    pipe = 0;    disconnect();    return;    }    -  +  data_buffer = 0;    pipe = 0; -  if(objectp(my_fd)) -  { -  MARK_FD("HTTP closed"); -  mixed err = catch -  { -  // Don't set to blocking mode if SSL. -  if (!my_fd->CipherSpec) { -  my_fd->set_blocking(); -  } -  my_fd->close(); -  destruct(my_fd); -  }; - #ifdef DEBUG -  if (err) report_debug ("Close failure (1): %s", describe_backtrace (err)); - #endif -  err = catch { -  my_fd = 0; -  }; - #ifdef DEBUG -  if (err) report_debug ("Close failure (2): %s", describe_backtrace (err)); - #endif -  } +     disconnect();   }      static void do_timeout()   {    int elapsed = predef::time(1)-time;    if(time && elapsed >= 30)    {    REQUEST_WERR("HTTP: Connection timed out. Closing.");    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)   {    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/'><img border='0' src='/internal-roxen-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>      ";   }    - string format_backtrace(int eid) + static string get_err_md5(array(string|array(string)|array(array)) err_info)   { -  array info = cache_lookup( "http_bt_error", eid); -  if(!info) -  return error_page_header("Unregistered Error"); -  [string msg, array(string) rxml_bt, array(array) bt, -  string raw_bt_descr, string raw_url, string raw] = info; +  if (err_info) { +  return String.string2hex(Crypto.MD5.hash(err_info[3])); +  } +  return "NONE"; + }    -  + static array(string|array(string)|array(array)) get_err_info(int eid, +  string|void md5) + { +  array(string|array(string)|array(array)) err_info = +  core.query_var ("errors")[eid];    -  +  if (!err_info || +  (md5 && (md5 != get_err_md5(err_info)))) { +  // Extra safety... +  return 0; +  } +  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"); +  } +  +  [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";       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";    }   
Roxen.git/server/plugins/protocols/http.pike:1001:    (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>", "&nbsp;"})) +    "</li>\n";    }    res += "</ul>\n\n";    }    -  res += ("<p><b><a href=\"/(old_error,plain)/error/?error="+eid+"\">" +  res += ("<p><b><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>";   }      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()?
Roxen.git/server/plugins/protocols/http.pike:1033:    return "No backtrace";    if(sscanf(what, "%suthorization:%s\n%s", a, b, c)==3)    return a+"uthorization: ################ (censored)\n"+c;    return what;   }      int store_error(mixed _err)   {    mixed err = _err;    _err = 0; // hide in backtrace, they are bad enough anyway... +  mapping e = core.query_var("errors"); +  if(!e) core.set_var("errors", ([])); +  e = core.query_var("errors"); /* threads... */    -  int id; -  do { -  id = random( (1<<64)-1 ); // 64 bit random number -  } while(!cache_lookup("http_bt_error", id)); +  int id = ++e[0]; +  if(id>1024) id = 1;       string msg;    array(string) rxml_bt;       if (!err) msg = "Unknown error";    else {    msg = describe_error (err);    // Ugly, but it's hard to fix it better..    int i = search (msg, "\nRXML frame backtrace:\n");    if (i >= 0) {
Roxen.git/server/plugins/protocols/http.pike:1105:    descr = func + "()";    }    else if (stringp (ent)) descr = ent;    else if (catch (descr = sprintf ("%O", ent)))    descr = "???";    bt += ({({file, line, func, descr})});    }    }       add_cvs_ids (err); -  cache_set( "http_bt_error", id, ({msg,rxml_bt,bt,describe_backtrace (err),raw_url,censor(raw)}) ); +  e[id] = ({msg,rxml_bt,bt,describe_backtrace (err),raw_url,censor(raw)});    return id;   }    - array get_error(string eid) + array get_error(string eid, string md5)   { -  return cache_lookup( "http_bt_error", (int)eid ); +  mapping e = core.query_var("errors"); +  if(e) { +  array r = e[(int)eid]; +  if (r && md5 == String.string2hex(Crypto.MD5.hash(r[3]))) { +  return r;    } -  +  } +  return 0; + }         void internal_error(array _err)   {    misc->no_proto_cache = 1;    mixed err = _err;    _err = 0; // hide in backtrace, they are bad enough anyway...    array err2;    if(port_obj && port_obj->query("show_internals"))    {
Roxen.git/server/plugins/protocols/http.pike:1146:    "fulfill your query, due to an internal error.</h1>");    }    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 + // call to internal_error() fails. That happens eg when "this" has been   // destructed.   #define INTERNAL_ERROR(err) do { \    if (mixed __eRr = catch (internal_error (err))) \ -  report_error("Internal server error: " + describe_backtrace(err) + \ -  "internal_error() also failed: "+describe_backtrace(__eRr));\ -  } while(0) +  report_error("Internal server error: " + describe_backtrace(err) + \ +  "internal_error() also failed: " + describe_backtrace(__eRr)); \ +  } while (0)      int wants_more()   {    return !!cache;   }      void do_log( Shuffler.Shuffle r, int reason )   {    MARK_FD("HTTP logging"); // fd can be closed here    int fsent = r->sent_data();
Roxen.git/server/plugins/protocols/http.pike:1178:    file->len += misc->_log_cheat_addition;    conf->log(file, this);    }       if( !port_obj )    {    TIMER_END(do_log);    MERGE_TIMERS(conf);    if( conf )    conf->connection_drop( this ); -  mixed err = catch // paranoia -  { -  my_fd->close(); -  destruct( my_fd ); -  destruct( ); -  }; - #ifdef DEBUG -  if (err) report_debug ("Close failure (3): %s", describe_backtrace (err)); - #endif +  call_out (disconnect, 0);    return;    }    TIMER_END(do_log);    end(1);    return;   }      #ifdef FD_DEBUG   void timer(int start)   {
Roxen.git/server/plugins/protocols/http.pike:1414:    } else    // Invalid syntax again...    return 0;    }    return ranges;   }      // Tell the client that it can start sending some more data   void ready_to_receive()   { -  if (clientprot == "HTTP/1.1" && request_headers->Expect && -  (request_headers->Expect == "100-continue" || -  has_value(request_headers->Expect, "100-continue" ))) +  // FIXME: Only send once? +  if (clientprot == "HTTP/1.1" && request_headers->expect && +  (request_headers->expect == "100-continue" || +  has_value(request_headers->expect, "100-continue" )))    my_fd->write("HTTP/1.1 100 Continue\r\n");   }      // Send the result.   void send_result(mapping|void result)   {    TIMER_START(send_result);    -  +  CHECK_FD_SAFE_USE; +     array err;    int tmp; -  mapping heads; +     string head_string="";    if (result)    file = result;   #ifdef PROFILE -  float elapsed = SECHR(HRTIME()-req_time); +  int elapsed = HRTIME()-req_time;    string nid =   #ifdef FILE_PROFILE    (raw_url/"?")[0]   #else    dirname((raw_url/"?")[0])   #endif -  ; +  + "?method="+method;    array p; -  if(!(p=conf->profile_map[nid])) -  p = conf->profile_map[nid] = ({0,0.0,0.0}); +  if(!(p=conf->profile_map[nid])) { +  // ({ count, sum, max }) +  p = conf->profile_map[nid] = ({0, 0, 0}); +  }    p[0]++;    p[1] += elapsed;    if(elapsed > p[2]) p[2]=elapsed;   #endif    -  REQUEST_WERR(sprintf("HTTP: response: prot %O, method %O, file %O, misc: %O", -  prot, method, file, misc)); + #ifdef DEBUG_CACHEABLE +  report_debug("<=== Request for %s returned cacheable %d (proto cache %s).\n", +  raw_url, misc->cacheable, +  misc->no_proto_cache ? "disabled" : "enabled"); + #endif       if( prot == "HTTP/0.9" ) misc->no_proto_cache = 1;       if(!leftovers)    leftovers = data||"";       if(!mappingp(file))    {    misc->no_proto_cache = 1;    if(misc->error_code) -  file = Roxen.http_low_answer(misc->error_code, errors[misc->error]); +  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);    file = 0;    pipe = 0;    if(do_not_disconnect)    return;    my_fd = 0;    return;    }       if(file->type == "raw") file->raw = 1; -  else if(!file->type) file->type="text/plain"; +     }    -  if(!file->raw) +  if(!file->raw && (prot != "HTTP/0.9"))    { -  heads = ([]); -  if (!file->stat) file->stat = misc->stat; -  if(objectp(file->file)) { -  if(!file->stat) -  file->stat = file->file->stat(); -  if (zero_type(misc->cacheable) && file->file->is_file) { -  // Assume a cacheablity on the order of the age of the file. -  misc->cacheable = (predef::time(1) - file->stat[ST_MTIME])/4; +  if (!sizeof (file) && multi_status) +  file = multi_status->http_answer(); +  +  if (file->error == Protocols.HTTP.HTTP_NO_CONTENT) { + #if 0 +  // We actually give some content cf comment below. +  file->len = 2; +  file->data = "\r\n"; + #else +  file->len = 0; +  file->data = ""; + #endif /* 0 */    } -  } +     -  if( Stat fstat = file->stat ) -  { -  if( !file->len ) -  file->len = fstat[1]; -  -  if ( fstat[ST_MTIME] > misc->last_modified ) -  misc->last_modified = fstat[ST_MTIME]; +  string head_status = file->rettext; +  if (head_status) { +  if (!file->file && !file->data && +  (!file->type || file->type == "text/html")) { +  // If we got no body then put the message there to make it +  // more visible. +  file->data = "<html><body>" + +  replace (Roxen.html_encode_string (head_status), "\n", "<br />\n") + +  "</body></html>"; +  file->len = sizeof (file->data); +  file->type = "text/html";    } -  +  if (has_value (head_status, "\n")) +  // Fold lines nicely. +  head_status = map (head_status / "\n", String.trim_all_whites) * " "; +  }    -  if( !zero_type(misc->cacheable) && -  misc->cacheable < INITIAL_CACHEABLE ) { -  if (misc->cacheable == 0) { -  heads["Expires"] = Roxen.http_date( 0 ); +  mapping(string:string) heads = make_response_headers (file);    -  if (misc->cacheable < INITIAL_CACHEABLE) { -  // Data with expiry is assumed to have been generated at -  // the same instant. -  misc->last_modified = predef::time(1); +  if (file->error == 200) { +  int conditional; +  if (none_match) { +  // NOTE: misc->etag may be zero below, but that's ok. +  if (none_match[misc->etag] || (misc->etag && none_match["*"])) { +  // We have a if-none-match header that matches our etag. +  if ((<"HEAD", "GET">)[method]) { +  // RFC 2616 14.26: +  // Instead, if the request method was GET or HEAD, the server +  // SHOULD respond with a 304 (Not Modified) response, including +  // the cache- related header fields (particularly ETag) of one +  // of the entities that matched. For all other request methods, +  // the server MUST respond with a status of 412 (Precondition +  // Failed). +  conditional = 304; +  } else { +  conditional = 412;    } -  +  } else { +  conditional = -1;    } -  else -  heads["Expires"] = Roxen.http_date( predef::time(1)+misc->cacheable ); +     } -  -  if (misc->last_modified) -  heads["Last-Modified"] = Roxen.http_date(misc->last_modified); -  -  if(since && (!file->error || file->error == 200) && misc->last_modified) +  if(since && misc->last_modified && (conditional >= 0))    { -  // ({ time, len }) +  /* ({ time, len }) */    array(int) since_info = Roxen.parse_since( since ); -  -  if ( (since_info[0] >= misc->last_modified) && -  ((since_info[1] == -1) || (since_info[1] == file->len)) + // werror("since: %{%O, %}\n" + // "lm: %O\n" + // "cacheable: %O\n", + // since_info, + // misc->last_modified, + // misc->cacheable); +  if ( ((since_info[0] >= misc->last_modified) && +  ((since_info[1] == -1) || (since_info[1] == file->len)))    // never say 'not modified' if cacheable has been lowered.    && (zero_type(misc->cacheable) || -  misc->cacheable >= INITIAL_CACHEABLE) ) +  (misc->cacheable >= INITIAL_CACHEABLE)) +  // actually ok, or... + // || ((misc->cacheable>0) + // && (since_info[0] + misc->cacheable<= predef::time(1)) + // // cacheable, and not enough time has passed. +  )    { -  file->error = 304; -  file->file = 0; -  file->data=""; +  conditional = conditional || 304; +  } else { +  conditional = -1;    }    } -  -  if(prot != "HTTP/0.9") -  { -  string h, charset=""; -  -  if( stringp(file->data) ) -  { -  if (file["type"][0..4] == "text/") -  { -  [charset,file->data] = output_encode( file->data, 1 ); -  if( charset && has_value(file["type"], "; charset=") ) -  charset = "; charset="+charset; -  else -  charset = ""; -  } -  file->len = strlen(file->data); -  } -  heads["Content-Type"] = file["type"]+charset; -  heads["Accept-Ranges"] = "bytes"; -  heads["Server"] = replace(version(), " ", "·"); -  if( misc->connection ) -  heads["Connection"] = misc->connection; -  -  if(file->encoding) heads["Content-Encoding"] = file->encoding; -  -  if(!file->error) -  file->error=200; -  -  heads->Date = Roxen.http_date(predef::time(1)); -  if(file->expires) -  heads->Expires = Roxen.http_date(file->expires); -  -  if(mappingp(file->extra_heads)) -  heads |= file->extra_heads; -  -  if(mappingp(misc->moreheads)) -  heads |= misc->moreheads; -  -  if(misc->range && file->len && objectp(file->file) && !file->data && -  file->error == 200 && (method == "GET" || method == "HEAD")) +  if (conditional > 0) { +  // All conditionals apply. +  file->error = conditional; +  file->file = file->data = file->len = 0; +  } else if(misc->range && file->len && objectp(file->file) && +  !file->data && (method == "GET" || method == "HEAD"))    // Plain and simple file and a Range header. Let's play.    // Also we only bother with 200-requests. Anything else should be    // nicely and completely ignored. Also this is only used for GET and    // HEAD requests.    {    // split the range header. If no valid ranges are found, ignore it.    // If one is found, send that range. If many are found we need to    // use a wrapper and send a multi-part message.    array ranges = parse_range_header(file->len);    if(ranges) // No incorrect syntax...    {    misc->no_proto_cache = 1;    if(sizeof(ranges)) // And we have valid ranges as well.    {    file->error = 206; // 206 Partial Content    if(sizeof(ranges) == 1)    {    heads["Content-Range"] = sprintf("bytes %d-%d/%d",    @ranges[0], file->len); -  file->start = ranges[0][0]; +  file->file->seek(ranges[0][0]); +  if(ranges[0][1] == (file->len - 1) && +  GLOBVAR(RestoreConnLogFull)) +  // Log continuations (ie REST in FTP), 'range XXX-' +  // using the entire length of the file, not just the +  // "sent" part. Ie add the "start" byte location when logging +  misc->_log_cheat_addition = ranges[0][0];    file->len = ranges[0][1] - ranges[0][0]+1;    } else {    // Multiple ranges. Multipart reply and stuff needed.    // We do this by replacing the file object with a wrapper.    // Nice and handy.    file->file = MultiRangeWrapper(file, heads, ranges, this);    }    } else { -  // Got the header, but the specified ranges was out of bounds. +  // Got the header, but the specified ranges were out of bounds.    // Reply with a 416 Requested Range not satisfiable.    file->error = 416;    heads["Content-Range"] = "*/"+file->len;    if(method == "GET") { -  file->data = "The requested byte range is out-of-bounds. Sorry."; -  file->len = strlen(file->data); -  file->file = 0; +  file->file = file->data = file->type = file->len = 0;    }    }    }    } -  head_string = sprintf("%s %d %s\r\n", -  prot, file->error, -  file->rettext ||errors[file->error]||""); +  }    - // if( file->len > 0 || (file->error != 200) ) +  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, doesn't trust a zero +  // 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";    } -  +     if( mixed err = catch( head_string += Roxen.make_http_headers( heads ) ) )    {   #ifdef DEBUG -  report_debug("Roxen.make_http_headers failed: " + -  describe_error(err)); +  report_debug ("Roxen.make_http_headers failed: " + +  describe_error (err));   #endif -  foreach( heads; string x; mixed head ) -  if( stringp( head ) ) -  head_string += x+": "+head+"\r\n"; -  else if( arrayp( head ) ) -  foreach( head, string xx ) +  foreach(heads; string x; string|array(string) val) { +  if (stringp(val)) +  head_string += x+": "+val+"\r\n"; +  else if( arrayp( val ) ) +  foreach( val, string xx )    head_string += x+": "+xx+"\r\n";    else if( catch { -  head_string += x+": "+(string)head; +  head_string += x+": "+(string)val+"\r\n";    } )    error("Illegal value in headers array! "    "Expected string or array(string)\n"); -  +  }    head_string += "\r\n";    }    -  if( strlen( charset ) || String.width( head_string ) > 8 ) +  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 += strlen(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!=304) ) +  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 + strlen( head_string )) <    conf->datacache->max_file_size)    && misc->cachekey )    {    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, -  +  "etag":misc->etag,    "callbacks":misc->_cachecallbacks,    "len":file->len,    // fix non-keep-alive when sending from cache    "raw":file->raw,    "error":file->error,    "mtime":(file->stat && file->stat[ST_MTIME]),    "rf":realfile,    ]),    misc->cacheable );    file = ([ "data":data, "raw":file->raw, "len":strlen(data) ]); -  MY_TRACE_LEAVE (""); +     }    }   #endif -  if(strlen(head_string)) -  send(head_string); -  if(file->data && strlen(file->data)) -  send(file->data, file->len, file->start); -  if(file->file) -  send(file->file, file->len, file->start); +  if(!kept_alive && +  (file->len > 0) && +  ((sizeof(head_string) + file->len) < (HTTP_BLOCKING_SIZE_THRESHOLD))) +  { +  // The first time we get a request, the output buffers will +  // be empty. We can thus just do a single blocking write() +  // if the data will fit in the output buffer (usually 4KB). +  int s; +  TIMER_END(send_result); +  TIMER_START(blocking_write); +  string data = head_string; +  if (file->data) +  data += file->data[..file->len-1]; +  if (file->file) +  data += file->file->read(file->len); + #ifdef CONNECTION_DEBUG +  werror ("HTTP: Response =================================================\n" +  "%s\n", +  replace (sprintf ("%O", data), +  ({"\\r\\n", "\\n", "\\t"}), +  ({"\n", "\n", "\t"}))); + #else +  REQUEST_WERR (sprintf ("HTTP: Send blocking %O", data)); + #endif +  s = my_fd->write(data); +  TIMER_END(blocking_write); +  return;    } -  +  if(strlen(head_string)) send(head_string); +  if(file->data && strlen(file->data)) send(file->data, file->len); +  if(file->file) send(file->file, file->len); +  }    else    { -  +  if( 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 +  return; +  }    send(head_string);    file->len = 1; // Keep those alive, please...    }    TIMER_END(send_result);    start_sender();   }    -  +    // Execute the request   void handle_request( )   {    REQUEST_WERR("HTTP: handle_request()");    TIMER_START(handle_request);   #ifdef MAGIC_ERROR    if(prestate->old_error)    { -  array err = get_error(variables->error); +  array err = get_error(variables->error, variables->error_md5 || "NONE");    if(err && arrayp(err))    {    if(prestate->plain)    {    file = ([    "type":"text/plain",    "data":generate_bugreport( @err ),    ]);    TIMER_END(handle_request);    send_result();
Roxen.git/server/plugins/protocols/http.pike:1767:    INTERNAL_ERROR( e );       else {    if (result && result->pipe)    // Could be destructed here already since handle_request might    // have handed over us to another thread that finished quickly.    return;    file = result;    }    -  if( file ) -  if( file->try_again_later ) +  if( file && file->try_again_later )    {    if( objectp( file->try_again_later ) )    ;    else    call_out( core.handle, file->try_again_later, handle_request );    return;    }       TIMER_END(handle_request);    send_result();
Roxen.git/server/plugins/protocols/http.pike:1805:    cached_url_base = port_obj->prot_name + "://" + host;    else    if (scanres < 2)    cached_url_base = port_obj->prot_name + "://" + host + ":" + port;    else    cached_url_base = port_obj->prot_name + "://" + tmp;    }       // Then use the port object.    else { -  string host = port_obj->conf_data[conf]->hostname; +  string host = (port_obj->conf_data[conf] || +  (["hostname":"*"]))->hostname;    if (host == "*")    if (conf && sizeof (host = conf->get_url()) &&    sscanf (host, "%*s://%[^:/]", host) == 2) {    // Use the hostname in the configuration url.    }    else    // Fall back to the numeric ip.    host = port_obj->ip;    cached_url_base = port_obj->prot_name + "://" + host;    if (port_obj->port != port_obj->default_port)
Roxen.git/server/plugins/protocols/http.pike:1832:    return cached_url_base;   }      /* We got some data on a socket.    * =================================================    */   int processed;   // array ccd = ({});   void got_data(mixed fooid, string s)   { + #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       if(wanted_data)    { -  +  // NOTE: No need to make a data buffer if it's a small request.    if(strlen(s) + have_data < wanted_data)    {    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);
Roxen.git/server/plugins/protocols/http.pike:1873:    if (mixed err = catch {    MARK_FD("HTTP got data");    raw += s;       // The port has been closed, but old (probably keep-alive)    // connections remain. Close those connections.    if( !port_obj )    {    if( conf )    conf->connection_drop( this ); -  mixed err = catch // paranoia -  { -  my_fd->set_blocking(); -  my_fd->close(); -  destruct( my_fd ); -  destruct( ); -  }; - #ifdef DEBUG -  if (err) report_debug ("Close failure (4): %s", describe_backtrace (err)); - #endif +  MARK_FD ("HTTP: Port closed."); +  call_out (disconnect, 0);    return;    }       switch( parse_got( s ) )    {    case 0:    REQUEST_WERR("HTTP: Request needs more data.");    return;       case 1:
Roxen.git/server/plugins/protocols/http.pike:1907:    "\r\n");    end();    return; // Stupid request.       case 2:    REQUEST_WERR("HTTP: Done.");    end();    return;    }    -  if( (< "GET", "HEAD" >)[method] ) + #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); + #endif +  }       TIMER_START(find_conf);    string path;    if( !conf || !(path = port_obj->path )    || (sizeof( path )    && raw_url[..sizeof(path) - 1] != path) )       {    // FIXME: port_obj->name & port_obj->default_port are constant    // consider caching them?
Roxen.git/server/plugins/protocols/http.pike:1988:    misc->proxyauth    = conf->auth_module->auth(misc->proxyauth, this);    }    }    if( conf )    {    conf->connection_add( this, ([]) );    conf->received += strlen(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" &&
Roxen.git/server/plugins/protocols/http.pike:2066: Inside #if defined(RAM_CACHE)
   {    string a, b;    if( sscanf( headers, "%sDate: %*s\n%s", a, b ) == 3 )    return a+"Date: "+Roxen.http_date( predef::time(1) ) +"\r\n"+b;    return headers;    };       MY_TRACE_LEAVE ("Using entry from ram cache");    conf->hsent += strlen(file->hs);    cache_status["protcache"] = 1; +  if( strlen( d ) < (HTTP_BLOCKING_SIZE_THRESHOLD) ) +  {    TIMER_END(cache_lookup); -  +  } +  else +  { +  TIMER_END(cache_lookup);    send( fix_date(file->hs)+d );    start_sender( ); -  +  }    return;    }   #ifndef RAM_CACHE_ASUME_STATIC_CONTENT    else    MY_TRACE_LEAVE (    sprintf ("Entry out of date (disk: %s, cache: mtime %d)",    st ? "mtime " + st->mtime : "gone", file->mtime));   #endif    } else    misc->no_proto_cache = 1; // Never cache in this case.    file = 0;    }    }    TIMER_END(cache_lookup);   #endif // RAM_CACHE    TIMER_START(parse_request);    if( things_to_do_when_not_sending_from_cache( ) )    return; -  +  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);    - #ifdef THREADS +     REQUEST_WERR("HTTP: Calling core.handle().");    core.handle(handle_request); - #else -  handle_request(); - #endif +     })    {    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() + object clone_me()   { -  this_program c=this_program(0, port_obj, conf); +  object c=object_program(this)(0, port_obj, conf);   #ifdef ID_OBJ_DEBUG    werror ("clone %O -> %O\n", this, c);   #endif       c->port_obj = port_obj;    c->conf = conf;    c->root_id = root_id;    c->time = time;    c->raw_url = raw_url;   
Roxen.git/server/plugins/protocols/http.pike:2138:    c->client_var = client_var;       c->remoteaddr = remoteaddr;    c->host = host;       c->client = client;    c->referer = referer;    c->pragma = pragma;       c->cookies = cookies; -  c->request_headers = request_headers; +  c->request_headers = request_headers + ([]);    c->my_fd = 0;    c->prot = prot;    c->clientprot = clientprot;    c->method = method;      // realfile virtfile // Should not be copied.    c->rest_query = rest_query;    c->raw = raw;    c->query = query;    c->not_query = not_query;
Roxen.git/server/plugins/protocols/http.pike:2173:    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);    my_fd = f; +  CHECK_FD_SAFE_USE;    MARK_FD("HTTP connection");    if( c ) port_obj = c;    if( cc ) conf = cc;    time = predef::time(1);    call_out(do_timeout, 90);    }    root_id = this;   }      void chain( object f, object c, string le )   {    my_fd = f;    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 ( strlen( le ) ) +  if ( le && strlen( le ) )    got_data( 0,le );    else    {    // If no pipelined data is available, call out...    remove_call_out(do_timeout);    call_out(do_timeout, 90);    }       if(!my_fd)    {    if(do_not_disconnect == -1)    {    do_not_disconnect=0;    disconnect();    }    }    else    {    if(do_not_disconnect == -1)    do_not_disconnect = 0; -  if(!processed) -  f->set_nonblocking(got_data, f->query_write_callback(), end); +  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